Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .github/rulesets/protect-openshell-next.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"name": "protect-openshell-next",
"target": "branch",
"enforcement": "active",
"conditions": { "ref_name": { "include": ["refs/heads/openshell-next"], "exclude": [] } },
"rules": [
{ "type": "deletion" },
{ "type": "non_fast_forward" }
],
"bypass_actors": []
}
2 changes: 2 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ on:
push:
branches: [main]
pull_request:
# Lets .github/workflows/openshell-next.yml run CI on the commits it pushes (GITHUB_TOKEN pushes trigger nothing else).
workflow_dispatch:

permissions:
contents: read
Expand Down
193 changes: 193 additions & 0 deletions .github/workflows/openshell-next.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
name: openshell-next

# Keeps the long-lived `openshell-next` branch building against the newest NVIDIA/OpenShell tag, prereleases included,
# so the next OpenShell release is integrated (and any breakage fixed) before it ships as stable.
# Daily: merge main into openshell-next, bump the OpenShell pin when upstream has a newer tag, run the tests, push, and
# report on the tracking issue (label `openshell-next`). See docs/DEVELOPING.md ("Tracking the next OpenShell release").
#
# Pushes made with GITHUB_TOKEN do not trigger other workflows, so this job runs the test suite itself and then
# dispatches ci.yml on the pushed commit (workflow_dispatch is the one event GITHUB_TOKEN may start), which puts the
# required `check` / `audit` results on the tracking PR's head commit.
# Local equivalent: scripts/openshell-next.sh sync && scripts/openshell-next.sh update

on:
schedule:
- cron: "17 4 * * *" # daily 04:17 UTC
workflow_dispatch:
inputs:
tag:
description: "OpenShell tag to pin (default: the newest upstream tag). Applied only when newer than the current pin."
required: false
default: ""
report:
description: "Comment on the tracking issue even when nothing changed"
type: boolean
default: false

permissions:
contents: read

concurrency:
group: openshell-next
cancel-in-progress: false

env:
CARGO_TERM_COLOR: always
BRANCH: openshell-next
UPSTREAM: https://github.com/NVIDIA/OpenShell

jobs:
track:
runs-on: ubuntu-latest
timeout-minutes: 60
permissions:
contents: write # push the main merge / pin bump to openshell-next (never to main)
issues: write # comment on the tracking issue
actions: write # dispatch ci.yml on the pushed commit
pull-requests: read # find the tracking PR
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0

- name: Check out openshell-next (created from main if missing)
id: branch
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
if git ls-remote --exit-code --heads origin "$BRANCH" >/dev/null; then
git checkout -B "$BRANCH" "origin/$BRANCH"
echo "created=false" >> "$GITHUB_OUTPUT"
else
git checkout -B "$BRANCH" origin/main
echo "created=true" >> "$GITHUB_OUTPUT"
fi
echo "before=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"

- name: Install Rust
run: rustup toolchain install 1.94 --profile minimal --component rustfmt,clippy && rustup default 1.94

- uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
with:
path: |
~/.cargo/registry
~/.cargo/git
target
key: ${{ runner.os }}-openshell-next-${{ hashFiles('Cargo.lock') }}
restore-keys: ${{ runner.os }}-openshell-next-

- name: Merge main into openshell-next
id: sync
run: scripts/openshell-next.sh sync origin/main 2>&1 | tee "$RUNNER_TEMP/sync.log"

- name: Bump to the newest OpenShell tag
id: bump
env:
WANT: ${{ inputs.tag }}
run: |
scripts/openshell-next.sh update "$WANT" | tee "$RUNNER_TEMP/update.out"
if ! git diff --quiet; then
tag=$(sed -n 's/^tag=//p' "$RUNNER_TEMP/update.out")
prev=$(sed -n 's/^previous=//p' "$RUNNER_TEMP/update.out")
git commit -a --signoff -m "chore(deps): track OpenShell $tag on openshell-next" \
-m "Automated by .github/workflows/openshell-next.yml. Upstream changes: $UPSTREAM/compare/$prev...$tag"
fi

