Skip to content

Release Canary

Release Canary #16

name: Release Canary
on:
workflow_dispatch:
inputs:
tag:
description: "Release tag to test (e.g. dev, v1.2.3)"
required: true
type: string
workflow_run:
workflows: ["Release Dev", "Release Tag"]
types: [completed]
permissions:
contents: read
packages: read
defaults:
run:
shell: bash
jobs:
# ---------------------------------------------------------------------------
# Verify the default install path (no OPENSHELL_VERSION) resolves to latest
# ---------------------------------------------------------------------------
install-default:
name: Install default (${{ matrix.arch }})
if: >-
github.event.workflow_run.conclusion == 'success'
&& github.event.workflow_run.name == 'Release Tag'
strategy:
fail-fast: false
matrix:
include:
- arch: amd64
runner: linux-amd64-cpu8
- arch: arm64
runner: linux-arm64-cpu8
runs-on: ${{ matrix.runner }}
timeout-minutes: 10
container:
image: ghcr.io/nvidia/openshell/ci:latest
credentials:
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
steps:
- name: Install CLI (default / latest)
run: |
set -euo pipefail
curl -LsSf https://raw.githubusercontent.com/NVIDIA/OpenShell/main/install.sh | sh
- name: Verify CLI installation
run: |
set -euo pipefail
command -v openshell
ACTUAL="$(openshell --version)"
echo "Installed: $ACTUAL"
# This job only runs after Release Tag, so the triggering tag
# should match the latest release the default installer resolves to.
TAG="${{ github.event.workflow_run.head_branch }}"
if [[ "$TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
EXPECTED="${TAG#v}"
if [[ "$ACTUAL" != *"$EXPECTED"* ]]; then
echo "::error::Version mismatch: expected '$EXPECTED' in '$ACTUAL'"
exit 1
fi
echo "Version check passed: found $EXPECTED in output"
fi
install-dev:
name: Install Debian package (${{ matrix.arch }})
if: ${{ github.event_name == 'workflow_dispatch' || github.event.workflow_run.conclusion == 'success' }}
strategy:
fail-fast: false
matrix:
include:
- arch: amd64
runner: linux-amd64-cpu8
- arch: arm64
runner: linux-arm64-cpu8
runs-on: ${{ matrix.runner }}
timeout-minutes: 10
container:
image: ghcr.io/nvidia/openshell/ci:latest
credentials:
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
steps:
- name: Determine release tag
id: release
run: |
set -euo pipefail
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
echo "tag=${{ inputs.tag }}" >> "$GITHUB_OUTPUT"
else
WORKFLOW_NAME="${{ github.event.workflow_run.name }}"
if [ "$WORKFLOW_NAME" = "Release Dev" ]; then
echo "tag=dev" >> "$GITHUB_OUTPUT"
elif [ "$WORKFLOW_NAME" = "Release Tag" ]; then
TAG="${{ github.event.workflow_run.head_branch }}"
if [ -z "$TAG" ]; then
echo "::error::Could not determine release tag from workflow_run"
exit 1
fi
echo "tag=${TAG}" >> "$GITHUB_OUTPUT"
else
echo "::error::Unexpected triggering workflow: ${WORKFLOW_NAME}"
exit 1
fi
fi
- name: Install Debian package
run: |
set -euo pipefail
curl -LsSf https://raw.githubusercontent.com/NVIDIA/OpenShell/main/install-dev.sh \
| OPENSHELL_VERSION=${{ steps.release.outputs.tag }} sh
- name: Verify gateway and VM driver versions
run: |
set -euo pipefail
command -v openshell-gateway
test -x /usr/libexec/openshell/openshell-driver-vm
GATEWAY_ACTUAL="$(openshell-gateway --version)"
DRIVER_ACTUAL="$(/usr/libexec/openshell/openshell-driver-vm --version)"
echo "Gateway: ${GATEWAY_ACTUAL}"
echo "Driver: ${DRIVER_ACTUAL}"
TAG="${{ steps.release.outputs.tag }}"
if [[ "$TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
EXPECTED="${TAG#v}"
for actual in "$GATEWAY_ACTUAL" "$DRIVER_ACTUAL"; do
if [[ "$actual" != *"$EXPECTED"* ]]; then
echo "::error::Version mismatch: expected '$EXPECTED' in '$actual'"
exit 1
fi
done
echo "Version check passed: found $EXPECTED in both binaries"
else
echo "Non-release tag ($TAG), skipping version check"
fi
canary:
name: Canary package gateway (${{ matrix.arch }})
if: ${{ github.event_name == 'workflow_dispatch' || github.event.workflow_run.conclusion == 'success' }}
strategy:
fail-fast: false
matrix:
include:
- arch: amd64
runner: linux-amd64-cpu8
- arch: arm64
runner: linux-arm64-cpu8
runs-on: ${{ matrix.runner }}
timeout-minutes: 30
env:
OPENSHELL_REGISTRY_TOKEN: ${{ secrets.GITHUB_TOKEN }}
OPENSHELL_CANARY_PORT: "17670"
steps:
- uses: actions/checkout@v6
- name: Determine release tag
id: release
run: |
set -euo pipefail
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
echo "tag=${{ inputs.tag }}" >> "$GITHUB_OUTPUT"
else
WORKFLOW_NAME="${{ github.event.workflow_run.name }}"
if [ "$WORKFLOW_NAME" = "Release Dev" ]; then
echo "tag=dev" >> "$GITHUB_OUTPUT"
elif [ "$WORKFLOW_NAME" = "Release Tag" ]; then
TAG="${{ github.event.workflow_run.head_branch }}"
if [ -z "$TAG" ]; then
echo "::error::Could not determine release tag from workflow_run"
exit 1
fi
echo "tag=${TAG}" >> "$GITHUB_OUTPUT"
else
echo "::error::Unexpected triggering workflow: ${WORKFLOW_NAME}"
exit 1
fi
fi
- name: Install Debian package
run: |
set -euo pipefail
curl -LsSf https://raw.githubusercontent.com/NVIDIA/OpenShell/main/install-dev.sh \
| OPENSHELL_VERSION=${{ steps.release.outputs.tag }} sh
- name: Verify package binaries
run: |
set -euo pipefail
command -v openshell
command -v openshell-gateway
test -x /usr/libexec/openshell/openshell-driver-vm
CLI_ACTUAL="$(openshell --version)"
GATEWAY_ACTUAL="$(openshell-gateway --version)"
DRIVER_ACTUAL="$(/usr/libexec/openshell/openshell-driver-vm --version)"
echo "CLI: ${CLI_ACTUAL}"
echo "Gateway: ${GATEWAY_ACTUAL}"
echo "Driver: ${DRIVER_ACTUAL}"
TAG="${{ steps.release.outputs.tag }}"
if [[ "$TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
EXPECTED="${TAG#v}"
for actual in "$CLI_ACTUAL" "$GATEWAY_ACTUAL" "$DRIVER_ACTUAL"; do
if [[ "$actual" != *"$EXPECTED"* ]]; then
echo "::error::Version mismatch: expected '$EXPECTED' in '$actual'"
exit 1
fi
done
echo "Version check passed: found $EXPECTED in package binaries"
else
echo "Non-release tag ($TAG), skipping version check"
fi
- name: Start packaged gateway
run: |
set -euo pipefail
# The CLI no longer owns gateway lifecycle. In CI we start the
# gateway binary installed by the Debian package directly, using the
# Docker driver so this canary can launch a real sandbox.
systemctl --user stop openshell-gateway >/dev/null 2>&1 || true
STATE_DIR="$(mktemp -d)"
LOG="${STATE_DIR}/openshell-gateway.log"
echo "GATEWAY_LOG=${LOG}" >> "$GITHUB_ENV"
echo "GATEWAY_STATE_DIR=${STATE_DIR}" >> "$GITHUB_ENV"
OPENSHELL_BIND_ADDRESS=0.0.0.0 \
OPENSHELL_SERVER_PORT="${OPENSHELL_CANARY_PORT}" \
OPENSHELL_DISABLE_TLS=true \
OPENSHELL_DISABLE_GATEWAY_AUTH=true \
OPENSHELL_DRIVERS=docker \
OPENSHELL_DB_URL="sqlite:${STATE_DIR}/openshell.db?mode=rwc" \
OPENSHELL_GRPC_ENDPOINT="http://host.openshell.internal:${OPENSHELL_CANARY_PORT}" \
OPENSHELL_SSH_GATEWAY_HOST=127.0.0.1 \
OPENSHELL_SSH_GATEWAY_PORT="${OPENSHELL_CANARY_PORT}" \
OPENSHELL_SANDBOX_NAMESPACE="canary-${{ matrix.arch }}-${{ github.run_id }}" \
nohup openshell-gateway >"${LOG}" 2>&1 &
PID=$!
echo "GATEWAY_PID=${PID}" >> "$GITHUB_ENV"
for _ in $(seq 1 60); do
if curl -fsS "http://127.0.0.1:${OPENSHELL_CANARY_PORT}/healthz" >/dev/null; then
break
fi
if ! kill -0 "$PID" 2>/dev/null; then
echo "::error::openshell-gateway exited before becoming healthy"
cat "$LOG"
exit 1
fi
sleep 1
done
curl -fsS "http://127.0.0.1:${OPENSHELL_CANARY_PORT}/healthz"
openshell gateway remove local >/dev/null 2>&1 || true
openshell gateway add "http://127.0.0.1:${OPENSHELL_CANARY_PORT}" --local --name local
- name: Run canary test
run: |
set -euo pipefail
echo "Creating sandbox and running 'echo hello world'..."
OUTPUT=$(openshell sandbox create --no-keep --no-tty -- echo "hello world" 2>&1) || {
EXIT_CODE=$?
echo "::error::openshell sandbox create failed with exit code ${EXIT_CODE}"
echo "$OUTPUT"
exit $EXIT_CODE
}
echo "$OUTPUT"
if echo "$OUTPUT" | grep -q "hello world"; then
echo "Canary test passed: 'hello world' found in output"
else
echo "::error::Canary test failed: 'hello world' not found in output"
exit 1
fi
- name: Stop packaged gateway
if: always()
run: |
set -euo pipefail
if [ -n "${GATEWAY_PID:-}" ]; then
kill "$GATEWAY_PID" >/dev/null 2>&1 || true
fi
if [ "${{ job.status }}" != "success" ] && [ -n "${GATEWAY_LOG:-}" ] && [ -f "$GATEWAY_LOG" ]; then
echo "Gateway log:"
cat "$GATEWAY_LOG"
fi