Skip to content

fix: website for page deployment (#854) #2412

fix: website for page deployment (#854)

fix: website for page deployment (#854) #2412

name: CI / Docker Release
on:
push:
branches: ['main']
pull_request:
types: [opened, synchronize, reopened]
workflow_dispatch:
inputs:
release:
description: 'Create a new versioned release (Docker + npm)'
required: false
type: boolean
default: false
confirm_major:
description: 'Type CONFIRM to allow major version bump (breaking changes)'
required: false
type: string
default: ''
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@v6
name: Install pnpm
with:
run_install: false
- name: Install Node.js
uses: actions/setup-node@v7
with:
node-version: 24
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@v6
name: Install pnpm
with:
run_install: false
- name: Install Node.js
uses: actions/setup-node@v7
with:
node-version: 24
cache: 'pnpm'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Build SDK Core
run: pnpm --filter @eudiplo/sdk-core build
build-backend:
name: Build Backend
permissions:
contents: read
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: pnpm/action-setup@v6
name: Install pnpm
with:
run_install: false
- name: Install Node.js
uses: actions/setup-node@v7
with:
node-version: 24
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@v6
name: Install pnpm
with:
run_install: false
- name: Install Node.js
uses: actions/setup-node@v7
with:
node-version: 24
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@v6
name: Install pnpm
with:
run_install: false
- name: Install Node.js
uses: actions/setup-node@v7
with:
node-version: 24
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@v6
name: Install pnpm
with:
run_install: false
- name: Install Node.js
uses: actions/setup-node@v7
with:
node-version: 24
cache: 'pnpm'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Set up Python
uses: actions/setup-python@v6
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: read
pages: write
id-token: write
steps:
- name: Checkout repository
uses: actions/checkout@v7
- uses: pnpm/action-setup@v6
name: Install pnpm
with:
run_install: false
- name: Install Node.js
uses: actions/setup-node@v7
with:
node-version: 24
cache: 'pnpm'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: '3.12'
- name: Install Python dependencies
run: pip install -r requirements.txt
- name: Configure GitHub Pages
uses: actions/configure-pages@v5
- 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
mike set-default --branch gh-pages --deploy-prefix docs main
git worktree add gh-pages-out gh-pages
- name: Assemble Pages artifact
run: |
mkdir -p pages/docs
cp -R website/. pages/
cp -R gh-pages-out/docs/. pages/docs/
touch pages/.nojekyll
- name: Cleanup temporary worktree
if: always()
run: git worktree remove gh-pages-out --force || true
- name: Upload Pages artifact
uses: actions/upload-pages-artifact@v4
with:
path: pages
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
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@v6
name: Install pnpm
with:
run_install: false
- name: Install Node.js
uses: actions/setup-node@v7
with:
node-version: 24
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@v6
name: Install pnpm
with:
run_install: false
- name: Install Node.js
uses: actions/setup-node@v7
with:
node-version: 24
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
# =============================================================================
# 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
- 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
- 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
- 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
# =============================================================================
# NPM Pre-release (Main Branch Only)
# Publishes @eudiplo/sdk-core with a "main" tag as a pre-release version.
# =============================================================================
npm-prerelease:
name: Publish SDK Pre-release to npm
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
needs: [build-sdk-core]
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
steps:
- uses: actions/checkout@v7
with:
fetch-depth: 0
- uses: pnpm/action-setup@v6
name: Install pnpm
with:
run_install: false
- name: Install Node.js
uses: actions/setup-node@v7
with:
node-version: 24
cache: 'pnpm'
registry-url: 'https://registry.npmjs.org'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Build SDK
run: pnpm --filter @eudiplo/sdk-core build
- name: Set pre-release version
working-directory: packages/eudiplo-sdk-core
run: |
# Base prerelease on the latest repository semver tag (e.g., v5.1.0 -> 5.1.0).
# Fallback to package.json if tags are not available.
LATEST_TAG=$(git tag --sort=-v:refname | grep -E '^v?[0-9]+\.[0-9]+\.[0-9]+$' | head -n 1 || true)
if [ -n "$LATEST_TAG" ]; then
CURRENT_VERSION="${LATEST_TAG#v}"
else
CURRENT_VERSION=$(node -p "require('./package.json').version")
fi
SHORT_SHA=$(echo "${{ github.sha }}" | cut -c1-7)
PRE_VERSION="${CURRENT_VERSION}-main.${SHORT_SHA}"
echo "Publishing version: ${PRE_VERSION}"
npm version "${PRE_VERSION}" --no-git-tag-version
- name: Publish to npm with main tag
working-directory: packages/eudiplo-sdk-core
run: npm publish --tag main --access public --provenance
# =============================================================================
# Versioned Release (Manual Trigger Only)
# Creates a new semantic version, publishes Docker images with version tags,
# publishes npm package, and updates documentation.
# =============================================================================
release:
name: Create Versioned Release
if: github.event_name == 'workflow_dispatch' && inputs.release == true
needs: [test-e2e, docker-backend, docker-client]
runs-on: ubuntu-latest
permissions:
contents: write
packages: write
pull-requests: write
actions: read
pages: write
id-token: write
steps:
- name: Checkout repository
uses: actions/checkout@v7
with:
fetch-depth: 0 # Fetch full history for mike versioning
- uses: pnpm/action-setup@v6
name: Install pnpm
with:
run_install: false
- name: Install Node.js
uses: actions/setup-node@v7
with:
node-version: 24
cache: 'pnpm'
registry-url: 'https://registry.npmjs.org'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Build SDK
run: pnpm --filter @eudiplo/sdk-core build
- name: Set up QEMU
uses: docker/setup-qemu-action@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4
- name: Check for major version bump
id: version_check
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
echo "Running semantic-release dry-run to detect version bump..."
DRY_OUTPUT=$(npx semantic-release --dry-run 2>&1) || true
echo "$DRY_OUTPUT"
# Extract the next version from dry-run output
NEXT_VERSION=$(echo "$DRY_OUTPUT" | grep -oP "The next release version is \K[0-9]+\.[0-9]+\.[0-9]+" || echo "")
CURRENT_VERSION=$(git describe --tags --abbrev=0 2>/dev/null | sed 's/^v//' || echo "0.0.0")
echo "Current version: $CURRENT_VERSION"
echo "Next version: $NEXT_VERSION"
if [ -z "$NEXT_VERSION" ]; then
echo "No release will be created"
echo "is_major=false" >> $GITHUB_OUTPUT
exit 0
fi
# Extract major versions
CURRENT_MAJOR=$(echo "$CURRENT_VERSION" | cut -d. -f1)
NEXT_MAJOR=$(echo "$NEXT_VERSION" | cut -d. -f1)
if [ "$NEXT_MAJOR" -gt "$CURRENT_MAJOR" ]; then
echo "⚠️ MAJOR VERSION BUMP DETECTED: $CURRENT_VERSION → $NEXT_VERSION"
echo "is_major=true" >> $GITHUB_OUTPUT
else
echo "Minor/patch release: $CURRENT_VERSION → $NEXT_VERSION"
echo "is_major=false" >> $GITHUB_OUTPUT
fi
- name: Abort if major release not confirmed
if: steps.version_check.outputs.is_major == 'true' && github.event.inputs.confirm_major != 'CONFIRM'
run: |
echo "❌ MAJOR VERSION RELEASE BLOCKED"
echo ""
echo "A major version bump was detected but not confirmed."
echo "To release a major version, trigger this workflow manually with:"
echo " confirm_major: CONFIRM"
echo ""
echo "This safeguard prevents accidental breaking changes from being released."
exit 1
- name: Run semantic-release
id: semantic_release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_CONFIG_PROVENANCE: true
DOCKER_REGISTRY_USER: ${{ github.actor }}
DOCKER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }}
run: |
echo "Running semantic-release..."
npx semantic-release
# Check if a new release was created by checking if a new tag was created
echo "Checking for new tags..."
git fetch --tags
NEW_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "")
echo "Latest tag found: $NEW_TAG"
if [ -n "$NEW_TAG" ]; then
echo "New release detected: $NEW_TAG"
echo "new_release=true" >> $GITHUB_OUTPUT
echo "version=$NEW_TAG" >> $GITHUB_OUTPUT
else
echo "No new release detected"
echo "new_release=false" >> $GITHUB_OUTPUT
fi
- name: Debug release detection
run: |
echo "New release: ${{ steps.semantic_release.outputs.new_release }}"
echo "Version: ${{ steps.semantic_release.outputs.version }}"
- name: Set up Python for docs
if: steps.semantic_release.outputs.new_release == 'true'
uses: actions/setup-python@v6
with:
python-version: '3.11'
- name: Install Python dependencies for docs
if: steps.semantic_release.outputs.new_release == 'true'
run: pip install -r requirements.txt
- name: Copy env file for docs
if: steps.semantic_release.outputs.new_release == 'true'
run: cp .env.example .env
- name: Configure git for mike
if: steps.semantic_release.outputs.new_release == 'true'
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
- name: Build and deploy release docs
if: steps.semantic_release.outputs.new_release == 'true'
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: |
FULL_VERSION="${{ steps.semantic_release.outputs.version }}"
# Extract major.minor (e.g., 3.2.0 → 3.2) so patch releases update the same version slot
MINOR_VERSION="${FULL_VERSION%.*}"
pnpm run doc:generate
pnpm run compodoc
# Deploy under major.minor (e.g., 3.2), patches overwrite the same entry
mike deploy --push --update-aliases "$MINOR_VERSION"
# Deploy to latest and set as default
mike deploy --push --update-aliases latest
mike set-default --push latest