- name: Test (fmt, clippy, test, docs)
id: test
continue-on-error: true
run: |
set -o pipefail
{
cargo fmt --check &&
cargo clippy --all-targets --locked -- -D warnings &&
cargo test --locked &&
scripts/gen-docs.sh --check
} 2>&1 | tee "$RUNNER_TEMP/test.log"

# The branch mirrors upstream, so a bump is pushed even when the tests fail: the fix is then made on top of it.
- name: Push openshell-next
id: push
env:
BEFORE: ${{ steps.branch.outputs.before }}
CREATED: ${{ steps.branch.outputs.created }}
run: |
if [ "$CREATED" = true ] || [ "$(git rev-parse HEAD)" != "$BEFORE" ]; then
git push origin "HEAD:refs/heads/$BRANCH"
echo "pushed=true" >> "$GITHUB_OUTPUT"
else
echo "nothing new to push"
echo "pushed=false" >> "$GITHUB_OUTPUT"
fi

- name: Run CI on the pushed commit
if: steps.push.outputs.pushed == 'true'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: gh workflow run ci.yml --ref "$BRANCH"

- name: Report on the tracking issue
if: always()
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SYNC: ${{ steps.sync.outcome }}
TEST: ${{ steps.test.outcome }}
CHANGED: ${{ steps.bump.outputs.changed }}
PREV: ${{ steps.bump.outputs.previous }}
TAG: ${{ steps.bump.outputs.tag }}
STABLE: ${{ steps.bump.outputs.stable }}
CREATED: ${{ steps.branch.outputs.created }}
PUSHED: ${{ steps.push.outputs.pushed }}
FORCE_REPORT: ${{ inputs.report }}
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
REPO_URL: ${{ github.server_url }}/${{ github.repository }}
run: |
icon() { case "$1" in success) echo "✅ passed" ;; failure) echo "❌ failed" ;; *) echo "⏭️ skipped" ;; esac; }
if [ "$CHANGED" != true ] && [ "$SYNC" = success ] && [ "$TEST" = success ] && [ "$CREATED" != true ] && [ "$FORCE_REPORT" != true ]; then
echo "pin unchanged (${TAG:-?}) and everything passed: nothing to report" | tee -a "$GITHUB_STEP_SUMMARY"
exit 0
fi
sha=$(git rev-parse HEAD)
pr=$(gh pr list --head "$BRANCH" --base main --state open --json url --jq '.[0].url // empty')
{
if [ "$CHANGED" = true ]; then echo "### OpenShell \`$TAG\` is now pinned on \`$BRANCH\`"; else echo "### \`$BRANCH\` check (pin \`${TAG:-unknown}\`)"; fi
echo
echo "| | |"
echo "|---|---|"
if [ "$CHANGED" = true ]; then
echo "| Pin | \`$PREV\` → \`$TAG\` |"
echo "| Upstream | [release]($UPSTREAM/releases/tag/$TAG) · [changes since \`$PREV\`]($UPSTREAM/compare/$PREV...$TAG) |"
fi
echo "| Merge of main | $(icon "$SYNC") |"
echo "| Tests (fmt, clippy, test, docs) | $(icon "$TEST") |"
echo "| Branch | [\`$BRANCH\` @ \`${sha:0:12}\`]($REPO_URL/commit/$sha)$([ "$PUSHED" = true ] && echo ", pushed; ci.yml dispatched") |"
echo "| Tracking PR | ${pr:-none open: [open one]($REPO_URL/compare/main...$BRANCH?expand=1) as a draft} |"
echo "| Run | $RUN_URL |"
if [ "$SYNC" = failure ]; then
echo; echo "Merging main hit conflicts outside Cargo.toml/Cargo.lock; nothing was pushed. Resolve them by hand:"
echo '```'; tail -n 20 "$RUNNER_TEMP/sync.log" 2>/dev/null; echo '```'
fi
if [ "$TEST" = failure ]; then
echo; echo "Last lines of the test log (fix on \`$BRANCH\`; fixes that also hold on main's pin go to main first):"
echo '```'; tail -n 40 "$RUNNER_TEMP/test.log" 2>/dev/null; echo '```'
fi
if [ "$CHANGED" = true ] && [ "$STABLE" = true ]; then
echo
echo "**\`$TAG\` is a stable OpenShell release.** Promote \`$BRANCH\` to main as described in"
echo "[docs/RELEASING.md]($REPO_URL/blob/main/docs/RELEASING.md#promoting-openshell-next-when-openshell-ships-stable):"
echo "update the version strings and CHANGELOG on \`$BRANCH\`, mark the tracking PR ready for review, and squash-merge it."
fi
} > "$RUNNER_TEMP/body.md"
cat "$RUNNER_TEMP/body.md" >> "$GITHUB_STEP_SUMMARY"
gh label create openshell-next --color 1D76DB --description "Tracking the next OpenShell release" 2>/dev/null || true
issue=$(gh issue list --label openshell-next --state open --json number --jq '.[0].number // empty')
if [ -n "$issue" ]; then
gh issue comment "$issue" --body-file "$RUNNER_TEMP/body.md"
else
gh issue create --label openshell-next --title "Track the next OpenShell release on openshell-next" \
--body-file "$RUNNER_TEMP/body.md"
fi

