Skip to content

Refactor CLI into modular Commander commands (#946) #2717

Refactor CLI into modular Commander commands (#946)

Refactor CLI into modular Commander commands (#946) #2717

name: CI / Docker
on:
push:
branches: ['main']
pull_request:
types: [opened, synchronize, reopened]
merge_group:
types: [checks_requested]
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
lint:
permissions:
contents: read
name: Lint Codebase
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: pnpm/action-setup@v5
name: Install pnpm
with:
run_install: false
- name: Install Node.js
uses: actions/setup-node@v7
with:
node-version: 26
cache: 'pnpm'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Run linter for code
run: pnpm run lint
- name: Run linter for documentation
run: pnpm run doc:lint
- name: Run license check (backend, production)
working-directory: apps/backend
run: pnpm dlx license-checker --production --onlyAllow "MIT;Apache-2.0;BSD-2-Clause;BSD-3-Clause;0BSD;ISC;OFL-1.1"
- name: Run license check (client, production)
working-directory: apps/client
run: pnpm dlx license-checker --production --onlyAllow "MIT;Apache-2.0;BSD-2-Clause;BSD-3-Clause;0BSD;ISC;OFL-1.1"
- name: Run knip
run: pnpm knip --no-progress
build-sdk-core:
name: Build SDK Core
permissions:
contents: read
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: pnpm/action-setup@v5
name: Install pnpm
with:
run_install: false
- name: Install Node.js
uses: actions/setup-node@v7
with:
node-version: 26
cache: 'pnpm'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Build SDK Core
run: pnpm --filter @eudiplo/sdk-core build
build-cli:
name: Build CLI
permissions:
contents: read
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: pnpm/action-setup@v5
name: Install pnpm
with:
run_install: false
- name: Install Node.js
uses: actions/setup-node@v7
with:
node-version: 26
cache: 'pnpm'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Build CLI
run: pnpm --filter @eudiplo/cli build
- name: Run CLI tests
run: pnpm --filter @eudiplo/cli test
- name: Build CLI SEA
run: pnpm --filter @eudiplo/cli build:sea
- name: Smoke test standalone CLI
run: ./apps/cli/dist-sea/eudiplo --help
build-backend:
name: Build Backend
permissions:
contents: read
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: pnpm/action-setup@v5
name: Install pnpm
with:
run_install: false
- name: Install Node.js
uses: actions/setup-node@v7
with:
node-version: 26
cache: 'pnpm'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Build backend
run: pnpm --filter @eudiplo/backend build
build-client:
name: Build Client
permissions:
contents: read
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: pnpm/action-setup@v5
name: Install pnpm
with:
run_install: false
- name: Install Node.js
uses: actions/setup-node@v7
with:
node-version: 26
cache: 'pnpm'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Build sdk core
run: pnpm --filter @eudiplo/sdk-core build
- name: Build client
run: pnpm --filter @eudiplo/client build
build-webhook:
name: Build Webhook
permissions:
contents: read
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: pnpm/action-setup@v5
name: Install pnpm
with:
run_install: false
- name: Install Node.js
uses: actions/setup-node@v7
with:
node-version: 26
cache: 'pnpm'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Build webhook
run: pnpm --filter test-rp build
build-doc:
name: Build Documentation
permissions:
contents: read
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: pnpm/action-setup@v5
name: Install pnpm
with:
run_install: false
- name: Install Node.js
uses: actions/setup-node@v7
with:
node-version: 26
cache: 'pnpm'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Set up Python
uses: actions/setup-python@v7
with:
python-version: '3.11'
- name: Install Python dependencies
run: pip install -r requirements.txt
- name: Build documentation
env:
MASTER_SECRET: ci-docs-build-secret-minimum-32-chars
AUTH_CLIENT_ID: ci-docs-client
AUTH_CLIENT_SECRET: ci-docs-secret
ENCRYPTION_KEY: 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
run: pnpm run doc:generate && pnpm run compodoc && mkdocs build --strict
- name: Upload built documentation
uses: actions/upload-artifact@v7
with:
name: generated-docs
path: site/
deploy-docs:
name: Deploy Website & Documentation
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
needs: [build-doc]
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Checkout repository
uses: actions/checkout@v7
- uses: pnpm/action-setup@v5
name: Install pnpm
with:
run_install: false
- name: Install Node.js
uses: actions/setup-node@v7
with:
node-version: 26
cache: 'pnpm'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Set up Python
uses: actions/setup-python@v7
with:
python-version: '3.12'
- name: Install Python dependencies
run: pip install -r requirements.txt
- name: Build generated docs content
env:
PUBLIC_URL: https://example.com
ENABLE_SWAGGER: true
MASTER_SECRET: ci-docs-build-secret-minimum-32-chars
AUTH_CLIENT_ID: ci-docs-client
AUTH_CLIENT_SECRET: ci-docs-secret
ENCRYPTION_KEY: 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
run: pnpm run doc:generate && pnpm run compodoc
- name: Build versioned docs with mike under /docs
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
# Reuse existing gh-pages history when available so mike can maintain version metadata.
git fetch origin gh-pages:gh-pages || true
mike deploy --branch gh-pages --deploy-prefix docs --alias-type copy --update-aliases main latest-dev
git worktree add gh-pages-out gh-pages
git -C gh-pages-out pull --rebase origin gh-pages
# Keep gh-pages root in sync with website/ from main while preserving docs/ generated by mike.
rsync -a --delete \
--exclude='/.git' \
--exclude='/docs/' \
website/ gh-pages-out/
test -f gh-pages-out/.git
test "$(git -C gh-pages-out branch --show-current)" = "gh-pages"
git -C gh-pages-out add -A
if ! git -C gh-pages-out diff --cached --quiet; then
git -C gh-pages-out commit -m "chore(docs): sync website root from main (${GITHUB_SHA::7})"
else
echo "No gh-pages root changes detected"
fi
git -C gh-pages-out push origin gh-pages
- name: Cleanup temporary worktree
if: always()
run: git worktree remove gh-pages-out --force || true
test-e2e:
permissions:
contents: read
name: E2E Tests (non-OIDF)
runs-on: ubuntu-latest
needs: [lint]
steps:
- uses: actions/checkout@v7
- name: Add entry to /etc/hosts
run: echo "127.0.0.1 host.testcontainers.internal" | sudo tee -a /etc/hosts
- uses: pnpm/action-setup@v5
name: Install pnpm
with:
run_install: false
- name: Install Node.js
uses: actions/setup-node@v7
with:
node-version: 26
cache: 'pnpm'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: setup test webhooks
run: |
nohup pnpm --filter test-rp dev &
- name: Run E2E tests
run: pnpm run --filter @eudiplo/backend test:e2e
env:
TESTCONTAINERS_HOST_OVERRIDE: host.testcontainers.internal
test-e2e-oidf:
permissions:
contents: read
name: E2E Tests (OIDF)
runs-on: ubuntu-latest
needs: [lint]
steps:
- uses: actions/checkout@v7
- name: Add entry to /etc/hosts
run: echo "127.0.0.1 host.testcontainers.internal" | sudo tee -a /etc/hosts
- uses: pnpm/action-setup@v5
name: Install pnpm
with:
run_install: false
- name: Install Node.js
uses: actions/setup-node@v7
with:
node-version: 26
cache: 'pnpm'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: setup test webhooks
run: |
nohup pnpm --filter test-rp dev &
- name: Run OIDF E2E tests
run: pnpm run --filter @eudiplo/backend test:oidf
env:
TESTCONTAINERS_HOST_OVERRIDE: host.testcontainers.internal
- name: Upload OIDF test results
if: ${{ !cancelled() }}
uses: actions/upload-artifact@v7
with:
name: oidf-test-results
path: tmp/oidf-logs
if-no-files-found: ignore
test-coverage-report:
permissions:
contents: read
name: Test Coverage Report
runs-on: ubuntu-latest
needs: [lint]
steps:
- uses: actions/checkout@v7
with:
fetch-depth: 0
ref: ${{ github.event.pull_request.head.sha || github.sha }}
- name: Add entry to /etc/hosts
run: echo "127.0.0.1 host.testcontainers.internal" | sudo tee -a /etc/hosts
- uses: pnpm/action-setup@v5
name: Install pnpm
with:
run_install: false
- name: Install Node.js
uses: actions/setup-node@v7
with:
node-version: 26
cache: 'pnpm'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: setup test webhooks
run: |
nohup pnpm --filter test-rp dev &
- name: Run backend unit tests with coverage
run: pnpm --filter @eudiplo/backend exec vitest run --coverage --config ./vitest.config.ts
- name: Run backend E2E tests with coverage
run: pnpm run --filter @eudiplo/backend test:e2e
env:
TESTCONTAINERS_HOST_OVERRIDE: host.testcontainers.internal
- name: Upload Cobertura coverage artifacts
if: ${{ !cancelled() }}
uses: actions/upload-artifact@v7
with:
name: backend-cobertura-coverage
path: |
apps/backend/coverage/unit/cobertura-coverage.xml
apps/backend/coverage/e2e/cobertura-coverage.xml
if-no-files-found: error
upload-code-coverage:
permissions:
contents: read
pull-requests: read
code-quality: write
continue-on-error: true
name: Upload Coverage to GitHub Code Quality
runs-on: ubuntu-latest
needs: [test-coverage-report]
if: >-
needs.test-coverage-report.result == 'success' &&
(
github.event_name != 'pull_request' ||
github.event.pull_request.head.repo.full_name == github.repository
)
steps:
- name: Download Cobertura coverage artifact
uses: actions/download-artifact@v8
with:
name: backend-cobertura-coverage
path: apps/backend/coverage
- uses: actions/upload-code-coverage@v1
with:
file: apps/backend/coverage/unit/cobertura-coverage.xml
language: TypeScript
label: backend-unit
fail-on-error: false
- uses: actions/upload-code-coverage@v1
with:
file: apps/backend/coverage/e2e/cobertura-coverage.xml
language: TypeScript
label: backend-e2e
fail-on-error: false
# =============================================================================
# Docker Backend: Build, Test & Push (multi-platform with native runners)
# Uses native ARM runners instead of QEMU for ~4x faster arm64 builds.
# Skip arm64 on PRs to save time - only build amd64 for validation.
# =============================================================================
docker-backend:
name: Build & Test Backend Docker (${{ matrix.arch }})
needs: [test-e2e, test-e2e-oidf]
strategy:
fail-fast: false
matrix:
# Dynamic matrix: include arm64 only on main branch push
include: '${{ fromJSON(github.event_name == ''push'' && github.ref == ''refs/heads/main'' && ''[{"platform": "linux/amd64", "runner": "ubuntu-latest", "arch": "amd64"}, {"platform": "linux/arm64", "runner": "ubuntu-24.04-arm", "arch": "arm64"}]'' || ''[{"platform": "linux/amd64", "runner": "ubuntu-latest", "arch": "amd64"}]'') }}'
runs-on: ${{ matrix.runner }}
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v7
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4
- name: Docker meta
id: meta
uses: docker/metadata-action@v6
with:
images: ghcr.io/openwallet-foundation/eudiplo
- name: Log in to GitHub Container Registry
uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
# --- Health test (amd64 only) ---
- name: Build Backend Docker image (for testing)
if: matrix.arch == 'amd64'
uses: docker/build-push-action@v7
with:
context: .
target: eudiplo
load: true
tags: ghcr.io/openwallet-foundation/eudiplo:test
build-args: VERSION=main
cache-from: type=gha,scope=backend-${{ matrix.arch }}
cache-to: type=gha,scope=backend-${{ matrix.arch }},mode=max
- name: Run container (detached)
if: matrix.arch == 'amd64'
run: |
docker run -d \
--name eudiplo-test \
-p 8080:8080 \
-e MASTER_SECRET=ci-docker-test-secret-min-32-chars \
-e AUTH_CLIENT_ID=ci-docker-client \
-e AUTH_CLIENT_SECRET=ci-docker-secret \
-e ENCRYPTION_KEY=0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef \
ghcr.io/openwallet-foundation/eudiplo:test
- name: Wait for healthy
if: matrix.arch == 'amd64'
run: |
set -e
# Check if container is running
if ! docker ps -q -f name=eudiplo-test | grep -q .; then
echo "Container failed to start or exited immediately ❌"
echo ""
echo "=== Container Status ==="
docker ps -a -f name=eudiplo-test
echo ""
echo "=== Container Logs ==="
docker logs eudiplo-test 2>&1 || echo "No logs available"
exit 1
fi
# wait up to ~90s (45 * 2s) for HEALTHCHECK to pass
for i in $(seq 1 45); do
# Check if container is still running
if ! docker ps -q -f name=eudiplo-test | grep -q .; then
echo "Container stopped unexpectedly ❌"
echo ""
echo "=== Container Status ==="
docker ps -a -f name=eudiplo-test
echo ""
echo "=== Container Logs ==="
docker logs eudiplo-test 2>&1 || true
exit 1
fi
STATUS=$(docker inspect -f '{{if .State.Health}}{{.State.Health.Status}}{{else}}none{{end}}' eudiplo-test)
if [ "$STATUS" = "healthy" ]; then
echo "Container is healthy ✅"
exit 0
fi
if [ "$STATUS" = "unhealthy" ]; then
echo "Container reported unhealthy ❌"
echo ""
echo "=== Health Check Log ==="
docker inspect --format='{{range .State.Health.Log}}{{.Output}}{{end}}' eudiplo-test || true
echo ""
echo "=== Container Logs ==="
docker logs eudiplo-test || true
exit 1
fi
sleep 2
done
echo "Timed out waiting for healthy ❌"
docker ps
echo ""
echo "=== Health Check Log ==="
docker inspect --format='{{range .State.Health.Log}}{{.Output}}{{end}}' eudiplo-test || true
echo ""
echo "=== Container Logs ==="
docker logs eudiplo-test || true
exit 1
- name: Stop test container
if: ${{ always() && matrix.arch == 'amd64' }}
run: docker rm -f eudiplo-test || true
# --- Build and push by digest (main push only) ---
- name: Build and push by digest
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
id: push
uses: docker/build-push-action@v7
with:
context: .
target: eudiplo
platforms: ${{ matrix.platform }}
build-args: VERSION=main
labels: ${{ steps.meta.outputs.labels }}
outputs: type=image,name=ghcr.io/openwallet-foundation/eudiplo,push-by-digest=true,name-canonical=true,push=true
cache-from: type=gha,scope=backend-${{ matrix.arch }}
cache-to: type=gha,scope=backend-${{ matrix.arch }},mode=max
- name: Export digest
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
run: |
mkdir -p ${{ runner.temp }}/digests
digest="${{ steps.push.outputs.digest }}"
touch "${{ runner.temp }}/digests/${digest#sha256:}"
- name: Upload digest
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
uses: actions/upload-artifact@v7
with:
name: backend-digest-${{ matrix.arch }}
path: ${{ runner.temp }}/digests/*
if-no-files-found: error
retention-days: 1
docker-backend-manifest:
name: Create Backend Docker Manifest
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
needs: [docker-backend]
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Download digests
uses: actions/download-artifact@v8
with:
pattern: backend-digest-*
merge-multiple: true
path: ${{ runner.temp }}/digests
- name: Log in to GitHub Container Registry
uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4
- name: Docker meta
id: meta
uses: docker/metadata-action@v6
with:
images: ghcr.io/openwallet-foundation/eudiplo
tags: |
type=raw,value=main
type=raw,value=sha-${{ github.sha }}
- name: Create manifest list and push
working-directory: ${{ runner.temp }}/digests
run: |
docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
$(printf 'ghcr.io/openwallet-foundation/eudiplo@sha256:%s ' *)
- name: Inspect image
run: docker buildx imagetools inspect ghcr.io/openwallet-foundation/eudiplo:main
# =============================================================================
# Docker Backend Demo: Build & Push (multi-platform with native runners)
# Publishes ghcr.io/openwallet-foundation/eudiplo-demo:main on main branch.
# =============================================================================
docker-backend-demo:
name: Build & Push Backend Demo Docker (${{ matrix.arch }})
needs: [test-e2e, test-e2e-oidf]
strategy:
fail-fast: false
matrix:
include: '${{ fromJSON(github.event_name == ''push'' && github.ref == ''refs/heads/main'' && ''[{"platform": "linux/amd64", "runner": "ubuntu-latest", "arch": "amd64"}, {"platform": "linux/arm64", "runner": "ubuntu-24.04-arm", "arch": "arm64"}]'' || ''[{"platform": "linux/amd64", "runner": "ubuntu-latest", "arch": "amd64"}]'') }}'
runs-on: ${{ matrix.runner }}
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v7
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4
- name: Docker meta
id: meta
uses: docker/metadata-action@v6
with:
images: ghcr.io/openwallet-foundation/eudiplo-demo
- name: Log in to GitHub Container Registry
uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
# --- Build to validate (PRs/workflow_dispatch only) ---
- name: Build to validate
if: ${{ !(github.ref == 'refs/heads/main' && github.event_name == 'push') }}
uses: docker/build-push-action@v7
with:
context: .
target: eudiplo-demo
platforms: ${{ matrix.platform }}
build-args: VERSION=main
cache-from: type=gha,scope=backend-demo-${{ matrix.arch }}
cache-to: type=gha,scope=backend-demo-${{ matrix.arch }},mode=max
# --- Build and push by digest (main push only) ---
- name: Build and push by digest
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
id: push
uses: docker/build-push-action@v7
with:
context: .
target: eudiplo-demo
platforms: ${{ matrix.platform }}
build-args: VERSION=main
labels: ${{ steps.meta.outputs.labels }}
outputs: type=image,name=ghcr.io/openwallet-foundation/eudiplo-demo,push-by-digest=true,name-canonical=true,push=true
cache-from: type=gha,scope=backend-demo-${{ matrix.arch }}
cache-to: type=gha,scope=backend-demo-${{ matrix.arch }},mode=max
- name: Export digest
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
run: |
mkdir -p ${{ runner.temp }}/digests
digest="${{ steps.push.outputs.digest }}"
touch "${{ runner.temp }}/digests/${digest#sha256:}"
- name: Upload digest
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
uses: actions/upload-artifact@v7
with:
name: backend-demo-digest-${{ matrix.arch }}
path: ${{ runner.temp }}/digests/*
if-no-files-found: error
retention-days: 1
docker-backend-demo-manifest:
name: Create Backend Demo Docker Manifest
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
needs: [docker-backend-demo]
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Download digests
uses: actions/download-artifact@v8
with:
pattern: backend-demo-digest-*
merge-multiple: true
path: ${{ runner.temp }}/digests
- name: Log in to GitHub Container Registry
uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4
- name: Docker meta
id: meta
uses: docker/metadata-action@v6
with:
images: ghcr.io/openwallet-foundation/eudiplo-demo
tags: |
type=raw,value=main
type=raw,value=sha-${{ github.sha }}
- name: Create manifest list and push
working-directory: ${{ runner.temp }}/digests
run: |
docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
$(printf 'ghcr.io/openwallet-foundation/eudiplo-demo@sha256:%s ' *)
- name: Inspect image
run: docker buildx imagetools inspect ghcr.io/openwallet-foundation/eudiplo-demo:main
# =============================================================================
# Docker Client: Build & Push (multi-platform with native runners)
# Skip arm64 on PRs to save time - only build amd64 for validation.
# =============================================================================
docker-client:
name: Build & Push Client Docker (${{ matrix.arch }})
needs: [lint]
strategy:
fail-fast: false
matrix:
# Dynamic matrix: include arm64 only on main branch push
include: '${{ fromJSON(github.event_name == ''push'' && github.ref == ''refs/heads/main'' && ''[{"platform": "linux/amd64", "runner": "ubuntu-latest", "arch": "amd64"}, {"platform": "linux/arm64", "runner": "ubuntu-24.04-arm", "arch": "arm64"}]'' || ''[{"platform": "linux/amd64", "runner": "ubuntu-latest", "arch": "amd64"}]'') }}'
runs-on: ${{ matrix.runner }}
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v7
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4
- name: Docker meta
id: meta
uses: docker/metadata-action@v6
with:
images: ghcr.io/openwallet-foundation/eudiplo-client
- name: Log in to GitHub Container Registry
uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
# --- Build and push by digest (main push only) ---
- name: Build and push by digest
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
id: push
uses: docker/build-push-action@v7
with:
context: .
target: client
platforms: ${{ matrix.platform }}
labels: ${{ steps.meta.outputs.labels }}
outputs: type=image,name=ghcr.io/openwallet-foundation/eudiplo-client,push-by-digest=true,name-canonical=true,push=true
cache-from: type=gha,scope=client-${{ matrix.arch }}
cache-to: type=gha,scope=client-${{ matrix.arch }},mode=max
# --- Build to validate (PRs/workflow_dispatch only) ---
- name: Build to validate
if: ${{ !(github.ref == 'refs/heads/main' && github.event_name == 'push') }}
uses: docker/build-push-action@v7
with:
context: .
target: client
platforms: ${{ matrix.platform }}
cache-from: type=gha,scope=client-${{ matrix.arch }}
cache-to: type=gha,scope=client-${{ matrix.arch }},mode=max
- name: Export digest
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
run: |
mkdir -p ${{ runner.temp }}/digests
digest="${{ steps.push.outputs.digest }}"
touch "${{ runner.temp }}/digests/${digest#sha256:}"
- name: Upload digest
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
uses: actions/upload-artifact@v7
with:
name: client-digest-${{ matrix.arch }}
path: ${{ runner.temp }}/digests/*
if-no-files-found: error
retention-days: 1
docker-client-manifest:
name: Create Client Docker Manifest
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
needs: [docker-client]
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Download digests
uses: actions/download-artifact@v8
with:
pattern: client-digest-*
merge-multiple: true
path: ${{ runner.temp }}/digests
- name: Log in to GitHub Container Registry
uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4
- name: Docker meta
id: meta
uses: docker/metadata-action@v6
with:
images: ghcr.io/openwallet-foundation/eudiplo-client
tags: |
type=raw,value=main
type=raw,value=sha-${{ github.sha }}
- name: Create manifest list and push
working-directory: ${{ runner.temp }}/digests
run: |
docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
$(printf 'ghcr.io/openwallet-foundation/eudiplo-client@sha256:%s ' *)
- name: Inspect image
run: docker buildx imagetools inspect ghcr.io/openwallet-foundation/eudiplo-client:main