-
Notifications
You must be signed in to change notification settings - Fork 55
feat(ci): open a pull request bumping stack pins when a chart releases #1213
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
6 commits
Select commit
Hold shift + click to select a range
9925210
feat(ci): open a pull request bumping stack pins when a chart releases
balajinvda 0f0ad37
fix(ci): scope the stack bump's change detection to what it stages
balajinvda a58242e
refactor(ci): rewrite the stack pin resolver in Go
balajinvda a6025ba
fix(ci): address review findings on the stack pin bump
balajinvda ad0907a
fix(ci): reject unmatched quotes in a stack version pin
balajinvda ab03a75
fix(ci): make the generated stack pin commit cut a stack release
balajinvda 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,180 @@ | ||
| # SPDX-FileCopyrightText: Copyright (c) NVIDIA CORPORATION & AFFILIATES. All rights reserved. | ||
| # SPDX-License-Identifier: Apache-2.0 | ||
| # | ||
| # When a chart release is published, open a pull request moving the | ||
| # self-managed stack's pin to that version. | ||
| # | ||
| # The released tag carries the version, so there is no "newest version" lookup | ||
| # and none of the ordering questions that come with one. A tag of | ||
| # deploy/helm/nats/v0.8.0 states the answer. | ||
| # | ||
| # The failure this is built to avoid is silence. A chart releases, nothing in | ||
| # the stack resolves to it, no pin moves, and the run goes green. The resolver | ||
| # therefore enumerates every release in the stack and treats one it cannot | ||
| # resolve as an error, so a gap shows up as a red run rather than as nothing. | ||
|
|
||
| name: stack pin bump | ||
|
|
||
| on: | ||
| release: | ||
| types: [published] | ||
| # Manual entry point for re-running a release whose bump did not land, and | ||
| # for exercising the job without cutting a tag. | ||
| workflow_dispatch: | ||
| inputs: | ||
| tag: | ||
| description: Chart release tag, for example deploy/helm/nats/v0.8.0 | ||
| required: true | ||
|
|
||
| permissions: | ||
| contents: read | ||
|
|
||
| concurrency: | ||
| # One bump at a time. Several releases landing together refresh the same | ||
| # pull request rather than racing on the same file. | ||
| group: stack-pin-bump | ||
| cancel-in-progress: false | ||
|
|
||
| jobs: | ||
| bump: | ||
| runs-on: ubuntu-latest | ||
| permissions: | ||
| contents: write | ||
| pull-requests: write | ||
| steps: | ||
| - uses: actions/checkout@v4 | ||
| with: | ||
| # The default for a release event is the tagged commit, but the pull | ||
| # request targets the default branch. Pinning against the tag's tree | ||
| # would carry whatever the stack looked like then onto a branch cut | ||
| # from today's main. | ||
| ref: ${{ github.event.repository.default_branch }} | ||
|
|
||
| - uses: actions/setup-go@v5 | ||
| with: | ||
| # Derived from the anchor, never a literal: tools/ci/check-go-version | ||
| # fails any workflow that pins one. | ||
| go-version-file: tools/go-toolchain/go.mod | ||
|
|
||
| - name: Test the resolver | ||
| # The resolver decides which line in the shipped stack gets rewritten, | ||
| # so its tests run here rather than somewhere that might not be | ||
| # reached. A test that gates nothing is not a test. | ||
| run: go test -C tools/stack-pin-resolver ./... | ||
|
|
||
| - name: Select the tag | ||
| id: tag | ||
| run: | | ||
| set -euo pipefail | ||
| tag="${{ github.event.inputs.tag || github.event.release.tag_name }}" | ||
| echo "tag=${tag}" >> "${GITHUB_OUTPUT}" | ||
| # Only chart releases move stack pins. Everything else is a normal | ||
| # release and is not this job's business. | ||
| case "${tag}" in | ||
| deploy/helm/*/v*) echo "applies=true" >> "${GITHUB_OUTPUT}" ;; | ||
| *) echo "applies=false" >> "${GITHUB_OUTPUT}" | ||
| echo "${tag} is not a chart release; nothing to do" ;; | ||
| esac | ||
|
|
||
| - name: Audit the stack | ||
| if: steps.tag.outputs.applies == 'true' | ||
| # Runs before the edit so an unresolvable release fails the job with a | ||
| # name attached, rather than being quietly skipped over. | ||
| run: tools/ci/stack-pin-resolver --audit | ||
|
|
||
| - name: Check out the bump branch | ||
| if: steps.tag.outputs.applies == 'true' | ||
| env: | ||
| BRANCH: chore/stack-pin-bumps | ||
| run: | | ||
| set -euo pipefail | ||
| # The bump is applied ON the pull request branch, not on the default | ||
| # branch and moved across afterwards. Bumping first and stashing the | ||
| # result over a checkout collides whenever the branch already carries | ||
| # a bump for the same pin, and a swallowed stash conflict either drops | ||
| # that earlier bump or commits conflict markers. Starting here also | ||
| # makes the tool idempotent: it sees the existing value and reports | ||
| # "already <version>". | ||
| git config user.name "github-actions[bot]" | ||
| git config user.email "41898282+github-actions[bot]@users.noreply.github.com" | ||
| git fetch origin "${BRANCH}" || true | ||
| if git rev-parse --verify -q "origin/${BRANCH}" >/dev/null; then | ||
| git checkout -B "${BRANCH}" "origin/${BRANCH}" | ||
| else | ||
| git checkout -B "${BRANCH}" | ||
| fi | ||
|
|
||
| - name: Apply the bump | ||
| id: bump | ||
| if: steps.tag.outputs.applies == 'true' | ||
| env: | ||
| # Through env, never expanded into the script body: a tag is chosen by | ||
| # whoever pushes it, and ${{ }} interpolation into a run: block is the | ||
| # standard Actions injection shape. | ||
| TAG: ${{ steps.tag.outputs.tag }} | ||
| run: | | ||
| set -euo pipefail | ||
| tools/ci/stack-pin-resolver --tag "${TAG}" --write | ||
| # Scoped to the same paths the commit below stages. Repo-wide, any | ||
| # unrelated modification in the workspace would set changed=true and | ||
| # the commit would then abort with nothing staged. | ||
| if git diff --quiet -- deploy/stacks/self-managed/helmfile.d; then | ||
| echo "changed=false" >> "${GITHUB_OUTPUT}" | ||
| echo "stack already pins this version" | ||
| else | ||
| echo "changed=true" >> "${GITHUB_OUTPUT}" | ||
| git --no-pager diff --stat -- deploy/stacks/self-managed/helmfile.d | ||
| fi | ||
|
|
||
| - name: Open or refresh the pull request | ||
| if: steps.bump.outputs.changed == 'true' | ||
| env: | ||
| GH_TOKEN: ${{ secrets.NV_GITHUB_TOKEN || github.token }} | ||
| TAG: ${{ steps.tag.outputs.tag }} | ||
| SERVER_URL: ${{ github.server_url }} | ||
| REPO: ${{ github.repository }} | ||
| run: | | ||
| set -euo pipefail | ||
| branch="chore/stack-pin-bumps" | ||
|
|
||
| git add deploy/stacks/self-managed/helmfile.d | ||
| # Separate -m flags rather than an embedded multi-line string: the | ||
| # continuation lines of one would have to sit at column zero, which | ||
| # ends the YAML block scalar this script lives in. | ||
| # fix, not chore. deploy/stacks/self-managed is itself a release | ||
| # subproject, and tools/ci/github-release feeds RELEASE_RULES to | ||
| # semantic-release, where chore carries "release": false. A chore | ||
| # commit would move the pin on main without ever cutting a stack | ||
| # release, so nothing downstream would see the new pin. | ||
| git commit \ | ||
| -m "fix(stack): pin ${TAG#deploy/helm/}" \ | ||
| -m "Opened by the stack pin bump workflow on release of ${TAG}." | ||
| git push --force-with-lease origin "${branch}" | ||
|
|
||
| body="$(printf '%s\n' \ | ||
| "Opened by \`.github/workflows/stack-pin-bump.yml\` when \`${TAG}\` was published." \ | ||
| "" \ | ||
| "The released tag carries the version, so this is a direct pin update rather than a lookup of the newest published chart." \ | ||
| "" \ | ||
| "Release notes: ${SERVER_URL}/${REPO}/releases/tag/${TAG}" \ | ||
| "" \ | ||
| "If this pull request sits unmerged, later chart releases add their bumps to the same branch, so merging it applies all of them." \ | ||
| "" \ | ||
| "Github commit:" \ | ||
| "fix(stack): pin ${TAG#deploy/helm/}" \ | ||
| "")" | ||
|
|
||
| # gh api, not `gh pr edit`. Against this repository `gh pr edit` fails | ||
| # with "Projects (classic) is being deprecated ... | ||
| # (repository.pullRequest.projectCards)", because it queries project | ||
| # cards it does not need. The REST endpoint has no such dependency. | ||
| number="$(gh api "repos/${REPO}/pulls?head=${REPO%%/*}:${branch}&state=open" -q '.[0].number')" | ||
| if [ -n "${number}" ] && [ "${number}" != "null" ]; then | ||
| jq -n --arg b "${body}" '{body: $b}' \ | ||
| | gh api -X PATCH "repos/${REPO}/pulls/${number}" --input - >/dev/null | ||
| echo "refreshed pull request #${number}" | ||
| else | ||
| gh pr create --base main --head "${branch}" \ | ||
| --title "fix(stack): bump self-managed stack chart pins" \ | ||
| --body "${body}" | ||
| fi | ||
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,34 @@ | ||
| #!/usr/bin/env bash | ||
| # SPDX-FileCopyrightText: Copyright (c) NVIDIA CORPORATION & AFFILIATES. All rights reserved. | ||
| # SPDX-License-Identifier: Apache-2.0 | ||
| # | ||
| # Stable CI entrypoint for the Go tool in tools/stack-pin-resolver. | ||
| # | ||
| # The wrapper exists for two reasons. | ||
| # | ||
| # The repository root. `go run -C <dir>` leaves the process running with that | ||
| # directory as its working directory, so the tool cannot find the helmfiles or | ||
| # the release metadata on its own. Resolving the root from this script's own | ||
| # location means callers do not have to pass it. | ||
| # | ||
| # The exit code. `go run` does NOT propagate the program's status: it prints | ||
| # "exit status N" and exits 1, collapsing every non-zero code into one. This | ||
| # tool only uses 0 and 1 today, so nothing is lost yet, but building the binary | ||
| # keeps that from becoming a trap the first time a distinct code is added. | ||
| # | ||
| # Run the tests with: go test -C tools/stack-pin-resolver ./... | ||
| set -euo pipefail | ||
|
|
||
| repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" | ||
| bin_dir="$(mktemp -d)" | ||
| trap 'rm -rf "${bin_dir}"' EXIT | ||
|
|
||
| go build -C "${repo_root}/tools/stack-pin-resolver" -o "${bin_dir}/stack-pin-resolver" . | ||
|
|
||
| # Not exec, so the trap above still runs, and not under errexit, so the exit | ||
| # code reaches the caller rather than aborting the shell first. | ||
| set +e | ||
| "${bin_dir}/stack-pin-resolver" --root "${repo_root}" "$@" | ||
| status=$? | ||
| set -e | ||
| exit "${status}" |
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,2 @@ | ||
| # go build ./... drops the binary here; it must never be committed. | ||
| /stack-pin-resolver |
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,3 @@ | ||
| module stack-pin-resolver | ||
|
|
||
| go 1.26 |
Oops, something went wrong.
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.