- name: Fail the run when the merge or the tests failed
if: steps.sync.outcome == 'failure' || steps.test.outcome == 'failure'
run: exit 1
23 changes: 16 additions & 7 deletions .github/workflows/upstream-compat.yml
Original file line number Diff line number Diff line change
@@ -1,19 +1,28 @@
name: Upstream compatibility

# Builds and tests openshell-annotate against NVIDIA/OpenShell's newest release, newest prerelease and its main branch,
# so changes to the policy schema show up here before they bite users.
# Informational only: this is NOT a required check (see .github/rulesets/protect-main.json).
# Builds and tests main against NVIDIA/OpenShell's newest release, newest prerelease and its main branch (both policy
# crates repointed to the same upstream commit), so upstream changes show up here before they bite users.
# Informational only: this is NOT a required check (see .github/rulesets/protect-main.json), and only scheduled and
# manual runs can fail. The `openshell-next` branch (.github/workflows/openshell-next.yml) is where a newer pin is
# actually integrated and fixed.
# Run the same thing locally with: scripts/upstream-compat.sh <latest-release|latest-prerelease|main>

on:
schedule:
- cron: "0 5 * * 1" # Mondays 05:00 UTC
workflow_dispatch:
# Every merge to main, so a change that breaks the next OpenShell is visible right away (non-blocking).
push:
branches: [main]
paths-ignore:
- "**.md"
- "docs/**"
pull_request:
paths:
- Cargo.toml
- Cargo.lock
- scripts/upstream-compat.sh
- scripts/openshell-tags.sh
- .github/workflows/upstream-compat.yml

permissions:
Expand All @@ -30,8 +39,8 @@ jobs:
compat:
name: compat (${{ matrix.target }})
runs-on: ubuntu-latest
# Never turns a normal PR red; scheduled and manual runs report real failures.
continue-on-error: ${{ github.event_name == 'pull_request' }}
# Never turns a PR or a push to main red; scheduled and manual runs report real failures.
continue-on-error: ${{ github.event_name == 'pull_request' || github.event_name == 'push' }}
permissions:
contents: read
issues: write # only used by the "report" step on scheduled runs
Expand All @@ -40,10 +49,10 @@ jobs:
matrix:
target: [latest-release, latest-prerelease, main]
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Install Rust
run: rustup toolchain install 1.94 --profile minimal && rustup default 1.94
- uses: actions/cache@v4
- uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
with:
path: |
~/.cargo/registry
Expand Down
5 changes: 4 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ subprocesses; add directives with reasons; run `scan`, `generate`, `lint`, `drif
## When changing this tool
- Build/test: `cargo build && cargo test` (Rust 1.94+). The schema crate is a git dependency pinned to the
OpenShell `v0.1.7` tag (see `Cargo.toml`, `Cargo.lock` pins the commit). Update the tag deliberately and re-run
the golden tests.
the golden tests. `openshell-next` builds against the newest upstream tag, prereleases included, and is moved by
`.github/workflows/openshell-next.yml`; fixes that need the newer OpenShell go there, fixes valid on the current pin
go to `main` (see `docs/DEVELOPING.md`, "Tracking the next OpenShell release").
- Never emit a field the pinned `openshell-policy-schema` rejects. `generate` re-parses its own output with
`parse_policy`; tests assert this.
- Generated YAML is written as text (comments with `file:line`) because `serialize_policy` drops comments.
Expand All @@ -37,6 +39,7 @@ A change is not done until the docs that describe it match. Update them in the s
| Directives or attributes (`src/directive.rs`, `src/spec.rs`, `src/generate.rs`) | `scripts/gen-docs.sh` (regenerates `docs/DIRECTIVES.md`) and `skills/annotate-openshell-policy/SKILL.md` if authoring guidance changes |
| Release process (`scripts/release.sh`, `release.yml`, `install.sh`, packaging) | `docs/RELEASING.md` |
| Pinned OpenShell version (`Cargo.toml`) | every doc that names the version (`git grep -n 'v0\.1\.[0-9]'`) |
| OpenShell tracking (`scripts/openshell-*.sh`, `scripts/upstream-compat.sh`, `openshell-next.yml`, `upstream-compat.yml`) | `docs/DEVELOPING.md` (section 4) and the promotion steps in `docs/RELEASING.md` |
| Branch, issue or CI rules | `CONTRIBUTING.md`, `docs/DEVELOPING.md`, `.github/pull_request_template.md` |
| Anything user-visible | a line under `## [Unreleased]` in `CHANGELOG.md` |

