Release Canary #134
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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: build-amd64 | |
| - arch: arm64 | |
| runner: build-arm64 | |
| 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 | |
| canary: | |
| name: Canary ${{ matrix.mode }} (${{ matrix.arch }}) | |
| if: ${{ github.event_name == 'workflow_dispatch' || github.event.workflow_run.conclusion == 'success' }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| arch: | |
| - amd64 | |
| - arm64 | |
| mode: | |
| - auto-bootstrap | |
| - two-step | |
| include: | |
| - arch: amd64 | |
| runner: build-amd64 | |
| target: x86_64-unknown-linux-musl | |
| - arch: arm64 | |
| runner: build-arm64 | |
| target: aarch64-unknown-linux-musl | |
| runs-on: ${{ matrix.runner }} | |
| timeout-minutes: 30 | |
| container: | |
| image: ghcr.io/nvidia/openshell/ci:latest | |
| credentials: | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| options: --privileged | |
| volumes: | |
| - /var/run/docker.sock:/var/run/docker.sock | |
| env: | |
| OPENSHELL_REGISTRY_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| # The CI container mounts the host Docker socket, so the gateway | |
| # container is a sibling — not reachable at 127.0.0.1 from inside | |
| # this container. OPENSHELL_GATEWAY_HOST tells the auto-bootstrap | |
| # to advertise a reachable address instead. | |
| OPENSHELL_GATEWAY_HOST: host.docker.internal | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - 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 CLI from published install script | |
| run: | | |
| set -euo pipefail | |
| curl -LsSf https://raw.githubusercontent.com/NVIDIA/OpenShell/main/install.sh | OPENSHELL_VERSION=${{ steps.release.outputs.tag }} OPENSHELL_INSTALL_DIR=/usr/local/bin sh | |
| - name: Verify CLI installation | |
| run: | | |
| set -euo pipefail | |
| command -v openshell | |
| ACTUAL="$(openshell --version)" | |
| echo "Installed: $ACTUAL" | |
| TAG="${{ steps.release.outputs.tag }}" | |
| # For tagged releases (v1.2.3), verify the semver appears in the version string | |
| 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" | |
| else | |
| echo "Non-release tag ($TAG), skipping version check" | |
| fi | |
| - name: Resolve gateway host | |
| run: | | |
| # On Linux CI runners host.docker.internal is not set automatically | |
| # (it's a Docker Desktop feature). Add it via the Docker bridge IP. | |
| if ! getent hosts host.docker.internal >/dev/null 2>&1; then | |
| BRIDGE_IP=$(docker network inspect bridge --format '{{(index .IPAM.Config 0).Gateway}}') | |
| echo "Adding /etc/hosts entry: ${BRIDGE_IP} host.docker.internal" | |
| echo "${BRIDGE_IP} host.docker.internal" >> /etc/hosts | |
| fi | |
| # Two-step mode: explicitly start the gateway before creating a sandbox. | |
| # --gateway-host is required because the gateway container is a Docker | |
| # sibling (not in the same network namespace). Without it the metadata | |
| # stores 127.0.0.1 which is unreachable from this CI container. | |
| - name: Start gateway | |
| if: matrix.mode == 'two-step' | |
| run: | | |
| set -euo pipefail | |
| echo "Starting gateway..." | |
| openshell gateway start --gateway-host "$OPENSHELL_GATEWAY_HOST" | |
| - 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 |