Skip to content
Open
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
54 changes: 41 additions & 13 deletions .github/workflows/openshell-next.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ name: openshell-next
# 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.
# The scripts that move the branch (scripts/openshell-next.sh, scripts/openshell-tags.sh) always come from main: they
# are copied to a temp dir before openshell-next is checked out and run against that checkout (OPENSHELL_ANNOTATE_ROOT).
# Local equivalent: scripts/openshell-next.sh sync && scripts/openshell-next.sh update

on:
Expand All @@ -27,6 +29,12 @@ on:
permissions:
contents: read

# `shell: bash` runs steps with `bash --noprofile --norc -eo pipefail`; the implicit default (`bash -e`) has no pipefail,
# so a failing command piped into `tee` would pass.
defaults:
run:
shell: bash

concurrency:
group: openshell-next
cancel-in-progress: false
Expand All @@ -50,6 +58,15 @@ jobs:
with:
fetch-depth: 0

- name: Keep main's branch tooling
run: |
mkdir -p "$RUNNER_TEMP/tools"
cp scripts/openshell-next.sh scripts/openshell-tags.sh "$RUNNER_TEMP/tools/"
{
echo "TOOLS=$RUNNER_TEMP/tools"
echo "OPENSHELL_ANNOTATE_ROOT=$GITHUB_WORKSPACE"
} >> "$GITHUB_ENV"

- name: Check out openshell-next (created from main if missing)
id: branch
run: |
Expand All @@ -63,6 +80,7 @@ jobs:
echo "created=true" >> "$GITHUB_OUTPUT"
fi
echo "before=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"
echo "pin=$("$TOOLS/openshell-tags.sh" pinned)" >> "$GITHUB_OUTPUT"

- name: Install Rust
run: rustup toolchain install 1.94 --profile minimal --component rustfmt,clippy && rustup default 1.94
Expand All @@ -78,17 +96,17 @@ jobs:

- name: Merge main into openshell-next
id: sync
run: scripts/openshell-next.sh sync origin/main 2>&1 | tee "$RUNNER_TEMP/sync.log"
run: '"$TOOLS/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"
"$TOOLS/openshell-next.sh" update "$WANT" 2>&1 | tee "$RUNNER_TEMP/update.log"
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")
tag=$(sed -n 's/^tag=//p' "$RUNNER_TEMP/update.log")
prev=$(sed -n 's/^previous=//p' "$RUNNER_TEMP/update.log")
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
Expand All @@ -97,7 +115,6 @@ jobs:
id: test
continue-on-error: true
run: |
set -o pipefail
{
cargo fmt --check &&
cargo clippy --all-targets --locked -- -D warnings &&
Expand Down Expand Up @@ -131,7 +148,9 @@ jobs:
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SYNC: ${{ steps.sync.outcome }}
BUMP: ${{ steps.bump.outcome }}
TEST: ${{ steps.test.outcome }}
START_PIN: ${{ steps.branch.outputs.pin }}
CHANGED: ${{ steps.bump.outputs.changed }}
PREV: ${{ steps.bump.outputs.previous }}
TAG: ${{ steps.bump.outputs.tag }}
Expand All @@ -142,15 +161,19 @@ jobs:
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
icon() { case "$1" in success) echo "✅ passed" ;; failure) echo "❌ failed" ;; cancelled) echo "🚫 cancelled" ;; *) echo "⏭️ skipped" ;; esac; }
# The pin as the branch has it now (after any bump), else as the bump step or the checkout saw it.
TAG=$("$TOOLS/openshell-tags.sh" pinned 2>/dev/null || echo "${TAG:-${START_PIN:-unknown}}")
if [ "$CHANGED" != true ] && [ "$SYNC" = success ] && [ "$BUMP" = 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
failed=""
for o in "$SYNC" "$BUMP" "$TEST"; do [ "$o" = failure ] && failed=" ❌"; done
if [ "$CHANGED" = true ]; then echo "### OpenShell \`$TAG\` is now pinned on \`$BRANCH\`$failed"; else echo "### \`$BRANCH\` check (pin \`$TAG\`)$failed"; fi
echo
echo "| | |"
echo "|---|---|"
Expand All @@ -159,17 +182,22 @@ jobs:
echo "| Upstream | [release]($UPSTREAM/releases/tag/$TAG) · [changes since \`$PREV\`]($UPSTREAM/compare/$PREV...$TAG) |"
fi
echo "| Merge of main | $(icon "$SYNC") |"
echo "| Pin update | $(icon "$BUMP")$([ "$CHANGED" = true ] || [ "$BUMP" != success ] || echo ", already on the newest tag") |"
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 '```'
echo; echo "Merging main failed (usually a conflict outside Cargo.toml/Cargo.lock, which needs resolving by hand); nothing was pushed:"
echo '```'; tail -n 20 "$RUNNER_TEMP/sync.log" 2>/dev/null || echo "(no log captured)"; echo '```'
fi
if [ "$BUMP" = failure ]; then
echo; echo "Moving the pin failed; nothing was pushed:"
echo '```'; tail -n 20 "$RUNNER_TEMP/update.log" 2>/dev/null || echo "(no log captured)"; 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 '```'
echo '```'; tail -n 40 "$RUNNER_TEMP/test.log" 2>/dev/null || echo "(no log captured)"; echo '```'
fi
if [ "$CHANGED" = true ] && [ "$STABLE" = true ]; then
echo
Expand All @@ -188,6 +216,6 @@ jobs:
--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'
- name: Fail the run when the merge, the pin update or the tests failed
if: always() && (steps.sync.outcome == 'failure' || steps.bump.outcome == 'failure' || steps.test.outcome == 'failure')
run: exit 1
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ 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): the `openshell-next` workflow runs main's copy of its scripts against the branch (`OPENSHELL_ANNOTATE_ROOT`), runs steps with `pipefail` so merge/bump/test failures fail the run, and its issue comment shows the pin and every failed step
- 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
Expand Down
7 changes: 6 additions & 1 deletion docs/DEVELOPING.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,11 @@ specific tag, `report` to comment even when nothing changed). It:
adds a note to promote the branch (see "Promoting `openshell-next`" in `docs/RELEASING.md`).

