Skip to content

Prepare release 2026.9.4.1 #143

Prepare release 2026.9.4.1

Prepare release 2026.9.4.1 #143

Workflow file for this run

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