Merge pull request #256 from NVIDIA/feat/cherry-pick #17
Workflow file for this run
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
| # SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. | |
| # SPDX-License-Identifier: Apache-2.0 | |
| # | |
| # | |
| # Licensed under the Apache License, Version 2.0 (the "License"); | |
| # you may not use this file except in compliance with the License. | |
| # You may obtain a copy of the License at | |
| # | |
| # http://www.apache.org/licenses/LICENSE-2.0 | |
| # | |
| # Unless required by applicable law or agreed to in writing, software | |
| # distributed under the License is distributed on an "AS IS" BASIS, | |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| # See the License for the specific language governing permissions and | |
| # limitations under the License. | |
| name: Create GitHub Release | |
| # Triggered on tag pushes for operator, agent, and chart components. | |
| # CLI releases are handled separately by cli-release.yaml (which also builds binaries). | |
| # | |
| # Tags follow the pattern <component>/v<version> (e.g. operator/v0.14.0). | |
| # Release notes are generated by git-cliff scoped to the tagged component. | |
| # Preview locally with: make changelog-preview COMPONENT=<name> | |
| on: | |
| push: | |
| tags: | |
| - 'operator/**' | |
| - 'agent/**' | |
| - 'chart/**' | |
| jobs: | |
| release: | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: write | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Extract component name from tag | |
| id: extract | |
| run: | | |
| COMPONENT=$(echo "${{ github.ref_name }}" | cut -f 1 -d /) | |
| echo "component=${COMPONENT}" >> $GITHUB_OUTPUT | |
| - name: Install git-cliff | |
| run: | | |
| GC_VERSION=$(curl -sSf https://api.github.com/repos/orhun/git-cliff/releases/latest | grep '"tag_name"' | cut -d '"' -f 4) | |
| curl -sSL "https://github.com/orhun/git-cliff/releases/download/${GC_VERSION}/git-cliff-${GC_VERSION#v}-x86_64-unknown-linux-gnu.tar.gz" \ | |
| | tar xz --wildcards "*/git-cliff" --strip-components=1 | |
| sudo mv git-cliff /usr/local/bin/ | |
| - name: Generate release notes | |
| id: release_notes | |
| run: | | |
| COMPONENT="${{ steps.extract.outputs.component }}" | |
| INCLUDE_PATH="${COMPONENT}/**" | |
| # CLI code lives under operator/cmd/cli/ but is handled by cli-release.yaml | |
| NOTES=$(git cliff \ | |
| --include-path "${INCLUDE_PATH}" \ | |
| --tag-pattern "${COMPONENT}/.*" \ | |
| --latest \ | |
| --strip all) | |
| echo "notes<<EOF" >> $GITHUB_OUTPUT | |
| echo "${NOTES}" >> $GITHUB_OUTPUT | |
| echo "EOF" >> $GITHUB_OUTPUT | |
| - name: Set up Go | |
| uses: actions/setup-go@v6 | |
| with: | |
| go-version-file: operator/go.mod | |
| cache: false | |
| - name: Set up Python | |
| uses: actions/setup-python@v6 | |
| with: | |
| # Pinned to match agent-ci.yaml PYTHON_VERSION so notices generation | |
| # uses the same interpreter as the agent build. | |
| python-version: '3.13' | |
| - name: Install go-licenses | |
| run: make -C operator go-licenses | |
| - name: Regenerate third-party notices | |
| run: make notices | |
| - name: Create GitHub Release | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| # Idempotent: skip create if the release already exists so workflow | |
| # re-runs on the same tag can still reach the upload step below. | |
| run: | | |
| PRERELEASE_FLAG="" | |
| # Release candidate tags (e.g. operator/v0.16.0-rc1) → mark as | |
| # prerelease so they don't become "Latest" on the Releases page. | |
| # Only -rc<N> is accepted; any other suffix after the version is | |
| # rejected to keep the tag format predictable. | |
| VERSION="${GITHUB_REF_NAME#*/}" | |
| if [[ "${VERSION}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then | |
| : # plain release | |
| elif [[ "${VERSION}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+-rc[0-9]+$ ]]; then | |
| PRERELEASE_FLAG="--prerelease" | |
| else | |
| echo "ERROR: tag '${GITHUB_REF_NAME}' does not match v<MAJOR>.<MINOR>.<PATCH> or v<MAJOR>.<MINOR>.<PATCH>-rc<N>" >&2 | |
| exit 1 | |
| fi | |
| if ! gh release view "${{ github.ref_name }}" >/dev/null 2>&1; then | |
| gh release create "${{ github.ref_name }}" \ | |
| --title "${{ github.ref_name }}" \ | |
| --notes "${{ steps.release_notes.outputs.notes }}" \ | |
| ${PRERELEASE_FLAG} | |
| fi | |
| - name: Upload third-party notices to release | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| COMPONENT="${{ steps.extract.outputs.component }}" | |
| case "${COMPONENT}" in | |
| operator) NOTICES_FILE="operator/THIRD_PARTY_NOTICES.md" ;; | |
| agent) NOTICES_FILE="agent/THIRD_PARTY_NOTICES.md" ;; | |
| chart) NOTICES_FILE="THIRD_PARTY_NOTICES.md" ;; | |
| *) | |
| echo "ERROR: unknown component '${COMPONENT}' — no notices file mapping." >&2 | |
| exit 1 | |
| ;; | |
| esac | |
| gh release upload "${{ github.ref_name }}" "${NOTICES_FILE}" --clobber | |
| publish-chart: | |
| if: startsWith(github.ref_name, 'chart/') | |
| needs: release | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| packages: write | |
| attestations: write | |
| id-token: write | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| - name: Set up Helm | |
| uses: azure/setup-helm@v4 | |
| with: | |
| version: v3.16.2 | |
| - name: Verify chart version matches tag | |
| id: chart-metadata | |
| run: | | |
| CHART_NAME=$(yq '.name' chart/Chart.yaml) | |
| TAG_VERSION="${GITHUB_REF_NAME#chart/}" | |
| CHART_VERSION=$(yq '.version' chart/Chart.yaml) | |
| if [ "${TAG_VERSION}" != "${CHART_VERSION}" ]; then | |
| echo "Tag version ${TAG_VERSION} does not match chart/Chart.yaml version ${CHART_VERSION}" >&2 | |
| exit 1 | |
| fi | |
| echo "name=${CHART_NAME}" >> "${GITHUB_OUTPUT}" | |
| echo "version=${CHART_VERSION}" >> "${GITHUB_OUTPUT}" | |
| - name: Package chart | |
| run: | | |
| mkdir -p dist | |
| helm package chart/ --destination dist/ | |
| - name: Log in to ghcr.io | |
| run: | | |
| echo "${{ secrets.GITHUB_TOKEN }}" \ | |
| | helm registry login ghcr.io --username "${{ github.actor }}" --password-stdin | |
| # cosign reads ~/.docker/config.json; helm registry login writes its own | |
| # config so cosign can't see those creds. Authenticate docker too so the | |
| # downstream sign/attest steps can upload the .sig and SBOM layers. | |
| - name: Log in to ghcr.io (docker, for cosign) | |
| uses: docker/login-action@v4 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Push chart to ghcr.io | |
| id: push-chart | |
| env: | |
| CHART_NAME: ${{ steps.chart-metadata.outputs.name }} | |
| CHART_VERSION: ${{ steps.chart-metadata.outputs.version }} | |
| run: | | |
| set -euo pipefail | |
| chart_repo="ghcr.io/nvidia/nodewright/charts" | |
| chart_subject="${chart_repo}/${CHART_NAME}" | |
| # `helm push` writes "Pushed:" and "Digest:" to stderr (helm 3.16+), | |
| # so capture both streams or awk sees an empty string. | |
| push_output="$(helm push "dist/${CHART_NAME}-${CHART_VERSION}.tgz" "oci://${chart_repo}" 2>&1)" | |
| printf '%s\n' "${push_output}" | |
| chart_digest="$(awk '/^Digest:/ {print $2}' <<< "${push_output}")" | |
| if ! [[ "${chart_digest}" =~ ^sha256:[a-f0-9]{64}$ ]]; then | |
| echo "::error::failed to parse Helm chart digest from helm push output" | |
| exit 1 | |
| fi | |
| echo "subject-name=${chart_subject}" >> "${GITHUB_OUTPUT}" | |
| echo "digest=${chart_digest}" >> "${GITHUB_OUTPUT}" | |
| - name: Sign GHCR Helm chart and attach SBOM | |
| uses: ./.github/actions/cosign-sign-sbom | |
| with: | |
| subject-name: ${{ steps.push-chart.outputs.subject-name }} | |
| subject-digest: ${{ steps.push-chart.outputs.digest }} | |
| sbom-source: chart | |
| - name: Attest GHCR Helm chart provenance | |
| uses: actions/attest-build-provenance@a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32 # v4.1.0 | |
| with: | |
| subject-name: ${{ steps.push-chart.outputs.subject-name }} | |
| subject-digest: ${{ steps.push-chart.outputs.digest }} | |
| push-to-registry: true | |
| - name: Verify GHCR Helm chart signature and attestations | |
| uses: ./.github/actions/cosign-verify-release | |
| with: | |
| subject-name: ${{ steps.push-chart.outputs.subject-name }} | |
| subject-digest: ${{ steps.push-chart.outputs.digest }} | |
| certificate-identity-regexp: ^https://github.com/${{ github.repository }}/\.github/workflows/release\.yml@refs/tags/chart/.*$ |