Expand Down
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ version section and adds the squash-merged PR titles since the previous tag. Ver

## [Unreleased]

- ci: `openshell-next` branch automation: a daily workflow merges main into `openshell-next`, moves its OpenShell pin to the newest upstream tag (prereleases included), runs the tests and reports on the tracking issue; `scripts/openshell-tags.sh` and `scripts/openshell-next.sh` do the same by hand
- fix(ci): `scripts/upstream-compat.sh` repoints both OpenShell crates (it left `openshell-policy` on the old tag) and runs on macOS; `upstream-compat.yml` also runs, non-blocking, on every push to main
- chore(audit): ignore RUSTSEC-2026-0459 (`libsqlite3-sys` 0.37.0, pinned by OpenShell v0.1.7 through `sqlx` 0.9.0) in `.cargo/audit.toml` until OpenShell upgrades it (#76)
- chore(deps): build against OpenShell v0.1.7 (`openshell-policy-schema`, `openshell-policy`); docs and generated policy headers now name v0.1.7
- docs(actions): `docs/GITHUB_ACTIONS.md` is now a setup and usage guide (versions, recipes, troubleshooting); its inputs/outputs tables are generated from `action.yml` by `scripts/gen-actions-docs.sh` and checked in CI
Expand Down
8 changes: 7 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ required (0 approvals, conversations resolved), status checks `check` and `audit
history, no force pushes, no deletion, and no bypass for admins. GitHub only enforces rulesets on private repositories
with a paid plan; until then the rules are followed by convention.

The one exception to "branch from `main`" is `openshell-next`, a long-lived integration branch that builds against the
newest OpenShell prerelease and is updated daily by `.github/workflows/openshell-next.yml`. Fixes that only apply to the
new OpenShell version go in a branch off `openshell-next` with a PR into it; everything else goes to `main` first.
Never force-push or delete it. Details: `docs/DEVELOPING.md`, "Tracking the next OpenShell release".

## Issues

GitHub issues are how we track all work: bugs, features and follow-ups.
Expand All @@ -30,7 +35,8 @@ GitHub issues are how we track all work: bugs, features and follow-ups.
disabled; security problems go through `SECURITY.md`, not an issue).
2. Labels: `bug` or `enhancement`, plus an area (`scan`, `generate`, `lint`, `drift`, `diff-logs`, `providers`,
`languages`, `docs`). `upstream-openshell` marks behaviour of OpenShell itself that we only track locally;
`upstream-compat` is used by the weekly compatibility job. Create or refresh the labels with `scripts/labels.sh`.
`upstream-compat` is used by the weekly compatibility job and `openshell-next` by the tracking issue for the next
OpenShell release. Create or refresh the labels with `scripts/labels.sh`.
3. Work on a branch (see above) and put `Closes #<issue>` in the pull request description, one line per issue, so the
issue closes automatically when the PR is squash-merged. Use `Refs #<issue>` when the issue should stay open.
4. Keep one pull request per issue, or a small group of closely related issues.
Expand Down
Loading
Loading