From 171e208986cf687e418b69157d368e46ae5703c3 Mon Sep 17 00:00:00 2001 From: svelderrainruiz Date: Thu, 26 Feb 2026 16:15:25 -0800 Subject: [PATCH 1/2] feat(ops): publish deterministic cdev-cli runtime image --- .github/workflows/ci.yml | 3 +- .../workflows/publish-cli-runtime-image.yml | 113 ++++++++++++++++++ AGENTS.md | 1 + README.md | 18 +++ ...evCliRuntimeImagePublishContract.Tests.ps1 | 50 ++++++++ tools/cli-runtime/Dockerfile | 14 +++ tools/cli-runtime/README.md | 23 ++++ 7 files changed, 221 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/publish-cli-runtime-image.yml create mode 100644 tests/CdevCliRuntimeImagePublishContract.Tests.ps1 create mode 100644 tools/cli-runtime/Dockerfile create mode 100644 tools/cli-runtime/README.md diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4c6c7f3..6a8b53c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -36,7 +36,8 @@ jobs: './tests/CdevCliLinuxContract.Tests.ps1', './tests/CdevCliCiContract.Tests.ps1', './tests/CdevCliSyncGuardContract.Tests.ps1', - './tests/CdevCliForceAlignOpsContract.Tests.ps1' + './tests/CdevCliForceAlignOpsContract.Tests.ps1', + './tests/CdevCliRuntimeImagePublishContract.Tests.ps1' ) -CI -Output Detailed cli-contract: diff --git a/.github/workflows/publish-cli-runtime-image.yml b/.github/workflows/publish-cli-runtime-image.yml new file mode 100644 index 0000000..dd8b632 --- /dev/null +++ b/.github/workflows/publish-cli-runtime-image.yml @@ -0,0 +1,113 @@ +name: publish-cli-runtime-image + +on: + workflow_dispatch: + inputs: + promote_v1: + description: Also refresh the v1 tag. + required: false + default: true + type: boolean + additional_tag: + description: Optional extra tag (for example canary or rc1). + required: false + default: '' + type: string + push: + branches: + - main + paths: + - tools/cli-runtime/Dockerfile + - scripts/Invoke-CdevCli.ps1 + - scripts/lib/** + - cli-contract.json + +permissions: + contents: read + packages: write + +concurrency: + group: publish-cli-runtime-image-${{ github.ref }} + cancel-in-progress: false + +jobs: + publish: + name: Publish CLI Runtime Image + runs-on: ubuntu-latest + env: + IMAGE_REPO: ghcr.io/svelderrainruiz/labview-cdev-cli-runtime + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Resolve deterministic tags + id: resolve + shell: bash + run: | + set -euo pipefail + + date_utc="$(date -u +%Y%m%d)" + short_sha="${GITHUB_SHA:0:12}" + promote_v1="${{ github.event.inputs.promote_v1 }}" + additional_tag="${{ github.event.inputs.additional_tag }}" + + if [[ -z "$promote_v1" ]]; then + promote_v1="true" + fi + + if [[ -n "$additional_tag" ]] && [[ ! "$additional_tag" =~ ^[A-Za-z0-9._-]+$ ]]; then + echo "additional_tag must match ^[A-Za-z0-9._-]+$" >&2 + exit 1 + fi + + tags=() + tags+=("${IMAGE_REPO}:sha-${short_sha}") + tags+=("${IMAGE_REPO}:v1-${date_utc}") + if [[ "$promote_v1" == "true" ]]; then + tags+=("${IMAGE_REPO}:v1") + fi + if [[ -n "$additional_tag" ]]; then + tags+=("${IMAGE_REPO}:${additional_tag}") + fi + + { + echo "date_utc=$date_utc" + echo "short_sha=$short_sha" + echo "tags<> "$GITHUB_OUTPUT" + + - name: Log in to GHCR + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Build and push image + id: build + uses: docker/build-push-action@v6 + with: + context: . + file: ./tools/cli-runtime/Dockerfile + push: true + tags: ${{ steps.resolve.outputs.tags }} + + - name: Publish summary + shell: bash + run: | + { + echo "## cdev CLI Runtime Image Published" + echo "" + echo "- Image: \`${IMAGE_REPO}\`" + echo "- Digest: \`${{ steps.build.outputs.digest }}\`" + echo "- Commit: \`${GITHUB_SHA}\`" + echo "- Tags:" + while IFS= read -r tag; do + echo " - \`$tag\`" + done <<< "${{ steps.resolve.outputs.tags }}" + } >> "$GITHUB_STEP_SUMMARY" diff --git a/AGENTS.md b/AGENTS.md index 9577b07..33cb558 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -81,3 +81,4 @@ This repository is the control-plane CLI for deterministic `C:\dev` workspace or - `.sha256` - `cdev-cli.spdx.json` - `cdev-cli.slsa.json` +- `publish-cli-runtime-image.yml` publishes base runtime image `ghcr.io/svelderrainruiz/labview-cdev-cli-runtime` with immutable tags (`sha-*`, `v1-YYYYMMDD`) and optional mutable `v1`. diff --git a/README.md b/README.md index 1418f75..00e3a1c 100644 --- a/README.md +++ b/README.md @@ -54,6 +54,24 @@ Release artifacts: - `cdev-cli.spdx.json` - `cdev-cli.slsa.json` +## Runtime Image (Base Layer) + +`publish-cli-runtime-image.yml` publishes the base CLI runtime image: +- `ghcr.io/svelderrainruiz/labview-cdev-cli-runtime` + +Deterministic tags: +- `sha-<12-char-commit>` +- `v1-YYYYMMDD` +- `v1` (when promoted) + +Dispatch manually: + +```powershell +gh workflow run publish-cli-runtime-image.yml ` + -R svelderrainruiz/labview-cdev-cli ` + -f promote_v1=true +``` + ## Operations Runbooks - Controlled fork/upstream SHA parity recovery: diff --git a/tests/CdevCliRuntimeImagePublishContract.Tests.ps1 b/tests/CdevCliRuntimeImagePublishContract.Tests.ps1 new file mode 100644 index 0000000..db7a95f --- /dev/null +++ b/tests/CdevCliRuntimeImagePublishContract.Tests.ps1 @@ -0,0 +1,50 @@ +#Requires -Version 7.0 +#Requires -Modules Pester + +$ErrorActionPreference = 'Stop' + +Describe 'cdev CLI runtime image publish contract' { + BeforeAll { + $script:repoRoot = (Resolve-Path -Path (Join-Path $PSScriptRoot '..')).Path + $script:dockerfilePath = Join-Path $script:repoRoot 'tools/cli-runtime/Dockerfile' + $script:workflowPath = Join-Path $script:repoRoot '.github/workflows/publish-cli-runtime-image.yml' + $script:agentsPath = Join-Path $script:repoRoot 'AGENTS.md' + + foreach ($path in @($script:dockerfilePath, $script:workflowPath, $script:agentsPath)) { + if (-not (Test-Path -LiteralPath $path -PathType Leaf)) { + throw "Missing runtime-image contract file: $path" + } + } + + $script:dockerfile = Get-Content -LiteralPath $script:dockerfilePath -Raw + $script:workflow = Get-Content -LiteralPath $script:workflowPath -Raw + $script:agents = Get-Content -LiteralPath $script:agentsPath -Raw + } + + It 'builds a PowerShell-based CLI runtime image with required tooling and entrypoint' { + $script:dockerfile | Should -Match 'mcr\.microsoft\.com/powershell' + $script:dockerfile | Should -Match 'git jq gh' + $script:dockerfile | Should -Match 'ENTRYPOINT \["pwsh", "-NoProfile", "-File", "/opt/cdev-cli/scripts/Invoke-CdevCli\.ps1"\]' + $script:dockerfile | Should -Match 'COPY scripts' + } + + It 'defines deterministic GHCR publish flow with package write permission' { + $script:workflow | Should -Match 'workflow_dispatch:' + $script:workflow | Should -Match 'push:' + $script:workflow | Should -Match 'packages:\s*write' + $script:workflow | Should -Match 'ghcr\.io/svelderrainruiz/labview-cdev-cli-runtime' + $script:workflow | Should -Match 'docker/login-action@v3' + $script:workflow | Should -Match 'docker/build-push-action@v6' + } + + It 'publishes immutable tags and summary digest evidence' { + $script:workflow | Should -Match 'sha-\$\{short_sha\}' + $script:workflow | Should -Match 'v1-\$\{date_utc\}' + $script:workflow | Should -Match 'steps\.build\.outputs\.digest' + } + + It 'documents fork-safe mutation target for runtime publish operations' { + $script:agents | Should -Match 'Allowed mutation target' + $script:agents | Should -Match 'svelderrainruiz/labview-cdev-cli' + } +} diff --git a/tools/cli-runtime/Dockerfile b/tools/cli-runtime/Dockerfile new file mode 100644 index 0000000..5877d1e --- /dev/null +++ b/tools/cli-runtime/Dockerfile @@ -0,0 +1,14 @@ +FROM mcr.microsoft.com/powershell:7.4-ubuntu-22.04 + +RUN apt-get update \ + && apt-get install -y --no-install-recommends git jq gh ca-certificates \ + && rm -rf /var/lib/apt/lists/* + +WORKDIR /opt/cdev-cli + +COPY scripts ./scripts +COPY cli-contract.json ./cli-contract.json +COPY README.md ./README.md + +ENTRYPOINT ["pwsh", "-NoProfile", "-File", "/opt/cdev-cli/scripts/Invoke-CdevCli.ps1"] +CMD ["help"] diff --git a/tools/cli-runtime/README.md b/tools/cli-runtime/README.md new file mode 100644 index 0000000..2b9c68f --- /dev/null +++ b/tools/cli-runtime/README.md @@ -0,0 +1,23 @@ +# cdev CLI Runtime Image + +Base runtime image for `labview-cdev-cli` command execution. + +Default repository: +- `ghcr.io/svelderrainruiz/labview-cdev-cli-runtime` + +Deterministic tags: +- `sha-<12-char-commit>` +- `v1-YYYYMMDD` +- `v1` (when promoted) + +Local build: + +```powershell +docker build -f .\tools\cli-runtime\Dockerfile -t cdev-cli-runtime:local . +``` + +Local run: + +```powershell +docker run --rm cdev-cli-runtime:local help +``` From 61e7e3adcd13fe943c8c5b6f63d51107b19e98a4 Mon Sep 17 00:00:00 2001 From: svelderrainruiz Date: Thu, 26 Feb 2026 18:38:16 -0800 Subject: [PATCH 2/2] feat(ops): owner-derive CLI runtime publish namespace --- .github/workflows/publish-cli-runtime-image.yml | 2 +- AGENTS.md | 3 ++- README.md | 9 ++++++--- tests/CdevCliRuntimeImagePublishContract.Tests.ps1 | 3 ++- tools/cli-runtime/README.md | 7 +++++-- 5 files changed, 16 insertions(+), 8 deletions(-) diff --git a/.github/workflows/publish-cli-runtime-image.yml b/.github/workflows/publish-cli-runtime-image.yml index dd8b632..153663f 100644 --- a/.github/workflows/publish-cli-runtime-image.yml +++ b/.github/workflows/publish-cli-runtime-image.yml @@ -35,7 +35,7 @@ jobs: name: Publish CLI Runtime Image runs-on: ubuntu-latest env: - IMAGE_REPO: ghcr.io/svelderrainruiz/labview-cdev-cli-runtime + IMAGE_REPO: ghcr.io/${{ github.repository_owner }}/labview-cdev-cli-runtime steps: - name: Checkout uses: actions/checkout@v4 diff --git a/AGENTS.md b/AGENTS.md index 33cb558..1200fd0 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -81,4 +81,5 @@ This repository is the control-plane CLI for deterministic `C:\dev` workspace or - `.sha256` - `cdev-cli.spdx.json` - `cdev-cli.slsa.json` -- `publish-cli-runtime-image.yml` publishes base runtime image `ghcr.io/svelderrainruiz/labview-cdev-cli-runtime` with immutable tags (`sha-*`, `v1-YYYYMMDD`) and optional mutable `v1`. +- `publish-cli-runtime-image.yml` publishes base runtime image `ghcr.io//labview-cdev-cli-runtime` with immutable tags (`sha-*`, `v1-YYYYMMDD`) and optional mutable `v1`. +- Canonical consumer image path is `ghcr.io/labview-community-ci-cd/labview-cdev-cli-runtime`. diff --git a/README.md b/README.md index 00e3a1c..3745e37 100644 --- a/README.md +++ b/README.md @@ -56,8 +56,11 @@ Release artifacts: ## Runtime Image (Base Layer) -`publish-cli-runtime-image.yml` publishes the base CLI runtime image: -- `ghcr.io/svelderrainruiz/labview-cdev-cli-runtime` +`publish-cli-runtime-image.yml` publishes the base CLI runtime image to: +- `ghcr.io//labview-cdev-cli-runtime` + +Canonical consumer reference remains: +- `ghcr.io/labview-community-ci-cd/labview-cdev-cli-runtime` Deterministic tags: - `sha-<12-char-commit>` @@ -68,7 +71,7 @@ Dispatch manually: ```powershell gh workflow run publish-cli-runtime-image.yml ` - -R svelderrainruiz/labview-cdev-cli ` + -R /labview-cdev-cli ` -f promote_v1=true ``` diff --git a/tests/CdevCliRuntimeImagePublishContract.Tests.ps1 b/tests/CdevCliRuntimeImagePublishContract.Tests.ps1 index db7a95f..f33cbc6 100644 --- a/tests/CdevCliRuntimeImagePublishContract.Tests.ps1 +++ b/tests/CdevCliRuntimeImagePublishContract.Tests.ps1 @@ -32,7 +32,7 @@ Describe 'cdev CLI runtime image publish contract' { $script:workflow | Should -Match 'workflow_dispatch:' $script:workflow | Should -Match 'push:' $script:workflow | Should -Match 'packages:\s*write' - $script:workflow | Should -Match 'ghcr\.io/svelderrainruiz/labview-cdev-cli-runtime' + $script:workflow | Should -Match 'ghcr\.io/\$\{\{\s*github\.repository_owner\s*\}\}/labview-cdev-cli-runtime' $script:workflow | Should -Match 'docker/login-action@v3' $script:workflow | Should -Match 'docker/build-push-action@v6' } @@ -46,5 +46,6 @@ Describe 'cdev CLI runtime image publish contract' { It 'documents fork-safe mutation target for runtime publish operations' { $script:agents | Should -Match 'Allowed mutation target' $script:agents | Should -Match 'svelderrainruiz/labview-cdev-cli' + $script:agents | Should -Match 'ghcr\.io/labview-community-ci-cd/labview-cdev-cli-runtime' } } diff --git a/tools/cli-runtime/README.md b/tools/cli-runtime/README.md index 2b9c68f..ac1058b 100644 --- a/tools/cli-runtime/README.md +++ b/tools/cli-runtime/README.md @@ -2,8 +2,11 @@ Base runtime image for `labview-cdev-cli` command execution. -Default repository: -- `ghcr.io/svelderrainruiz/labview-cdev-cli-runtime` +Publish repository: +- `ghcr.io//labview-cdev-cli-runtime` + +Canonical consumer repository: +- `ghcr.io/labview-community-ci-cd/labview-cdev-cli-runtime` Deterministic tags: - `sha-<12-char-commit>`