fix(org-plan): enforce supervision completion loop #32
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Release | |
| on: | |
| push: | |
| branches: | |
| - main | |
| permissions: | |
| contents: read | |
| # GITHUB_TOKEN pushes do not recursively trigger this workflow. Protected main | |
| # must allow GitHub Actions to write; an atomic push rejects both branch and tag | |
| # if either update is denied. | |
| concurrency: | |
| group: release-${{ github.repository }} | |
| cancel-in-progress: false | |
| jobs: | |
| test: | |
| uses: ./.github/workflows/test.yml | |
| release: | |
| needs: test | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: write | |
| steps: | |
| - uses: actions/checkout@v7 | |
| with: | |
| fetch-depth: 0 | |
| - name: Detect release baseline | |
| id: baseline | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| latest_tag=$(git tag --list --sort=-version:refname | | |
| grep -E '^v([2-9]|[1-9][0-9]+)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)$' | | |
| head -n 1 || true) | |
| if [[ -n "$latest_tag" ]]; then | |
| echo "has_tag=true" >>"$GITHUB_OUTPUT" | |
| else | |
| echo "has_tag=false" >>"$GITHUB_OUTPUT" | |
| fi | |
| - name: Calculate conventional-commit version | |
| if: steps.baseline.outputs.has_tag == 'true' | |
| id: semver | |
| uses: ietf-tools/semver-action@v1 | |
| with: | |
| token: ${{ github.token }} | |
| branch: main | |
| majorList: '' | |
| minorList: feat, feature | |
| patchList: fix, bugfix, perf, refactor, test, tests | |
| skipInvalidTags: true | |
| tagFilter: '^v([2-9]|[1-9][0-9]+)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)$' | |
| maxTagsToFetch: 100 | |
| noNewCommitBehavior: current | |
| noVersionBumpBehavior: current | |
| - name: Normalize release decision | |
| id: release | |
| shell: bash | |
| env: | |
| HAS_TAG: ${{ steps.baseline.outputs.has_tag }} | |
| BUMP: ${{ steps.semver.outputs.bump }} | |
| NEXT_TAG: ${{ steps.semver.outputs.next }} | |
| NEXT_VERSION: ${{ steps.semver.outputs.nextStrict }} | |
| run: | | |
| set -euo pipefail | |
| if [[ "$HAS_TAG" == false ]]; then | |
| tag=v2.0.0 | |
| version=2.0.0 | |
| should_release=true | |
| else | |
| tag=$NEXT_TAG | |
| version=$NEXT_VERSION | |
| case "$BUMP" in | |
| major|minor|patch) should_release=true ;; | |
| none) should_release=false ;; | |
| *) echo "Unexpected semver bump: $BUMP" >&2; exit 1 ;; | |
| esac | |
| fi | |
| [[ "$tag" =~ ^v([2-9]|[1-9][0-9]+)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)$ ]] | |
| [[ "$version" == "${tag#v}" ]] | |
| { | |
| printf 'tag=%s\n' "$tag" | |
| printf 'version=%s\n' "$version" | |
| printf 'should_release=%s\n' "$should_release" | |
| } >>"$GITHUB_OUTPUT" | |
| - name: Synchronize plugin manifests | |
| if: steps.release.outputs.should_release == 'true' | |
| env: | |
| VERSION: ${{ steps.release.outputs.version }} | |
| run: | | |
| set -euo pipefail | |
| scripts/set-plugin-version.py "$VERSION" | |
| python3 - "$VERSION" <<'PY' | |
| import json | |
| import sys | |
| from pathlib import Path | |
| version = sys.argv[1] | |
| versioned_json = ( | |
| Path("plugins/gestalt/.codex-plugin/plugin.json"), | |
| Path("plugins/context-mode/.codex-plugin/plugin.json"), | |
| Path("plugins/context-mode/package.json"), | |
| ) | |
| for path in versioned_json: | |
| assert json.loads(path.read_text())["version"] == version, path | |
| provenance = Path("plugins/context-mode/UPSTREAM.md").read_text() | |
| assert f"- Downstream package version: `{version}`" in provenance | |
| PY | |
| bash tests/plugins/context-mode/test-upstream-vendor.sh | |
| - name: Commit synchronized plugin versions | |
| if: steps.release.outputs.should_release == 'true' | |
| env: | |
| TAG: ${{ steps.release.outputs.tag }} | |
| run: | | |
| set -euo pipefail | |
| git config user.name github-actions[bot] | |
| git config user.email 41898282+github-actions[bot]@users.noreply.github.com | |
| release_paths=( | |
| plugins/gestalt/.codex-plugin/plugin.json | |
| plugins/context-mode/.codex-plugin/plugin.json | |
| plugins/context-mode/package.json | |
| plugins/context-mode/UPSTREAM.md | |
| tests/plugins/context-mode/fixtures/context-mode-codex-hardening-4b1348d.sha256 | |
| ) | |
| if git diff --quiet -- "${release_paths[@]}"; then | |
| echo "Plugin versions already match $TAG" | |
| else | |
| git add -- "${release_paths[@]}" | |
| git commit -m "chore(release): $TAG [skip ci]" | |
| fi | |
| - name: Tag and atomically publish release | |
| if: steps.release.outputs.should_release == 'true' | |
| env: | |
| TAG: ${{ steps.release.outputs.tag }} | |
| run: | | |
| set -euo pipefail | |
| if git rev-parse --verify --quiet "refs/tags/$TAG"; then | |
| echo "Release tag already exists: $TAG" >&2 | |
| exit 1 | |
| fi | |
| git tag "$TAG" HEAD | |
| git push --atomic origin HEAD:main "refs/tags/$TAG" |