Prepare release 2026.9.4.1 #143
Workflow file for this run
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: Build and Push (Both Arch) | |
| on: | |
| push: | |
| tags: | |
| - "v*" | |
| schedule: | |
| - cron: "0 4 * * 1" | |
| workflow_dispatch: | |
| permissions: | |
| contents: read | |
| concurrency: | |
| group: build-both-production-publish | |
| cancel-in-progress: false | |
| jobs: | |
| source: | |
| runs-on: ubuntu-latest | |
| outputs: | |
| commit_sha: ${{ steps.release.outputs.commit_sha }} | |
| release_ref: ${{ steps.release.outputs.release_ref }} | |
| release_tag: ${{ steps.release.outputs.release_tag }} | |
| release_version: ${{ steps.release.outputs.release_version }} | |
| steps: | |
| - name: Checkout release metadata source | |
| uses: actions/checkout@v6 | |
| with: | |
| fetch-depth: 0 | |
| - name: Resolve immutable release source | |
| id: release | |
| run: | | |
| set -euo pipefail | |
| package_version="$(node -p "require('./package.json').version")" | |
| release_tag="v${package_version}" | |
| git fetch --force origin "refs/tags/${release_tag}:refs/tags/${release_tag}" | |
| release_sha="$(git rev-parse "${release_tag}^{commit}")" | |
| tagged_version="$(git show "${release_sha}:package.json" | node -e 'let data = ""; process.stdin.on("data", chunk => data += chunk); process.stdin.on("end", () => process.stdout.write(JSON.parse(data).version));')" | |
| [[ "$tagged_version" == "$package_version" ]] | |
| if [[ "${GITHUB_EVENT_NAME}" == "push" ]]; then | |
| [[ "${GITHUB_REF}" == "refs/tags/${release_tag}" ]] | |
| event_sha="$(git rev-parse "${GITHUB_SHA}^{commit}")" | |
| [[ "$event_sha" == "$release_sha" ]] | |
| fi | |
| echo "commit_sha=${release_sha}" >> "$GITHUB_OUTPUT" | |
| echo "release_ref=refs/tags/${release_tag}" >> "$GITHUB_OUTPUT" | |
| echo "release_tag=${release_tag}" >> "$GITHUB_OUTPUT" | |
| echo "release_version=${package_version}" >> "$GITHUB_OUTPUT" | |
| safety-tests: | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| needs: source | |
| steps: | |
| - name: Checkout immutable release source | |
| uses: actions/checkout@v6 | |
| with: | |
| ref: ${{ needs.source.outputs.commit_sha }} | |
| - name: Set up Node.js | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version: 26 | |
| - name: Install dependencies | |
| run: npm ci | |
| - name: Verify application production build | |
| run: npm run verify:release | |
| - name: Build portable CLI | |
| run: npm run cli:build | |
| - name: Verify operation lock and Postgres recovery | |
| run: | | |
| npm run test:cli:operation-lock | |
| npm run test:cli:postgres-recovery | |
| npm run test:cli:postgres-reconcile | |
| npm run test:cli:database | |
| npm run test:cli:database-provider | |
| npm run test:cli:admin | |
| npm run test:cli:backup | |
| npm run test:install:managed-env | |
| - name: Verify update rollback and CLI artifact gate | |
| run: | | |
| npm run test:cli:update-rollback | |
| npm run test:cli:update | |
| npm run test:cli:portable-update | |
| npm run test:cli:portable | |
| - name: Verify deterministic release metadata and host CLI package | |
| run: | | |
| npm run test:release:payload | |
| npm run test:release:host-cli | |
| build-amd64: | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| packages: write | |
| needs: source | |
| outputs: | |
| digest: ${{ steps.build.outputs.digest }} | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v6 | |
| with: | |
| ref: ${{ needs.source.outputs.commit_sha }} | |
| - name: Set up Node.js for amd64 Linux CLI | |
| if: startsWith(github.ref, 'refs/tags/v') | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version: 22 | |
| cache: npm | |
| - name: Build and verify amd64 Linux CLI | |
| if: startsWith(github.ref, 'refs/tags/v') | |
| run: | | |
| npm ci --ignore-scripts | |
| npm run cli:build | |
| npm run test:cli:linux-self-update | |
| npm run test:release:linux-cli-integration | |
| - name: Upload amd64 Linux CLI release asset | |
| if: startsWith(github.ref, 'refs/tags/v') | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: linux-cli-amd64-${{ needs.source.outputs.release_version }} | |
| if-no-files-found: error | |
| retention-days: 90 | |
| path: | | |
| dist-linux-cli/amd64/canvas-notebook-linux-cli-amd64 | |
| dist-linux-cli/amd64/canvas-notebook-linux-cli-amd64.tar.gz | |
| dist-linux-cli/amd64/canvas-notebook-linux-cli-amd64.sha256 | |
| - name: Set GHCR image name | |
| run: echo "GHCR_IMAGE=ghcr.io/${GITHUB_REPOSITORY,,}" >> "$GITHUB_ENV" | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v4 | |
| - name: Log in to GHCR | |
| uses: docker/login-action@v4 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Log in to Docker Hub | |
| uses: docker/login-action@v4 | |
| with: | |
| username: ${{ secrets.DOCKERHUB_USERNAME }} | |
| password: ${{ secrets.DOCKERHUB_TOKEN }} | |
| - name: Build and push | |
| id: build | |
| uses: docker/build-push-action@v7 | |
| with: | |
| context: . | |
| file: ./Dockerfile | |
| push: true | |
| platforms: linux/amd64 | |
| tags: | | |
| ${{ env.GHCR_IMAGE }}:build-${{ github.run_id }}-${{ github.run_attempt }}-${{ needs.source.outputs.commit_sha }}-amd64 | |
| canvascoding/canvas-notebook:build-${{ github.run_id }}-${{ github.run_attempt }}-${{ needs.source.outputs.commit_sha }}-amd64 | |
| labels: | | |
| org.opencontainers.image.revision=${{ needs.source.outputs.commit_sha }} | |
| org.opencontainers.image.version=${{ needs.source.outputs.release_version }} | |
| provenance: false | |
| sbom: false | |
| no-cache: ${{ github.event_name == 'schedule' }} | |
| - name: Verify amd64 native compliance payload | |
| env: | |
| IMAGE: ${{ env.GHCR_IMAGE }}:build-${{ github.run_id }}-${{ github.run_attempt }}-${{ needs.source.outputs.commit_sha }}-amd64 | |
| run: | | |
| set -euo pipefail | |
| docker pull "$IMAGE" | |
| container_id="$(docker create "$IMAGE")" | |
| trap 'docker rm -f "$container_id" >/dev/null 2>&1 || true' EXIT | |
| mkdir -p runtime-compliance/amd64 | |
| docker cp "$container_id:/app/docs/compliance/runtime-components.json" runtime-compliance/amd64/runtime-components-linux-amd64.json | |
| docker cp "$container_id:/app/docs/compliance/sharp-linkage.json" runtime-compliance/amd64/sharp-linkage-linux-amd64.json | |
| docker cp "$container_id:/app/THIRD_PARTY_NOTICES.md" runtime-compliance/amd64/THIRD_PARTY_NOTICES.md | |
| docker cp "$container_id:/app/docs/compliance/third-party-components.json" runtime-compliance/amd64/third-party-components.json | |
| docker cp "$container_id:/usr/share/canvas-notebook/corresponding-source/vips-8.18.3.tar.xz" runtime-compliance/amd64/vips-8.18.3.tar.xz | |
| docker rm "$container_id" | |
| trap - EXIT | |
| node scripts/runtime-component-inventory-test.mjs \ | |
| runtime-compliance/amd64/runtime-components-linux-amd64.json \ | |
| requirements/runtime-python.txt \ | |
| docs/compliance/docker-native-distribution-policy.json | |
| cmp THIRD_PARTY_NOTICES.md runtime-compliance/amd64/THIRD_PARTY_NOTICES.md | |
| cmp docs/compliance/third-party-components.json runtime-compliance/amd64/third-party-components.json | |
| echo "f41285b61bfb495605494f074ca341f7791a1d406e2f157dcea606ef1ae1b146 runtime-compliance/amd64/vips-8.18.3.tar.xz" | sha256sum -c - | |
| - name: Upload amd64 native compliance evidence | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: runtime-compliance-amd64 | |
| path: runtime-compliance/amd64 | |
| if-no-files-found: error | |
| retention-days: 90 | |
| build-arm64: | |
| runs-on: ubuntu-24.04-arm | |
| permissions: | |
| contents: read | |
| packages: write | |
| needs: source | |
| outputs: | |
| digest: ${{ steps.build.outputs.digest }} | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v6 | |
| with: | |
| ref: ${{ needs.source.outputs.commit_sha }} | |
| - name: Set up Node.js for arm64 Linux CLI | |
| if: startsWith(github.ref, 'refs/tags/v') | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version: 22 | |
| cache: npm | |
| - name: Build and verify arm64 Linux CLI | |
| if: startsWith(github.ref, 'refs/tags/v') | |
| run: | | |
| npm ci --ignore-scripts | |
| npm run cli:build | |
| npm run test:cli:linux-self-update | |
| npm run test:release:linux-cli-integration | |
| - name: Upload arm64 Linux CLI release asset | |
| if: startsWith(github.ref, 'refs/tags/v') | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: linux-cli-arm64-${{ needs.source.outputs.release_version }} | |
| if-no-files-found: error | |
| retention-days: 90 | |
| path: | | |
| dist-linux-cli/arm64/canvas-notebook-linux-cli-arm64 | |
| dist-linux-cli/arm64/canvas-notebook-linux-cli-arm64.tar.gz | |
| dist-linux-cli/arm64/canvas-notebook-linux-cli-arm64.sha256 | |
| - name: Set GHCR image name | |
| run: echo "GHCR_IMAGE=ghcr.io/${GITHUB_REPOSITORY,,}" >> "$GITHUB_ENV" | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v4 | |
| - name: Log in to GHCR | |
| uses: docker/login-action@v4 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Log in to Docker Hub | |
| uses: docker/login-action@v4 | |
| with: | |
| username: ${{ secrets.DOCKERHUB_USERNAME }} | |
| password: ${{ secrets.DOCKERHUB_TOKEN }} | |
| - name: Build and push | |
| id: build | |
| uses: docker/build-push-action@v7 | |
| with: | |
| context: . | |
| file: ./Dockerfile | |
| push: true | |
| platforms: linux/arm64 | |
| tags: | | |
| ${{ env.GHCR_IMAGE }}:build-${{ github.run_id }}-${{ github.run_attempt }}-${{ needs.source.outputs.commit_sha }}-arm64 | |
| canvascoding/canvas-notebook:build-${{ github.run_id }}-${{ github.run_attempt }}-${{ needs.source.outputs.commit_sha }}-arm64 | |
| labels: | | |
| org.opencontainers.image.revision=${{ needs.source.outputs.commit_sha }} | |
| org.opencontainers.image.version=${{ needs.source.outputs.release_version }} | |
| provenance: false | |
| sbom: false | |
| no-cache: ${{ github.event_name == 'schedule' }} | |
| - name: Verify arm64 native compliance payload | |
| env: | |
| IMAGE: ${{ env.GHCR_IMAGE }}:build-${{ github.run_id }}-${{ github.run_attempt }}-${{ needs.source.outputs.commit_sha }}-arm64 | |
| run: | | |
| set -euo pipefail | |
| docker pull "$IMAGE" | |
| container_id="$(docker create "$IMAGE")" | |
| trap 'docker rm -f "$container_id" >/dev/null 2>&1 || true' EXIT | |
| mkdir -p runtime-compliance/arm64 | |
| docker cp "$container_id:/app/docs/compliance/runtime-components.json" runtime-compliance/arm64/runtime-components-linux-arm64.json | |
| docker cp "$container_id:/app/docs/compliance/sharp-linkage.json" runtime-compliance/arm64/sharp-linkage-linux-arm64.json | |
| docker cp "$container_id:/app/THIRD_PARTY_NOTICES.md" runtime-compliance/arm64/THIRD_PARTY_NOTICES.md | |
| docker cp "$container_id:/app/docs/compliance/third-party-components.json" runtime-compliance/arm64/third-party-components.json | |
| docker cp "$container_id:/usr/share/canvas-notebook/corresponding-source/vips-8.18.3.tar.xz" runtime-compliance/arm64/vips-8.18.3.tar.xz | |
| docker rm "$container_id" | |
| trap - EXIT | |
| node scripts/runtime-component-inventory-test.mjs \ | |
| runtime-compliance/arm64/runtime-components-linux-arm64.json \ | |
| requirements/runtime-python.txt \ | |
| docs/compliance/docker-native-distribution-policy.json | |
| cmp THIRD_PARTY_NOTICES.md runtime-compliance/arm64/THIRD_PARTY_NOTICES.md | |
| cmp docs/compliance/third-party-components.json runtime-compliance/arm64/third-party-components.json | |
| echo "f41285b61bfb495605494f074ca341f7791a1d406e2f157dcea606ef1ae1b146 runtime-compliance/arm64/vips-8.18.3.tar.xz" | sha256sum -c - | |
| - name: Upload arm64 native compliance evidence | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: runtime-compliance-arm64 | |
| path: runtime-compliance/arm64 | |
| if-no-files-found: error | |
| retention-days: 90 | |
| merge: | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| packages: write | |
| needs: [source, safety-tests, build-amd64, build-arm64] | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v6 | |
| with: | |
| ref: ${{ needs.source.outputs.commit_sha }} | |
| - name: Set up Node.js for compliance and release assets | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version: 26 | |
| - name: Install release packaging dependencies | |
| if: startsWith(github.ref, 'refs/tags/v') | |
| run: npm ci --ignore-scripts | |
| - name: Set GHCR image name | |
| run: echo "GHCR_IMAGE=ghcr.io/${GITHUB_REPOSITORY,,}" >> "$GITHUB_ENV" | |
| - name: Download amd64 native compliance evidence | |
| uses: actions/download-artifact@v8 | |
| with: | |
| name: runtime-compliance-amd64 | |
| path: runtime-compliance/amd64 | |
| - name: Download arm64 native compliance evidence | |
| uses: actions/download-artifact@v8 | |
| with: | |
| name: runtime-compliance-arm64 | |
| path: runtime-compliance/arm64 | |
| - name: Download amd64 Linux CLI release asset | |
| if: startsWith(github.ref, 'refs/tags/v') | |
| uses: actions/download-artifact@v8 | |
| with: | |
| name: linux-cli-amd64-${{ needs.source.outputs.release_version }} | |
| path: dist-linux-cli/amd64 | |
| - name: Download arm64 Linux CLI release asset | |
| if: startsWith(github.ref, 'refs/tags/v') | |
| uses: actions/download-artifact@v8 | |
| with: | |
| name: linux-cli-arm64-${{ needs.source.outputs.release_version }} | |
| path: dist-linux-cli/arm64 | |
| - name: Verify multi-architecture native compliance | |
| run: | | |
| set -euo pipefail | |
| node scripts/runtime-multiarch-compliance-test.mjs \ | |
| runtime-compliance/amd64/runtime-components-linux-amd64.json \ | |
| runtime-compliance/arm64/runtime-components-linux-arm64.json \ | |
| runtime-compliance/amd64/sharp-linkage-linux-amd64.json \ | |
| runtime-compliance/arm64/sharp-linkage-linux-arm64.json | |
| cmp runtime-compliance/amd64/THIRD_PARTY_NOTICES.md runtime-compliance/arm64/THIRD_PARTY_NOTICES.md | |
| cmp runtime-compliance/amd64/third-party-components.json runtime-compliance/arm64/third-party-components.json | |
| cmp runtime-compliance/amd64/vips-8.18.3.tar.xz runtime-compliance/arm64/vips-8.18.3.tar.xz | |
| - name: Package native compliance evidence | |
| run: | | |
| set -euo pipefail | |
| asset="canvas-native-compliance-${{ needs.source.outputs.release_version }}" | |
| mkdir -p "dist-native-compliance/${asset}/linux-amd64" "dist-native-compliance/${asset}/linux-arm64" | |
| cp runtime-compliance/amd64/runtime-components-linux-amd64.json "dist-native-compliance/${asset}/linux-amd64/" | |
| cp runtime-compliance/amd64/sharp-linkage-linux-amd64.json "dist-native-compliance/${asset}/linux-amd64/" | |
| cp runtime-compliance/arm64/runtime-components-linux-arm64.json "dist-native-compliance/${asset}/linux-arm64/" | |
| cp runtime-compliance/arm64/sharp-linkage-linux-arm64.json "dist-native-compliance/${asset}/linux-arm64/" | |
| cp runtime-compliance/amd64/vips-8.18.3.tar.xz "dist-native-compliance/${asset}/" | |
| cp Dockerfile requirements/runtime-python.txt THIRD_PARTY_NOTICES.md "dist-native-compliance/${asset}/" | |
| cp docs/compliance/third-party-components.json \ | |
| docs/compliance/docker-native-distribution-policy.json \ | |
| docs/compliance/sharp-libvips-relinking.md \ | |
| docs/compliance/license-texts/LGPL-2.1-or-later.txt \ | |
| "dist-native-compliance/${asset}/" | |
| tar -C dist-native-compliance -czf "dist-native-compliance/${asset}.tar.gz" "${asset}" | |
| (cd dist-native-compliance && sha256sum "${asset}.tar.gz" > "${asset}.sha256") | |
| - name: Upload native compliance release evidence | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: native-compliance-${{ needs.source.outputs.release_version }} | |
| path: | | |
| dist-native-compliance/canvas-native-compliance-${{ needs.source.outputs.release_version }}.tar.gz | |
| dist-native-compliance/canvas-native-compliance-${{ needs.source.outputs.release_version }}.sha256 | |
| if-no-files-found: error | |
| retention-days: 90 | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v4 | |
| - name: Log in to GHCR | |
| uses: docker/login-action@v4 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Log in to Docker Hub | |
| uses: docker/login-action@v4 | |
| with: | |
| username: ${{ secrets.DOCKERHUB_USERNAME }} | |
| password: ${{ secrets.DOCKERHUB_TOKEN }} | |
| - name: Resolve merge tag | |
| id: tag | |
| run: | | |
| if [ "${{ github.event_name }}" = "push" ] && [[ "${GITHUB_REF}" == refs/tags/v* ]]; then | |
| MERGE_TAG="${{ needs.source.outputs.release_tag }}" | |
| else | |
| MERGE_TAG="latest" | |
| fi | |
| echo "merge_tag=${MERGE_TAG}" >> "$GITHUB_OUTPUT" | |
| echo "Merging manifest for tag: ${MERGE_TAG}" | |
| - name: Verify release tag matches package version | |
| if: startsWith(github.ref, 'refs/tags/v') | |
| run: | | |
| set -euo pipefail | |
| package_version="$(node -p "require('./package.json').version")" | |
| [[ "${{ needs.source.outputs.release_version }}" == "$package_version" ]] | |
| [[ "${{ needs.source.outputs.release_tag }}" == "v${package_version}" ]] | |
| - name: Create multi-arch manifest | |
| env: | |
| GHCR_IMAGE: ${{ env.GHCR_IMAGE }} | |
| HUB_IMAGE: canvascoding/canvas-notebook | |
| MERGE_TAG: ${{ steps.tag.outputs.merge_tag }} | |
| AMD64_DIGEST: ${{ needs.build-amd64.outputs.digest }} | |
| ARM64_DIGEST: ${{ needs.build-arm64.outputs.digest }} | |
| run: | | |
| set -euo pipefail | |
| [[ "$AMD64_DIGEST" =~ ^sha256:[a-f0-9]{64}$ ]] | |
| [[ "$ARM64_DIGEST" =~ ^sha256:[a-f0-9]{64}$ ]] | |
| ghcr_tags=(-t "${GHCR_IMAGE}:latest") | |
| hub_tags=(-t "${HUB_IMAGE}:latest") | |
| if [ "${MERGE_TAG}" != "latest" ]; then | |
| ghcr_tags+=(-t "${GHCR_IMAGE}:${MERGE_TAG}") | |
| hub_tags+=(-t "${HUB_IMAGE}:${MERGE_TAG}") | |
| fi | |
| docker buildx imagetools create \ | |
| "${ghcr_tags[@]}" \ | |
| "${GHCR_IMAGE}@${AMD64_DIGEST}" \ | |
| "${GHCR_IMAGE}@${ARM64_DIGEST}" | |
| docker buildx imagetools create \ | |
| "${hub_tags[@]}" \ | |
| "${HUB_IMAGE}@${AMD64_DIGEST}" \ | |
| "${HUB_IMAGE}@${ARM64_DIGEST}" | |
| echo "Created multi-arch manifests from immutable architecture digests." | |
| - name: Verify multi-arch manifest | |
| env: | |
| GHCR_IMAGE: ${{ env.GHCR_IMAGE }} | |
| run: | | |
| set -euo pipefail | |
| echo "Verifying ${GHCR_IMAGE}:latest ..." | |
| docker manifest inspect "${GHCR_IMAGE}:latest" | head -40 | |
| - name: Resolve immutable multi-arch digest | |
| id: image | |
| env: | |
| GHCR_IMAGE: ${{ env.GHCR_IMAGE }} | |
| MERGE_TAG: ${{ steps.tag.outputs.merge_tag }} | |
| run: | | |
| set -euo pipefail | |
| target_ref="${GHCR_IMAGE}:${MERGE_TAG}" | |
| digest="$(docker buildx imagetools inspect "$target_ref" --format '{{.Manifest.Digest}}')" | |
| [[ "$digest" =~ ^sha256:[a-f0-9]{64}$ ]] | |
| echo "digest=${digest}" >> "$GITHUB_OUTPUT" | |
| echo "Resolved ${target_ref}@${digest}" | |
| - name: Package Linux host CLI release asset | |
| if: startsWith(github.ref, 'refs/tags/v') | |
| run: npm run cli:package:host | |
| - name: Package portable CLI release asset | |
| if: startsWith(github.ref, 'refs/tags/v') | |
| run: npm run cli:package | |
| - name: Resolve host CLI release metadata | |
| id: cli | |
| if: startsWith(github.ref, 'refs/tags/v') | |
| run: | | |
| set -euo pipefail | |
| sha256="$(awk '$2 == "canvas-notebook-host-cli.tar.gz" { print $1 }' dist-host-cli/canvas-notebook-host-cli.sha256)" | |
| [[ "$sha256" =~ ^[a-f0-9]{64}$ ]] | |
| echo "version=${GITHUB_REF_NAME}" >> "$GITHUB_OUTPUT" | |
| echo "sha256=${sha256}" >> "$GITHUB_OUTPUT" | |
| - name: Package immutable release metadata | |
| if: startsWith(github.ref, 'refs/tags/v') | |
| env: | |
| RELEASE_TAG: ${{ needs.source.outputs.release_tag }} | |
| RELEASE_VERSION: ${{ needs.source.outputs.release_version }} | |
| RELEASE_COMMIT_SHA: ${{ needs.source.outputs.commit_sha }} | |
| GHCR_IMAGE: ${{ env.GHCR_IMAGE }} | |
| IMAGE_DIGEST: ${{ steps.image.outputs.digest }} | |
| HOST_CLI_SHA256: ${{ steps.cli.outputs.sha256 }} | |
| run: | | |
| set -euo pipefail | |
| portable_sha256="$(awk '$2 == "canvas-notebook-cli.tar.gz" { print $1 }' dist-portable-cli/canvas-notebook-cli.sha256)" | |
| linux_amd64_sha256="$(awk '$2 == "canvas-notebook-linux-cli-amd64.tar.gz" { print $1 }' dist-linux-cli/amd64/canvas-notebook-linux-cli-amd64.sha256)" | |
| linux_arm64_sha256="$(awk '$2 == "canvas-notebook-linux-cli-arm64.tar.gz" { print $1 }' dist-linux-cli/arm64/canvas-notebook-linux-cli-arm64.sha256)" | |
| [[ "$portable_sha256" =~ ^[a-f0-9]{64}$ ]] | |
| [[ "$linux_amd64_sha256" =~ ^[a-f0-9]{64}$ ]] | |
| [[ "$linux_arm64_sha256" =~ ^[a-f0-9]{64}$ ]] | |
| [[ "$HOST_CLI_SHA256" =~ ^[a-f0-9]{64}$ ]] | |
| [[ "$IMAGE_DIGEST" =~ ^sha256:[a-f0-9]{64}$ ]] | |
| export PORTABLE_CLI_SHA256="$portable_sha256" | |
| export LINUX_CLI_AMD64_SHA256="$linux_amd64_sha256" | |
| export LINUX_CLI_ARM64_SHA256="$linux_arm64_sha256" | |
| node --input-type=module <<'NODE' | |
| import fs from 'node:fs'; | |
| const metadata = { | |
| schemaVersion: 1, | |
| repository: process.env.GITHUB_REPOSITORY, | |
| version: process.env.RELEASE_VERSION, | |
| tag: process.env.RELEASE_TAG, | |
| commitSha: process.env.RELEASE_COMMIT_SHA, | |
| image: { | |
| name: process.env.GHCR_IMAGE, | |
| digest: process.env.IMAGE_DIGEST, | |
| }, | |
| hostCli: { | |
| filename: 'canvas-notebook-host-cli.tar.gz', | |
| sha256: process.env.HOST_CLI_SHA256, | |
| }, | |
| portableCli: { | |
| filename: 'canvas-notebook-cli.tar.gz', | |
| sha256: process.env.PORTABLE_CLI_SHA256, | |
| }, | |
| linuxCli: { | |
| amd64: { | |
| filename: 'canvas-notebook-linux-cli-amd64.tar.gz', | |
| sha256: process.env.LINUX_CLI_AMD64_SHA256, | |
| }, | |
| arm64: { | |
| filename: 'canvas-notebook-linux-cli-arm64.tar.gz', | |
| sha256: process.env.LINUX_CLI_ARM64_SHA256, | |
| }, | |
| }, | |
| build: { | |
| runId: process.env.GITHUB_RUN_ID, | |
| runNumber: process.env.GITHUB_RUN_NUMBER, | |
| runAttempt: process.env.GITHUB_RUN_ATTEMPT, | |
| }, | |
| }; | |
| fs.mkdirSync('dist-release', { recursive: true }); | |
| fs.writeFileSync('dist-release/canvas-notebook-release-metadata.json', `${JSON.stringify(metadata, null, 2)}\n`); | |
| NODE | |
| - name: Upload gated release bundle | |
| if: startsWith(github.ref, 'refs/tags/v') | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: release-bundle-${{ needs.source.outputs.release_version }} | |
| if-no-files-found: error | |
| retention-days: 90 | |
| path: | | |
| dist-portable-cli/canvas-notebook-cli.tar.gz | |
| dist-portable-cli/canvas-notebook-cli.sha256 | |
| dist-host-cli/canvas-notebook-host-cli.tar.gz | |
| dist-host-cli/canvas-notebook-host-cli.sha256 | |
| dist-linux-cli/amd64/canvas-notebook-linux-cli-amd64.tar.gz | |
| dist-linux-cli/amd64/canvas-notebook-linux-cli-amd64.sha256 | |
| dist-linux-cli/arm64/canvas-notebook-linux-cli-arm64.tar.gz | |
| dist-linux-cli/arm64/canvas-notebook-linux-cli-arm64.sha256 | |
| dist-native-compliance/canvas-native-compliance-${{ needs.source.outputs.release_version }}.tar.gz | |
| dist-native-compliance/canvas-native-compliance-${{ needs.source.outputs.release_version }}.sha256 | |
| dist-release/canvas-notebook-release-metadata.json |