It uses only `GITHUB_TOKEN` (`contents: write` for `openshell-next`, `issues: write`, `actions: write` to dispatch CI,
`pull-requests: read`). It never pushes to `main` or opens pull requests.
`pull-requests: read`). It never pushes to `main` or opens pull requests. The branch tooling always comes from `main`:
the workflow copies `scripts/openshell-next.sh` and `scripts/openshell-tags.sh` to a temp dir before checking out
`openshell-next` and runs them against that checkout (`OPENSHELL_ANNOTATE_ROOT=<checkout>`), so a fix to the scripts
takes effect on the next run and the branch does not need its own copy. Steps run with `shell: bash` (`-eo pipefail`),
so a failure piped into `tee` still fails the step, the run, and the issue comment.

Working on the branch by hand:

Expand All @@ -124,6 +128,7 @@ git fetch origin && git switch openshell-next && git pull --ff-only
scripts/openshell-next.sh sync # merge origin/main (what the workflow does first)
scripts/openshell-next.sh update # move the pin if upstream has a newer tag (edits files only)
scripts/openshell-next.sh bump v0.1.8-pre.3 # or pin an exact tag
OPENSHELL_ANNOTATE_ROOT=../other-checkout scripts/openshell-next.sh update # work on another checkout
TOOLCHAIN=1.94 scripts/ci-local.sh
```

Expand Down
9 changes: 7 additions & 2 deletions scripts/openshell-next.sh
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,15 @@
# branch both changed the pin, `sync` keeps the newer of the two tags and re-resolves Cargo.lock; any other conflict
# aborts the merge (exit 3) so a human can resolve it.
# `update` prints `previous=`, `tag=`, `changed=` and `stable=` lines (also appended to $GITHUB_OUTPUT when set).
# Env: OPENSHELL_ANNOTATE_ROOT, the checkout to work on (default: the one this script lives in). The workflow runs
# main's copy of these scripts from a temp dir against the openshell-next checkout, so the branch needs no copy.
# Exit: 0 ok, 2 usage / bad tag, 3 merge conflict that needs a human.
set -euo pipefail
cd "$(dirname "$0")/.."
TAGS=scripts/openshell-tags.sh
HERE=$(cd "$(dirname "$0")" && pwd)
ROOT=$(cd "${OPENSHELL_ANNOTATE_ROOT:-$HERE/..}" && pwd)
export OPENSHELL_ANNOTATE_ROOT="$ROOT" # so openshell-tags.sh reads the same Cargo.toml
cd "$ROOT"
TAGS="$HERE/openshell-tags.sh"
CRATES=(openshell-policy-schema openshell-policy)

bump() {
Expand Down
5 changes: 3 additions & 2 deletions scripts/openshell-tags.sh
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,11 @@
#
# Only vX.Y.Z and vX.Y.Z-pre.N tags count; anything else upstream (`dev`, `vm-runtime`, ...) is ignored.
# Env: UPSTREAM_REPO (default https://github.com/NVIDIA/OpenShell); OPENSHELL_TAGS (newline-separated tag list to use
# instead of asking the remote, for tests and offline runs).
# instead of asking the remote, for tests and offline runs); OPENSHELL_ANNOTATE_ROOT (checkout whose Cargo.toml
# `pinned` reads; default: the checkout this script lives in).
# Exit: 0 ok / true, 1 false, 2 usage or no tags found.
set -euo pipefail
cd "$(dirname "$0")/.."
cd "${OPENSHELL_ANNOTATE_ROOT:-$(dirname "$0")/..}"

REPO="${UPSTREAM_REPO:-https://github.com/NVIDIA/OpenShell}"
RE='^v[0-9]+\.[0-9]+\.[0-9]+(-pre\.[0-9]+)?$'
Expand Down
Loading