mirror of
https://github.com/NVIDIA/OpenShell.git
synced 2026-10-29 07:23:25 +08:00
* ci(release): infer optional workflow tags Signed-off-by: Simon Scatton <sscatton@nvidia.com> * fix(release): use resolved tag in success notifications Signed-off-by: Simon Scatton <sscatton@nvidia.com> * fix(release): pin prerelease notes to resolved candidate Signed-off-by: Simon Scatton <sscatton@nvidia.com> --------- Signed-off-by: Simon Scatton <sscatton@nvidia.com>
177 lines
6.5 KiB
YAML
177 lines
6.5 KiB
YAML
# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
|
# SPDX-License-Identifier: Apache-2.0
|
|
|
|
name: Release
|
|
|
|
on:
|
|
workflow_dispatch:
|
|
inputs:
|
|
tag:
|
|
description: "Stable version (e.g. v0.0.4); omit to detect the highest prerelease version on this branch"
|
|
required: false
|
|
type: string
|
|
notes-only:
|
|
description: "Generate a release notes artifact without promoting or publishing"
|
|
required: false
|
|
default: false
|
|
type: boolean
|
|
|
|
permissions:
|
|
contents: read
|
|
|
|
concurrency:
|
|
group: ${{ inputs.notes-only && format('release-notes-preview-{0}', github.ref) || 'release-stable' }}
|
|
cancel-in-progress: false
|
|
queue: max
|
|
|
|
jobs:
|
|
preview-notes:
|
|
name: Preview Release Notes
|
|
if: ${{ inputs.notes-only }}
|
|
permissions:
|
|
contents: read
|
|
pull-requests: read
|
|
uses: ./.github/workflows/release-notes.yml
|
|
with:
|
|
release-tag: ${{ inputs.tag }}
|
|
secrets:
|
|
LLM_INFERENCE_API_KEY: ${{ secrets.LLM_INFERENCE_API_KEY }}
|
|
|
|
promote:
|
|
name: Select Qualified Prerelease
|
|
if: ${{ !inputs.notes-only }}
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 15
|
|
permissions:
|
|
actions: read
|
|
contents: write
|
|
outputs:
|
|
tag: ${{ steps.candidate.outputs.tag }}
|
|
source-sha: ${{ steps.candidate.outputs.source_sha }}
|
|
make-latest: ${{ steps.candidate.outputs.make_latest }}
|
|
notes-reused: ${{ steps.notes.outputs.reused }}
|
|
steps:
|
|
- name: Require a release branch
|
|
env:
|
|
REF_TYPE: ${{ github.ref_type }}
|
|
run: test "${REF_TYPE}" = branch
|
|
|
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
with:
|
|
fetch-depth: 0
|
|
persist-credentials: false
|
|
|
|
- uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
|
|
with:
|
|
version: "0.10.12"
|
|
python-version: "3.13"
|
|
|
|
- name: Select candidate and verify qualification evidence
|
|
id: candidate
|
|
env:
|
|
GH_TOKEN: ${{ github.token }}
|
|
RELEASE_TAG: ${{ inputs.tag }}
|
|
RELEASE_BRANCH: ${{ github.ref_name }}
|
|
run: |
|
|
set -euo pipefail
|
|
uv run --no-project python tasks/scripts/select-release-candidate.py \
|
|
"${RELEASE_TAG}" --branch "${RELEASE_BRANCH}" > promotion.json
|
|
jq -r 'to_entries[] | "\(.key)=\(.value)"' promotion.json >> "${GITHUB_OUTPUT}"
|
|
{
|
|
echo "## Stable release promotion"
|
|
jq -r '"- Prerelease: `\(.candidate_tag)`\n- Stable tag: `\(.tag)`\n- Source commit: `\(.source_sha)`\n- Qualification: \(.qualification_run_url)\n- Update shared latest channels: \(.make_latest)"' promotion.json
|
|
echo
|
|
echo "Stable artifacts are rebuilt from this qualified commit with the stable version."
|
|
} >> "${GITHUB_STEP_SUMMARY}"
|
|
|
|
- name: Retain promotion record
|
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
|
with:
|
|
name: stable-promotion-${{ steps.candidate.outputs.tag }}-${{ github.run_id }}-${{ github.run_attempt }}
|
|
path: promotion.json
|
|
retention-days: 90
|
|
if-no-files-found: error
|
|
|
|
# Without a preview (missing or expired), Publish generates the notes instead.
|
|
- name: Reuse the prerelease notes preview
|
|
id: notes
|
|
env:
|
|
GH_TOKEN: ${{ github.token }}
|
|
RUN_ID: ${{ steps.candidate.outputs.qualification_run_id }}
|
|
CANDIDATE_TAG: ${{ steps.candidate.outputs.candidate_tag }}
|
|
RELEASE_TAG: ${{ steps.candidate.outputs.tag }}
|
|
run: |
|
|
set -euo pipefail
|
|
if gh run download "${RUN_ID}" -R "${GITHUB_REPOSITORY}" -n release-notes -D notes; then
|
|
sed -i "s/${CANDIDATE_TAG//./\\.}\b/${RELEASE_TAG}/g" notes/release-notes.md
|
|
echo "reused=true" >> "${GITHUB_OUTPUT}"
|
|
echo "Release notes are reused from the \`${CANDIDATE_TAG}\` preview." >> "${GITHUB_STEP_SUMMARY}"
|
|
else
|
|
echo "Release notes will be regenerated; the \`${CANDIDATE_TAG}\` preview is unavailable." >> "${GITHUB_STEP_SUMMARY}"
|
|
fi
|
|
|
|
- name: Upload release notes
|
|
if: ${{ steps.notes.outputs.reused == 'true' }}
|
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
|
with:
|
|
name: release-notes
|
|
path: notes/release-notes.md
|
|
if-no-files-found: error
|
|
|
|
- name: Create stable tag at the qualified commit
|
|
env:
|
|
GH_TOKEN: ${{ github.token }}
|
|
RELEASE_TAG: ${{ steps.candidate.outputs.tag }}
|
|
SOURCE_SHA: ${{ steps.candidate.outputs.source_sha }}
|
|
run: |
|
|
set -euo pipefail
|
|
if git show-ref --verify --quiet "refs/tags/${RELEASE_TAG}"; then
|
|
test "$(git rev-parse "refs/tags/${RELEASE_TAG}^{commit}")" = "${SOURCE_SHA}"
|
|
else
|
|
gh api --method POST "repos/${GITHUB_REPOSITORY}/git/refs" \
|
|
-f "ref=refs/tags/${RELEASE_TAG}" -f "sha=${SOURCE_SHA}"
|
|
fi
|
|
|
|
build:
|
|
name: Build
|
|
needs: promote
|
|
permissions:
|
|
actions: read
|
|
contents: read
|
|
packages: write
|
|
uses: ./.github/workflows/release-build.yml
|
|
with:
|
|
release-tag: ${{ needs.promote.outputs.tag }}
|
|
expected-source-sha: ${{ needs.promote.outputs.source-sha }}
|
|
is-prerelease: false
|
|
secrets:
|
|
CACHIX_AUTH_TOKEN: ${{ secrets.CACHIX_AUTH_TOKEN }}
|
|
|
|
publish:
|
|
name: Publish
|
|
needs: [promote, build]
|
|
permissions:
|
|
actions: read
|
|
contents: write
|
|
pull-requests: read
|
|
packages: write
|
|
id-token: write
|
|
attestations: write
|
|
artifact-metadata: write
|
|
uses: ./.github/workflows/release-publish.yml
|
|
with:
|
|
release-tag: ${{ needs.promote.outputs.tag }}
|
|
source-sha: ${{ needs.build.outputs.source_sha }}
|
|
semver: ${{ needs.build.outputs.semver }}
|
|
python-version: ${{ needs.build.outputs.python_version }}
|
|
npm-version: ${{ needs.build.outputs.npm_version }}
|
|
is-prerelease: false
|
|
make-latest: ${{ needs.promote.outputs.make-latest == 'true' }}
|
|
release-notes-reused: ${{ needs.promote.outputs.notes-reused == 'true' }}
|
|
secrets:
|
|
SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAPCRAFT_STORE_CREDENTIALS }}
|
|
FERN_TOKEN: ${{ secrets.FERN_TOKEN }}
|
|
GITLAB_CI_TRIGGER_TOKEN: ${{ secrets.GITLAB_CI_TRIGGER_TOKEN }}
|
|
GITLAB_CI_TRIGGER_URL: ${{ secrets.GITLAB_CI_TRIGGER_URL }}
|
|
LLM_INFERENCE_API_KEY: ${{ secrets.LLM_INFERENCE_API_KEY }}
|