mirror of
https://github.com/NVIDIA/OpenShell.git
synced 2026-10-20 06:00:06 +08:00
chore: merge main into supervisor middleware SDK branch
Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
This commit is contained in:
@@ -7,11 +7,10 @@ contact_links:
|
||||
Vouch Request discussion describing what you want to work on. A
|
||||
maintainer will approve you with /vouch.
|
||||
- name: Have a question?
|
||||
url: https://github.com/NVIDIA/OpenShell/blob/main/CONTRIBUTING.md#agent-skills-for-contributors
|
||||
url: https://github.com/NVIDIA/OpenShell/blob/main/CONTRIBUTING.md#skills-for-using-openshell
|
||||
about: >
|
||||
Point your agent at the repo. It has skills for CLI usage, cluster and
|
||||
inference debugging, policy generation, and more. See CONTRIBUTING.md
|
||||
for the full skills table.
|
||||
Install the public OpenShell skills for CLI usage, cluster and inference
|
||||
debugging, and policy generation. See CONTRIBUTING.md for details.
|
||||
- name: Security vulnerability?
|
||||
url: https://github.com/NVIDIA/OpenShell/blob/main/SECURITY.md
|
||||
about: >
|
||||
|
||||
@@ -14,9 +14,6 @@ inputs:
|
||||
triple:
|
||||
description: Rust target triple
|
||||
required: true
|
||||
dev-shell:
|
||||
description: Nix development shell used to build the binary
|
||||
required: true
|
||||
cargo-version:
|
||||
description: Cargo package version embedded in the binary
|
||||
required: true
|
||||
@@ -32,10 +29,6 @@ inputs:
|
||||
description: Additional flags passed to cargo build
|
||||
required: false
|
||||
default: ""
|
||||
interpreter:
|
||||
description: ELF interpreter for a dynamically linked Linux binary
|
||||
required: false
|
||||
default: ""
|
||||
|
||||
runs:
|
||||
using: composite
|
||||
@@ -43,9 +36,9 @@ runs:
|
||||
- name: Hash development shell
|
||||
id: dev-shell
|
||||
shell: bash
|
||||
env:
|
||||
DEV_SHELL: ${{ inputs.dev-shell }}
|
||||
run: echo "hash=$(nix hash file --type sha256 --base16 "$(nix eval --raw "${DEV_SHELL}.drvPath")")" >> "$GITHUB_OUTPUT"
|
||||
run: |
|
||||
shell_drv=$(nix eval --raw --impure .#devShells --apply 'shells: shells.${builtins.currentSystem}.default.drvPath')
|
||||
echo "hash=$(nix hash file --type sha256 --base16 "$shell_drv")" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Cache Rust artifacts
|
||||
uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
|
||||
@@ -54,90 +47,41 @@ runs:
|
||||
cache-on-failure: "true"
|
||||
cache-workspace-crates: "true"
|
||||
cache-bin: "false"
|
||||
cmd-format: nix develop ${{ inputs.dev-shell }} -c {0}
|
||||
cmd-format: nix develop -c {0}
|
||||
|
||||
- name: Set version
|
||||
shell: nix develop ${{ inputs.dev-shell }} -c bash -euo pipefail {0}
|
||||
shell: nix develop -c bash -euo pipefail {0}
|
||||
env:
|
||||
INPUTS_CARGO_VERSION: ${{ inputs.cargo-version }}
|
||||
run: sed -i "s/^version = \"0\\.0\\.0\"$/version = \"${INPUTS_CARGO_VERSION}\"/" Cargo.toml
|
||||
|
||||
- name: Build ${{ inputs.binary }}
|
||||
shell: nix develop ${{ inputs.dev-shell }} -c bash -euo pipefail {0}
|
||||
shell: nix develop -c bash -euo pipefail {0}
|
||||
env:
|
||||
GIT_DIR: /nonexistent
|
||||
OPENSHELL_IMAGE_TAG: ${{ inputs.image-tag }}
|
||||
INPUTS_PACKAGE: ${{ inputs.package }}
|
||||
INPUTS_BINARY: ${{ inputs.binary }}
|
||||
INPUTS_TRIPLE: ${{ inputs.triple }}
|
||||
INPUTS_EXTRA_CARGO_FLAGS: ${{ inputs.extra-cargo-flags }}
|
||||
run: GIT_DIR=/nonexistent cargo auditable build --release --package "${INPUTS_PACKAGE}" --bin "${INPUTS_BINARY}" ${INPUTS_EXTRA_CARGO_FLAGS}
|
||||
|
||||
- name: Normalize Darwin dynamic binary
|
||||
if: endsWith(inputs.triple, '-apple-darwin')
|
||||
shell: bash -euo pipefail {0}
|
||||
env:
|
||||
INPUTS_BINARY: ${{ inputs.binary }}
|
||||
run: |
|
||||
binary=target/release/${INPUTS_BINARY}
|
||||
nix_iconv_paths=$(otool -L "$binary" | awk '$1 ~ "^/nix/store/.*/lib/libiconv\\.2\\.dylib$" { print $1 }')
|
||||
if [[ -n "$nix_iconv_paths" ]]; then
|
||||
while IFS= read -r nix_iconv_path; do
|
||||
install_name_tool -change "$nix_iconv_path" /usr/lib/libiconv.2.dylib "$binary"
|
||||
done <<< "$nix_iconv_paths"
|
||||
codesign --force --sign - "$binary"
|
||||
fi
|
||||
|
||||
- name: Verify Darwin portability
|
||||
if: endsWith(inputs.triple, '-apple-darwin')
|
||||
shell: bash -euo pipefail {0}
|
||||
env:
|
||||
INPUTS_BINARY: ${{ inputs.binary }}
|
||||
run: |
|
||||
binary=target/release/${INPUTS_BINARY}
|
||||
nix_dependencies=$(otool -L "$binary" | awk '$1 ~ "^/nix/store/" { print $1 }')
|
||||
if [[ -n "$nix_dependencies" ]]; then
|
||||
echo "error: $binary has non-portable Nix store dependencies:" >&2
|
||||
printf ' %s\n' "$nix_dependencies" >&2
|
||||
exit 1
|
||||
fi
|
||||
codesign --verify --verbose=2 "$binary"
|
||||
run: cargo auditable build --release --target "${INPUTS_TRIPLE}" --package "${INPUTS_PACKAGE}" --bin "${INPUTS_BINARY}" ${INPUTS_EXTRA_CARGO_FLAGS}
|
||||
|
||||
- name: Verify ${{ inputs.binary }}
|
||||
shell: nix develop ${{ inputs.dev-shell }} -c bash -euo pipefail {0}
|
||||
shell: nix develop -c bash -euo pipefail {0}
|
||||
env:
|
||||
INPUTS_BINARY: ${{ inputs.binary }}
|
||||
INPUTS_TRIPLE: ${{ inputs.triple }}
|
||||
run: |
|
||||
# Confirm the binary runs and reports the expected name.
|
||||
target/release/${INPUTS_BINARY} --version | grep -q "^${INPUTS_BINARY} "
|
||||
target/${INPUTS_TRIPLE}/release/${INPUTS_BINARY} --version | grep -q "^${INPUTS_BINARY} "
|
||||
# Confirm Syft can decode the embedded cargo-auditable metadata.
|
||||
SYFT_CHECK_FOR_APP_UPDATE=false syft file:target/release/${INPUTS_BINARY} -o cyclonedx-json | grep 'pkg:cargo/' > /dev/null
|
||||
|
||||
- name: Verify static linkage
|
||||
if: endsWith(inputs.triple, '-linux-musl')
|
||||
shell: nix develop ${{ inputs.dev-shell }} -c bash -euo pipefail {0}
|
||||
env:
|
||||
INPUTS_BINARY: ${{ inputs.binary }}
|
||||
run: tasks/scripts/verify-static-binary.sh target/release/${INPUTS_BINARY}
|
||||
|
||||
- name: Normalize Linux dynamic binary
|
||||
if: endsWith(inputs.triple, '-linux-gnu')
|
||||
shell: nix develop ${{ inputs.dev-shell }} -c bash -euo pipefail {0}
|
||||
env:
|
||||
INPUTS_BINARY: ${{ inputs.binary }}
|
||||
INPUTS_INTERPRETER: ${{ inputs.interpreter }}
|
||||
run: |
|
||||
binary=target/release/${INPUTS_BINARY}
|
||||
# Remove Nix store paths so the binary can run on other distributions.
|
||||
patchelf --set-interpreter "${INPUTS_INTERPRETER}" --remove-rpath "$binary"
|
||||
# Z3 must be embedded instead of loaded from the target system.
|
||||
test -z "$(patchelf --print-needed "$binary" | grep '^libz3')"
|
||||
# Reject symbols introduced after glibc 2.28.
|
||||
tasks/scripts/verify-glibc-symbols.sh 2.28 "$binary"
|
||||
SYFT_CHECK_FOR_APP_UPDATE=false syft file:target/${INPUTS_TRIPLE}/release/${INPUTS_BINARY} -o cyclonedx-json | grep 'pkg:cargo/' > /dev/null
|
||||
|
||||
- name: Upload ${{ inputs.binary }}
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
with:
|
||||
name: ${{ inputs.artifact-name || format('{0}-{1}', inputs.binary, inputs.triple) }}
|
||||
path: target/release/${{ inputs.binary }}
|
||||
path: target/${{ inputs.triple }}/release/${{ inputs.binary }}
|
||||
compression-level: 0
|
||||
retention-days: 5
|
||||
if-no-files-found: error
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
name: Release Helm OCI
|
||||
description: >
|
||||
Patch chart version/appVersion, refuse duplicate OCI versions on public
|
||||
releases, package the chart, and push to GHCR OCI.
|
||||
releases, package the gateway and workspace charts, and push them to GHCR OCI.
|
||||
|
||||
inputs:
|
||||
chart-version:
|
||||
@@ -51,11 +51,16 @@ runs:
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
CHART_DIR="${RUNNER_TEMP}/chart-build"
|
||||
cp -a deploy/helm/openshell/. "${CHART_DIR}"
|
||||
sed -i "s/^version:.*/version: ${CHART_VERSION}/" "${CHART_DIR}/Chart.yaml"
|
||||
sed -i "s/^appVersion:.*/appVersion: \"${APP_VERSION}\"/" "${CHART_DIR}/Chart.yaml"
|
||||
echo "chart_dir=${CHART_DIR}" >> "$GITHUB_OUTPUT"
|
||||
GATEWAY_CHART_DIR="${RUNNER_TEMP}/gateway-chart-build"
|
||||
WORKSPACE_CHART_DIR="${RUNNER_TEMP}/workspace-chart-build"
|
||||
cp -a deploy/helm/openshell/. "${GATEWAY_CHART_DIR}"
|
||||
cp -a deploy/helm/openshell-workspace/. "${WORKSPACE_CHART_DIR}"
|
||||
for chart_dir in "${GATEWAY_CHART_DIR}" "${WORKSPACE_CHART_DIR}"; do
|
||||
sed -i "s/^version:.*/version: ${CHART_VERSION}/" "${chart_dir}/Chart.yaml"
|
||||
sed -i "s/^appVersion:.*/appVersion: \"${APP_VERSION}\"/" "${chart_dir}/Chart.yaml"
|
||||
done
|
||||
echo "gateway_chart_dir=${GATEWAY_CHART_DIR}" >> "$GITHUB_OUTPUT"
|
||||
echo "workspace_chart_dir=${WORKSPACE_CHART_DIR}" >> "$GITHUB_OUTPUT"
|
||||
echo "chart_version=${CHART_VERSION}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Refuse duplicate chart version
|
||||
@@ -65,38 +70,52 @@ runs:
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
OCI_CHART="oci://ghcr.io/nvidia/openshell/helm-chart"
|
||||
if helm show chart "${OCI_CHART}" --version "${CHART_VERSION}" >/dev/null 2>&1; then
|
||||
echo "::error::Chart ${CHART_VERSION} is already published. Use a new tag or delete the existing package first."
|
||||
exit 1
|
||||
fi
|
||||
for chart in helm-chart openshell-workspace; do
|
||||
OCI_CHART="oci://ghcr.io/nvidia/openshell/${chart}"
|
||||
if helm show chart "${OCI_CHART}" --version "${CHART_VERSION}" >/dev/null 2>&1; then
|
||||
echo "::error::Chart ${chart}:${CHART_VERSION} is already published. Use a new tag or delete the existing package first."
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
- name: Package Helm chart
|
||||
env:
|
||||
CHART_DIR: ${{ steps.prep.outputs.chart_dir }}
|
||||
GATEWAY_CHART_DIR: ${{ steps.prep.outputs.gateway_chart_dir }}
|
||||
WORKSPACE_CHART_DIR: ${{ steps.prep.outputs.workspace_chart_dir }}
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
helm package "${CHART_DIR}" --destination /tmp
|
||||
ls /tmp/helm-chart-*.tgz
|
||||
mkdir -p /tmp/helm-charts
|
||||
helm package "${GATEWAY_CHART_DIR}" --destination /tmp/helm-charts
|
||||
helm package "${WORKSPACE_CHART_DIR}" --destination /tmp/helm-charts
|
||||
ls /tmp/helm-charts/*.tgz
|
||||
|
||||
- name: Push Helm chart to GHCR OCI
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
helm push /tmp/helm-chart-*.tgz oci://ghcr.io/nvidia/openshell
|
||||
for archive in /tmp/helm-charts/*.tgz; do
|
||||
helm push "${archive}" oci://ghcr.io/nvidia/openshell
|
||||
done
|
||||
|
||||
- name: Push SHA-pinned chart
|
||||
if: inputs.pin-sha != ''
|
||||
env:
|
||||
PIN_SHA: ${{ inputs.pin-sha }}
|
||||
CHART_DIR: ${{ steps.prep.outputs.chart_dir }}
|
||||
GATEWAY_CHART_DIR: ${{ steps.prep.outputs.gateway_chart_dir }}
|
||||
WORKSPACE_CHART_DIR: ${{ steps.prep.outputs.workspace_chart_dir }}
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
SHA_CHART_DIR="${RUNNER_TEMP}/chart-build-sha"
|
||||
cp -a "${CHART_DIR}/." "${SHA_CHART_DIR}"
|
||||
sed -i "s/^version:.*/version: 0.0.0-dev.${PIN_SHA}/" "${SHA_CHART_DIR}/Chart.yaml"
|
||||
sed -i "s/^appVersion:.*/appVersion: \"${PIN_SHA}\"/" "${SHA_CHART_DIR}/Chart.yaml"
|
||||
helm package "${SHA_CHART_DIR}" --destination /tmp/sha-pin
|
||||
helm push /tmp/sha-pin/helm-chart-*.tgz oci://ghcr.io/nvidia/openshell
|
||||
mkdir -p /tmp/sha-pin
|
||||
for source_dir in "${GATEWAY_CHART_DIR}" "${WORKSPACE_CHART_DIR}"; do
|
||||
chart_name="$(basename "${source_dir}")"
|
||||
sha_chart_dir="${RUNNER_TEMP}/${chart_name}-sha"
|
||||
cp -a "${source_dir}/." "${sha_chart_dir}"
|
||||
sed -i "s/^version:.*/version: 0.0.0-dev.${PIN_SHA}/" "${sha_chart_dir}/Chart.yaml"
|
||||
sed -i "s/^appVersion:.*/appVersion: \"${PIN_SHA}\"/" "${sha_chart_dir}/Chart.yaml"
|
||||
helm package "${sha_chart_dir}" --destination /tmp/sha-pin
|
||||
done
|
||||
for archive in /tmp/sha-pin/*.tgz; do
|
||||
helm push "${archive}" oci://ghcr.io/nvidia/openshell
|
||||
done
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
name: Setup E2E CLI
|
||||
description: Download an architecture-matched prebuilt OpenShell CLI for E2E tests
|
||||
description: Download architecture-matched prebuilt OpenShell host CLIs for E2E tests
|
||||
|
||||
inputs:
|
||||
conformance-artifact-prefix:
|
||||
description: Optional conformance CLI artifact name prefix; linux-<arch> is appended automatically
|
||||
required: false
|
||||
default: ""
|
||||
|
||||
runs:
|
||||
using: composite
|
||||
@@ -17,3 +23,27 @@ runs:
|
||||
chmod +x "$cli"
|
||||
"$cli" --version
|
||||
echo "OPENSHELL_BIN=$cli" >> "$GITHUB_ENV"
|
||||
|
||||
- name: Download prebuilt conformance CLI
|
||||
if: inputs.conformance-artifact-prefix != ''
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
with:
|
||||
name: ${{ format('{0}-{1}', inputs.conformance-artifact-prefix, runner.arch == 'X64' && 'x86_64-unknown-linux-musl' || 'aarch64-unknown-linux-musl') }}
|
||||
path: .e2e/prebuilt-conformance
|
||||
|
||||
- name: Configure prebuilt conformance CLI
|
||||
if: inputs.conformance-artifact-prefix != ''
|
||||
shell: bash
|
||||
run: | # zizmor: ignore[github-env] validated filename under the trusted workspace path
|
||||
set -euo pipefail
|
||||
conformance="$GITHUB_WORKSPACE/.e2e/prebuilt-conformance/openshell-conformance"
|
||||
if [[ ! -f "$conformance" ]]; then
|
||||
echo "downloaded artifact is missing $conformance" >&2
|
||||
exit 1
|
||||
fi
|
||||
# TODO: Remove this temporary mode diagnostic after CI confirms artifact permission handling.
|
||||
echo "conformance binary mode before chmod: $(ls -l "$conformance")"
|
||||
chmod +x "$conformance"
|
||||
echo "conformance binary mode after chmod: $(ls -l "$conformance")"
|
||||
"$conformance" list --output json >/dev/null
|
||||
echo "OPENSHELL_CONFORMANCE_BIN=$conformance" >> "$GITHUB_ENV"
|
||||
|
||||
@@ -41,6 +41,7 @@ jobs:
|
||||
needs: pr_metadata
|
||||
if: needs.pr_metadata.outputs.should_run == 'true'
|
||||
runs-on: linux-amd64-cpu8
|
||||
timeout-minutes: 30
|
||||
container:
|
||||
image: ghcr.io/nvidia/openshell/ci:latest
|
||||
credentials:
|
||||
@@ -65,6 +66,7 @@ jobs:
|
||||
needs: pr_metadata
|
||||
if: needs.pr_metadata.outputs.should_run == 'true'
|
||||
runs-on: linux-amd64-cpu8
|
||||
timeout-minutes: 30
|
||||
container:
|
||||
image: ghcr.io/nvidia/openshell/ci:latest
|
||||
credentials:
|
||||
@@ -84,6 +86,7 @@ jobs:
|
||||
needs: pr_metadata
|
||||
if: needs.pr_metadata.outputs.should_run == 'true'
|
||||
runs-on: linux-amd64-cpu8
|
||||
timeout-minutes: 30
|
||||
defaults:
|
||||
run:
|
||||
shell: nix develop .#devShells.x86_64-linux.default -c bash -euo pipefail {0}
|
||||
@@ -117,6 +120,7 @@ jobs:
|
||||
- runner: macos-15-xlarge
|
||||
system: aarch64-darwin
|
||||
runs-on: ${{ matrix.runner }}
|
||||
timeout-minutes: 90
|
||||
defaults:
|
||||
run:
|
||||
shell: nix develop .#devShells.${{ matrix.system }}.default -c bash -euo pipefail {0}
|
||||
@@ -154,12 +158,14 @@ jobs:
|
||||
cargo fmt --all -- --check
|
||||
cargo fmt --manifest-path e2e/rust/Cargo.toml --all -- --check
|
||||
cargo fmt --manifest-path examples/governance-interceptor/Cargo.toml --all -- --check
|
||||
cargo fmt --manifest-path examples/supervisor-middleware-content-guard/Cargo.toml --all -- --check
|
||||
|
||||
- name: Lint
|
||||
run: |
|
||||
cargo clippy --workspace --all-targets -- -D warnings
|
||||
cargo clippy --manifest-path e2e/rust/Cargo.toml --all-targets -- -D warnings
|
||||
cargo check --manifest-path examples/governance-interceptor/Cargo.toml --all-targets
|
||||
cargo check --manifest-path examples/supervisor-middleware-content-guard/Cargo.toml --all-targets
|
||||
|
||||
- name: Test
|
||||
env:
|
||||
@@ -167,6 +173,30 @@ jobs:
|
||||
run: |
|
||||
cargo nextest run --profile ci --workspace --features openshell-server/test-support
|
||||
|
||||
- name: Verify telemetry can be compiled out
|
||||
run: |
|
||||
cargo build -p openshell-gateway --bin openshell-gateway
|
||||
tasks/scripts/verify-telemetry-compiled-out.sh present target/debug/openshell-gateway
|
||||
cargo build -p openshell-gateway --bin openshell-gateway --no-default-features --features defaults-without-telemetry
|
||||
tasks/scripts/verify-telemetry-compiled-out.sh absent target/debug/openshell-gateway
|
||||
cargo build -p openshell-sandbox --bin openshell-sandbox --no-default-features --features defaults-without-telemetry
|
||||
tasks/scripts/verify-telemetry-compiled-out.sh absent target/debug/openshell-sandbox
|
||||
|
||||
- name: Verify the defaults-without-telemetry feature alias tracks the default feature set
|
||||
run: tasks/scripts/verify-defaults-without-telemetry.sh
|
||||
|
||||
- name: Verify system CA roots build mode compiles and excludes bundled Mozilla roots
|
||||
run: |
|
||||
cargo check -p openshell-sandbox --all-targets --no-default-features --features system-ca-roots
|
||||
if cargo tree -p openshell-sandbox -i webpki-roots --no-default-features --features system-ca-roots 2>/dev/null | grep -q webpki-roots; then
|
||||
echo "ERROR: webpki-roots found in system CA roots build" >&2
|
||||
exit 1
|
||||
fi
|
||||
if cargo tree -p openshell-sandbox -i webpki-root-certs --no-default-features --features system-ca-roots 2>/dev/null | grep -q webpki-root-certs; then
|
||||
echo "ERROR: webpki-root-certs found in system CA roots build" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
python:
|
||||
name: Python (${{ matrix.runner }})
|
||||
needs: pr_metadata
|
||||
@@ -176,6 +206,7 @@ jobs:
|
||||
matrix:
|
||||
runner: [linux-amd64-cpu8, linux-arm64-cpu8]
|
||||
runs-on: ${{ matrix.runner }}
|
||||
timeout-minutes: 30
|
||||
container:
|
||||
image: ghcr.io/nvidia/openshell/ci:latest
|
||||
credentials:
|
||||
@@ -207,6 +238,7 @@ jobs:
|
||||
needs: pr_metadata
|
||||
if: needs.pr_metadata.outputs.should_run == 'true'
|
||||
runs-on: linux-amd64-cpu8
|
||||
timeout-minutes: 30
|
||||
container:
|
||||
image: ghcr.io/nvidia/openshell/ci:latest
|
||||
credentials:
|
||||
@@ -226,6 +258,7 @@ jobs:
|
||||
needs: pr_metadata
|
||||
if: needs.pr_metadata.outputs.should_run == 'true'
|
||||
runs-on: linux-amd64-cpu8
|
||||
timeout-minutes: 30
|
||||
container:
|
||||
image: ghcr.io/nvidia/openshell/ci:latest
|
||||
credentials:
|
||||
@@ -245,6 +278,7 @@ jobs:
|
||||
needs: pr_metadata
|
||||
if: needs.pr_metadata.outputs.should_run == 'true'
|
||||
runs-on: linux-amd64-cpu8
|
||||
timeout-minutes: 30
|
||||
container:
|
||||
image: ghcr.io/nvidia/openshell/ci:latest
|
||||
credentials:
|
||||
|
||||
@@ -105,6 +105,28 @@ jobs:
|
||||
cargo-version: ${{ needs.version.outputs.cargo }}
|
||||
secrets: inherit
|
||||
|
||||
build-conformance:
|
||||
needs: [pr_metadata, version]
|
||||
if: needs.pr_metadata.outputs.should_run == 'true' && needs.pr_metadata.outputs.run_any_e2e == 'true'
|
||||
permissions:
|
||||
contents: read
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- triple: x86_64-unknown-linux-musl
|
||||
runner: linux-amd64-cpu8
|
||||
- triple: aarch64-unknown-linux-musl
|
||||
runner: linux-arm64-cpu8
|
||||
uses: ./.github/workflows/build-binaries.yml
|
||||
with:
|
||||
package: openshell-conformance-cli
|
||||
binary: openshell-conformance
|
||||
artifact-name: openshell-conformance-${{ matrix.triple }}
|
||||
triple: ${{ matrix.triple }}
|
||||
runner: ${{ matrix.runner }}
|
||||
cargo-version: ${{ needs.version.outputs.cargo }}
|
||||
secrets: inherit
|
||||
|
||||
build-sandbox:
|
||||
needs: version
|
||||
permissions:
|
||||
@@ -133,23 +155,17 @@ jobs:
|
||||
include:
|
||||
- triple: x86_64-unknown-linux-gnu
|
||||
runner: linux-amd64-cpu8
|
||||
dev_shell: .#devShells.x86_64-linux.glibc-2-28
|
||||
interpreter: /lib64/ld-linux-x86-64.so.2
|
||||
- triple: aarch64-unknown-linux-gnu
|
||||
runner: linux-arm64-cpu8
|
||||
dev_shell: .#devShells.aarch64-linux.glibc-2-28
|
||||
interpreter: /lib/ld-linux-aarch64.so.1
|
||||
uses: ./.github/workflows/build-binaries.yml
|
||||
with:
|
||||
package: openshell-server
|
||||
package: openshell-gateway
|
||||
binary: openshell-gateway
|
||||
artifact-name: openshell-gateway-plain-${{ matrix.triple }}
|
||||
triple: ${{ matrix.triple }}
|
||||
runner: ${{ matrix.runner }}
|
||||
dev-shell: ${{ matrix.dev_shell }}
|
||||
cargo-version: ${{ needs.version.outputs.cargo }}
|
||||
extra-cargo-flags: --no-default-features
|
||||
interpreter: ${{ matrix.interpreter }}
|
||||
secrets: inherit
|
||||
|
||||
build-driver-docker:
|
||||
@@ -163,9 +179,7 @@ jobs:
|
||||
binary: openshell-driver-docker
|
||||
triple: aarch64-unknown-linux-gnu
|
||||
runner: linux-arm64-cpu8
|
||||
dev-shell: .#devShells.aarch64-linux.glibc-2-28
|
||||
cargo-version: ${{ needs.version.outputs.cargo }}
|
||||
interpreter: /lib/ld-linux-aarch64.so.1
|
||||
secrets: inherit
|
||||
|
||||
build-driver-podman:
|
||||
@@ -179,9 +193,7 @@ jobs:
|
||||
binary: openshell-driver-podman
|
||||
triple: x86_64-unknown-linux-gnu
|
||||
runner: linux-amd64-cpu8
|
||||
dev-shell: .#devShells.x86_64-linux.glibc-2-28
|
||||
cargo-version: ${{ needs.version.outputs.cargo }}
|
||||
interpreter: /lib64/ld-linux-x86-64.so.2
|
||||
secrets: inherit
|
||||
|
||||
build-driver-kubernetes:
|
||||
@@ -195,9 +207,7 @@ jobs:
|
||||
binary: openshell-driver-kubernetes
|
||||
triple: x86_64-unknown-linux-gnu
|
||||
runner: linux-amd64-cpu8
|
||||
dev-shell: .#devShells.x86_64-linux.glibc-2-28
|
||||
cargo-version: ${{ needs.version.outputs.cargo }}
|
||||
interpreter: /lib64/ld-linux-x86-64.so.2
|
||||
secrets: inherit
|
||||
|
||||
build-vm-driver:
|
||||
@@ -241,7 +251,7 @@ jobs:
|
||||
secrets: inherit
|
||||
|
||||
docker-e2e:
|
||||
needs: [pr_metadata, build-cli, build-gateway, build-supervisor-image]
|
||||
needs: [pr_metadata, build-cli, build-conformance, build-gateway, build-supervisor-image]
|
||||
if: needs.pr_metadata.outputs.should_run == 'true' && needs.pr_metadata.outputs.run_core_e2e == 'true'
|
||||
permissions:
|
||||
actions: read
|
||||
@@ -251,9 +261,10 @@ jobs:
|
||||
with:
|
||||
image-tag: ${{ github.sha }}
|
||||
runner: linux-arm64-cpu8
|
||||
conformance-artifact-prefix: openshell-conformance
|
||||
|
||||
podman-e2e:
|
||||
needs: [pr_metadata, build-cli, build-gateway, build-supervisor-image]
|
||||
needs: [pr_metadata, build-cli, build-conformance, build-gateway, build-supervisor-image]
|
||||
if: needs.pr_metadata.outputs.should_run == 'true' && needs.pr_metadata.outputs.run_core_e2e == 'true'
|
||||
permissions:
|
||||
actions: read
|
||||
@@ -262,18 +273,21 @@ jobs:
|
||||
uses: ./.github/workflows/e2e-podman-test.yml
|
||||
with:
|
||||
image-tag: ${{ github.sha }}
|
||||
conformance-artifact-prefix: openshell-conformance
|
||||
|
||||
vm-e2e:
|
||||
needs: [pr_metadata, build-cli, build-gateway, build-vm-driver]
|
||||
needs: [pr_metadata, build-cli, build-conformance, build-gateway, build-vm-driver]
|
||||
if: needs.pr_metadata.outputs.should_run == 'true' && needs.pr_metadata.outputs.run_core_e2e == 'true'
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
packages: read
|
||||
uses: ./.github/workflows/e2e-vm-test.yml
|
||||
with:
|
||||
conformance-artifact-prefix: openshell-conformance
|
||||
|
||||
docker-external-driver-e2e:
|
||||
needs: [pr_metadata, build-cli, build-gateway-plain, build-driver-docker, build-supervisor-image]
|
||||
needs: [pr_metadata, build-cli, build-conformance, build-gateway-plain, build-driver-docker, build-supervisor-image]
|
||||
if: needs.pr_metadata.outputs.should_run == 'true' && needs.pr_metadata.outputs.run_core_e2e == 'true'
|
||||
permissions:
|
||||
actions: read
|
||||
@@ -285,11 +299,12 @@ jobs:
|
||||
runner: linux-arm64-cpu8
|
||||
gateway-artifact: openshell-gateway-plain-aarch64-unknown-linux-gnu
|
||||
external-driver-binary: openshell-driver-docker
|
||||
conformance-artifact-prefix: openshell-conformance
|
||||
suite-matrix: >-
|
||||
[{"suite":"external-driver","cmd":"mise run --no-deps --skip-deps e2e:docker:external-driver","apt_packages":"openssh-client","python_proto":false,"mcp":false}]
|
||||
|
||||
podman-external-driver-e2e:
|
||||
needs: [pr_metadata, build-cli, build-gateway-plain, build-driver-podman, build-supervisor-image]
|
||||
needs: [pr_metadata, build-cli, build-conformance, build-gateway-plain, build-driver-podman, build-supervisor-image]
|
||||
if: needs.pr_metadata.outputs.should_run == 'true' && needs.pr_metadata.outputs.run_core_e2e == 'true'
|
||||
permissions:
|
||||
actions: read
|
||||
@@ -300,11 +315,12 @@ jobs:
|
||||
image-tag: ${{ github.sha }}
|
||||
gateway-artifact: openshell-gateway-plain-x86_64-unknown-linux-gnu
|
||||
external-driver-binary: openshell-driver-podman
|
||||
conformance-artifact-prefix: openshell-conformance
|
||||
suite-matrix: >-
|
||||
[{"suite":"external-driver","runner":"ubuntu-26.04","podman_major":"5","podman_package_version":"5.7.0+ds2-3build1","conmon_package_version":"2.1.13+ds1-2","cmd":"mise run --no-deps --skip-deps e2e:podman:external-driver"}]
|
||||
|
||||
vm-external-driver-e2e:
|
||||
needs: [pr_metadata, build-cli, build-gateway-plain, build-vm-driver]
|
||||
needs: [pr_metadata, build-cli, build-conformance, build-gateway-plain, build-vm-driver]
|
||||
if: needs.pr_metadata.outputs.should_run == 'true' && needs.pr_metadata.outputs.run_core_e2e == 'true'
|
||||
permissions:
|
||||
actions: read
|
||||
@@ -315,9 +331,10 @@ jobs:
|
||||
gateway-artifact: openshell-gateway-plain-x86_64-unknown-linux-gnu
|
||||
suite-name: external-driver
|
||||
e2e-task: e2e:vm:external-driver
|
||||
conformance-artifact-prefix: openshell-conformance
|
||||
|
||||
gpu-e2e:
|
||||
needs: [pr_metadata, build-cli, build-gateway, build-supervisor-image]
|
||||
needs: [pr_metadata, build-cli, build-conformance, build-gateway, build-supervisor-image]
|
||||
if: needs.pr_metadata.outputs.should_run == 'true' && needs.pr_metadata.outputs.run_gpu_e2e == 'true'
|
||||
permissions:
|
||||
actions: read
|
||||
@@ -326,9 +343,10 @@ jobs:
|
||||
uses: ./.github/workflows/e2e-gpu-test.yaml
|
||||
with:
|
||||
image-tag: ${{ github.sha }}
|
||||
conformance-artifact-prefix: openshell-conformance
|
||||
|
||||
kubernetes-e2e:
|
||||
needs: [pr_metadata, build-cli, build-gateway-image, build-supervisor-image]
|
||||
needs: [pr_metadata, build-cli, build-conformance, build-gateway-image, build-supervisor-image]
|
||||
if: needs.pr_metadata.outputs.should_run == 'true' && needs.pr_metadata.outputs.run_core_e2e == 'true'
|
||||
strategy:
|
||||
fail-fast: false
|
||||
@@ -356,9 +374,10 @@ jobs:
|
||||
job-name: Kubernetes E2E (Rust smoke, ${{ matrix.topology }}, Agent Sandbox ${{ matrix.agent_sandbox_api }})
|
||||
agent-sandbox-version: ${{ matrix.agent_sandbox_version }}
|
||||
extra-helm-values: ${{ matrix.extra_helm_values }}
|
||||
conformance-artifact-prefix: openshell-conformance
|
||||
|
||||
kubernetes-workspace-managed-e2e:
|
||||
needs: [pr_metadata, build-cli, build-gateway-image, build-supervisor-image]
|
||||
needs: [pr_metadata, build-cli, build-conformance, build-gateway-image, build-supervisor-image]
|
||||
if: needs.pr_metadata.outputs.should_run == 'true' && needs.pr_metadata.outputs.run_core_e2e == 'true'
|
||||
permissions:
|
||||
actions: read
|
||||
@@ -369,9 +388,10 @@ jobs:
|
||||
image-tag: ${{ github.sha }}
|
||||
job-name: Kubernetes E2E (workspace managed mode)
|
||||
e2e-task: e2e:kubernetes:workspace-managed
|
||||
conformance-artifact-prefix: openshell-conformance
|
||||
|
||||
kubernetes-external-driver-e2e:
|
||||
needs: [pr_metadata, build-cli, build-gateway-plain, build-driver-kubernetes, build-supervisor-image]
|
||||
needs: [pr_metadata, build-cli, build-conformance, build-gateway-plain, build-driver-kubernetes, build-supervisor-image]
|
||||
if: needs.pr_metadata.outputs.should_run == 'true' && needs.pr_metadata.outputs.run_core_e2e == 'true'
|
||||
permissions:
|
||||
actions: read
|
||||
@@ -385,9 +405,10 @@ jobs:
|
||||
gateway-artifact: openshell-gateway-plain-x86_64-unknown-linux-gnu
|
||||
external-driver-binary: openshell-driver-kubernetes
|
||||
cluster-images: supervisor
|
||||
conformance-artifact-prefix: openshell-conformance
|
||||
|
||||
kubernetes-workspace-operator-e2e:
|
||||
needs: [pr_metadata, build-cli, build-gateway-image, build-supervisor-image]
|
||||
needs: [pr_metadata, build-cli, build-conformance, build-gateway-image, build-supervisor-image]
|
||||
if: needs.pr_metadata.outputs.should_run == 'true' && needs.pr_metadata.outputs.run_core_e2e == 'true'
|
||||
permissions:
|
||||
actions: read
|
||||
@@ -398,9 +419,10 @@ jobs:
|
||||
image-tag: ${{ github.sha }}
|
||||
job-name: Kubernetes E2E (workspace operator mode)
|
||||
e2e-task: e2e:kubernetes:workspace-operator
|
||||
conformance-artifact-prefix: openshell-conformance
|
||||
|
||||
kubernetes-ha-e2e:
|
||||
needs: [pr_metadata, build-cli, build-gateway-image, build-supervisor-image]
|
||||
needs: [pr_metadata, build-cli, build-conformance, build-gateway-image, build-supervisor-image]
|
||||
if: needs.pr_metadata.outputs.should_run == 'true' && needs.pr_metadata.outputs.run_kubernetes_ha_e2e == 'true'
|
||||
permissions:
|
||||
actions: read
|
||||
@@ -412,9 +434,10 @@ jobs:
|
||||
job-name: Kubernetes HA E2E (Rust smoke)
|
||||
extra-helm-values: deploy/helm/openshell/ci/values-high-availability.yaml
|
||||
external-postgres-secret: openshell-ha-pg
|
||||
conformance-artifact-prefix: openshell-conformance
|
||||
|
||||
kubernetes-credential-drivers-e2e:
|
||||
needs: [pr_metadata, build-cli, build-gateway-image, build-supervisor-image]
|
||||
needs: [pr_metadata, build-cli, build-conformance, build-gateway-image, build-supervisor-image]
|
||||
if: needs.pr_metadata.outputs.should_run == 'true' && needs.pr_metadata.outputs.run_kubernetes_credential_drivers_e2e == 'true'
|
||||
permissions:
|
||||
actions: read
|
||||
@@ -425,6 +448,7 @@ jobs:
|
||||
image-tag: ${{ github.sha }}
|
||||
job-name: Kubernetes Credential Drivers E2E
|
||||
e2e-task: e2e:kubernetes:credential-drivers
|
||||
conformance-artifact-prefix: openshell-conformance
|
||||
|
||||
core-e2e-result:
|
||||
name: Core E2E result
|
||||
|
||||
@@ -18,9 +18,6 @@ on:
|
||||
runner:
|
||||
required: true
|
||||
type: string
|
||||
dev-shell:
|
||||
required: true
|
||||
type: string
|
||||
cargo-version:
|
||||
required: true
|
||||
type: string
|
||||
@@ -36,10 +33,6 @@ on:
|
||||
required: false
|
||||
type: string
|
||||
default: ""
|
||||
interpreter:
|
||||
required: false
|
||||
type: string
|
||||
default: ""
|
||||
checkout-ref:
|
||||
required: false
|
||||
type: string
|
||||
@@ -70,9 +63,7 @@ jobs:
|
||||
package: ${{ inputs.package }}
|
||||
binary: ${{ inputs.binary }}
|
||||
triple: ${{ inputs.triple }}
|
||||
dev-shell: ${{ inputs['dev-shell'] }}
|
||||
cargo-version: ${{ inputs['cargo-version'] }}
|
||||
image-tag: ${{ inputs['image-tag'] }}
|
||||
artifact-name: ${{ inputs['artifact-name'] }}
|
||||
extra-cargo-flags: ${{ inputs['extra-cargo-flags'] }}
|
||||
interpreter: ${{ inputs.interpreter }}
|
||||
|
||||
@@ -31,20 +31,16 @@ jobs:
|
||||
include:
|
||||
- triple: x86_64-unknown-linux-musl
|
||||
runner: linux-amd64-cpu8
|
||||
dev_shell: .#devShells.x86_64-linux.musl
|
||||
- triple: aarch64-unknown-linux-musl
|
||||
runner: linux-arm64-cpu8
|
||||
dev_shell: .#devShells.aarch64-linux.musl
|
||||
- triple: aarch64-apple-darwin
|
||||
runner: macos-15-xlarge
|
||||
dev_shell: .#devShells.aarch64-darwin.default
|
||||
uses: ./.github/workflows/build-binaries.yml
|
||||
with:
|
||||
package: openshell-cli
|
||||
binary: openshell
|
||||
triple: ${{ matrix.triple }}
|
||||
runner: ${{ matrix.runner }}
|
||||
dev-shell: ${{ matrix.dev_shell }}
|
||||
cargo-version: ${{ inputs.cargo-version }}
|
||||
image-tag: ${{ inputs.image-tag }}
|
||||
checkout-ref: ${{ inputs.checkout-ref }}
|
||||
|
||||
@@ -31,25 +31,17 @@ jobs:
|
||||
include:
|
||||
- triple: x86_64-unknown-linux-gnu
|
||||
runner: linux-amd64-cpu8
|
||||
dev_shell: .#devShells.x86_64-linux.glibc-2-28
|
||||
interpreter: /lib64/ld-linux-x86-64.so.2
|
||||
- triple: aarch64-unknown-linux-gnu
|
||||
runner: linux-arm64-cpu8
|
||||
dev_shell: .#devShells.aarch64-linux.glibc-2-28
|
||||
interpreter: /lib/ld-linux-aarch64.so.1
|
||||
- triple: aarch64-apple-darwin
|
||||
runner: macos-15-xlarge
|
||||
dev_shell: .#devShells.aarch64-darwin.default
|
||||
interpreter: ""
|
||||
uses: ./.github/workflows/build-binaries.yml
|
||||
with:
|
||||
package: openshell-server
|
||||
package: openshell-gateway
|
||||
binary: openshell-gateway
|
||||
triple: ${{ matrix.triple }}
|
||||
runner: ${{ matrix.runner }}
|
||||
dev-shell: ${{ matrix.dev_shell }}
|
||||
cargo-version: ${{ inputs.cargo-version }}
|
||||
image-tag: ${{ inputs.image-tag }}
|
||||
interpreter: ${{ matrix.interpreter }}
|
||||
checkout-ref: ${{ inputs.checkout-ref }}
|
||||
secrets: inherit
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
name: Build RPM
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
checkout-ref:
|
||||
required: true
|
||||
type: string
|
||||
arch:
|
||||
required: true
|
||||
type: string
|
||||
runner:
|
||||
required: true
|
||||
type: string
|
||||
cli-target:
|
||||
required: true
|
||||
type: string
|
||||
gateway-target:
|
||||
required: true
|
||||
type: string
|
||||
rpm-version:
|
||||
required: false
|
||||
type: string
|
||||
default: ""
|
||||
rpm-release:
|
||||
required: false
|
||||
type: string
|
||||
default: ""
|
||||
cargo-version:
|
||||
required: false
|
||||
type: string
|
||||
default: ""
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build RPM Package (Linux ${{ inputs.arch }})
|
||||
runs-on: ${{ inputs.runner }}
|
||||
timeout-minutes: 60
|
||||
container:
|
||||
image: docker.io/library/fedora:44@sha256:43b29f65a41eb9c35e1cd5323e3bdf3b655c2357a9f4f1ff2f9c2798e5045d80
|
||||
steps:
|
||||
- name: Install packaging dependencies
|
||||
run: |
|
||||
dnf install -y \
|
||||
packit rpm-build \
|
||||
cargo cargo-rpm-macros git-core \
|
||||
pandoc python3-devel systemd-rpm-macros
|
||||
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
ref: ${{ inputs.checkout-ref }}
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Cache Cargo dependencies
|
||||
uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
|
||||
with:
|
||||
shared-key: rpm-vendor-${{ inputs.arch }}
|
||||
cache-targets: "false"
|
||||
cache-bin: "false"
|
||||
cache-on-failure: "true"
|
||||
|
||||
- name: Download CLI artifact
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
with:
|
||||
name: openshell-${{ inputs.cli-target }}
|
||||
path: package-binaries/
|
||||
|
||||
- name: Download gateway artifact
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
with:
|
||||
name: openshell-gateway-${{ inputs.gateway-target }}
|
||||
path: package-binaries/
|
||||
|
||||
- name: Configure package inputs
|
||||
run: |
|
||||
set -euo pipefail
|
||||
chmod +x package-binaries/openshell{,-gateway}
|
||||
ls -lah package-binaries
|
||||
|
||||
- name: Mark workspace safe for git
|
||||
run: git config --global --add safe.directory "$GITHUB_WORKSPACE"
|
||||
|
||||
- name: Fetch tags
|
||||
run: git fetch --tags --force
|
||||
|
||||
- name: Build RPMs via Packit
|
||||
env:
|
||||
OPENSHELL_RPM_VERSION: ${{ inputs.rpm-version }}
|
||||
OPENSHELL_RPM_RELEASE: ${{ inputs.rpm-release }}
|
||||
OPENSHELL_CARGO_VERSION: ${{ inputs.cargo-version }}
|
||||
OPENSHELL_PREBUILT_BINARIES_DIR: ${{ github.workspace }}/package-binaries
|
||||
run: packit build locally
|
||||
|
||||
- name: Collect RPM artifacts
|
||||
run: |
|
||||
set -euo pipefail
|
||||
mkdir -p artifacts
|
||||
mapfile -t rpms < <(find "$GITHUB_WORKSPACE" -maxdepth 3 -type f -name '*.rpm' ! -name '*.src.rpm' | sort)
|
||||
if [ "${#rpms[@]}" -eq 0 ]; then
|
||||
echo "::error::No RPM artifacts found under $GITHUB_WORKSPACE"
|
||||
find "$GITHUB_WORKSPACE" -maxdepth 3 -type f | sort
|
||||
exit 1
|
||||
fi
|
||||
cp "${rpms[@]}" artifacts/
|
||||
echo "=== Built RPMs ==="
|
||||
ls -lah artifacts/
|
||||
|
||||
- name: Upload RPM artifacts
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
with:
|
||||
name: rpm-linux-${{ inputs.arch }}
|
||||
path: artifacts/*.rpm
|
||||
retention-days: 5
|
||||
@@ -31,17 +31,14 @@ jobs:
|
||||
include:
|
||||
- triple: x86_64-unknown-linux-musl
|
||||
runner: linux-amd64-cpu8
|
||||
dev_shell: .#devShells.x86_64-linux.musl
|
||||
- triple: aarch64-unknown-linux-musl
|
||||
runner: linux-arm64-cpu8
|
||||
dev_shell: .#devShells.aarch64-linux.musl
|
||||
uses: ./.github/workflows/build-binaries.yml
|
||||
with:
|
||||
package: openshell-sandbox
|
||||
binary: openshell-sandbox
|
||||
triple: ${{ matrix.triple }}
|
||||
runner: ${{ matrix.runner }}
|
||||
dev-shell: ${{ matrix.dev_shell }}
|
||||
cargo-version: ${{ inputs.cargo-version }}
|
||||
image-tag: ${{ inputs.image-tag }}
|
||||
checkout-ref: ${{ inputs.checkout-ref }}
|
||||
|
||||
@@ -33,22 +33,17 @@ jobs:
|
||||
- arch: x86_64
|
||||
triple: x86_64-unknown-linux-gnu
|
||||
runner: linux-amd64-cpu8
|
||||
dev_shell: .#devShells.x86_64-linux.glibc-2-28
|
||||
interpreter: /lib64/ld-linux-x86-64.so.2
|
||||
- arch: aarch64
|
||||
triple: aarch64-unknown-linux-gnu
|
||||
runner: linux-arm64-cpu8
|
||||
dev_shell: .#devShells.aarch64-linux.glibc-2-28
|
||||
interpreter: /lib/ld-linux-aarch64.so.1
|
||||
- arch: aarch64
|
||||
triple: aarch64-apple-darwin
|
||||
runner: macos-15-xlarge
|
||||
dev_shell: .#devShells.aarch64-darwin.default
|
||||
runs-on: ${{ matrix.runner }}
|
||||
timeout-minutes: 60
|
||||
defaults:
|
||||
run:
|
||||
shell: nix develop ${{ matrix.dev_shell }} -c bash -euo pipefail {0}
|
||||
shell: nix develop -c bash -euo pipefail {0}
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
@@ -82,7 +77,5 @@ jobs:
|
||||
package: openshell-driver-vm
|
||||
binary: openshell-driver-vm
|
||||
triple: ${{ matrix.triple }}
|
||||
dev-shell: ${{ matrix.dev_shell }}
|
||||
cargo-version: ${{ inputs['cargo-version'] }}
|
||||
image-tag: ${{ inputs['image-tag'] }}
|
||||
interpreter: ${{ matrix.interpreter }}
|
||||
|
||||
@@ -0,0 +1,286 @@
|
||||
# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
name: Codex Security Release Qualification
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "v*.*.*-pre.*"
|
||||
workflow_call:
|
||||
inputs:
|
||||
candidate_ref:
|
||||
description: Pre-release tag to scan (vMAJOR.MINOR.PATCH-pre.N)
|
||||
required: true
|
||||
type: string
|
||||
stable_ref:
|
||||
description: Optional previous stable tag override
|
||||
required: false
|
||||
default: ""
|
||||
type: string
|
||||
allow_full_bootstrap:
|
||||
description: Allow a full scan when no previous stable tag exists
|
||||
required: false
|
||||
default: false
|
||||
type: boolean
|
||||
outputs:
|
||||
base_sha:
|
||||
description: Previous stable commit, empty for a full bootstrap scan
|
||||
value: ${{ jobs.analyze.outputs.base_sha }}
|
||||
candidate_sha:
|
||||
description: Qualified pre-release commit
|
||||
value: ${{ jobs.analyze.outputs.candidate_sha }}
|
||||
category:
|
||||
description: Code Scanning category for the release train
|
||||
value: ${{ jobs.analyze.outputs.category }}
|
||||
train:
|
||||
description: Stable version targeted by the pre-release train
|
||||
value: ${{ jobs.analyze.outputs.train }}
|
||||
secrets:
|
||||
CODEX_SECURITY_API_KEY:
|
||||
required: true
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
candidate_ref:
|
||||
description: Pre-release tag to scan (vMAJOR.MINOR.PATCH-pre.N)
|
||||
required: true
|
||||
type: string
|
||||
stable_ref:
|
||||
description: Optional previous stable tag override
|
||||
required: false
|
||||
type: string
|
||||
upload_sarif:
|
||||
description: Upload manual-run results to Code Scanning
|
||||
required: false
|
||||
default: false
|
||||
type: boolean
|
||||
allow_full_bootstrap:
|
||||
description: Allow a full scan when no previous stable tag exists
|
||||
required: false
|
||||
default: false
|
||||
type: boolean
|
||||
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
security-events: write
|
||||
|
||||
concurrency:
|
||||
group: codex-security-release-qualification
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
CODEX_SECURITY_MAX_CONCURRENT_THREADS: "8"
|
||||
CODEX_SECURITY_REASONING_EFFORT: medium
|
||||
NVIDIA_INFERENCE_BASE_URL: https://inference-api.nvidia.com/v1
|
||||
NVIDIA_INFERENCE_MODEL: openai/openai/gpt-5.6-sol
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Codex Security (${{ inputs.candidate_ref || github.ref_name }})
|
||||
# The agent executes no shell commands on the repository self-hosted runner,
|
||||
# so its preflight never scopes the diff and it seals no draft.
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 120
|
||||
outputs:
|
||||
base_sha: ${{ steps.range.outputs.base_sha }}
|
||||
candidate_sha: ${{ steps.range.outputs.candidate_sha }}
|
||||
category: ${{ steps.range.outputs.category }}
|
||||
train: ${{ steps.range.outputs.train }}
|
||||
steps:
|
||||
# Install the trusted scanner before repository-controlled files exist in
|
||||
# the workspace, and invoke it later through its absolute path.
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
node-version: "26"
|
||||
package-manager-cache: false
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
|
||||
with:
|
||||
python-version: "3.14"
|
||||
|
||||
# Codex confines model-run commands with bubblewrap, which needs
|
||||
# unprivileged user namespaces. Ubuntu 24.04 restricts those through
|
||||
# AppArmor, so bubblewrap cannot set up the sandbox network namespace and
|
||||
# the scan agent executes nothing at all.
|
||||
- name: Allow unprivileged user namespaces
|
||||
run: |
|
||||
set -euo pipefail
|
||||
if [ -e /proc/sys/kernel/apparmor_restrict_unprivileged_userns ]; then
|
||||
sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0
|
||||
fi
|
||||
|
||||
- name: Install Codex Security
|
||||
run: |
|
||||
set -euo pipefail
|
||||
npm install \
|
||||
--prefix "$RUNNER_TEMP/codex-security" \
|
||||
--ignore-scripts \
|
||||
--no-audit \
|
||||
--no-fund \
|
||||
@openai/codex-security@0.1.24
|
||||
|
||||
- name: Verify Codex Security
|
||||
env:
|
||||
CODEX_SECURITY_BIN: ${{ runner.temp }}/codex-security/node_modules/.bin/codex-security
|
||||
run: |
|
||||
set -euo pipefail
|
||||
test -x "$CODEX_SECURITY_BIN"
|
||||
"$CODEX_SECURITY_BIN" --version
|
||||
|
||||
# The range resolver has to come from the workflow's own revision. A
|
||||
# scanned candidate predates it, and running the resolver from the
|
||||
# revision under scan would let that revision pick its own scan range.
|
||||
- name: Check out the workflow revision
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
path: workflow-revision
|
||||
sparse-checkout: tasks/scripts
|
||||
persist-credentials: false
|
||||
|
||||
- name: Stage the range resolver
|
||||
run: |
|
||||
set -euo pipefail
|
||||
install -d -m 700 "$RUNNER_TEMP/range-resolver"
|
||||
cp workflow-revision/tasks/scripts/release.py \
|
||||
workflow-revision/tasks/scripts/codex_security_range.py \
|
||||
"$RUNNER_TEMP/range-resolver/"
|
||||
rm -rf workflow-revision
|
||||
|
||||
- name: Check out the pre-release
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
ref: ${{ inputs.candidate_ref || github.ref }}
|
||||
fetch-depth: 0
|
||||
persist-credentials: false
|
||||
|
||||
- name: Resolve release range
|
||||
id: range
|
||||
env:
|
||||
ALLOW_FULL_BOOTSTRAP: ${{ inputs.allow_full_bootstrap || false }}
|
||||
CANDIDATE_REF: ${{ inputs.candidate_ref || github.ref_name }}
|
||||
STABLE_REF: ${{ inputs.stable_ref || '' }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
git show-ref --verify --quiet refs/remotes/origin/main
|
||||
|
||||
args=(
|
||||
--candidate "$CANDIDATE_REF"
|
||||
--main-ref origin/main
|
||||
)
|
||||
if [ -n "$STABLE_REF" ]; then
|
||||
args+=(--stable "$STABLE_REF")
|
||||
fi
|
||||
if [ "$ALLOW_FULL_BOOTSTRAP" = "true" ]; then
|
||||
args+=(--allow-full-bootstrap)
|
||||
fi
|
||||
|
||||
python3 "$RUNNER_TEMP/range-resolver/codex_security_range.py" "${args[@]}"
|
||||
|
||||
- name: Scan changes since the previous stable
|
||||
env:
|
||||
BASE_SHA: ${{ steps.range.outputs.base_sha }}
|
||||
CODEX_SECURITY_BIN: ${{ runner.temp }}/codex-security/node_modules/.bin/codex-security
|
||||
CODEX_SECURITY_STATE_DIR: ${{ runner.temp }}/codex-security-state-${{ github.run_id }}-${{ github.run_attempt }}
|
||||
HEAD_SHA: ${{ steps.range.outputs.candidate_sha }}
|
||||
NVIDIA_INFERENCE_API_KEY: ${{ secrets.CODEX_SECURITY_API_KEY }}
|
||||
OPENAI_API_KEY: ${{ secrets.CODEX_SECURITY_API_KEY }}
|
||||
SCAN_DIR: ${{ runner.temp }}/codex-security-results-${{ github.run_id }}-${{ github.run_attempt }}
|
||||
SCAN_SCOPE: ${{ steps.range.outputs.scan_scope }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
install -d -m 700 "$CODEX_SECURITY_STATE_DIR" "$SCAN_DIR"
|
||||
|
||||
target_args=()
|
||||
if [ "$SCAN_SCOPE" = "diff" ]; then
|
||||
target_args=(--diff "$BASE_SHA" --head "$HEAD_SHA")
|
||||
elif [ "$SCAN_SCOPE" != "full" ]; then
|
||||
echo "::error::Unsupported Codex Security scan scope: $SCAN_SCOPE"
|
||||
exit 2
|
||||
fi
|
||||
|
||||
"$CODEX_SECURITY_BIN" scan . \
|
||||
"${target_args[@]}" \
|
||||
--auth api-key \
|
||||
--model "$NVIDIA_INFERENCE_MODEL" \
|
||||
--effort "$CODEX_SECURITY_REASONING_EFFORT" \
|
||||
--codex 'model_provider="nvidia"' \
|
||||
--codex 'model_providers.nvidia.name="NVIDIA Inference"' \
|
||||
--codex "model_providers.nvidia.base_url=\"$NVIDIA_INFERENCE_BASE_URL\"" \
|
||||
--codex 'model_providers.nvidia.env_key="NVIDIA_INFERENCE_API_KEY"' \
|
||||
--codex 'model_providers.nvidia.wire_api="responses"' \
|
||||
--codex 'model_providers.nvidia.supports_websockets=false' \
|
||||
--codex "features.multi_agent_v2.max_concurrent_threads_per_session=$CODEX_SECURITY_MAX_CONCURRENT_THREADS" \
|
||||
--codex 'approval_policy="never"' \
|
||||
--output-dir "$SCAN_DIR" \
|
||||
--headless > /dev/null
|
||||
|
||||
- name: Export SARIF
|
||||
env:
|
||||
CODEX_SECURITY_BIN: ${{ runner.temp }}/codex-security/node_modules/.bin/codex-security
|
||||
SARIF_FILE: ${{ runner.temp }}/codex-security.sarif
|
||||
SCAN_DIR: ${{ runner.temp }}/codex-security-results-${{ github.run_id }}-${{ github.run_attempt }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
"$CODEX_SECURITY_BIN" export "$SCAN_DIR" \
|
||||
--export-format sarif \
|
||||
--source-root "$GITHUB_WORKSPACE" \
|
||||
--output "$SARIF_FILE"
|
||||
|
||||
- name: Summarize findings
|
||||
env:
|
||||
BASE_TAG: ${{ steps.range.outputs.base_tag }}
|
||||
CANDIDATE_TAG: ${{ steps.range.outputs.candidate_tag }}
|
||||
COMMIT_COUNT: ${{ steps.range.outputs.commit_count }}
|
||||
SARIF_FILE: ${{ runner.temp }}/codex-security.sarif
|
||||
SCAN_SCOPE: ${{ steps.range.outputs.scan_scope }}
|
||||
TRAIN: ${{ steps.range.outputs.train }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
finding_count=$(jq '[.runs[]?.results[]?] | length' "$SARIF_FILE")
|
||||
{
|
||||
echo "### Codex Security release qualification"
|
||||
echo
|
||||
echo "- Train: \`$TRAIN\`"
|
||||
echo "- Candidate: \`$CANDIDATE_TAG\`"
|
||||
echo "- Maximum concurrent agent threads: $CODEX_SECURITY_MAX_CONCURRENT_THREADS"
|
||||
if [ "$SCAN_SCOPE" = "diff" ]; then
|
||||
echo "- Previous stable: \`$BASE_TAG\`"
|
||||
echo "- Commits in cumulative diff: $COMMIT_COUNT"
|
||||
else
|
||||
echo "- Scope: approved full bootstrap scan"
|
||||
fi
|
||||
echo "- Coverage: complete"
|
||||
echo "- Findings: $finding_count"
|
||||
echo
|
||||
echo "Findings are informational during the observation phase."
|
||||
} >> "$GITHUB_STEP_SUMMARY"
|
||||
|
||||
- name: Upload SARIF to Code Scanning
|
||||
if: ${{ github.event_name != 'workflow_dispatch' || inputs.upload_sarif }}
|
||||
uses: github/codeql-action/upload-sarif@ff2f1c621b7f889edc0d3c761ac2e6a3f8cdb0dd # v4.37.7
|
||||
with:
|
||||
sarif_file: ${{ runner.temp }}/codex-security.sarif
|
||||
ref: refs/heads/main
|
||||
sha: ${{ steps.range.outputs.candidate_sha }}
|
||||
category: ${{ steps.range.outputs.category }}
|
||||
|
||||
result:
|
||||
name: OpenShell / Codex Security (informational)
|
||||
if: ${{ always() }}
|
||||
needs: analyze
|
||||
runs-on: ubuntu-latest
|
||||
permissions: {}
|
||||
steps:
|
||||
- name: Evaluate scanner execution
|
||||
env:
|
||||
ANALYZE_RESULT: ${{ needs.analyze.result }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
if [ "$ANALYZE_RESULT" != "success" ]; then
|
||||
echo "::error::Codex Security release qualification did not complete successfully."
|
||||
exit 1
|
||||
fi
|
||||
echo "Codex Security completed; findings remain informational."
|
||||
@@ -0,0 +1,179 @@
|
||||
# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
name: Conformance
|
||||
|
||||
on:
|
||||
workflow_dispatch: {}
|
||||
|
||||
permissions: {}
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
pr_metadata:
|
||||
name: Resolve PR metadata
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: read
|
||||
outputs:
|
||||
should_run: ${{ steps.gate.outputs.should_run }}
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- id: gate
|
||||
uses: ./.github/actions/pr-gate
|
||||
|
||||
version:
|
||||
needs: pr_metadata
|
||||
if: needs.pr_metadata.outputs.should_run == 'true'
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
permissions:
|
||||
contents: read
|
||||
outputs:
|
||||
cargo: ${{ steps.version.outputs.cargo }}
|
||||
rpm_version: ${{ steps.version.outputs.rpm_version }}
|
||||
rpm_release: ${{ steps.version.outputs.rpm_release }}
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Compute versions
|
||||
id: version
|
||||
run: |
|
||||
cargo="$(python3 tasks/scripts/release.py get-version --cargo)"
|
||||
rpm_version="$(python3 tasks/scripts/release.py get-version --rpm-version)"
|
||||
rpm_release="$(python3 tasks/scripts/release.py get-version --rpm-release)"
|
||||
{
|
||||
echo "cargo=$cargo"
|
||||
echo "rpm_version=$rpm_version"
|
||||
echo "rpm_release=$rpm_release"
|
||||
} >> "$GITHUB_OUTPUT"
|
||||
|
||||
build-cli:
|
||||
needs: version
|
||||
permissions:
|
||||
contents: read
|
||||
uses: ./.github/workflows/build-binaries.yml
|
||||
with:
|
||||
package: openshell-cli
|
||||
binary: openshell
|
||||
triple: x86_64-unknown-linux-musl
|
||||
runner: linux-amd64-cpu8
|
||||
cargo-version: ${{ needs.version.outputs.cargo }}
|
||||
checkout-ref: ${{ github.sha }}
|
||||
secrets: inherit
|
||||
|
||||
build-conformance:
|
||||
needs: version
|
||||
permissions:
|
||||
contents: read
|
||||
uses: ./.github/workflows/build-binaries.yml
|
||||
with:
|
||||
package: openshell-conformance-cli
|
||||
binary: openshell-conformance
|
||||
triple: x86_64-unknown-linux-musl
|
||||
runner: linux-amd64-cpu8
|
||||
cargo-version: ${{ needs.version.outputs.cargo }}
|
||||
checkout-ref: ${{ github.sha }}
|
||||
secrets: inherit
|
||||
|
||||
build-gateway:
|
||||
needs: version
|
||||
permissions:
|
||||
contents: read
|
||||
uses: ./.github/workflows/build-binaries.yml
|
||||
with:
|
||||
package: openshell-gateway
|
||||
binary: openshell-gateway
|
||||
triple: x86_64-unknown-linux-gnu
|
||||
runner: linux-amd64-cpu8
|
||||
cargo-version: ${{ needs.version.outputs.cargo }}
|
||||
image-tag: dev
|
||||
checkout-ref: ${{ github.sha }}
|
||||
secrets: inherit
|
||||
|
||||
build-rpm:
|
||||
needs: [version, build-cli, build-gateway]
|
||||
permissions:
|
||||
contents: read
|
||||
uses: ./.github/workflows/build-rpm.yml
|
||||
with:
|
||||
checkout-ref: ${{ github.sha }}
|
||||
arch: x86_64
|
||||
runner: linux-amd64-cpu8
|
||||
cli-target: x86_64-unknown-linux-musl
|
||||
gateway-target: x86_64-unknown-linux-gnu
|
||||
cargo-version: ${{ needs.version.outputs.cargo }}
|
||||
rpm-version: ${{ needs.version.outputs.rpm_version }}
|
||||
rpm-release: ${{ needs.version.outputs.rpm_release }}
|
||||
|
||||
fedora:
|
||||
name: Fedora with Rootless Podman
|
||||
needs: [build-conformance, build-rpm]
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 45
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Enable KVM access
|
||||
run: |
|
||||
set -euo pipefail
|
||||
if [[ ! -c /dev/kvm ]]; then
|
||||
echo "::error::The runner did not expose /dev/kvm"
|
||||
exit 1
|
||||
fi
|
||||
sudo chmod 0666 /dev/kvm
|
||||
exec 3<>/dev/kvm
|
||||
exec 3>&-
|
||||
|
||||
- uses: ./.github/actions/setup-nix
|
||||
|
||||
- name: Download RPM artifacts
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
with:
|
||||
name: rpm-linux-x86_64
|
||||
path: rpm-input
|
||||
|
||||
- name: Download conformance CLI
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
with:
|
||||
name: openshell-conformance-x86_64-unknown-linux-musl
|
||||
path: conformance-input
|
||||
|
||||
- name: Run RPM gateway continuity conformance
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
chmod +x conformance-input/openshell-conformance
|
||||
shopt -s nullglob
|
||||
candidate_cli_package=(rpm-input/openshell-[0-9]*.rpm)
|
||||
candidate_gateway_package=(rpm-input/openshell-gateway-[0-9]*.rpm)
|
||||
if [[ ${#candidate_cli_package[@]} -ne 1 || ${#candidate_gateway_package[@]} -ne 1 ]]; then
|
||||
echo "expected one candidate CLI and gateway RPM" >&2
|
||||
printf 'RPM artifacts:\n' >&2
|
||||
printf ' %s\n' rpm-input/* >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
OPENSHELL_TEST_GUEST_CACHE_DISABLE=1 nix run .#test-guest -- \
|
||||
--distro fedora \
|
||||
--with podman-rootless \
|
||||
--with selinux \
|
||||
--copy "${candidate_cli_package[0]}:/var/lib/openshell-conformance/candidate/openshell.rpm" \
|
||||
--copy "${candidate_gateway_package[0]}:/var/lib/openshell-conformance/candidate/openshell-gateway.rpm" \
|
||||
--copy conformance-input/openshell-conformance:/tmp/openshell-conformance \
|
||||
--copy nix/test-guest/conformance-plans/gateway-upgrade-restart.toml:/tmp/conformance-plan.toml \
|
||||
--provision openshell-rpm-latest-release \
|
||||
--provision gateway-podman \
|
||||
--provision openshell-rpm-gateway-upgrade \
|
||||
-- /tmp/openshell-conformance run --plan /tmp/conformance-plan.toml
|
||||
@@ -25,6 +25,10 @@ on:
|
||||
required: false
|
||||
type: string
|
||||
default: ""
|
||||
conformance-artifact-prefix:
|
||||
required: false
|
||||
type: string
|
||||
default: ""
|
||||
suite-matrix:
|
||||
required: false
|
||||
type: string
|
||||
@@ -66,6 +70,8 @@ jobs:
|
||||
persist-credentials: false
|
||||
|
||||
- uses: ./.github/actions/setup-e2e-cli
|
||||
with:
|
||||
conformance-artifact-prefix: ${{ inputs.conformance-artifact-prefix }}
|
||||
|
||||
- uses: ./.github/actions/setup-e2e-gateway
|
||||
with:
|
||||
|
||||
@@ -7,6 +7,11 @@ on:
|
||||
description: "Image tag to test (typically the commit SHA)"
|
||||
required: true
|
||||
type: string
|
||||
conformance-artifact-prefix:
|
||||
description: "Optional prebuilt conformance CLI artifact prefix (artifact suffix is <triple>)"
|
||||
required: false
|
||||
type: string
|
||||
default: ""
|
||||
|
||||
permissions:
|
||||
actions: read
|
||||
@@ -61,6 +66,8 @@ jobs:
|
||||
|
||||
- name: Use prebuilt OpenShell CLI
|
||||
uses: ./.github/actions/setup-e2e-cli
|
||||
with:
|
||||
conformance-artifact-prefix: ${{ inputs.conformance-artifact-prefix }}
|
||||
|
||||
- name: Use prebuilt OpenShell gateway
|
||||
uses: ./.github/actions/setup-e2e-gateway
|
||||
|
||||
@@ -42,6 +42,16 @@ on:
|
||||
required: false
|
||||
type: string
|
||||
default: "e2e:kubernetes"
|
||||
test-name:
|
||||
description: "Rust e2e test target to run (sets OPENSHELL_E2E_KUBE_TEST)"
|
||||
required: false
|
||||
type: string
|
||||
default: ""
|
||||
kubernetes-features:
|
||||
description: "Cargo feature list for the Kubernetes e2e crate (uses script defaults when empty)"
|
||||
required: false
|
||||
type: string
|
||||
default: ""
|
||||
mise-version:
|
||||
description: "mise version to install on the bare Kubernetes e2e runner"
|
||||
required: false
|
||||
@@ -52,6 +62,11 @@ on:
|
||||
required: false
|
||||
type: string
|
||||
default: ""
|
||||
conformance-artifact-prefix:
|
||||
description: "Optional prebuilt conformance CLI artifact prefix (artifact suffix is <triple>)"
|
||||
required: false
|
||||
type: string
|
||||
default: ""
|
||||
external-driver-binary:
|
||||
description: "Optional standalone compute driver binary used to compose a local test image"
|
||||
required: false
|
||||
@@ -92,6 +107,8 @@ jobs:
|
||||
|
||||
- name: Use prebuilt OpenShell CLI
|
||||
uses: ./.github/actions/setup-e2e-cli
|
||||
with:
|
||||
conformance-artifact-prefix: ${{ inputs.conformance-artifact-prefix }}
|
||||
|
||||
- name: Use prebuilt OpenShell gateway
|
||||
if: inputs.gateway-artifact != ''
|
||||
@@ -121,6 +138,8 @@ jobs:
|
||||
OPENSHELL_E2E_KUBE_CONTEXT: kind-${{ env.KIND_CLUSTER_NAME }}
|
||||
OPENSHELL_E2E_KUBE_EXTRA_VALUES: ${{ inputs.extra-helm-values }}
|
||||
OPENSHELL_E2E_KUBE_EXTERNAL_POSTGRES_SECRET: ${{ inputs.external-postgres-secret }}
|
||||
OPENSHELL_E2E_KUBE_TEST: ${{ inputs.test-name }}
|
||||
OPENSHELL_E2E_KUBERNETES_FEATURES: ${{ inputs.kubernetes-features }}
|
||||
IMAGE_TAG: ${{ inputs.image-tag }}
|
||||
OPENSHELL_REGISTRY: ghcr.io/nvidia/openshell
|
||||
E2E_TASK: ${{ inputs.e2e-task }}
|
||||
|
||||
@@ -21,6 +21,10 @@ on:
|
||||
required: false
|
||||
type: string
|
||||
default: ""
|
||||
conformance-artifact-prefix:
|
||||
required: false
|
||||
type: string
|
||||
default: ""
|
||||
suite-matrix:
|
||||
required: false
|
||||
type: string
|
||||
@@ -53,6 +57,8 @@ jobs:
|
||||
persist-credentials: false
|
||||
|
||||
- uses: ./.github/actions/setup-e2e-cli
|
||||
with:
|
||||
conformance-artifact-prefix: ${{ inputs.conformance-artifact-prefix }}
|
||||
|
||||
- uses: ./.github/actions/setup-e2e-gateway
|
||||
with:
|
||||
|
||||
@@ -22,6 +22,10 @@ on:
|
||||
required: false
|
||||
type: string
|
||||
default: e2e:vm
|
||||
conformance-artifact-prefix:
|
||||
required: false
|
||||
type: string
|
||||
default: ""
|
||||
|
||||
permissions:
|
||||
actions: read
|
||||
@@ -43,6 +47,8 @@ jobs:
|
||||
persist-credentials: false
|
||||
|
||||
- uses: ./.github/actions/setup-e2e-cli
|
||||
with:
|
||||
conformance-artifact-prefix: ${{ inputs.conformance-artifact-prefix }}
|
||||
|
||||
- uses: ./.github/actions/setup-e2e-gateway
|
||||
with:
|
||||
|
||||
@@ -25,6 +25,7 @@ permissions:
|
||||
concurrency:
|
||||
group: docs-website
|
||||
cancel-in-progress: false
|
||||
queue: max
|
||||
|
||||
defaults:
|
||||
run:
|
||||
|
||||
@@ -2,6 +2,11 @@ name: Release Canary
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
release-dev-run-id:
|
||||
description: "Successful Release Dev run ID whose Snap artifact to test"
|
||||
required: false
|
||||
type: string
|
||||
workflow_run:
|
||||
workflows: ["Release Dev"]
|
||||
types: [completed]
|
||||
@@ -154,7 +159,7 @@ jobs:
|
||||
|
||||
ubuntu-snap:
|
||||
name: Ubuntu Snap
|
||||
if: ${{ github.event.workflow_run.conclusion == 'success' }}
|
||||
if: ${{ github.event.workflow_run.conclusion == 'success' || inputs.release-dev-run-id != '' }}
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 20
|
||||
steps:
|
||||
@@ -176,7 +181,7 @@ jobs:
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
with:
|
||||
github-token: ${{ github.token }}
|
||||
run-id: ${{ github.event.workflow_run.id }}
|
||||
run-id: ${{ inputs.release-dev-run-id || github.event.workflow_run.id }}
|
||||
pattern: snap-linux-amd64
|
||||
path: release/
|
||||
merge-multiple: true
|
||||
@@ -202,7 +207,27 @@ jobs:
|
||||
sudo snap services openshell
|
||||
openshell gateway add http://127.0.0.1:17670 --local --name snap-docker
|
||||
openshell gateway select snap-docker
|
||||
openshell status
|
||||
for _ in $(seq 1 30); do
|
||||
if openshell status; then
|
||||
exit 0
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
echo "Gateway did not become ready within 30 seconds" >&2
|
||||
exit 1
|
||||
|
||||
- name: Collect Snap diagnostics
|
||||
if: failure()
|
||||
run: |
|
||||
set +e
|
||||
sudo snap services openshell
|
||||
sudo snap connections openshell
|
||||
sudo snap changes
|
||||
sudo systemctl status snap.openshell.gateway.service --no-pager
|
||||
sudo journalctl -b -u snap.openshell.gateway.service --no-pager -n 300
|
||||
sudo journalctl -b -u snapd.service --no-pager -n 300
|
||||
sudo snap logs openshell.gateway -n=300
|
||||
sudo ss -ltnp '( sport = :17670 )'
|
||||
|
||||
kubernetes:
|
||||
name: Kubernetes Helm (kind)
|
||||
|
||||
@@ -28,6 +28,7 @@ jobs:
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
outputs:
|
||||
python_version: ${{ steps.v.outputs.python }}
|
||||
docs_version: ${{ steps.v.outputs.docs_version }}
|
||||
cargo_version: ${{ steps.v.outputs.cargo }}
|
||||
deb_version: ${{ steps.v.outputs.deb }}
|
||||
rpm_version: ${{ steps.v.outputs.rpm_version }}
|
||||
@@ -47,11 +48,15 @@ jobs:
|
||||
id: v
|
||||
run: |
|
||||
set -euo pipefail
|
||||
echo "python=$(uv run python tasks/scripts/release.py get-version --python)" >> "$GITHUB_OUTPUT"
|
||||
echo "cargo=$(uv run python tasks/scripts/release.py get-version --cargo)" >> "$GITHUB_OUTPUT"
|
||||
echo "deb=$(uv run python tasks/scripts/release.py get-version --deb)" >> "$GITHUB_OUTPUT"
|
||||
echo "rpm_version=$(uv run python tasks/scripts/release.py get-version --rpm-version)" >> "$GITHUB_OUTPUT"
|
||||
echo "rpm_release=$(uv run python tasks/scripts/release.py get-version --rpm-release)" >> "$GITHUB_OUTPUT"
|
||||
python_version=$(uv run python tasks/scripts/release.py get-version --dev --python)
|
||||
{
|
||||
echo "python=${python_version}"
|
||||
echo "docs_version=${python_version%%+*}"
|
||||
echo "cargo=$(uv run python tasks/scripts/release.py get-version --dev --cargo)"
|
||||
echo "deb=$(uv run python tasks/scripts/release.py get-version --dev --deb)"
|
||||
echo "rpm_version=$(uv run python tasks/scripts/release.py get-version --dev --rpm-version)"
|
||||
echo "rpm_release=$(uv run python tasks/scripts/release.py get-version --dev --rpm-release)"
|
||||
} >> "$GITHUB_OUTPUT"
|
||||
|
||||
build-cli:
|
||||
needs: compute-versions
|
||||
@@ -63,6 +68,27 @@ jobs:
|
||||
image-tag: dev
|
||||
secrets: inherit
|
||||
|
||||
build-conformance:
|
||||
needs: compute-versions
|
||||
permissions:
|
||||
contents: read
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- triple: x86_64-unknown-linux-musl
|
||||
runner: linux-amd64-cpu8
|
||||
- triple: aarch64-unknown-linux-musl
|
||||
runner: linux-arm64-cpu8
|
||||
uses: ./.github/workflows/build-binaries.yml
|
||||
with:
|
||||
package: openshell-conformance-cli
|
||||
binary: openshell-conformance
|
||||
artifact-name: openshell-conformance-${{ matrix.triple }}
|
||||
triple: ${{ matrix.triple }}
|
||||
runner: ${{ matrix.runner }}
|
||||
cargo-version: ${{ needs.compute-versions.outputs.cargo_version }}
|
||||
secrets: inherit
|
||||
|
||||
build-gateway:
|
||||
needs: compute-versions
|
||||
permissions:
|
||||
@@ -125,7 +151,7 @@ jobs:
|
||||
secrets: inherit
|
||||
|
||||
docker-e2e:
|
||||
needs: [build-cli, build-gateway, build-supervisor-image]
|
||||
needs: [build-cli, build-conformance, build-gateway, build-supervisor-image]
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
@@ -134,9 +160,10 @@ jobs:
|
||||
with:
|
||||
image-tag: ${{ github.sha }}
|
||||
runner: linux-arm64-cpu8
|
||||
conformance-artifact-prefix: openshell-conformance
|
||||
|
||||
podman-e2e:
|
||||
needs: [build-cli, build-gateway, build-supervisor-image]
|
||||
needs: [build-cli, build-conformance, build-gateway, build-supervisor-image]
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
@@ -144,14 +171,17 @@ jobs:
|
||||
uses: ./.github/workflows/e2e-podman-test.yml
|
||||
with:
|
||||
image-tag: ${{ github.sha }}
|
||||
conformance-artifact-prefix: openshell-conformance
|
||||
|
||||
vm-e2e:
|
||||
needs: [build-cli, build-gateway, build-vm-driver]
|
||||
needs: [build-cli, build-conformance, build-gateway, build-vm-driver]
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
packages: read
|
||||
uses: ./.github/workflows/e2e-vm-test.yml
|
||||
with:
|
||||
conformance-artifact-prefix: openshell-conformance
|
||||
|
||||
tag-ghcr-dev:
|
||||
name: Tag GHCR Images as Dev
|
||||
@@ -600,6 +630,22 @@ jobs:
|
||||
release-kind: dev
|
||||
pin-sha: ${{ github.sha }}
|
||||
|
||||
publish-fern-docs:
|
||||
name: Sync and Publish Fern Docs
|
||||
needs: [compute-versions, release-dev, release-helm, trigger-wheel-publish]
|
||||
permissions:
|
||||
contents: write
|
||||
uses: ./.github/workflows/sync-docs.yml
|
||||
with:
|
||||
operation: sync
|
||||
channel: dev
|
||||
source_ref: ${{ github.sha }}
|
||||
release_version: ${{ needs.compute-versions.outputs.docs_version }}
|
||||
display_name: Dev
|
||||
availability: beta
|
||||
publish: true
|
||||
secrets: inherit
|
||||
|
||||
trigger-wheel-publish:
|
||||
name: Trigger Wheel Publish
|
||||
needs: [compute-versions, release-dev]
|
||||
|
||||
@@ -48,6 +48,7 @@ jobs:
|
||||
semver: ${{ steps.v.outputs.semver }}
|
||||
# Commit resolved from RELEASE_TAG, used for image tags and downstream metadata
|
||||
source_sha: ${{ steps.v.outputs.source_sha }}
|
||||
is_prerelease: ${{ steps.v.outputs.is_prerelease }}
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
@@ -64,14 +65,25 @@ jobs:
|
||||
id: v
|
||||
run: |
|
||||
set -euo pipefail
|
||||
echo "python=$(uv run python tasks/scripts/release.py get-version --python)" >> "$GITHUB_OUTPUT"
|
||||
echo "cargo=$(uv run python tasks/scripts/release.py get-version --cargo)" >> "$GITHUB_OUTPUT"
|
||||
echo "npm=$(uv run python tasks/scripts/release.py get-version --npm)" >> "$GITHUB_OUTPUT"
|
||||
echo "deb=$(uv run python tasks/scripts/release.py get-version --deb)" >> "$GITHUB_OUTPUT"
|
||||
echo "rpm_version=$(uv run python tasks/scripts/release.py get-version --rpm-version)" >> "$GITHUB_OUTPUT"
|
||||
echo "rpm_release=$(uv run python tasks/scripts/release.py get-version --rpm-release)" >> "$GITHUB_OUTPUT"
|
||||
echo "semver=${RELEASE_TAG#v}" >> "$GITHUB_OUTPUT"
|
||||
echo "source_sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"
|
||||
if [[ "${RELEASE_TAG}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+-pre\.[1-9][0-9]*$ ]]; then
|
||||
is_prerelease=true
|
||||
elif [[ "${RELEASE_TAG}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
||||
is_prerelease=false
|
||||
else
|
||||
echo "Unsupported release tag: ${RELEASE_TAG}" >&2
|
||||
exit 1
|
||||
fi
|
||||
{
|
||||
echo "is_prerelease=${is_prerelease}"
|
||||
echo "python=$(uv run python tasks/scripts/release.py get-version --python)"
|
||||
echo "cargo=$(uv run python tasks/scripts/release.py get-version --cargo)"
|
||||
echo "npm=$(uv run python tasks/scripts/release.py get-version --npm)"
|
||||
echo "deb=$(uv run python tasks/scripts/release.py get-version --deb)"
|
||||
echo "rpm_version=$(uv run python tasks/scripts/release.py get-version --rpm-version)"
|
||||
echo "rpm_release=$(uv run python tasks/scripts/release.py get-version --rpm-release)"
|
||||
echo "semver=${RELEASE_TAG#v}"
|
||||
echo "source_sha=$(git rev-parse HEAD)"
|
||||
} >> "$GITHUB_OUTPUT"
|
||||
|
||||
build-cli:
|
||||
needs: compute-versions
|
||||
@@ -84,6 +96,28 @@ jobs:
|
||||
checkout-ref: ${{ inputs.tag || github.ref }}
|
||||
secrets: inherit
|
||||
|
||||
build-conformance:
|
||||
needs: compute-versions
|
||||
permissions:
|
||||
contents: read
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- triple: x86_64-unknown-linux-musl
|
||||
runner: linux-amd64-cpu8
|
||||
- triple: aarch64-unknown-linux-musl
|
||||
runner: linux-arm64-cpu8
|
||||
uses: ./.github/workflows/build-binaries.yml
|
||||
with:
|
||||
package: openshell-conformance-cli
|
||||
binary: openshell-conformance
|
||||
artifact-name: openshell-conformance-${{ matrix.triple }}
|
||||
triple: ${{ matrix.triple }}
|
||||
runner: ${{ matrix.runner }}
|
||||
cargo-version: ${{ needs.compute-versions.outputs.cargo_version }}
|
||||
checkout-ref: ${{ inputs.tag || github.ref }}
|
||||
secrets: inherit
|
||||
|
||||
build-gateway:
|
||||
needs: compute-versions
|
||||
permissions:
|
||||
@@ -153,7 +187,7 @@ jobs:
|
||||
secrets: inherit
|
||||
|
||||
docker-e2e:
|
||||
needs: [compute-versions, build-cli, build-gateway, build-supervisor-image]
|
||||
needs: [compute-versions, build-cli, build-conformance, build-gateway, build-supervisor-image]
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
@@ -163,9 +197,10 @@ jobs:
|
||||
image-tag: ${{ needs.compute-versions.outputs.source_sha }}
|
||||
checkout-ref: ${{ inputs.tag || github.ref }}
|
||||
runner: linux-arm64-cpu8
|
||||
conformance-artifact-prefix: openshell-conformance
|
||||
|
||||
podman-e2e:
|
||||
needs: [compute-versions, build-cli, build-gateway, build-supervisor-image]
|
||||
needs: [compute-versions, build-cli, build-conformance, build-gateway, build-supervisor-image]
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
@@ -174,9 +209,10 @@ jobs:
|
||||
with:
|
||||
image-tag: ${{ needs.compute-versions.outputs.source_sha }}
|
||||
checkout-ref: ${{ inputs.tag || github.ref }}
|
||||
conformance-artifact-prefix: openshell-conformance
|
||||
|
||||
vm-e2e:
|
||||
needs: [compute-versions, build-cli, build-gateway, build-vm-driver]
|
||||
needs: [compute-versions, build-cli, build-conformance, build-gateway, build-vm-driver]
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
@@ -184,9 +220,10 @@ jobs:
|
||||
uses: ./.github/workflows/e2e-vm-test.yml
|
||||
with:
|
||||
checkout-ref: ${{ inputs.tag || github.ref }}
|
||||
conformance-artifact-prefix: openshell-conformance
|
||||
|
||||
tag-ghcr-release:
|
||||
name: Tag GHCR Images for Release
|
||||
name: Tag GHCR Images
|
||||
needs: [compute-versions, build-gateway-image, build-supervisor-image, release]
|
||||
runs-on: linux-amd64-cpu8
|
||||
timeout-minutes: 10
|
||||
@@ -194,22 +231,27 @@ jobs:
|
||||
- name: Log in to GHCR
|
||||
run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u "${GITHUB_ACTOR}" --password-stdin
|
||||
|
||||
- name: Tag images with version and latest
|
||||
- name: Tag images with release version
|
||||
env:
|
||||
IS_PRERELEASE: ${{ needs.compute-versions.outputs.is_prerelease }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
REGISTRY="ghcr.io/nvidia/openshell"
|
||||
VERSION="${{ needs.compute-versions.outputs.semver }}"
|
||||
SOURCE_TAG="${{ needs.compute-versions.outputs.source_sha }}"
|
||||
for component in gateway supervisor; do
|
||||
echo "Tagging ${REGISTRY}/${component}:${SOURCE_TAG} as ${VERSION} and latest..."
|
||||
echo "Tagging ${REGISTRY}/${component}:${SOURCE_TAG} as ${VERSION}..."
|
||||
docker buildx imagetools create \
|
||||
--prefer-index=false \
|
||||
-t "${REGISTRY}/${component}:${VERSION}" \
|
||||
"${REGISTRY}/${component}:${SOURCE_TAG}"
|
||||
docker buildx imagetools create \
|
||||
--prefer-index=false \
|
||||
-t "${REGISTRY}/${component}:latest" \
|
||||
"${REGISTRY}/${component}:${SOURCE_TAG}"
|
||||
if [[ "${IS_PRERELEASE}" != "true" ]]; then
|
||||
echo "Tagging ${REGISTRY}/${component}:${SOURCE_TAG} as latest..."
|
||||
docker buildx imagetools create \
|
||||
--prefer-index=false \
|
||||
-t "${REGISTRY}/${component}:latest" \
|
||||
"${REGISTRY}/${component}:${SOURCE_TAG}"
|
||||
fi
|
||||
done
|
||||
|
||||
build-python-wheel:
|
||||
@@ -265,8 +307,9 @@ jobs:
|
||||
uses: ./.github/workflows/snap-package.yml
|
||||
with:
|
||||
checkout-ref: ${{ inputs.tag || github.ref }}
|
||||
upload-channel: latest/stable
|
||||
github-environment: latest/stable
|
||||
upload-channel: ${{ needs.compute-versions.outputs.is_prerelease == 'true' && 'latest/edge' || 'latest/stable' }}
|
||||
github-environment: ${{ needs.compute-versions.outputs.is_prerelease == 'true' && 'latest/edge' || 'latest/stable' }}
|
||||
publish: ${{ needs.compute-versions.outputs.is_prerelease != 'true' }}
|
||||
secrets:
|
||||
publish-credentials: ${{ secrets.SNAPCRAFT_STORE_CREDENTIALS }}
|
||||
|
||||
@@ -529,6 +572,7 @@ jobs:
|
||||
cat openshell-sandbox-checksums-sha256.txt
|
||||
|
||||
- name: Generate Homebrew formula
|
||||
if: needs.compute-versions.outputs.is_prerelease != 'true'
|
||||
run: |
|
||||
set -euo pipefail
|
||||
python3 tasks/scripts/release.py generate-homebrew-formula \
|
||||
@@ -547,6 +591,7 @@ jobs:
|
||||
release/*.whl
|
||||
|
||||
- name: Prune removed VM checksum asset
|
||||
if: needs.compute-versions.outputs.is_prerelease != 'true'
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
|
||||
with:
|
||||
script: |
|
||||
@@ -569,6 +614,7 @@ jobs:
|
||||
}
|
||||
|
||||
- name: Create GitHub Release
|
||||
if: needs.compute-versions.outputs.is_prerelease != 'true'
|
||||
uses: softprops/action-gh-release@3d0d9888cb7fd7b750713d6e236d1fcb99157228 # v3.0.2
|
||||
with:
|
||||
name: OpenShell ${{ env.RELEASE_TAG }}
|
||||
@@ -605,35 +651,35 @@ jobs:
|
||||
release/openshell-gateway-checksums-sha256.txt
|
||||
release/openshell-sandbox-checksums-sha256.txt
|
||||
|
||||
- name: Upload pre-release artifacts
|
||||
if: needs.compute-versions.outputs.is_prerelease == 'true'
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
with:
|
||||
name: openshell-${{ env.RELEASE_TAG }}
|
||||
path: release/
|
||||
retention-days: 90
|
||||
if-no-files-found: error
|
||||
|
||||
publish-fern-docs:
|
||||
name: Publish Fern Docs
|
||||
needs: [release]
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 15
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
ref: ${{ inputs.tag || github.ref }}
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
node-version: "24"
|
||||
|
||||
- name: Install Fern CLI
|
||||
run: |
|
||||
FERN_VERSION=$(node -p "require('./fern/fern.config.json').version")
|
||||
npm install -g "fern-api@${FERN_VERSION}"
|
||||
|
||||
- name: Publish Fern docs
|
||||
env:
|
||||
FERN_TOKEN: ${{ secrets.FERN_TOKEN }}
|
||||
working-directory: ./fern
|
||||
run: fern generate --docs
|
||||
name: Sync and Publish Fern Docs
|
||||
needs: [compute-versions, release, publish-sdk-typescript, release-helm, trigger-wheel-publish]
|
||||
if: needs.compute-versions.outputs.is_prerelease != 'true'
|
||||
permissions:
|
||||
contents: write
|
||||
uses: ./.github/workflows/sync-docs.yml
|
||||
with:
|
||||
operation: sync
|
||||
channel: latest
|
||||
source_ref: ${{ needs.compute-versions.outputs.source_sha }}
|
||||
release_version: ${{ needs.compute-versions.outputs.semver }}
|
||||
display_name: Latest (v${{ needs.compute-versions.outputs.semver }})
|
||||
publish: true
|
||||
secrets: inherit
|
||||
|
||||
publish-sdk-typescript:
|
||||
name: Publish TypeScript SDK
|
||||
needs: [compute-versions, release]
|
||||
if: needs.compute-versions.outputs.is_prerelease != 'true'
|
||||
runs-on: linux-amd64-cpu8
|
||||
timeout-minutes: 15
|
||||
permissions:
|
||||
@@ -690,6 +736,7 @@ jobs:
|
||||
trigger-wheel-publish:
|
||||
name: Trigger Wheel Publish
|
||||
needs: [compute-versions, release]
|
||||
if: needs.compute-versions.outputs.is_prerelease != 'true'
|
||||
runs-on: [self-hosted, nv]
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
|
||||
@@ -37,83 +37,20 @@ jobs:
|
||||
matrix:
|
||||
include:
|
||||
- arch: x86_64
|
||||
artifact_arch: amd64
|
||||
runner: linux-amd64-cpu8
|
||||
cli_target: x86_64-unknown-linux-musl
|
||||
gnu_target: x86_64-unknown-linux-gnu
|
||||
gateway_target: x86_64-unknown-linux-gnu
|
||||
- arch: aarch64
|
||||
artifact_arch: arm64
|
||||
runner: linux-arm64-cpu8
|
||||
cli_target: aarch64-unknown-linux-musl
|
||||
gnu_target: aarch64-unknown-linux-gnu
|
||||
runs-on: ${{ matrix.runner }}
|
||||
timeout-minutes: 60
|
||||
container:
|
||||
image: fedora:latest
|
||||
steps:
|
||||
- name: Install build dependencies
|
||||
run: |
|
||||
dnf install -y \
|
||||
packit rpm-build \
|
||||
rust cargo gcc gcc-c++ make cmake pkg-config \
|
||||
clang-devel z3-devel systemd-rpm-macros \
|
||||
pandoc python3-devel git-core \
|
||||
cargo-rpm-macros
|
||||
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
ref: ${{ inputs.checkout-ref }}
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Download CLI artifact
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
with:
|
||||
name: openshell-${{ matrix.cli_target }}
|
||||
path: package-binaries/
|
||||
|
||||
- name: Download gateway artifact
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
with:
|
||||
name: openshell-gateway-${{ matrix.gnu_target }}
|
||||
path: package-binaries/
|
||||
|
||||
- name: Configure package inputs
|
||||
run: |
|
||||
set -euo pipefail
|
||||
chmod +x package-binaries/openshell{,-gateway}
|
||||
ls -lah package-binaries
|
||||
|
||||
- name: Mark workspace safe for git
|
||||
run: git config --global --add safe.directory "$GITHUB_WORKSPACE"
|
||||
|
||||
- name: Fetch tags
|
||||
run: git fetch --tags --force
|
||||
|
||||
- name: Build RPMs via Packit
|
||||
env:
|
||||
OPENSHELL_RPM_VERSION: ${{ inputs['rpm-version'] }}
|
||||
OPENSHELL_RPM_RELEASE: ${{ inputs['rpm-release'] }}
|
||||
OPENSHELL_CARGO_VERSION: ${{ inputs['cargo-version'] }}
|
||||
OPENSHELL_PREBUILT_BINARIES_DIR: ${{ github.workspace }}/package-binaries
|
||||
run: packit build locally
|
||||
|
||||
- name: Collect RPM artifacts
|
||||
run: |
|
||||
set -euo pipefail
|
||||
mkdir -p artifacts
|
||||
mapfile -t rpms < <(find "$GITHUB_WORKSPACE" -maxdepth 3 -type f -name '*.rpm' ! -name '*.src.rpm' | sort)
|
||||
if [ "${#rpms[@]}" -eq 0 ]; then
|
||||
echo "::error::No RPM artifacts found under $GITHUB_WORKSPACE"
|
||||
find "$GITHUB_WORKSPACE" -maxdepth 3 -type f | sort
|
||||
exit 1
|
||||
fi
|
||||
cp "${rpms[@]}" artifacts/
|
||||
echo "=== Built RPMs ==="
|
||||
ls -lah artifacts/
|
||||
|
||||
- name: Upload RPM artifacts
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
with:
|
||||
name: rpm-linux-${{ matrix.arch }}
|
||||
path: artifacts/*.rpm
|
||||
retention-days: 5
|
||||
gateway_target: aarch64-unknown-linux-gnu
|
||||
uses: ./.github/workflows/build-rpm.yml
|
||||
with:
|
||||
checkout-ref: ${{ inputs.checkout-ref }}
|
||||
arch: ${{ matrix.arch }}
|
||||
runner: ${{ matrix.runner }}
|
||||
cli-target: ${{ matrix.cli_target }}
|
||||
gateway-target: ${{ matrix.gateway_target }}
|
||||
rpm-version: ${{ inputs.rpm-version }}
|
||||
rpm-release: ${{ inputs.rpm-release }}
|
||||
cargo-version: ${{ inputs.cargo-version }}
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
name: Rust Cache Seed
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
CARGO_INCREMENTAL: "0"
|
||||
MISE_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
SCCACHE_GHA_ENABLED: "true"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
packages: read
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
rust:
|
||||
name: Rust (${{ matrix.runner }})
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
runner: [linux-amd64-cpu8, linux-arm64-cpu8]
|
||||
runs-on: ${{ matrix.runner }}
|
||||
env:
|
||||
SCCACHE_GHA_VERSION: branch-checks-rust-${{ matrix.runner }}
|
||||
container:
|
||||
image: ghcr.io/nvidia/openshell/ci:latest
|
||||
credentials:
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
|
||||
- name: Install tools
|
||||
run: mise install --locked
|
||||
|
||||
- name: Configure GHA sccache backend
|
||||
uses: mozilla-actions/sccache-action@9e7fa8a12102821edf02ca5dbea1acd0f89a2696 # v0.0.10
|
||||
|
||||
- name: Cache Rust target and registry
|
||||
uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2
|
||||
with:
|
||||
shared-key: rust-checks-${{ matrix.runner }}
|
||||
cache-on-failure: true
|
||||
|
||||
- name: Format
|
||||
run: mise run rust:format:check
|
||||
|
||||
- name: Lint
|
||||
run: mise run rust:lint
|
||||
|
||||
- name: Test
|
||||
run: mise run test:rust
|
||||
|
||||
- name: sccache stats
|
||||
if: always()
|
||||
run: |
|
||||
set +e
|
||||
stats_bin="${SCCACHE_PATH:-sccache}"
|
||||
"$stats_bin" --show-stats
|
||||
status=$?
|
||||
if [ "$status" -ne 0 ]; then
|
||||
echo "::warning::sccache stats unavailable (exit $status)"
|
||||
fi
|
||||
exit 0
|
||||
@@ -17,6 +17,11 @@ on:
|
||||
required: true
|
||||
type: string
|
||||
description: "GitHub deployment environment for approval gates (e.g., latest/edge, latest/stable)"
|
||||
publish:
|
||||
required: false
|
||||
type: boolean
|
||||
default: true
|
||||
description: "Whether to upload the built snap to the Snap Store"
|
||||
|
||||
secrets:
|
||||
publish-credentials:
|
||||
@@ -161,6 +166,7 @@ jobs:
|
||||
retention-days: 5
|
||||
|
||||
- name: Upload snap to Snap Store
|
||||
if: inputs.publish
|
||||
env:
|
||||
SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.publish-credentials }}
|
||||
INPUTS_UPLOAD_CHANNEL: ${{ inputs.upload-channel }}
|
||||
|
||||
@@ -4,6 +4,49 @@
|
||||
name: Sync Docs Website
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
operation:
|
||||
description: "Whether to sync or remove a docs snapshot"
|
||||
required: true
|
||||
type: string
|
||||
channel:
|
||||
description: "Docs channel to update or remove"
|
||||
required: true
|
||||
type: string
|
||||
source_ref:
|
||||
description: "Source commit SHA, branch, or tag to snapshot when operation=sync"
|
||||
required: false
|
||||
type: string
|
||||
release_version:
|
||||
description: "Release version used to order mutable channels"
|
||||
required: false
|
||||
type: string
|
||||
version_slug:
|
||||
description: "Version slug when channel=version"
|
||||
required: false
|
||||
type: string
|
||||
display_name:
|
||||
description: "Optional version selector display name"
|
||||
required: false
|
||||
type: string
|
||||
availability:
|
||||
description: "Optional Fern availability status"
|
||||
required: false
|
||||
type: string
|
||||
publish:
|
||||
description: "Publish production docs after syncing"
|
||||
required: false
|
||||
default: false
|
||||
type: boolean
|
||||
allow_rollback:
|
||||
description: "Allow an explicitly requested mutable-channel rollback"
|
||||
required: false
|
||||
default: false
|
||||
type: boolean
|
||||
secrets:
|
||||
FERN_TOKEN:
|
||||
required: false
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
operation:
|
||||
@@ -21,19 +64,38 @@ on:
|
||||
options:
|
||||
- dev
|
||||
- latest
|
||||
- stable
|
||||
- version
|
||||
source_ref:
|
||||
description: "Source commit SHA, branch, or tag to snapshot when operation=sync"
|
||||
required: false
|
||||
type: string
|
||||
release_version:
|
||||
description: "Release version, e.g. 0.1.2 or 0.1.3.dev4"
|
||||
required: false
|
||||
type: string
|
||||
version_slug:
|
||||
description: "Version slug when channel=version, e.g. v0.0.36"
|
||||
required: false
|
||||
type: string
|
||||
display_name:
|
||||
description: "Optional version selector display name"
|
||||
description: "Optional selector name, e.g. Dev"
|
||||
required: false
|
||||
type: string
|
||||
availability:
|
||||
description: "Optional Fern status: beta, deprecated, ga, or stable"
|
||||
required: false
|
||||
type: string
|
||||
publish:
|
||||
description: "Publish production docs after syncing"
|
||||
required: false
|
||||
default: false
|
||||
type: boolean
|
||||
allow_rollback:
|
||||
description: "Allow an explicitly requested mutable-channel rollback"
|
||||
required: false
|
||||
default: false
|
||||
type: boolean
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
@@ -41,6 +103,7 @@ permissions:
|
||||
concurrency:
|
||||
group: docs-website
|
||||
cancel-in-progress: false
|
||||
queue: max
|
||||
|
||||
defaults:
|
||||
run:
|
||||
@@ -65,6 +128,7 @@ jobs:
|
||||
OPERATION: ${{ inputs.operation }}
|
||||
CHANNEL: ${{ inputs.channel }}
|
||||
SOURCE_REF: ${{ inputs.source_ref }}
|
||||
RELEASE_VERSION: ${{ inputs.release_version }}
|
||||
VERSION_SLUG: ${{ inputs.version_slug }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
@@ -72,8 +136,12 @@ jobs:
|
||||
echo "source_ref is required when operation=sync" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ "$CHANNEL" == "version" && -z "$VERSION_SLUG" ]]; then
|
||||
echo "version_slug is required when channel=version" >&2
|
||||
if [[ "$CHANNEL" =~ ^(dev|latest|stable)$ && -z "$RELEASE_VERSION" ]]; then
|
||||
echo "release_version is required for dev, latest, and stable channels" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ "$CHANNEL" =~ ^(stable|version)$ && -z "$VERSION_SLUG" ]]; then
|
||||
echo "version_slug is required for stable and version channels" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -96,6 +164,7 @@ jobs:
|
||||
uses: astral-sh/setup-uv@ae62891fec2bb8e7d6c99fc78c9fec3a63790f8d # v10.0.0
|
||||
with:
|
||||
version: "0.10.12"
|
||||
python-version: "3.13"
|
||||
|
||||
- name: Update docs snapshot
|
||||
# Inputs flow in as quoted env vars to avoid shell injection; see the
|
||||
@@ -104,17 +173,32 @@ jobs:
|
||||
OPERATION: ${{ inputs.operation }}
|
||||
CHANNEL: ${{ inputs.channel }}
|
||||
SOURCE_REF: ${{ inputs.source_ref }}
|
||||
RELEASE_VERSION: ${{ inputs.release_version }}
|
||||
VERSION_SLUG: ${{ inputs.version_slug }}
|
||||
DISPLAY_NAME: ${{ inputs.display_name }}
|
||||
AVAILABILITY: ${{ inputs.availability }}
|
||||
ALLOW_ROLLBACK: ${{ inputs.allow_rollback }}
|
||||
run: |
|
||||
SOURCE_SHA=""
|
||||
if [[ "$OPERATION" == "sync" ]]; then
|
||||
SOURCE_SHA=$(git -C source rev-parse HEAD)
|
||||
fi
|
||||
rollback_args=()
|
||||
if [[ "$ALLOW_ROLLBACK" == "true" ]]; then
|
||||
rollback_args+=(--allow-rollback)
|
||||
fi
|
||||
uv run automation/tasks/scripts/sync_docs_website.py \
|
||||
--operation "$OPERATION" \
|
||||
--source-root source \
|
||||
--docs-website-root docs-website \
|
||||
--channel "$CHANNEL" \
|
||||
--source-ref "$SOURCE_REF" \
|
||||
--source-sha "$SOURCE_SHA" \
|
||||
--release-version "$RELEASE_VERSION" \
|
||||
--version-slug "$VERSION_SLUG" \
|
||||
--display-name "$DISPLAY_NAME"
|
||||
--display-name "$DISPLAY_NAME" \
|
||||
--availability "$AVAILABILITY" \
|
||||
"${rollback_args[@]}"
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
@@ -160,3 +244,10 @@ jobs:
|
||||
git commit -m "docs(website): remove ${target} docs"
|
||||
fi
|
||||
git push origin HEAD:docs-website
|
||||
|
||||
- name: Publish Fern docs
|
||||
if: ${{ inputs.publish }}
|
||||
env:
|
||||
FERN_TOKEN: ${{ secrets.FERN_TOKEN }}
|
||||
working-directory: docs-website/fern
|
||||
run: fern generate --docs
|
||||
|
||||
@@ -0,0 +1,171 @@
|
||||
# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
name: Trivy Changes
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
merge_group:
|
||||
types: [checks_requested]
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
base_sha:
|
||||
description: Base commit SHA to compare
|
||||
required: true
|
||||
type: string
|
||||
head_sha:
|
||||
description: Candidate commit SHA to compare
|
||||
required: true
|
||||
type: string
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
changes:
|
||||
name: Detect deployment configuration changes
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: read
|
||||
outputs:
|
||||
should_run: ${{ steps.default.outputs.should_run || steps.changed.outputs.any_modified }}
|
||||
steps:
|
||||
- id: default
|
||||
if: github.event_name != 'pull_request'
|
||||
run: echo "should_run=true" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
if: github.event_name == 'pull_request'
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- id: changed
|
||||
if: github.event_name == 'pull_request'
|
||||
uses: tj-actions/changed-files@aa08304bd477b800d468db44fe10f6c61f7f7b11 # v42.1.0
|
||||
with:
|
||||
# `any_modified` covers deletions, which `any_changed` omits, and a
|
||||
# failed diff has to fail the job: both otherwise report no relevant
|
||||
# change, and removing the scanner or a value fixture would skip the
|
||||
# scan behind a green status.
|
||||
fail_on_initial_diff_error: true
|
||||
files: |
|
||||
deploy/docker/**
|
||||
deploy/helm/**
|
||||
deploy/kube/**
|
||||
.trivyignore.yaml
|
||||
flake.nix
|
||||
flake.lock
|
||||
tasks/scripts/trivy-scan.sh
|
||||
tasks/scripts/trivy-scan-test.sh
|
||||
tasks/scripts/trivy-config-report.jq
|
||||
.github/workflows/trivy-scan.yml
|
||||
.github/workflows/trivy-changes.yml
|
||||
|
||||
scan:
|
||||
name: Scan changed deployment configuration
|
||||
needs: changes
|
||||
if: needs.changes.outputs.should_run == 'true'
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
env:
|
||||
BASE_REF: ${{ github.event.pull_request.base.sha || github.event.merge_group.base_sha || inputs.base_sha }}
|
||||
HEAD_REF: ${{ inputs.head_sha || github.sha }}
|
||||
defaults:
|
||||
run:
|
||||
shell: nix develop --command bash -euo pipefail {0}
|
||||
steps:
|
||||
- name: Check out candidate
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
ref: ${{ env.HEAD_REF }}
|
||||
persist-credentials: false
|
||||
|
||||
- name: Check out baseline
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
ref: ${{ env.BASE_REF }}
|
||||
path: .trivy-base
|
||||
persist-credentials: false
|
||||
|
||||
- name: Set up Nix
|
||||
uses: ./.github/actions/setup-nix
|
||||
|
||||
- name: Test report comparison
|
||||
run: tasks/scripts/trivy-scan-test.sh
|
||||
|
||||
- name: Validate candidate ignore policy
|
||||
run: tasks/scripts/trivy-scan.sh validate-ignore
|
||||
|
||||
# Ignore-policy changes take effect only after merge. Applying the
|
||||
# baseline policy to both scans prevents a candidate from exempting a new
|
||||
# finding in the same change that introduces it.
|
||||
- name: Prepare baseline ignore policy
|
||||
run: |
|
||||
if [ -f .trivy-base/.trivyignore.yaml ]; then
|
||||
cp .trivy-base/.trivyignore.yaml "$RUNNER_TEMP/trivy-baseline-ignore.yaml"
|
||||
else
|
||||
printf 'misconfigurations: []\n' >"$RUNNER_TEMP/trivy-baseline-ignore.yaml"
|
||||
fi
|
||||
|
||||
- name: Scan baseline
|
||||
env:
|
||||
TRIVY_SOURCE_ROOT: ${{ github.workspace }}/.trivy-base
|
||||
TRIVY_IGNORE_FILE: ${{ runner.temp }}/trivy-baseline-ignore.yaml
|
||||
TRIVY_REPORT_DIR: ${{ runner.temp }}/trivy-base
|
||||
run: |
|
||||
mkdir -p "$TRIVY_REPORT_DIR"
|
||||
"$GITHUB_WORKSPACE/tasks/scripts/trivy-scan.sh" config
|
||||
|
||||
- name: Scan candidate
|
||||
env:
|
||||
TRIVY_IGNORE_FILE: ${{ runner.temp }}/trivy-baseline-ignore.yaml
|
||||
TRIVY_REPORT_DIR: ${{ runner.temp }}/trivy-head
|
||||
run: |
|
||||
mkdir -p "$TRIVY_REPORT_DIR"
|
||||
tasks/scripts/trivy-scan.sh config
|
||||
|
||||
- name: Reject new high or critical findings
|
||||
run: |
|
||||
tasks/scripts/trivy-scan.sh gate-config-diff \
|
||||
"$RUNNER_TEMP/trivy-base" "$RUNNER_TEMP/trivy-head"
|
||||
|
||||
- name: Upload reports
|
||||
if: always()
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: trivy-changes-${{ github.run_id }}
|
||||
path: |
|
||||
${{ runner.temp }}/trivy-base
|
||||
${{ runner.temp }}/trivy-head
|
||||
if-no-files-found: ignore
|
||||
retention-days: 14
|
||||
|
||||
result:
|
||||
name: OpenShell / Trivy Changes
|
||||
needs: [changes, scan]
|
||||
if: always()
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check scan result
|
||||
env:
|
||||
CHANGES_RESULT: ${{ needs.changes.result }}
|
||||
SHOULD_RUN: ${{ needs.changes.outputs.should_run }}
|
||||
SCAN_RESULT: ${{ needs.scan.result }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
if [ "$CHANGES_RESULT" != "success" ]; then
|
||||
echo "::error::Change detection concluded $CHANGES_RESULT."
|
||||
exit 1
|
||||
fi
|
||||
if [ "$SHOULD_RUN" = "true" ] && [ "$SCAN_RESULT" != "success" ]; then
|
||||
echo "::error::Trivy scan concluded $SCAN_RESULT."
|
||||
exit 1
|
||||
fi
|
||||
if [ "$SHOULD_RUN" != "true" ]; then
|
||||
echo "No Helm or Dockerfile changes to scan."
|
||||
fi
|
||||
@@ -0,0 +1,219 @@
|
||||
# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
name: Trivy Scan
|
||||
|
||||
# Manual or reusable scan of deployment configuration and supplied OCI
|
||||
# artifacts. Findings are informational by default; scanner errors remain fatal.
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
images:
|
||||
description: Newline-separated image references to scan
|
||||
type: string
|
||||
default: ""
|
||||
charts:
|
||||
description: Newline-separated packaged Helm chart OCI references
|
||||
type: string
|
||||
default: ""
|
||||
severity:
|
||||
description: Severities that fail the workflow
|
||||
type: string
|
||||
default: HIGH,CRITICAL
|
||||
ignore-unfixed:
|
||||
description: Ignore image vulnerabilities with no upstream fix
|
||||
type: boolean
|
||||
default: true
|
||||
fail-on-findings:
|
||||
description: Fail the run on findings instead of warning
|
||||
type: boolean
|
||||
default: false
|
||||
upload-sarif:
|
||||
description: Upload results to GitHub Code Scanning
|
||||
type: boolean
|
||||
default: true
|
||||
secrets:
|
||||
CACHIX_AUTH_TOKEN:
|
||||
description: Token used to write Nix build outputs to Cachix
|
||||
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
images:
|
||||
description: Newline-separated image references to scan
|
||||
type: string
|
||||
default: ""
|
||||
charts:
|
||||
description: Newline-separated packaged Helm chart OCI references to scan
|
||||
type: string
|
||||
default: ""
|
||||
severity:
|
||||
description: Severities that fail the workflow
|
||||
type: string
|
||||
default: HIGH,CRITICAL
|
||||
ignore-unfixed:
|
||||
description: Ignore image vulnerabilities with no upstream fix
|
||||
type: boolean
|
||||
default: true
|
||||
fail-on-findings:
|
||||
description: Fail the run on findings instead of warning
|
||||
type: boolean
|
||||
default: false
|
||||
upload-sarif:
|
||||
description: Upload results to GitHub Code Scanning
|
||||
type: boolean
|
||||
default: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: nix develop --command bash -euo pipefail {0}
|
||||
|
||||
env:
|
||||
TRIVY_SEVERITY: ${{ inputs.severity || 'HIGH,CRITICAL' }}
|
||||
TRIVY_REPORT_DIR: reports/trivy
|
||||
|
||||
jobs:
|
||||
scan:
|
||||
name: OpenShell / Trivy (informational)
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 60
|
||||
outputs:
|
||||
sarif-batches: ${{ steps.sarif.outputs.batches }}
|
||||
artifact-id: ${{ steps.reports.outputs.artifact-id }}
|
||||
permissions:
|
||||
contents: read
|
||||
packages: read
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ github.token }}
|
||||
|
||||
- name: Set up Nix
|
||||
uses: ./.github/actions/setup-nix
|
||||
with:
|
||||
cachix-auth-token: ${{ github.event_name != 'pull_request' && secrets.CACHIX_AUTH_TOKEN || '' }}
|
||||
|
||||
- name: Test scan reporting
|
||||
run: tasks/scripts/trivy-scan-test.sh
|
||||
|
||||
- name: Scan configuration
|
||||
id: config
|
||||
env:
|
||||
CHARTS: ${{ inputs.charts }}
|
||||
run: |
|
||||
args=()
|
||||
while IFS= read -r ref; do
|
||||
[ -n "$ref" ] || continue
|
||||
args+=(--chart-ref "$ref")
|
||||
done <<<"$CHARTS"
|
||||
tasks/scripts/trivy-scan.sh config "${args[@]}"
|
||||
|
||||
- name: Scan images
|
||||
id: images
|
||||
if: ${{ !cancelled() }}
|
||||
env:
|
||||
IMAGES: ${{ inputs.images }}
|
||||
TRIVY_IGNORE_UNFIXED: ${{ inputs.ignore-unfixed }}
|
||||
run: |
|
||||
refs=()
|
||||
while IFS= read -r ref; do
|
||||
[ -n "$ref" ] || continue
|
||||
refs+=("$ref")
|
||||
done <<<"$IMAGES"
|
||||
if [ "${#refs[@]}" -gt 0 ]; then
|
||||
tasks/scripts/trivy-scan.sh images "${refs[@]}"
|
||||
fi
|
||||
|
||||
- name: Prepare consolidated SARIF uploads
|
||||
id: sarif
|
||||
if: >-
|
||||
${{
|
||||
!cancelled()
|
||||
&& steps.config.conclusion == 'success'
|
||||
&& steps.images.conclusion == 'success'
|
||||
&& inputs.upload-sarif
|
||||
}}
|
||||
run: tasks/scripts/trivy-scan.sh prepare-sarif
|
||||
|
||||
- name: Upload reports
|
||||
id: reports
|
||||
if: always()
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: trivy-${{ github.run_id }}
|
||||
path: reports/trivy
|
||||
if-no-files-found: ignore
|
||||
retention-days: 14
|
||||
|
||||
- name: Report findings
|
||||
if: >-
|
||||
${{
|
||||
!cancelled()
|
||||
&& steps.config.conclusion == 'success'
|
||||
&& steps.images.conclusion == 'success'
|
||||
}}
|
||||
env:
|
||||
FAIL_ON_FINDINGS: ${{ inputs.fail-on-findings }}
|
||||
run: |
|
||||
set +e
|
||||
tasks/scripts/trivy-scan.sh gate
|
||||
status=$?
|
||||
set -e
|
||||
case "$status" in
|
||||
0) echo "No findings at ${TRIVY_SEVERITY}." ;;
|
||||
10)
|
||||
if [ "$FAIL_ON_FINDINGS" = "true" ]; then
|
||||
echo "::error::Trivy findings at ${TRIVY_SEVERITY}."
|
||||
exit 1
|
||||
fi
|
||||
echo "::warning::Trivy findings at ${TRIVY_SEVERITY}; this check is informational."
|
||||
;;
|
||||
*) echo "::error::Trivy could not evaluate the reports (exit $status)."; exit "$status" ;;
|
||||
esac
|
||||
|
||||
upload-sarif:
|
||||
name: Publish Trivy SARIF (${{ matrix.batch }})
|
||||
needs: scan
|
||||
# Findings may fail the scan job after reports are prepared. Publish those
|
||||
# complete results too, but never publish an incomplete/failed scan.
|
||||
if: >-
|
||||
${{
|
||||
!cancelled()
|
||||
&& inputs.upload-sarif
|
||||
&& needs.scan.outputs.sarif-batches != ''
|
||||
&& needs.scan.outputs.artifact-id != ''
|
||||
}}
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
permissions:
|
||||
contents: read
|
||||
security-events: write
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
batch: ${{ fromJSON(needs.scan.outputs.sarif-batches) }}
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
with:
|
||||
artifact-ids: ${{ needs.scan.outputs.artifact-id }}
|
||||
merge-multiple: true
|
||||
path: reports/trivy
|
||||
|
||||
# One configuration run, plus one run per image/platform or packaged
|
||||
# chart. Each batch holds at most GitHub's limit of 20 SARIF runs.
|
||||
- uses: github/codeql-action/upload-sarif@ff2f1c621b7f889edc0d3c761ac2e6a3f8cdb0dd # v4.37.7
|
||||
with:
|
||||
sarif_file: reports/trivy/code-scanning/uploads/${{ matrix.batch }}
|
||||
Reference in New Issue
Block a user