-
Notifications
You must be signed in to change notification settings - Fork 0
[COVAL-4319] Open weekly CLI API parity PRs and gate releases #97
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
55d1262
[COVAL-4319] Automate weekly CLI parity releases
callumreid b3a1e54
[COVAL-4319] Address release automation review
callumreid a382d3c
[COVAL-4319] Keep CLI automation repository-owned
callumreid cfb0eda
[COVAL-4319] Open parity drift PRs
callumreid 7d34642
[COVAL-4319] Address parity workflow review
callumreid File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,136 @@ | ||
| name: Weekly API parity PR | ||
|
|
||
| # Repository-owned drift detection for the hand-written CLI. The workflow | ||
| # refreshes a deterministic coverage report and opens or updates one rolling PR | ||
| # when the public API and first-class CLI command surface change. It does not | ||
| # claim to regenerate hand-written command UX from OpenAPI. | ||
| # | ||
| # Prerequisite: | ||
| # REGEN_PR_TOKEN: a token with Contents and Pull requests write access to this | ||
| # repository. The organization does not allow GITHUB_TOKEN to create PRs. | ||
|
|
||
| on: | ||
| workflow_dispatch: | ||
| schedule: | ||
| # Monday 10:00 UTC, after the SDK regeneration job. | ||
| - cron: "0 10 * * 1" | ||
|
|
||
| permissions: | ||
| contents: read | ||
| issues: write | ||
|
|
||
| concurrency: | ||
| group: weekly-api-parity-pr | ||
| cancel-in-progress: false | ||
|
|
||
| jobs: | ||
| refresh: | ||
| runs-on: ubuntu-latest | ||
| timeout-minutes: 20 | ||
| steps: | ||
| - uses: actions/checkout@v6 | ||
| with: | ||
| ref: main | ||
| persist-credentials: false | ||
|
|
||
| - uses: actions/setup-python@v6 | ||
| with: | ||
| python-version: "3.12" | ||
| cache: pip | ||
| cache-dependency-path: scripts/requirements-audit.txt | ||
|
|
||
| - name: Install pinned dependencies | ||
| run: python -m pip install --requirement scripts/requirements-audit.txt | ||
|
|
||
| - name: Test the audit | ||
| run: python -m unittest scripts/test_audit_api_coverage.py | ||
|
|
||
| - name: Refresh the deterministic parity report | ||
| run: >- | ||
| python scripts/audit_api_coverage.py | ||
| --write-markdown api-coverage-report.md | ||
| --allow-drift | ||
|
|
||
| - name: Open or update the parity PR | ||
| uses: peter-evans/create-pull-request@v7 | ||
| with: | ||
| token: ${{ secrets.REGEN_PR_TOKEN }} | ||
| commit-message: "chore(cli): refresh API parity report" | ||
| title: "chore(cli): reconcile weekly API parity" | ||
| body: | | ||
| Automated comparison of the live public OpenAPI catalog with first-class CLI commands. | ||
|
|
||
| - `api-coverage-report.md` is deterministic and changes only when API or CLI coverage changes. | ||
| - This PR does not claim to generate the CLI's hand-written command UX. | ||
| - If the report says `ACTION REQUIRED`, implement the missing commands or explicitly review the manifest exception, regenerate the report, and get CI green before merging. | ||
|
|
||
| The branch is stable, so future weekly runs update this PR instead of opening duplicates. | ||
| branch: chore/weekly-api-parity | ||
| delete-branch: false | ||
| add-paths: api-coverage-report.md | ||
|
|
||
| - name: Open or update the automation failure issue | ||
| if: failure() | ||
| uses: actions/github-script@v7 | ||
| with: | ||
| script: | | ||
| const title = 'Weekly CLI API parity PR automation is failing'; | ||
| const runUrl = `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`; | ||
| const body = [ | ||
| 'The scheduled CLI API parity workflow failed before it could produce or update its rolling PR.', | ||
| '', | ||
| `Run: ${runUrl}`, | ||
| '', | ||
| 'This issue reports broken automation, not ordinary API drift. Ordinary drift is reported through the rolling PR.', | ||
| ].join('\n'); | ||
| const issues = await github.paginate(github.rest.issues.listForRepo, { | ||
| owner: context.repo.owner, | ||
| repo: context.repo.repo, | ||
| state: 'open', | ||
| per_page: 100, | ||
| }); | ||
| const existing = issues.find((issue) => !issue.pull_request && issue.title === title); | ||
| if (existing) { | ||
| await github.rest.issues.createComment({ | ||
| owner: context.repo.owner, | ||
| repo: context.repo.repo, | ||
| issue_number: existing.number, | ||
| body, | ||
| }); | ||
| } else { | ||
| await github.rest.issues.create({ | ||
| owner: context.repo.owner, | ||
| repo: context.repo.repo, | ||
| title, | ||
| body, | ||
| }); | ||
| } | ||
|
|
||
| - name: Close a recovered automation failure issue | ||
| if: success() | ||
| uses: actions/github-script@v7 | ||
| with: | ||
| script: | | ||
| const title = 'Weekly CLI API parity PR automation is failing'; | ||
| const issues = await github.paginate(github.rest.issues.listForRepo, { | ||
| owner: context.repo.owner, | ||
| repo: context.repo.repo, | ||
| state: 'open', | ||
| per_page: 100, | ||
| }); | ||
| const existing = issues.find((issue) => !issue.pull_request && issue.title === title); | ||
| if (existing) { | ||
| await github.rest.issues.createComment({ | ||
| owner: context.repo.owner, | ||
| repo: context.repo.repo, | ||
| issue_number: existing.number, | ||
| body: `Recovered in ${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}.`, | ||
| }); | ||
| await github.rest.issues.update({ | ||
| owner: context.repo.owner, | ||
| repo: context.repo.repo, | ||
| issue_number: existing.number, | ||
| state: 'closed', | ||
| state_reason: 'completed', | ||
| }); | ||
| } | ||
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
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,116 @@ | ||
| name: Release on version bump | ||
|
|
||
| # Wait for the exact main-branch CI run to pass, then release an untagged Cargo | ||
| # version. The release workflow is called directly because a tag pushed with | ||
| # GITHUB_TOKEN does not trigger another workflow. | ||
|
|
||
| on: | ||
| workflow_run: | ||
| workflows: ["CI"] | ||
| types: [completed] | ||
| workflow_dispatch: | ||
|
|
||
| permissions: | ||
| contents: write | ||
| issues: write | ||
|
|
||
| concurrency: | ||
| group: release-on-version-bump | ||
| cancel-in-progress: false | ||
|
|
||
| jobs: | ||
| prepare: | ||
| if: >- | ||
| github.event_name == 'workflow_dispatch' || | ||
| ( | ||
| github.event.workflow_run.conclusion == 'success' && | ||
| github.event.workflow_run.event == 'push' && | ||
| github.event.workflow_run.head_branch == 'main' | ||
| ) | ||
| runs-on: ubuntu-latest | ||
| outputs: | ||
| release: ${{ steps.tag.outputs.release }} | ||
| release_ref: ${{ steps.tag.outputs.release_ref }} | ||
| tag: ${{ steps.version.outputs.tag }} | ||
| steps: | ||
| - name: Resolve the CI-tested commit | ||
| id: ref | ||
| env: | ||
| WORKFLOW_RUN_SHA: ${{ github.event.workflow_run.head_sha }} | ||
| run: | | ||
| set -euo pipefail | ||
| if [ "$GITHUB_EVENT_NAME" = "workflow_dispatch" ]; then | ||
| if [ "$GITHUB_REF_NAME" != "main" ]; then | ||
| echo "::error::Manual releases must be dispatched from main (got $GITHUB_REF_NAME)." | ||
| exit 1 | ||
| fi | ||
| echo "sha=$GITHUB_SHA" >> "$GITHUB_OUTPUT" | ||
| else | ||
| echo "sha=$WORKFLOW_RUN_SHA" >> "$GITHUB_OUTPUT" | ||
| fi | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
|
|
||
| - uses: actions/checkout@v6 | ||
| with: | ||
| ref: ${{ steps.ref.outputs.sha }} | ||
| fetch-depth: 0 | ||
|
|
||
| - uses: actions/setup-python@v6 | ||
| with: | ||
| python-version: "3.12" | ||
|
|
||
| - name: Read and validate the release version | ||
| id: version | ||
| run: | | ||
| if [ "$GITHUB_EVENT_NAME" = "workflow_dispatch" ]; then | ||
| python scripts/release_version.py --github-output "$GITHUB_OUTPUT" | ||
| else | ||
| python scripts/release_version.py \ | ||
| --allow-non-release \ | ||
| --github-output "$GITHUB_OUTPUT" | ||
| fi | ||
|
|
||
| - name: Tag an unreleased version | ||
| id: tag | ||
| env: | ||
| RELEASE_CANDIDATE: ${{ steps.version.outputs.release_candidate }} | ||
| RELEASE_SHA: ${{ steps.ref.outputs.sha }} | ||
| RELEASE_TAG: ${{ steps.version.outputs.tag }} | ||
| run: | | ||
| set -euo pipefail | ||
| if [ "$RELEASE_CANDIDATE" != "true" ]; then | ||
| echo "Cargo.toml does not contain a stable release candidate; nothing to do." | ||
| echo "release=false" >> "$GITHUB_OUTPUT" | ||
| exit 0 | ||
| fi | ||
|
|
||
| git fetch --tags --force | ||
|
|
||
| if git rev-parse -q --verify "refs/tags/$RELEASE_TAG" >/dev/null; then | ||
| existing_sha="$(git rev-list -n 1 "$RELEASE_TAG")" | ||
| if [ "$GITHUB_EVENT_NAME" = "workflow_dispatch" ]; then | ||
| echo "Manual recovery will rerun the existing ${RELEASE_TAG} release." | ||
| echo "release=true" >> "$GITHUB_OUTPUT" | ||
| echo "release_ref=$existing_sha" >> "$GITHUB_OUTPUT" | ||
| else | ||
| echo "${RELEASE_TAG} is already released; nothing to do." | ||
| echo "release=false" >> "$GITHUB_OUTPUT" | ||
| echo "release_ref=$existing_sha" >> "$GITHUB_OUTPUT" | ||
| fi | ||
| exit 0 | ||
| fi | ||
|
|
||
| git config user.name "github-actions[bot]" | ||
| git config user.email "41898282+github-actions[bot]@users.noreply.github.com" | ||
| git tag -a "$RELEASE_TAG" "$RELEASE_SHA" -m "$RELEASE_TAG" | ||
| git push origin "$RELEASE_TAG" | ||
| echo "release=true" >> "$GITHUB_OUTPUT" | ||
| echo "release_ref=$RELEASE_SHA" >> "$GITHUB_OUTPUT" | ||
|
|
||
| release: | ||
| needs: prepare | ||
| if: needs.prepare.outputs.release == 'true' | ||
| uses: ./.github/workflows/release.yml | ||
| with: | ||
| release_ref: ${{ needs.prepare.outputs.release_ref }} | ||
| tag_name: ${{ needs.prepare.outputs.tag }} | ||
| secrets: inherit | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.