Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
125 changes: 125 additions & 0 deletions .github/workflows/publish-dvm-image.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
# Publish DVM Image to GHCR
#
# Trigger scenarios:
# - Push of tag v* → build + push versioned tag + :latest, sign digest.
# - workflow_dispatch → build + push versioned tag only (NO :latest move), for rehearsals.
#
# Single-platform: linux/amd64 only. arm64 deferred — QEMU crashes on the
# Solana/Rust dependency chain ("Illegal instruction"). Native arm64 runners
# can be added later via per-arch matrix + imagetools create manifest-merge.
#
# Prerequisite: Dockerfile.dvm must build standalone from this repo's context
# (issue #2 — trim to dvm-only). Until #2 is merged the build step will fail
# because the Dockerfile still references monorepo paths (packages/*, docker/).
#
# Cosign verify (post-publish runbook):
# digest=$(docker buildx imagetools inspect ghcr.io/toon-protocol/store:latest \
# --format '{{.Manifest.Digest}}')
# cosign verify ghcr.io/toon-protocol/store@$digest \
# --certificate-identity-regexp '^https://github.com/toon-protocol/store/' \
# --certificate-oidc-issuer 'https://token.actions.githubusercontent.com'

name: Publish DVM Image

on:
push:
tags:
- 'v*'
workflow_dispatch:
inputs:
version:
description: 'Version tag for image (e.g. 0.1.0-rc1). Required for workflow_dispatch.'
required: true
type: string

concurrency:
group: '${{ github.workflow }}-${{ github.ref }}'
cancel-in-progress: false

permissions:
contents: read
packages: write
id-token: write # required for cosign keyless OIDC token

env:
IMAGE: ghcr.io/toon-protocol/store

jobs:
build-and-push:
runs-on: ubuntu-latest
timeout-minutes: 90

steps:
- name: Checkout repository
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3

- name: Log in to GHCR
uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Extract image metadata (tags)
id: meta
uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 # v5
with:
images: ${{ env.IMAGE }}
tags: |
type=raw,value=latest,enable=${{ github.ref_type == 'tag' && startsWith(github.ref, 'refs/tags/v') }}
type=match,pattern=v(.*),group=1
type=raw,value=${{ inputs.version }},enable=${{ inputs.version != '' }}
type=sha,prefix=sha-

# Pass 1: build only — load into local daemon for smoke test (no push yet)
- name: Build (no push)
id: build-local
uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6
with:
context: .
file: Dockerfile.dvm
platforms: linux/amd64
load: true
tags: smoke-test-local:latest
cache-from: type=gha,scope=store-linux-amd64
cache-to: type=gha,mode=max,scope=store-linux-amd64

- name: Smoke-test DVM image (pre-push)
run: |
set -euo pipefail
IMG="smoke-test-local:latest"
docker run --rm --entrypoint sh "${IMG}" -c \
"test -s '/app/entrypoint-dvm.js'" \
|| { echo "FAIL: /app/entrypoint-dvm.js missing or empty"; exit 1; }
NODE_VERSION=$(docker run --rm --entrypoint node "${IMG}" --version)
case "${NODE_VERSION}" in
v20.*) ;;
*) echo "FAIL: expected node v20.x, got ${NODE_VERSION}"; exit 1 ;;
esac
echo "/app/entrypoint-dvm.js present + ${NODE_VERSION}"

# Pass 2: push only — BuildKit GHA cache makes this near-instant (no rebuild)
- name: Push
id: build
uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6
with:
context: .
file: Dockerfile.dvm
platforms: linux/amd64
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha,scope=store-linux-amd64

- name: Install cosign
uses: sigstore/cosign-installer@398d4b0eeef1380460a10c8013a76f728fb906ac # v3.9.1

- name: Sign image digest
env:
COSIGN_YES: 'true'
DIGEST: ${{ steps.build.outputs.digest }}
run: |
cosign sign "${IMAGE}@${DIGEST}"
Loading