-
Notifications
You must be signed in to change notification settings - Fork 59
feat(ci): bump chart versions when a service releases #1222
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
9 commits
Select commit
Hold shift + click to select a range
60960c4
feat(ci): bump chart versions when a service releases
balajinvda 4c7ecfe
ci(charts): open a version bump PR on a service release
balajinvda ad1154f
fix(ci): address review findings on the chart version bump workflow
balajinvda aac0d98
fix(ci): make the generated chart bump commit cut a chart release
balajinvda 4f20454
fix(ci): stop a floating tag on another image refusing the chart
balajinvda a5095d0
fix(ci): refuse a chart whose service image cannot be identified
balajinvda e1f00e8
fix(ci): identify image tags by structure, not by key name
balajinvda 3d43ca9
fix(ci): refuse an inline image declaration instead of half-bumping
balajinvda ddb1aa7
fix(ci): refuse an inline image wherever the key appears on the line
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,254 @@ | ||
| # SPDX-FileCopyrightText: Copyright (c) NVIDIA CORPORATION & AFFILIATES. All rights reserved. | ||
| # SPDX-License-Identifier: Apache-2.0 | ||
| # | ||
| # When a service release is published, open a pull request moving the charts | ||
| # that deploy it to that version. | ||
| # | ||
| # This is the first hop of the service to chart to stack cascade. The second is | ||
| # stack-pin-bump.yml: merging this pull request does not by itself move the | ||
| # stack, because a chart version only reaches the stack once the chart is | ||
| # released. Cutting that chart release stays a human decision, and publishing | ||
| # it is what triggers the stack bump. | ||
| # | ||
| # Which charts deploy the released service is declared, not derived; see | ||
| # tools/ci/chart-service-edge for why deriving it is wrong. A service whose | ||
| # charts have not declared the edge yet bumps nothing and says so. | ||
|
|
||
| name: chart version 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: >- | ||
| Service release tag, for example | ||
| src/control-plane-services/notary/v1.9.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 files. | ||
| group: chart-version-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. Bumping the tag's tree would | ||
| # carry whatever the chart 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 bumper | ||
| # The bumper rewrites version fields in shipped charts, 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/chart-version-bumper ./... | ||
|
|
||
| - name: Select the tag | ||
| id: tag | ||
| env: | ||
| # Through env like every other step here. A release tag is chosen by | ||
| # whoever pushes it, so expanding it into the script body is the | ||
| # standard Actions injection shape. | ||
| INPUT_TAG: ${{ github.event.inputs.tag }} | ||
| RELEASE_TAG: ${{ github.event.release.tag_name }} | ||
| run: | | ||
| set -euo pipefail | ||
| tag="${INPUT_TAG:-${RELEASE_TAG}}" | ||
| echo "tag=${tag}" >> "${GITHUB_OUTPUT}" | ||
| # Chart releases move stack pins, which is stack-pin-bump.yml's job. | ||
| # Everything else is a service release and is this job's business: | ||
| # a tag that names no known service fails below rather than here, so | ||
| # a service missing from the release metadata is visible. | ||
| case "${tag}" in | ||
| deploy/helm/*/v*) echo "applies=false" >> "${GITHUB_OUTPUT}" | ||
| echo "${tag} is a chart release; stack-pin-bump.yml handles it" ;; | ||
| */v*) echo "applies=true" >> "${GITHUB_OUTPUT}" ;; | ||
| *) echo "applies=false" >> "${GITHUB_OUTPUT}" | ||
| echo "${tag} is not a subtree release tag; nothing to do" ;; | ||
| esac | ||
|
|
||
| - name: Check the chart to service edges | ||
| if: steps.tag.outputs.applies == 'true' | ||
| # Report-only. Runs here so the undeclared charts are listed in the same | ||
| # log as a bump that reached fewer charts than someone expected. | ||
| run: tools/ci/chart-service-edge --audit | ||
|
|
||
| - name: Check out the bump branch | ||
| if: steps.tag.outputs.applies == 'true' | ||
| env: | ||
| BRANCH: chore/chart-version-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 chart, and a swallowed stash conflict either | ||
| # drops that earlier bump or commits conflict markers. Starting here | ||
| # also makes the run idempotent: the bumper sees the current 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 +e | ||
| set -uo pipefail | ||
| # +e, deliberately. GitHub invokes this as `bash -e`, and the bumper | ||
| # exits 3 when a chart's appVersion and image tag disagree even though | ||
| # it still applied every chart it could move safely. Aborting here | ||
| # would throw those away and leave the refusal as the only outcome. | ||
| tools/ci/chart-version-bumper \ | ||
| --tag "${TAG}" --write 2>/tmp/refusals | ||
| status=$? | ||
| cat /tmp/refusals >&2 | ||
| # The exit code, not whether stderr is empty. SystemExit writes its | ||
| # message to stderr too, so an unresolvable tag looks exactly like a | ||
| # refused chart there. 3 means the charts that could move did; any | ||
| # other non-zero means nothing moved and the run should stop. | ||
| if [ "${status}" -ne 0 ] && [ "${status}" -ne 3 ]; then | ||
| echo "bumper failed (exit ${status}); no chart was changed" >&2 | ||
| exit "${status}" | ||
| fi | ||
| if [ "${status}" -eq 3 ]; then | ||
| { | ||
| echo "refused<<CHART_BUMP_REFUSALS" | ||
| cat /tmp/refusals | ||
| echo "CHART_BUMP_REFUSALS" | ||
| } >> "${GITHUB_OUTPUT}" | ||
| fi | ||
| # 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/helm; then | ||
| echo "changed=false" >> "${GITHUB_OUTPUT}" | ||
| echo "no chart moved" | ||
| else | ||
| echo "changed=true" >> "${GITHUB_OUTPUT}" | ||
| git --no-pager diff --stat -- deploy/helm | ||
| 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 }} | ||
| REFUSED: ${{ steps.bump.outputs.refused }} | ||
| SERVER_URL: ${{ github.server_url }} | ||
| REPO: ${{ github.repository }} | ||
| run: | | ||
| set -euo pipefail | ||
| branch="chore/chart-version-bumps" | ||
|
|
||
| git add deploy/helm | ||
| # 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. tools/ci/github-release feeds RELEASE_RULES to | ||
| # semantic-release, where chore carries "release": false. A chore | ||
| # commit therefore cuts no chart release, and since publishing a chart | ||
| # release is exactly what triggers stack-pin-bump.yml, the cascade | ||
| # would stop here: the chart would carry the new appVersion on main | ||
| # and the stack would never learn about it. | ||
| # | ||
| # A patch bump of the chart is the right size. The chart's own | ||
| # templates and values schema have not changed, only the application | ||
| # version it defaults to, which is the conventional reading of chart | ||
| # version against appVersion. | ||
| git commit \ | ||
| -m "fix(charts): bump for ${TAG}" \ | ||
| -m "Opened by the chart version bump workflow on release of ${TAG}." | ||
| git push --force-with-lease origin "${branch}" | ||
|
|
||
| notes="" | ||
| if [ -n "${REFUSED}" ]; then | ||
| notes="$(printf '%s\n' \ | ||
| "" \ | ||
| "Some charts were not bumped:" \ | ||
| "" \ | ||
| '```' \ | ||
| "${REFUSED}" \ | ||
| '```' \ | ||
| "" \ | ||
| "A chart is refused when its \`appVersion\` and image tag disagree, or when the tag is floating. Reconciling those two fields is a decision, so it is left to a person rather than resolved during an automated bump.")" | ||
| fi | ||
|
|
||
| body="$(printf '%s\n' \ | ||
| "Opened by \`.github/workflows/chart-version-bump.yml\` when \`${TAG}\` was published." \ | ||
| "" \ | ||
| "The released tag carries the version, so this is a direct update rather than a lookup of the newest published image." \ | ||
| "" \ | ||
| "Merging this does not move the self-managed stack. A chart version reaches the stack only once the chart itself is released, and publishing that chart release is what triggers \`stack-pin-bump.yml\`." \ | ||
| "" \ | ||
| "Release notes: ${SERVER_URL}/${REPO}/releases/tag/${TAG}" \ | ||
| "${notes}" \ | ||
| "" \ | ||
| "If this pull request sits unmerged, later service releases add their bumps to the same branch, so merging it applies all of them." \ | ||
| "" \ | ||
| "Github commit:" \ | ||
| "fix(charts): bump chart versions for released services" \ | ||
| "")" | ||
|
|
||
| # 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(charts): bump chart versions for released services" \ | ||
| --body "${body}" | ||
| fi | ||
|
|
||
| - name: Surface refusals | ||
| if: steps.bump.outputs.refused != '' | ||
| # Last, so it does not stop the safe bumps from being opened. A chart | ||
| # that wanted a bump and could not take one is a finding, and a green | ||
| # run would bury it. | ||
| env: | ||
| # Via env, not ${{ }} interpolation: expanding it into the script body | ||
| # is the standard Actions injection shape, even for text this | ||
| # repository produced. | ||
| REFUSED: ${{ steps.bump.outputs.refused }} | ||
| run: | | ||
| echo "::error::charts refused the bump:" | ||
| printf '%s\n' "${REFUSED}" | ||
| exit 1 | ||
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. | ||
| /chart-version-bumper |
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.