diff --git a/.github/workflows/release-dev.yml b/.github/workflows/release-dev.yml index aec77972e..cb489c3ee 100644 --- a/.github/workflows/release-dev.yml +++ b/.github/workflows/release-dev.yml @@ -265,12 +265,15 @@ jobs: publish-snap: name: Publish snap needs: build-snap + permissions: + actions: read + contents: read uses: ./.github/workflows/snap-publish.yml with: upload-channel: latest/edge github-environment: latest/edge secrets: - publish-credentials: ${{ secrets.SNAPCRAFT_STORE_CREDENTIALS }} + SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAPCRAFT_STORE_CREDENTIALS }} # --------------------------------------------------------------------------- # Create / update the dev GitHub Release with CLI, gateway, driver, and wheels diff --git a/.github/workflows/release-publish.yml b/.github/workflows/release-publish.yml index ba8493f51..d48bb9cc2 100644 --- a/.github/workflows/release-publish.yml +++ b/.github/workflows/release-publish.yml @@ -509,89 +509,45 @@ jobs: name: Publish Snap needs: release if: ${{ !inputs.is-prerelease && inputs.make-latest }} + permissions: + actions: read + contents: read uses: ./.github/workflows/snap-publish.yml with: upload-channel: latest/stable github-environment: latest/stable release-tag: ${{ inputs.release-tag }} secrets: - publish-credentials: ${{ secrets.SNAPCRAFT_STORE_CREDENTIALS }} + SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAPCRAFT_STORE_CREDENTIALS }} publish-prerelease-snap: name: Publish Prerelease Snap needs: release if: inputs.is-prerelease + permissions: + actions: read + contents: read uses: ./.github/workflows/snap-publish.yml with: upload-channel: latest/beta release-tag: ${{ inputs.release-tag }} github-environment: latest/beta secrets: - publish-credentials: ${{ secrets.SNAPCRAFT_STORE_CREDENTIALS }} + SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAPCRAFT_STORE_CREDENTIALS }} publish-sdk-typescript: name: Publish TypeScript SDK needs: release if: ${{ !inputs.is-prerelease }} - runs-on: linux-amd64-cpu8 - timeout-minutes: 15 permissions: contents: read packages: write - defaults: - run: - shell: nix shell --inputs-from . nixpkgs#nodejs_24 -c bash -euo pipefail {0} - steps: - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - persist-credentials: false - ref: ${{ inputs.source-sha }} - - - uses: ./.github/actions/setup-nix - - - name: Install TypeScript dependencies - working-directory: ./sdk/typescript - run: npm ci - - - name: Configure npm auth for GitHub Packages - working-directory: ./sdk/typescript - run: | - { - echo "@nvidia:registry=https://npm.pkg.github.com" - # Keep the token reference literal for npm to expand at publish time. - # shellcheck disable=SC2016 - echo '//npm.pkg.github.com/:_authToken=${NODE_AUTH_TOKEN}' - } > .npmrc - - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - if: inputs.make-latest - with: - persist-credentials: false - ref: ${{ github.workflow_sha }} - path: release-workflow - sparse-checkout: .github/actions/check-latest-release - - - name: Check current latest release - id: latest - if: inputs.make-latest - uses: ./release-workflow/.github/actions/check-latest-release - with: - release-tag: ${{ inputs.release-tag }} - - - name: Publish - working-directory: ./sdk/typescript - env: - OPENSHELL_NPM_VERSION: ${{ inputs.npm-version }} - MAKE_LATEST: ${{ steps.latest.outputs.make-latest }} - NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - set -euo pipefail - npm pkg set version="${OPENSHELL_NPM_VERSION}" - dist_tag=latest - if [[ "${MAKE_LATEST}" != "true" ]]; then - dist_tag="release-${OPENSHELL_NPM_VERSION%.*}" - fi - npm publish --tag "${dist_tag}" + uses: ./.github/workflows/sdk-typescript-publish.yml + with: + release-tag: ${{ inputs.release-tag }} + source-ref: ${{ inputs.source-sha }} + npm-version: ${{ inputs.npm-version }} + make-latest: ${{ inputs.make-latest }} # Go resolves submodule versions from directory-prefixed tags, so the SDK at # sdk/go needs its own sdk/go/vX.Y.Z tag to show up as a release on pkg.go.dev. @@ -629,41 +585,15 @@ jobs: name: Trigger Wheel Publish needs: release if: ${{ !inputs.is-prerelease }} - runs-on: [self-hosted, nv] - timeout-minutes: 10 - steps: - - name: Trigger GitLab CI - env: - GITLAB_CI_TRIGGER_TOKEN: ${{ secrets.GITLAB_CI_TRIGGER_TOKEN }} - GITLAB_CI_TRIGGER_URL: ${{ secrets.GITLAB_CI_TRIGGER_URL }} - COMMIT_SHA: ${{ inputs.source-sha }} - RELEASE_VERSION: ${{ inputs.python-version }} - RELEASE_TAG: ${{ env.RELEASE_TAG }} - WHEEL_FILENAMES: ${{ needs.release.outputs.wheel_filenames }} - run: | - set -euo pipefail - if [ -z "${WHEEL_FILENAMES}" ]; then - echo "No wheel filenames provided by build job" >&2 - exit 1 - fi - - response=$(curl -X POST \ - --fail \ - --silent \ - --show-error \ - -F "token=${GITLAB_CI_TRIGGER_TOKEN}" \ - -F "ref=main" \ - -F "variables[PIPELINE_ACTION]=publish_wheels" \ - -F "variables[GITHUB_REPOSITORY]=${GITHUB_REPOSITORY}" \ - -F "variables[COMMIT_SHA]=${COMMIT_SHA}" \ - -F "variables[RELEASE_TAG]=${RELEASE_TAG}" \ - -F "variables[RELEASE_VERSION]=${RELEASE_VERSION}" \ - -F "variables[RELEASE_KIND]=stable" \ - -F "variables[WHEEL_FILENAMES]=${WHEEL_FILENAMES}" \ - "${GITLAB_CI_TRIGGER_URL}") - pipeline_id=$(printf '%s' "$response" | sed -n 's/.*"id":\([0-9][0-9]*\).*/\1/p') - pipeline_status=$(printf '%s' "$response" | sed -n 's/.*"status":"\([^"]*\)".*/\1/p') - echo "Triggered GitLab pipeline ${pipeline_id:-unknown} with status=${pipeline_status:-unknown}" + uses: ./.github/workflows/wheel-publish.yml + with: + release-tag: ${{ inputs.release-tag }} + source-sha: ${{ inputs.source-sha }} + python-version: ${{ inputs.python-version }} + wheel-filenames: ${{ needs.release.outputs.wheel_filenames }} + secrets: + GITLAB_CI_TRIGGER_TOKEN: ${{ secrets.GITLAB_CI_TRIGGER_TOKEN }} + GITLAB_CI_TRIGGER_URL: ${{ secrets.GITLAB_CI_TRIGGER_URL }} publish-fern-docs: name: Sync and Publish Fern Docs diff --git a/.github/workflows/sdk-typescript-publish.yml b/.github/workflows/sdk-typescript-publish.yml new file mode 100644 index 000000000..a10ff0d28 --- /dev/null +++ b/.github/workflows/sdk-typescript-publish.yml @@ -0,0 +1,102 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +name: Publish TypeScript SDK + +on: + workflow_call: + inputs: + release-tag: + required: true + type: string + source-ref: + description: Commit to build the package from + required: true + type: string + npm-version: + required: true + type: string + make-latest: + description: Whether this release may update the npm latest dist-tag + required: false + default: false + type: boolean + workflow_dispatch: + inputs: + release-tag: + description: Stable tag to publish, e.g. v0.1.6 + required: true + type: string + npm-version: + description: npm package version, e.g. 0.1.6 + required: true + type: string + make-latest: + description: Update the latest dist-tag if this is still the newest stable release + required: false + default: true + type: boolean + +permissions: + contents: read + packages: write + +jobs: + publish: + name: Publish TypeScript SDK + runs-on: linux-amd64-cpu8 + timeout-minutes: 15 + defaults: + run: + shell: nix shell --inputs-from . nixpkgs#nodejs_24 -c bash -euo pipefail {0} + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + ref: ${{ inputs.source-ref || inputs.release-tag }} + + - uses: ./.github/actions/setup-nix + + - name: Install TypeScript dependencies + working-directory: ./sdk/typescript + run: npm ci + + - name: Configure npm auth for GitHub Packages + working-directory: ./sdk/typescript + run: | + { + echo "@nvidia:registry=https://npm.pkg.github.com" + # Keep the token reference literal for npm to expand at publish time. + # shellcheck disable=SC2016 + echo '//npm.pkg.github.com/:_authToken=${NODE_AUTH_TOKEN}' + } > .npmrc + + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + if: inputs.make-latest + with: + persist-credentials: false + ref: ${{ github.workflow_sha }} + path: release-workflow + sparse-checkout: .github/actions/check-latest-release + + - name: Check current latest release + id: latest + if: inputs.make-latest + uses: ./release-workflow/.github/actions/check-latest-release + with: + release-tag: ${{ inputs.release-tag }} + + - name: Publish + working-directory: ./sdk/typescript + env: + OPENSHELL_NPM_VERSION: ${{ inputs.npm-version }} + MAKE_LATEST: ${{ steps.latest.outputs.make-latest }} + NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + set -euo pipefail + npm pkg set version="${OPENSHELL_NPM_VERSION}" + dist_tag=latest + if [[ "${MAKE_LATEST}" != "true" ]]; then + dist_tag="release-${OPENSHELL_NPM_VERSION%.*}" + fi + npm publish --tag "${dist_tag}" diff --git a/.github/workflows/snap-publish.yml b/.github/workflows/snap-publish.yml index 670522fd3..b15616fb1 100644 --- a/.github/workflows/snap-publish.yml +++ b/.github/workflows/snap-publish.yml @@ -18,10 +18,30 @@ on: type: string default: "" secrets: - publish-credentials: + SNAPCRAFT_STORE_CREDENTIALS: required: true + workflow_dispatch: + inputs: + upload-channel: + description: Snap Store channel, also used as the GitHub environment + required: true + type: choice + options: + - latest/stable + - latest/beta + - latest/edge + release-tag: + description: Stable tag to recheck before updating the stable channel; required for latest/stable + required: false + type: string + default: "" + artifact-run-id: + description: Run whose snap-linux-* artifacts are uploaded + required: true + type: string permissions: + actions: read contents: read defaults: @@ -40,7 +60,7 @@ jobs: runner: linux-arm64-cpu8 runs-on: ${{ matrix.runner }} timeout-minutes: 10 - environment: ${{ inputs.github-environment }} + environment: ${{ inputs.github-environment || inputs.upload-channel }} concurrency: group: snap-publish-${{ inputs.upload-channel }}-${{ matrix.arch }} cancel-in-progress: false @@ -58,6 +78,8 @@ jobs: uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: snap-linux-${{ matrix.arch }} + run-id: ${{ inputs.artifact-run-id || github.run_id }} + github-token: ${{ inputs.artifact-run-id && github.token || '' }} - name: Install snapcraft run: | @@ -79,10 +101,15 @@ jobs: - name: Upload snap to Snap Store if: ${{ inputs.release-tag == '' || steps.latest.outputs.make-latest == 'true' }} env: - SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.publish-credentials }} + SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAPCRAFT_STORE_CREDENTIALS }} INPUTS_UPLOAD_CHANNEL: ${{ inputs.upload-channel }} + INPUTS_RELEASE_TAG: ${{ inputs.release-tag }} run: | set -euo pipefail + if [[ "${INPUTS_UPLOAD_CHANNEL}" == latest/stable && -z "${INPUTS_RELEASE_TAG}" ]]; then + echo "latest/stable uploads require release-tag" >&2 + exit 1 + fi shopt -s nullglob snaps=(*.snap) if [[ "${#snaps[@]}" -ne 1 ]]; then diff --git a/.github/workflows/wheel-publish.yml b/.github/workflows/wheel-publish.yml new file mode 100644 index 000000000..fa2ad83d0 --- /dev/null +++ b/.github/workflows/wheel-publish.yml @@ -0,0 +1,85 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +name: Publish Wheels + +on: + workflow_call: + inputs: + release-tag: + required: true + type: string + source-sha: + required: true + type: string + python-version: + required: true + type: string + wheel-filenames: + required: true + type: string + secrets: + GITLAB_CI_TRIGGER_TOKEN: + required: false + GITLAB_CI_TRIGGER_URL: + required: false + workflow_dispatch: + inputs: + release-tag: + description: Stable tag whose GitHub Release holds the wheels, e.g. v0.1.6 + required: true + type: string + source-sha: + description: Commit the tag points to + required: true + type: string + python-version: + description: Python package version, e.g. 0.1.6 + required: true + type: string + wheel-filenames: + description: Wheel filenames attached to the GitHub Release + required: true + type: string + +permissions: {} + +jobs: + trigger: + name: Trigger Wheel Publish + runs-on: [self-hosted, nv] + timeout-minutes: 10 + steps: + - name: Trigger GitLab CI + shell: bash + env: + GITLAB_CI_TRIGGER_TOKEN: ${{ secrets.GITLAB_CI_TRIGGER_TOKEN }} + GITLAB_CI_TRIGGER_URL: ${{ secrets.GITLAB_CI_TRIGGER_URL }} + COMMIT_SHA: ${{ inputs.source-sha }} + RELEASE_VERSION: ${{ inputs.python-version }} + RELEASE_TAG: ${{ inputs.release-tag }} + WHEEL_FILENAMES: ${{ inputs.wheel-filenames }} + run: | + set -euo pipefail + if [ -z "${WHEEL_FILENAMES}" ]; then + echo "No wheel filenames provided by build job" >&2 + exit 1 + fi + + response=$(curl -X POST \ + --fail \ + --silent \ + --show-error \ + -F "token=${GITLAB_CI_TRIGGER_TOKEN}" \ + -F "ref=main" \ + -F "variables[PIPELINE_ACTION]=publish_wheels" \ + -F "variables[GITHUB_REPOSITORY]=${GITHUB_REPOSITORY}" \ + -F "variables[COMMIT_SHA]=${COMMIT_SHA}" \ + -F "variables[RELEASE_TAG]=${RELEASE_TAG}" \ + -F "variables[RELEASE_VERSION]=${RELEASE_VERSION}" \ + -F "variables[RELEASE_KIND]=stable" \ + -F "variables[WHEEL_FILENAMES]=${WHEEL_FILENAMES}" \ + "${GITLAB_CI_TRIGGER_URL}") + pipeline_id=$(printf '%s' "$response" | sed -n 's/.*"id":\([0-9][0-9]*\).*/\1/p') + pipeline_status=$(printf '%s' "$response" | sed -n 's/.*"status":"\([^"]*\)".*/\1/p') + echo "Triggered GitLab pipeline ${pipeline_id:-unknown} with status=${pipeline_status:-unknown}" diff --git a/CI.md b/CI.md index 5252a4f33..31aef9d70 100644 --- a/CI.md +++ b/CI.md @@ -718,7 +718,9 @@ These workflows run after merge to publish dev/tagged artifacts and verify them. | `.github/workflows/release-notes.yml` | Shared prompt-driven release notes generation through Inference Hub, used by prerelease previews, notes-only previews, and stable publication when no preview is reused. | | `.github/workflows/release-qualification.yml` | Reusable qualification suites and result aggregation. Runs security scans, conformance and feature integration, Docker and VM E2E, protobuf compatibility, and advisory compatibility review. | | `.github/workflows/snap-package.yml` | Builds snap and component artifacts for Release Dev, Release, and Pre-release without Store credentials or publication. | -| `.github/workflows/snap-publish.yml` | Uploads existing snap and component artifacts directly to a Store channel without rebuilding. Release Dev uses `latest/edge`, Pre-release uses `latest/beta`, and Release uses `latest/stable` for the stable rebuild. Uploads queue per channel and architecture, so tagged beta and stable jobs recheck repository tags after earlier publication finishes and before updating their floating channels. | +| `.github/workflows/snap-publish.yml` | Uploads existing snap and component artifacts directly to a Store channel without rebuilding. Release Dev uses `latest/edge`, Pre-release uses `latest/beta`, and Release uses `latest/stable` for the stable rebuild. Uploads queue per channel and architecture, so tagged beta and stable jobs recheck repository tags after earlier publication finishes and before updating their floating channels. Manual dispatch uploads the `snap-linux-*` artifacts from `artifact-run-id`. | +| `.github/workflows/sdk-typescript-publish.yml` | Publishes the TypeScript SDK to GitHub Packages. Called by stable Release; manual dispatch publishes a given tag and npm version. | +| `.github/workflows/wheel-publish.yml` | Triggers the GitLab pipeline that publishes wheels from a GitHub Release to PyPI. Called by stable Release; manual dispatch republishes a given tag. | | `.github/workflows/release-canary.yml` | Smoke-tests published dev artifacts in the `macos`, `ubuntu-deb`, `ubuntu-snap-system-docker`, `fedora`, and `kubernetes` (kind + Helm) jobs. Each job reaches its gateway and creates, exercises, and deletes a sandbox. The `ubuntu-snap-system-docker` job verifies a compatible system Docker lifecycle, while `ubuntu-snap-docker-preflight` tests fail-fast behavior when Docker is absent or supplied by the Docker snap. The positive snap lane also runs a local policy containment check with the packaged prover. It runs automatically after `Release Dev` succeeds and supports manual dispatch (`gh workflow run release-canary.yml --ref `). See the `test-release-canary` skill for the playbook and local kind reproduction. | ## Required status contexts