mirror of
https://github.com/Donkie/Spoolman.git
synced 2026-10-21 17:42:16 -05:00
Dependabot and Weblate branches are excluded from push runs, and the root jobs skipped same-repo pull_request runs, so their PRs got no checks and stayed blocked. Let those PRs run the pull_request suite. Weblate PRs are now merged by a Monday schedule instead of as soon as each sync goes green, to cut down the merge commits on master.
826 lines
27 KiB
YAML
826 lines
27 KiB
YAML
name: CI
|
|
|
|
on:
|
|
push:
|
|
branches:
|
|
# Every branch builds, so a fix can be handed to a bug reporter as a Docker
|
|
# image before it merges (see publish-images). Weblate pushes locale-only
|
|
# commits constantly and dependabot's branches arrive as PRs anyway, so
|
|
# bot branches skip push runs and are built by the pull_request run instead.
|
|
- "**"
|
|
- "!weblate*"
|
|
- "!dependabot/**"
|
|
tags:
|
|
- "v*"
|
|
pull_request:
|
|
branches:
|
|
- master
|
|
|
|
permissions:
|
|
contents: read
|
|
packages: read
|
|
|
|
# A new push makes the previous run's result irrelevant, so cancel it and let the
|
|
# new one have the runners. Master and tags are exempt: both publish images, and a
|
|
# cancelled run there is a half-published release.
|
|
concurrency:
|
|
group: ${{ github.workflow }}-${{ github.ref }}
|
|
cancel-in-progress: ${{ github.ref != 'refs/heads/master' && !startsWith(github.ref, 'refs/tags/') }}
|
|
|
|
env:
|
|
GHCR_IMAGE: donkie/spoolman
|
|
|
|
jobs:
|
|
#
|
|
# Linting
|
|
#
|
|
style:
|
|
# Same-repo branches already got a full run from the push trigger, so skip the
|
|
# duplicate pull_request run. Bot branches excluded above still need it, as do
|
|
# forks, whose pushes fire no event here. Skipping the three jobs with no `needs`
|
|
# cascades to every other job, since a skipped dependency skips its dependents.
|
|
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.repository || startsWith(github.head_ref, 'dependabot/') || startsWith(github.head_ref, 'weblate') }}
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v6
|
|
|
|
- name: Install node
|
|
uses: actions/setup-node@v4.0.3
|
|
with:
|
|
node-version: '24'
|
|
|
|
- name: Install uv
|
|
uses: astral-sh/setup-uv@v7
|
|
with:
|
|
version: "0.9.26"
|
|
|
|
- name: "Set up Python"
|
|
uses: actions/setup-python@v6
|
|
with:
|
|
python-version-file: "pyproject.toml"
|
|
|
|
- name: Install lefthook
|
|
run: uv add --dev lefthook
|
|
|
|
- name: Install client dependencies
|
|
run: |
|
|
cd client
|
|
npm ci
|
|
|
|
# lefthook's `ci` hook lints/formats/type-checks client_v2 too, via
|
|
# hardcoded ./node_modules/.bin/* paths, so its deps must be present.
|
|
- name: Install client_v2 dependencies
|
|
run: |
|
|
cd client_v2
|
|
npm ci
|
|
|
|
- name: Run pre-commit
|
|
run: uv run lefthook run ci
|
|
#
|
|
# Build the Spoolman client for baking into the docker images
|
|
# This is done on native hardware to speed things up, vite is very slow on emulated hardware
|
|
#
|
|
build-client:
|
|
# Same-repo branches already got a full run from the push trigger, so skip the
|
|
# duplicate pull_request run. Bot branches excluded above still need it, as do
|
|
# forks, whose pushes fire no event here. Skipping the three jobs with no `needs`
|
|
# cascades to every other job, since a skipped dependency skips its dependents.
|
|
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.repository || startsWith(github.head_ref, 'dependabot/') || startsWith(github.head_ref, 'weblate') }}
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v6
|
|
|
|
- name: Install node
|
|
uses: actions/setup-node@v4.0.3
|
|
with:
|
|
node-version: '24'
|
|
|
|
- name: Install uv
|
|
uses: astral-sh/setup-uv@v7
|
|
with:
|
|
version: "0.9.26"
|
|
|
|
- name: "Set up Python"
|
|
uses: actions/setup-python@v6
|
|
with:
|
|
python-version-file: "pyproject.toml"
|
|
|
|
- name: Install client dependencies
|
|
run: |
|
|
cd client
|
|
npm ci
|
|
|
|
- name: Build
|
|
run: |
|
|
cd client
|
|
rm -f .env && echo "VITE_APIURL=/api/v1" > .env.production
|
|
npm run build
|
|
|
|
# Build the new Svelte client (client_v2), served by default at runtime.
|
|
# No VITE_APIURL is set: the client resolves the API relative to its base path.
|
|
- name: Install client_v2 dependencies
|
|
run: |
|
|
cd client_v2
|
|
npm ci
|
|
|
|
- name: Build client_v2
|
|
run: |
|
|
cd client_v2
|
|
npm run build
|
|
|
|
- name: Write build info
|
|
run: |
|
|
echo "GIT_COMMIT=$(git rev-parse --short HEAD)" > build.txt
|
|
echo "BUILD_DATE=$(date -u +"%Y-%m-%dT%H:%M:%SZ")" >> build.txt
|
|
|
|
# Remove client sources and keep only the built output for each client
|
|
- name: Clean
|
|
run: |
|
|
mv client/dist dist
|
|
rm -rf client
|
|
mkdir client
|
|
mv dist client/dist
|
|
mv client_v2/build build_v2
|
|
rm -rf client_v2
|
|
mkdir client_v2
|
|
mv build_v2 client_v2/build
|
|
rm -rf .git
|
|
rm -rf .venv
|
|
|
|
- name: Fix permissions
|
|
run: |
|
|
chmod +x scripts/*.sh
|
|
|
|
- name: Upload client Spoolman artifact
|
|
uses: actions/upload-artifact@v4.4.0
|
|
with:
|
|
name: client
|
|
path: client/dist
|
|
|
|
- name: Upload client_v2 Spoolman artifact
|
|
uses: actions/upload-artifact@v4.4.0
|
|
with:
|
|
name: client_v2
|
|
path: client_v2/build
|
|
|
|
- name: Upload full Spoolman artifact
|
|
uses: actions/upload-artifact@v4.4.0
|
|
with:
|
|
include-hidden-files: true
|
|
name: spoolman
|
|
path: .
|
|
#
|
|
# Build native image for integration tests
|
|
#
|
|
build-amd64:
|
|
needs: [build-client]
|
|
runs-on: ubuntu-latest
|
|
permissions:
|
|
packages: write
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v6
|
|
|
|
- name: Download client
|
|
uses: actions/download-artifact@v4.1.7
|
|
with:
|
|
name: client
|
|
path: client/dist
|
|
|
|
- name: Download client_v2
|
|
uses: actions/download-artifact@v4.1.7
|
|
with:
|
|
name: client_v2
|
|
path: client_v2/build
|
|
|
|
- name: Set up Docker Buildx
|
|
uses: docker/setup-buildx-action@v3.6.1
|
|
|
|
- name: Log in to the Github Container registry
|
|
uses: docker/login-action@v3.3.0
|
|
with:
|
|
registry: ghcr.io
|
|
username: ${{ github.actor }}
|
|
password: ${{ secrets.GITHUB_TOKEN }}
|
|
|
|
- name: Build Spoolman
|
|
if: ${{ github.event_name != 'pull_request' }}
|
|
uses: docker/build-push-action@v6.7.0
|
|
with:
|
|
context: .
|
|
tags: ${{ env.GHCR_IMAGE }}:test
|
|
outputs: type=docker,dest=/tmp/spoolman.tar
|
|
cache-from: type=registry,ref=ghcr.io/${{ env.GHCR_IMAGE }}:buildcache-amd64
|
|
cache-to: type=registry,ref=ghcr.io/${{ env.GHCR_IMAGE }}:buildcache-amd64,mode=max
|
|
|
|
# Separate build for pull requests since PRs dont have access to store cache
|
|
- name: Build Spoolman (PR)
|
|
if: ${{ github.event_name == 'pull_request' }}
|
|
uses: docker/build-push-action@v6.7.0
|
|
with:
|
|
context: .
|
|
tags: ${{ env.GHCR_IMAGE }}:test
|
|
outputs: type=docker,dest=/tmp/spoolman.tar
|
|
cache-from: type=registry,ref=ghcr.io/${{ env.GHCR_IMAGE }}:buildcache-amd64
|
|
|
|
- name: Upload artifact
|
|
uses: actions/upload-artifact@v4.4.0
|
|
with:
|
|
name: spoolman-image
|
|
path: /tmp/spoolman.tar
|
|
#
|
|
# Build tester image for integration tests
|
|
#
|
|
build-tester:
|
|
# Same-repo branches already got a full run from the push trigger, so skip the
|
|
# duplicate pull_request run. Bot branches excluded above still need it, as do
|
|
# forks, whose pushes fire no event here. Skipping the three jobs with no `needs`
|
|
# cascades to every other job, since a skipped dependency skips its dependents.
|
|
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.repository || startsWith(github.head_ref, 'dependabot/') || startsWith(github.head_ref, 'weblate') }}
|
|
runs-on: ubuntu-latest
|
|
permissions:
|
|
packages: write
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v6
|
|
|
|
- name: Set up Docker Buildx
|
|
uses: docker/setup-buildx-action@v3.6.1
|
|
|
|
- name: Log in to the Github Container registry
|
|
uses: docker/login-action@v3.3.0
|
|
with:
|
|
registry: ghcr.io
|
|
username: ${{ github.actor }}
|
|
password: ${{ secrets.GITHUB_TOKEN }}
|
|
|
|
- name: Build Spoolman Tester
|
|
if: ${{ github.event_name != 'pull_request' }}
|
|
uses: docker/build-push-action@v6.7.0
|
|
with:
|
|
context: ./tests_integration
|
|
tags: ${{ env.GHCR_IMAGE }}-tester:latest
|
|
outputs: type=docker,dest=/tmp/spoolman-tester.tar
|
|
cache-from: type=registry,ref=ghcr.io/${{ env.GHCR_IMAGE }}:buildcache-tester
|
|
cache-to: type=registry,ref=ghcr.io/${{ env.GHCR_IMAGE }}:buildcache-tester,mode=max
|
|
|
|
# Separate build for pull requests since PRs dont have access to store cache
|
|
- name: Build Spoolman Tester (PR)
|
|
if: ${{ github.event_name == 'pull_request' }}
|
|
uses: docker/build-push-action@v6.7.0
|
|
with:
|
|
context: ./tests_integration
|
|
tags: ${{ env.GHCR_IMAGE }}-tester:latest
|
|
outputs: type=docker,dest=/tmp/spoolman-tester.tar
|
|
cache-from: type=registry,ref=ghcr.io/${{ env.GHCR_IMAGE }}:buildcache-tester
|
|
|
|
- name: Upload artifact
|
|
uses: actions/upload-artifact@v4.4.0
|
|
with:
|
|
name: spoolman-tester-image
|
|
path: /tmp/spoolman-tester.tar
|
|
#
|
|
# Perform integration tests
|
|
#
|
|
test:
|
|
needs: [build-amd64, build-tester]
|
|
strategy:
|
|
matrix:
|
|
dbtype: ["postgres", "sqlite", "mariadb", "cockroachdb"]
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v6
|
|
|
|
- name: Download built images
|
|
uses: actions/download-artifact@v4.1.7
|
|
with:
|
|
pattern: spoolman*-image
|
|
path: /tmp
|
|
|
|
- name: Load built images
|
|
run: |
|
|
docker load --input /tmp/spoolman-image/spoolman.tar
|
|
docker load --input /tmp/spoolman-tester-image/spoolman-tester.tar
|
|
|
|
- name: Perform integration tests
|
|
uses: hoverkraft-tech/compose-action@v2.0.1
|
|
with:
|
|
compose-file: "./tests_integration/docker-compose-${{ matrix.dbtype }}.yml"
|
|
up-flags: "--abort-on-container-exit"
|
|
down-flags: "--volumes"
|
|
#
|
|
# Perform frontend (browser) integration tests against the real image
|
|
#
|
|
test-frontend:
|
|
needs: [build-amd64]
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v6
|
|
|
|
- name: Download built image
|
|
uses: actions/download-artifact@v4.1.7
|
|
with:
|
|
name: spoolman-image
|
|
path: /tmp/spoolman-image
|
|
|
|
- name: Load built image
|
|
run: docker load --input /tmp/spoolman-image/spoolman.tar
|
|
|
|
- name: Install node
|
|
uses: actions/setup-node@v4.0.3
|
|
with:
|
|
node-version: '24'
|
|
|
|
- name: Install Playwright
|
|
run: |
|
|
cd tests_frontend
|
|
npm ci
|
|
npx playwright install --with-deps chromium
|
|
|
|
- name: Start Spoolman
|
|
run: docker compose -f tests_frontend/docker-compose.yml up -d --wait
|
|
|
|
- name: Run frontend integration tests
|
|
run: |
|
|
cd tests_frontend
|
|
npx playwright test
|
|
|
|
- name: Dump Spoolman logs on failure
|
|
if: failure()
|
|
run: docker compose -f tests_frontend/docker-compose.yml logs spoolman
|
|
|
|
- name: Upload Playwright report
|
|
if: ${{ !cancelled() }}
|
|
uses: actions/upload-artifact@v4.4.0
|
|
with:
|
|
name: playwright-report
|
|
path: tests_frontend/playwright-report
|
|
retention-days: 7
|
|
|
|
- name: Stop Spoolman
|
|
if: always()
|
|
run: docker compose -f tests_frontend/docker-compose.yml down -v
|
|
#
|
|
# Perform frontend (browser) integration tests for client_v2 against the real image
|
|
#
|
|
test-frontend-v2:
|
|
needs: [build-amd64]
|
|
runs-on: ubuntu-latest
|
|
permissions:
|
|
contents: read
|
|
# Argos authenticates via OIDC; fork PRs get no id-token and fall back to tokenless upload.
|
|
id-token: write
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v6
|
|
|
|
- name: Download built image
|
|
uses: actions/download-artifact@v4.1.7
|
|
with:
|
|
name: spoolman-image
|
|
path: /tmp/spoolman-image
|
|
|
|
- name: Load built image
|
|
run: docker load --input /tmp/spoolman-image/spoolman.tar
|
|
|
|
- name: Install node
|
|
uses: actions/setup-node@v4.0.3
|
|
with:
|
|
node-version: '24'
|
|
|
|
- name: Install Playwright
|
|
run: |
|
|
cd tests_frontend_v2
|
|
npm ci
|
|
npx playwright install --with-deps chromium
|
|
|
|
- name: Start Spoolman
|
|
run: docker compose -f tests_frontend_v2/docker-compose.yml up -d --wait
|
|
|
|
# Screenshots first: they seed a fixed dataset and need the database empty.
|
|
# The Argos reporter uploads them and posts the visual diff on the PR.
|
|
- name: Take client_v2 screenshots
|
|
env:
|
|
ARGOS_UPLOAD: "1"
|
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
run: |
|
|
cd tests_frontend_v2
|
|
npx playwright test --project screenshots
|
|
|
|
- name: Run client_v2 frontend integration tests
|
|
run: |
|
|
cd tests_frontend_v2
|
|
npx playwright test --project chromium
|
|
|
|
- name: Dump Spoolman logs on failure
|
|
if: failure()
|
|
run: docker compose -f tests_frontend_v2/docker-compose.yml logs spoolman
|
|
|
|
- name: Upload Playwright report
|
|
if: ${{ !cancelled() }}
|
|
uses: actions/upload-artifact@v4.4.0
|
|
with:
|
|
name: playwright-report-v2
|
|
path: tests_frontend_v2/playwright-report
|
|
retention-days: 7
|
|
|
|
- name: Stop Spoolman
|
|
if: always()
|
|
run: docker compose -f tests_frontend_v2/docker-compose.yml down -v
|
|
#
|
|
# Build arm64 image
|
|
# Also exported to a tar so test-multiarch-smoke can boot it under QEMU.
|
|
# On pull requests we can't push to buildcache (forks don't have registry
|
|
# write access), so cache-to is skipped and we only read from it.
|
|
#
|
|
build-arm64:
|
|
needs: [build-client]
|
|
runs-on: ubuntu-latest
|
|
permissions:
|
|
packages: write
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v6
|
|
|
|
- name: Download client
|
|
uses: actions/download-artifact@v4.1.7
|
|
with:
|
|
name: client
|
|
path: client/dist
|
|
|
|
- name: Download client_v2
|
|
uses: actions/download-artifact@v4.1.7
|
|
with:
|
|
name: client_v2
|
|
path: client_v2/build
|
|
|
|
- name: Set up QEMU
|
|
uses: docker/setup-qemu-action@v3.2.0
|
|
|
|
- name: Set up Docker Buildx
|
|
uses: docker/setup-buildx-action@v3.6.1
|
|
|
|
- name: Log in to the Github Container registry
|
|
uses: docker/login-action@v3.3.0
|
|
with:
|
|
registry: ghcr.io
|
|
username: ${{ github.actor }}
|
|
password: ${{ secrets.GITHUB_TOKEN }}
|
|
|
|
- name: Build and cache Docker image
|
|
if: ${{ github.event_name != 'pull_request' }}
|
|
uses: docker/build-push-action@v6.7.0
|
|
with:
|
|
context: .
|
|
platforms: linux/arm64
|
|
tags: ${{ env.GHCR_IMAGE }}:test-arm64
|
|
outputs: type=docker,dest=/tmp/spoolman-arm64.tar
|
|
cache-from: type=registry,ref=ghcr.io/${{ env.GHCR_IMAGE }}:buildcache-arm64
|
|
cache-to: type=registry,ref=ghcr.io/${{ env.GHCR_IMAGE }}:buildcache-arm64,mode=max
|
|
|
|
# Separate build for pull requests since PRs dont have access to store cache
|
|
- name: Build and cache Docker image (PR)
|
|
if: ${{ github.event_name == 'pull_request' }}
|
|
uses: docker/build-push-action@v6.7.0
|
|
with:
|
|
context: .
|
|
platforms: linux/arm64
|
|
tags: ${{ env.GHCR_IMAGE }}:test-arm64
|
|
outputs: type=docker,dest=/tmp/spoolman-arm64.tar
|
|
cache-from: type=registry,ref=ghcr.io/${{ env.GHCR_IMAGE }}:buildcache-arm64
|
|
|
|
- name: Upload arm64 image artifact
|
|
uses: actions/upload-artifact@v4.4.0
|
|
with:
|
|
name: spoolman-image-arm64
|
|
path: /tmp/spoolman-arm64.tar
|
|
#
|
|
# Build arm/v7 image
|
|
# Also exported to a tar so test-multiarch-smoke can boot it under QEMU.
|
|
# On pull requests we can't push to buildcache (forks don't have registry
|
|
# write access), so cache-to is skipped and we only read from it.
|
|
#
|
|
build-armv7:
|
|
needs: [build-client]
|
|
runs-on: ubuntu-latest
|
|
permissions:
|
|
packages: write
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v6
|
|
|
|
- name: Download client
|
|
uses: actions/download-artifact@v4.1.7
|
|
with:
|
|
name: client
|
|
path: client/dist
|
|
|
|
- name: Download client_v2
|
|
uses: actions/download-artifact@v4.1.7
|
|
with:
|
|
name: client_v2
|
|
path: client_v2/build
|
|
|
|
- name: Set up QEMU
|
|
uses: docker/setup-qemu-action@v3.2.0
|
|
|
|
- name: Set up Docker Buildx
|
|
id: buildx
|
|
uses: docker/setup-buildx-action@v3.6.1
|
|
|
|
# Keep uv's cache of wheels built from source (asyncpg, greenlet, ...) across
|
|
# runs. They have no armv7 wheels, and compiling them under QEMU is ~9 of the
|
|
# ~11 minutes of a build that misses the layer cache (lockfile or base image
|
|
# changed). The Dockerfile prunes the cache down to just those wheels, so it's
|
|
# small enough to save on every run and always restore the newest one.
|
|
- name: Cache uv wheels
|
|
uses: actions/cache@v4
|
|
with:
|
|
path: uv-cache
|
|
key: uv-cache-armv7-${{ github.run_id }}
|
|
restore-keys: uv-cache-armv7-
|
|
|
|
- name: Inject uv cache into BuildKit
|
|
uses: reproducible-containers/buildkit-cache-dance@v3.4.0
|
|
with:
|
|
builder: ${{ steps.buildx.outputs.name }}
|
|
cache-map: |
|
|
{ "uv-cache": "/root/.cache/uv" }
|
|
|
|
- name: Log in to the Github Container registry
|
|
uses: docker/login-action@v3.3.0
|
|
with:
|
|
registry: ghcr.io
|
|
username: ${{ github.actor }}
|
|
password: ${{ secrets.GITHUB_TOKEN }}
|
|
|
|
- name: Build and cache Docker image
|
|
if: ${{ github.event_name != 'pull_request' }}
|
|
uses: docker/build-push-action@v6.7.0
|
|
with:
|
|
context: .
|
|
platforms: linux/arm/v7
|
|
tags: ${{ env.GHCR_IMAGE }}:test-armv7
|
|
outputs: type=docker,dest=/tmp/spoolman-armv7.tar
|
|
cache-from: type=registry,ref=ghcr.io/${{ env.GHCR_IMAGE }}:buildcache-armv7
|
|
cache-to: type=registry,ref=ghcr.io/${{ env.GHCR_IMAGE }}:buildcache-armv7,mode=max
|
|
|
|
# Separate build for pull requests since PRs dont have access to store cache
|
|
- name: Build and cache Docker image (PR)
|
|
if: ${{ github.event_name == 'pull_request' }}
|
|
uses: docker/build-push-action@v6.7.0
|
|
with:
|
|
context: .
|
|
platforms: linux/arm/v7
|
|
tags: ${{ env.GHCR_IMAGE }}:test-armv7
|
|
outputs: type=docker,dest=/tmp/spoolman-armv7.tar
|
|
cache-from: type=registry,ref=ghcr.io/${{ env.GHCR_IMAGE }}:buildcache-armv7
|
|
|
|
- name: Upload armv7 image artifact
|
|
uses: actions/upload-artifact@v4.4.0
|
|
with:
|
|
name: spoolman-image-armv7
|
|
path: /tmp/spoolman-armv7.tar
|
|
#
|
|
# Smoke test the arm64 and armv7 images under QEMU emulation: boot the
|
|
# container with no config (defaults to SQLite, no external DB needed) and
|
|
# confirm /api/v1/health comes up. Emulated boot (interpreter start +
|
|
# alembic migrations) is slow, hence the generous retry budget.
|
|
#
|
|
test-multiarch-smoke:
|
|
needs: [build-arm64, build-armv7]
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
include:
|
|
- arch: arm64
|
|
platform: linux/arm64
|
|
artifact: spoolman-image-arm64
|
|
tarball: spoolman-arm64.tar
|
|
- arch: armv7
|
|
platform: linux/arm/v7
|
|
artifact: spoolman-image-armv7
|
|
tarball: spoolman-armv7.tar
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 15
|
|
steps:
|
|
- name: Set up QEMU
|
|
uses: docker/setup-qemu-action@v3.2.0
|
|
|
|
- name: Download built image
|
|
uses: actions/download-artifact@v4.1.7
|
|
with:
|
|
name: ${{ matrix.artifact }}
|
|
path: /tmp/spoolman-image
|
|
|
|
- name: Load built image
|
|
run: docker load --input /tmp/spoolman-image/${{ matrix.tarball }}
|
|
|
|
- name: Start Spoolman (${{ matrix.arch }}, emulated via QEMU)
|
|
run: |
|
|
docker run -d --name spoolman-smoke \
|
|
--platform ${{ matrix.platform }} \
|
|
-p 8000:8000 \
|
|
${{ env.GHCR_IMAGE }}:test-${{ matrix.arch }}
|
|
|
|
- name: Wait for /health endpoint
|
|
run: |
|
|
for i in $(seq 1 90); do
|
|
if curl -fsS http://localhost:8000/api/v1/health; then
|
|
echo
|
|
echo "Spoolman (${{ matrix.arch }}) is healthy"
|
|
exit 0
|
|
fi
|
|
sleep 5
|
|
done
|
|
echo "Spoolman (${{ matrix.arch }}) did not become healthy in time" >&2
|
|
exit 1
|
|
|
|
- name: Dump Spoolman logs on failure
|
|
if: failure()
|
|
run: docker logs spoolman-smoke
|
|
|
|
- name: Stop Spoolman
|
|
if: always()
|
|
run: docker rm -f spoolman-smoke
|
|
#
|
|
# Release images GHCR and Docker Hub if tests pass
|
|
# Don't run this for pull requests
|
|
#
|
|
publish-images:
|
|
if: ${{ github.event_name != 'pull_request' }}
|
|
needs:
|
|
[
|
|
test,
|
|
test-frontend,
|
|
test-frontend-v2,
|
|
test-multiarch-smoke,
|
|
build-amd64,
|
|
build-arm64,
|
|
build-armv7,
|
|
style,
|
|
build-client,
|
|
]
|
|
runs-on: ubuntu-latest
|
|
permissions:
|
|
packages: write
|
|
# Lets cosign fetch an OIDC token from Github and sign without a private key.
|
|
id-token: write
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v6
|
|
|
|
- name: Download client
|
|
uses: actions/download-artifact@v4.1.7
|
|
with:
|
|
name: client
|
|
path: client/dist
|
|
|
|
- name: Download client_v2
|
|
uses: actions/download-artifact@v4.1.7
|
|
with:
|
|
name: client_v2
|
|
path: client_v2/build
|
|
|
|
- name: Set up QEMU
|
|
uses: docker/setup-qemu-action@v3.2.0
|
|
|
|
- name: Set up Docker Buildx
|
|
uses: docker/setup-buildx-action@v3.6.1
|
|
|
|
- name: Log in to the Github Container registry
|
|
uses: docker/login-action@v3.3.0
|
|
with:
|
|
registry: ghcr.io
|
|
username: ${{ github.actor }}
|
|
password: ${{ secrets.GITHUB_TOKEN }}
|
|
|
|
# The trailing type=ref,event=branch is what gives an unmerged branch a
|
|
# pullable image (ghcr.io/donkie/spoolman:my-branch) for a bug reporter to
|
|
# test. It is GHCR-only and skips master (already published as :edge), so
|
|
# Docker Hub keeps carrying nothing but :edge and tagged releases.
|
|
- name: Extract metadata (tags, labels) for GHCR
|
|
id: meta-ghcr
|
|
uses: docker/metadata-action@v5.5.1
|
|
with:
|
|
images: |
|
|
ghcr.io/${{ env.GHCR_IMAGE }}
|
|
tags: |
|
|
type=edge
|
|
type=semver,pattern={{version}}
|
|
type=semver,pattern={{major}}.{{minor}}
|
|
type=semver,pattern={{major}}
|
|
type=ref,event=branch,enable=${{ github.ref_name != 'master' }}
|
|
|
|
- name: Extract metadata (tags, labels) for Docker Hub
|
|
id: meta-dh
|
|
uses: docker/metadata-action@v5.5.1
|
|
with:
|
|
images: |
|
|
${{ vars.REMOTE_REGISTRY_URL }}/${{ vars.REMOTE_REGISTRY_NAMESPACE }}/${{ vars.REMOTE_REGISTRY_REPOSITORY }}
|
|
tags: |
|
|
type=edge
|
|
type=semver,pattern={{version}}
|
|
type=semver,pattern={{major}}.{{minor}}
|
|
type=semver,pattern={{major}}
|
|
|
|
- name: Log in to DockerHub
|
|
uses: docker/login-action@v3.3.0
|
|
with:
|
|
registry: ${{ vars.REMOTE_REGISTRY_URL }}
|
|
username: ${{ secrets.REMOTE_REGISTRY_USERNAME }}
|
|
password: ${{ secrets.REMOTE_REGISTRY_PASSWORD }}
|
|
|
|
- name: Store git commit and build date
|
|
run: |
|
|
echo "GIT_COMMIT=$(git rev-parse --short HEAD)" >> "$GITHUB_ENV"
|
|
echo "BUILD_DATE=$(date -u +"%Y-%m-%dT%H:%M:%SZ")" >> "$GITHUB_ENV"
|
|
|
|
- name: Install cosign
|
|
uses: sigstore/cosign-installer@v4.1.2
|
|
with:
|
|
# Installer v4+ verifies cosign v3 downloads via the Sigstore bundle
|
|
# format; pin an explicit, current cosign release for reproducibility.
|
|
cosign-release: v3.1.1
|
|
|
|
- name: Build and push Docker images
|
|
id: build
|
|
uses: docker/build-push-action@v6.7.0
|
|
with:
|
|
context: .
|
|
platforms: linux/amd64,linux/arm64,linux/arm/v7
|
|
push: true
|
|
tags: |
|
|
${{ steps.meta-ghcr.outputs.tags }}
|
|
${{ steps.meta-dh.outputs.tags }}
|
|
labels: ${{ steps.meta-ghcr.outputs.labels }}
|
|
build-args: |
|
|
GIT_COMMIT
|
|
BUILD_DATE
|
|
cache-from: |
|
|
type=registry,ref=ghcr.io/${{ env.GHCR_IMAGE }}:buildcache-amd64
|
|
type=registry,ref=ghcr.io/${{ env.GHCR_IMAGE }}:buildcache-arm64
|
|
type=registry,ref=ghcr.io/${{ env.GHCR_IMAGE }}:buildcache-armv7
|
|
|
|
# Sign the manifest by digest rather than by tag: all the tags above point
|
|
# at the same digest, and the same digest is valid in both registries, so
|
|
# signing every tag would just repeat the same signature.
|
|
- name: Sign the published images
|
|
env:
|
|
DIGEST: ${{ steps.build.outputs.digest }}
|
|
TAGS: |
|
|
${{ steps.meta-ghcr.outputs.tags }}
|
|
${{ steps.meta-dh.outputs.tags }}
|
|
run: |
|
|
echo "${TAGS}" | while read -r tag; do
|
|
[ -n "${tag}" ] || continue
|
|
echo "${tag%:*}"
|
|
done | sort --unique | while read -r repository; do
|
|
echo "Signing ${repository}@${DIGEST}"
|
|
cosign sign --yes "${repository}@${DIGEST}"
|
|
done
|
|
#
|
|
# Create Github Release if CI was triggered by a tag
|
|
#
|
|
publish-release:
|
|
if: ${{ (github.event_name != 'pull_request') && startsWith(github.event.ref, 'refs/tags/v') }}
|
|
needs: [publish-images]
|
|
runs-on: ubuntu-latest
|
|
permissions:
|
|
contents: write
|
|
steps:
|
|
- name: Download built spoolman
|
|
uses: actions/download-artifact@v4.1.7
|
|
with:
|
|
name: spoolman
|
|
path: /tmp/spoolman
|
|
|
|
- name: Generate release info
|
|
run: |
|
|
cd /tmp/spoolman
|
|
echo '{
|
|
"project_name": "Spoolman",
|
|
"project_owner": "Donkie",
|
|
"version": "${{ github.ref_name }}"
|
|
}' > release_info.json
|
|
|
|
- name: Zip
|
|
run: |
|
|
cd /tmp/spoolman
|
|
zip -r spoolman.zip .
|
|
|
|
- name: Create release and upload build
|
|
uses: softprops/action-gh-release@v1
|
|
id: create-release
|
|
with:
|
|
draft: true
|
|
name: ${{ github.ref_name }}
|
|
tag_name: ${{ github.ref_name }}
|
|
files: /tmp/spoolman/spoolman.zip
|
|
token: ${{ secrets.GITHUB_TOKEN }}
|
|
body: "⚠️ TODO ⚠️"
|