Skip to content
Merged
Show file tree
Hide file tree
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
141 changes: 38 additions & 103 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,31 @@
name: CI

# ci: calls sidick/amiga-workflows' build-test.yml, which implements the
# five-verb contract (see that repo's README.md, the document of record) --
# build/test-host/test-target/lint all resolve to Makefile targets of the
# same name (see Makefile's own "Verb contract" comment). Each of those
# jobs is independent (no artifact-passing between them), unlike this
# repo's previous bespoke pipeline (one m68k-build job producing both
# binaries, one host-tests job, one asm-crypto-tests job) -- the m68k
# cross-build now runs more than once per CI run (build and test-target
# each call it), a deliberate tradeoff of standardizing on the shared
# workflow rather than keeping a repo-specific optimization.
#
# The `changes` job stays local (repo-specific path patterns aren't
# something a shared workflow should know about) and its outputs feed
# build-test.yml's run-* inputs directly, preserving the path-based
# skip-on-docs-only-PR behavior. `differential` (opt-in OpenSSL fuzz) and
# `docs-build` (strict MkDocs + AmigaGuide check) stay local jobs too --
# neither fits the five verbs, and the shared workflow has no hook for an
# extra per-project job. `catalog-lint`'s check-catalog now runs as `make
# lint` inside build-test.yml's lint job instead of its own job.
#
# NOTE: branch protection's required status checks reference job names
# directly (e.g. "m68k Amiga build") -- those all changed shape here
# (nested reusable-workflow jobs report as "ci / build" etc). Updated to
# match as part of this same change; see amiga-dev's docs/plan.md Phase 4
# entry for the mapping.

on:
push:
branches: [main]
Expand Down Expand Up @@ -47,6 +73,18 @@ jobs:
echo "catalog=$(match "$always|^(locale/|tools/check_catalog\.py)")"
} >> "$GITHUB_OUTPUT"

ci:
name: ci
needs: changes
uses: sidick/amiga-workflows/.github/workflows/build-test.yml@v1
with:
run-build: ${{ needs.changes.outputs.build == 'true' }}
run-test-host: ${{ needs.changes.outputs.host == 'true' }}
run-test-target: ${{ needs.changes.outputs.target == 'true' }}
run-lint: ${{ needs.changes.outputs.catalog == 'true' }}
secrets:
AMIGA_REAL_ROM_B64: ${{ secrets.AMIGA_REAL_ROM_B64 }}

docs-build:
name: Docs site + AmigaGuide build
runs-on: ubuntu-latest
Expand All @@ -64,30 +102,6 @@ jobs:
- name: AmigaGuide conversion
run: make guide

host-tests:
name: Host vector tests
runs-on: ubuntu-latest
needs: changes
if: needs.changes.outputs.host == 'true'
steps:
- uses: actions/checkout@v7
- name: Build and run tests
run: make test
- name: Build native CLI
run: make cli
- name: CLI smoke test
run: make smoke

catalog-lint:
name: Catalog structural checks
runs-on: ubuntu-latest
needs: changes
if: needs.changes.outputs.catalog == 'true'
steps:
- uses: actions/checkout@v7
- name: Check placeholders, mnemonics, re-key prompt markers
run: make check-catalog

differential:
name: Differential fuzz (vs OpenSSL)
runs-on: ubuntu-latest
Expand All @@ -99,82 +113,3 @@ jobs:
run: sudo apt-get update && sudo apt-get install -y libssl-dev pkg-config
- name: Fuzz crypto primitives against OpenSSL
run: make diff DIFF_ITERS=20000

m68k-build:
name: m68k Amiga build
runs-on: ubuntu-latest
needs: changes
if: needs.changes.outputs.build == 'true'
container: ghcr.io/reinauer/container-amiga-gcc:latest
steps:
- uses: actions/checkout@v7
- name: Cross-build CLI for 68000
run: PATH="/opt/amiga/bin:$PATH" make m68k M68K_CC=m68k-amigaos-gcc
# The GUI pulls in sources the CLI build never touches (src/gui/,
# qrimage.c, arexx.c, the m68k quirc objects) - without this step a PR
# that breaks GUI compilation merges green and only fails at release
# tag time.
- name: Cross-build GUI for 68000
run: PATH="/opt/amiga/bin:$PATH" make gui M68K_CC=m68k-amigaos-gcc

asm-crypto-tests:
name: m68k asm crypto tests (vamos)
runs-on: ubuntu-latest
needs: changes
if: needs.changes.outputs.build == 'true'
steps:
- uses: actions/checkout@v7
- name: Install amitools (vamos)
run: pip install 'amitools[vamos]'
- name: Cross-build the asm test binaries
run: make asm-tests-docker
# Every existing SHA-1/HMAC/PBKDF2 RFC vector, run against the
# hand-written asm (#47) instead of the C reference, on both the plain
# 68000 baseline and 68020+ - the asm is meant to be correct on every
# CPU tier this project supports, not just an accelerated one. ChaCha20
# has no asm path (measured slower than C on real hardware; see
# src/core/crypto_dispatch.h), so there's nothing to validate here.
- name: Validate against RFC vectors (68000 + 68020)
run: |
for cpu in 000 020; do
echo "--- CPU $cpu ---"
vamos -C "$cpu" build/asm-test-sha1
done

copperline-smoke:
name: Copperline on-target smoke test (AROS)
runs-on: ubuntu-latest
needs: changes
if: needs.changes.outputs.target == 'true'
env:
# Pin the Copperline release; bump to adopt upstream changes.
COPPERLINE_VERSION: "0.14.0"
steps:
- uses: actions/checkout@v7

# Software Vulkan (lavapipe), in case the emulator initialises wgpu at
# start-up; the test itself runs windowless via --benchmark-until.
- name: Install runtime deps
run: sudo apt-get update && sudo apt-get install -y mesa-vulkan-drivers

# Use the prebuilt AppImage instead of compiling from source — a ~9 MB
# download vs a multi-minute cargo build. It bundles the emulator, its
# libs, and the redistributable AROS Kickstart replacement (no licensed
# ROM needed). Extracted (no FUSE on runners) and put on PATH.
- name: Install Copperline (prebuilt AppImage)
run: |
url="https://github.com/LinuxJedi/Copperline/releases/download/v${COPPERLINE_VERSION}/Copperline-${COPPERLINE_VERSION}-x86_64.AppImage"
curl -fsSL "$url" -o copperline.AppImage
chmod +x copperline.AppImage
./copperline.AppImage --appimage-extract >/dev/null
mkdir -p "$HOME/.local/bin"
ln -sf "$PWD/squashfs-root/AppRun" "$HOME/.local/bin/copperline"
echo "$HOME/.local/bin" >> "$GITHUB_PATH"
# Point at the AppImage's bundled AROS explicitly, so resolution
# doesn't depend on the extracted layout.
aros_bin=$(find "$PWD/squashfs-root" -name aros-amiga-m68k-rom.bin | head -1)
[ -n "$aros_bin" ] && echo "COPPERLINE_AROS_DIR=$(dirname "$aros_bin")" >> "$GITHUB_ENV"

# No KICK set -> run.sh boots the bundled AROS (no ROM needed).
- name: On-target smoke test
run: make copperline-smoke
55 changes: 19 additions & 36 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
name: Docs

# Publishes the versioned user-docs site (MkDocs Material + mike) to GitHub
# Pages: each release tag deploys its docs as a named version (v1.0, v1.1,
# ...) plus the floating `latest` alias, giving the site a per-release
# version picker. Day-to-day docs edits on main do NOT deploy - the site is
# refreshed at release time (PRs get a strict build check in ci.yml).
# Publishes the versioned user-docs site via sidick/amiga-workflows' docs.yml
# (MkDocs Material + mike -> GitHub Pages, each release tag deploys as a
# named version plus the floating `latest` alias). Day-to-day docs edits on
# main do NOT deploy -- the site is refreshed at release time (PRs get a
# strict build check in ci.yml's docs-build job).
#
# workflow_dispatch exists for the initial publish (the v1.0 tag predates
# this workflow, so its docs deploy from current main) and for re-publishing
# a version after a docs-only fix.
# workflow_dispatch exists for re-publishing a version after a docs-only
# fix, or for a first publish ahead of the next release tag.
#
# One-time repo setup: Settings -> Pages -> Source: "GitHub Actions" is NOT
# used here - mike works by committing the built site to the gh-pages
# branch, so Pages must be set to serve from gh-pages / root.
# One-time repo setup: Settings -> Pages -> Source: "Deploy from a branch" /
# gh-pages / root -- mike commits the built site to gh-pages itself, it
# does not use the "GitHub Actions" Pages source.

on:
push:
Expand All @@ -23,32 +22,16 @@ on:
description: 'Version to publish (e.g. v1.0)'
required: true

# A reusable workflow's own `permissions:` block can only narrow, never
# widen, what the calling job already has - so this repo's own contents:
# write grant (mike pushes to gh-pages) has to be declared here too, not
# just relied on inside amiga-workflows/docs.yml. The old inline docs.yml
# declared this same permission for the same reason.
permissions:
contents: write

jobs:
deploy:
name: Publish versioned docs site
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
with:
fetch-depth: 0 # mike commits to gh-pages; needs the full branch

- uses: actions/setup-python@v7
with:
python-version: '3.14'

- name: Install docs toolchain
run: pip install -r tools/docs-requirements.txt

- name: Strict build first (fail before touching gh-pages)
run: mkdocs build --strict

- name: Deploy versioned site with mike
run: |
version="${{ inputs.version || github.ref_name }}"
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
mike deploy --push --update-aliases "$version" latest
mike set-default --push latest
docs:
uses: sidick/amiga-workflows/.github/workflows/docs.yml@v1
with:
version: ${{ inputs.version || github.ref_name }}
130 changes: 27 additions & 103 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -1,116 +1,40 @@
name: Release

# Tag-driven releases: a release PR bumps src/version.h + AmiAuth.readme
# through normal review/CI, then pushing the matching v<version> tag runs this.
# The GitHub release (with the Aminet package attached) is created
# automatically; the actual Aminet FTP upload waits for manual approval via the
# `aminet` environment.
# through normal review/CI, then pushing the matching v<version> tag runs
# this. verify-version confirms the pushed tag actually matches those two
# files; release then delegates the build -> GitHub release -> Aminet-
# upload-behind-required-review pipeline to sidick/amiga-workflows'
# aminet-release.yml. The old inline $VER-embedded-in-binary check moved
# into the Makefile's own `dist` target (see its comment there) -
# aminet-release.yml's dist job runs `make dist` standalone with no
# separate build step to hang a caller-side check off of.

on:
push:
tags: ['v*']

permissions:
contents: write

jobs:
release:
name: Build and publish GitHub release
verify-version:
name: Verify tag matches src/version.h / AmiAuth.readme
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- run: ./scripts/verify-version.sh "${{ github.ref_name }}"

- name: Verify tag matches the source version
run: |
tag="${GITHUB_REF_NAME#v}"
src=$(sed -n 's/^#define AMIAUTH_VERSION[[:space:]]*"\(.*\)"$/\1/p' src/version.h)
readme=$(sed -n 's/^Version:[[:space:]]*\(.*\)$/\1/p' AmiAuth.readme)
echo "tag=$tag src/version.h=$src AmiAuth.readme=$readme"
[ "$tag" = "$src" ] || { echo "::error file=src/version.h::Tag v$tag does not match AMIAUTH_VERSION \"$src\""; exit 1; }
[ "$tag" = "$readme" ] || { echo "::error file=AmiAuth.readme::Tag v$tag does not match Version: \"$readme\""; exit 1; }

- name: Cross-build m68k binaries (CLI + GUI)
run: make m68k-docker gui-docker

- name: Check the $VER strings made it into the binaries
run: |
v="${GITHUB_REF_NAME#v}"
for b in AmiAuth AmiAuthGUI; do
# -F: fixed-string match (the pattern contains $ and parens);
# -a: search the binary as text.
grep -aqF "\$VER: $b $v (" "build/$b" || { echo "::error::build/$b lacks \"\$VER: $b $v (...)\""; exit 1; }
done

# `make dist` builds its own pinned lha (Ubuntu's lhasa is extract-only)
- name: Package the Aminet upload pair
run: make dist

- name: Validate the readme (no upload)
uses: sidick/aminet-release-action@v1
with:
filename: build/dist/AmiAuth.lha
readme: build/dist/AmiAuth.readme
category: util/crypt
validate-only: true

- name: Create GitHub release with the package attached
env:
GH_TOKEN: ${{ github.token }}
run: |
if [ "$(gh release list --limit 1 --json tagName -q 'length')" = "0" ]; then
# First-ever release: a --generate-notes PR-title dump has
# nothing meaningful to diff against (every merged PR since the
# repo's creation), so use a curated body sourced from
# AmiAuth.readme's own intro + Features section instead. Later
# releases have a previous tag to diff against, so they fall
# through to --generate-notes below, which becomes genuinely
# useful once there's real release-to-release history.
{
sed -n '/^Distribution:/,/^Features$/p' AmiAuth.readme | sed '1d;$d;/^--------$/d'
echo "**Features:**"
echo
sed -n '/^Features$/,/^Requirements$/p' AmiAuth.readme | sed '1d;$d;/^--------$/d;/^$/d'
echo
echo "See the [documentation site](https://sidick.github.io/amiauth/) for full documentation, and [docs/SECURITY.md](https://github.com/sidick/amiauth/blob/main/docs/SECURITY.md) for the honest threat model."
} > /tmp/release-notes.md
gh release create "$GITHUB_REF_NAME" \
--title "AmiAuth $GITHUB_REF_NAME" \
--notes-file /tmp/release-notes.md \
build/dist/AmiAuth.lha build/dist/AmiAuth.readme
else
gh release create "$GITHUB_REF_NAME" \
--title "AmiAuth $GITHUB_REF_NAME" \
--generate-notes \
build/dist/AmiAuth.lha build/dist/AmiAuth.readme
fi

- name: Keep the package for the Aminet job
uses: actions/upload-artifact@v7
with:
name: aminet-dist
path: |
build/dist/AmiAuth.lha
build/dist/AmiAuth.readme
if-no-files-found: error

aminet:
name: Upload to Aminet (waits for approval)
runs-on: ubuntu-latest
needs: release
# The `aminet` environment has a required reviewer, so this job pauses
# until the upload is explicitly approved in the Actions UI. Publishing to
# Aminet is irreversible-ish (uploads land in /new for moderation) - keep
# the human in the loop.
environment: aminet
steps:
- uses: actions/download-artifact@v8
with:
name: aminet-dist
path: build/dist

- name: Upload to Aminet and attach to the GitHub release
uses: sidick/aminet-release-action@v1
with:
filename: build/dist/AmiAuth.lha
readme: build/dist/AmiAuth.readme
category: util/crypt
release:
name: Build, release, and (on approval) publish to Aminet
needs: verify-version
# A reusable workflow's own `permissions:` can only narrow, never
# widen, what the calling job already has - aminet-release.yml's
# `gh release create` step needs contents: write, so it has to be
# granted here too, not just relied on inside that workflow.
permissions:
contents: write
uses: sidick/amiga-workflows/.github/workflows/aminet-release.yml@v1
with:
tag: ${{ github.ref_name }}
project-name: AmiAuth
lha-path: build/dist/AmiAuth.lha
readme-path: build/dist/AmiAuth.readme
category: util/crypt
2 changes: 1 addition & 1 deletion AmiAuth.readme
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ Short: TOTP/HOTP two-factor authenticator
Author: simond@irrelevant.org (Simon Dick)
Uploader: simond@irrelevant.org (Simon Dick)
Type: util/crypt
Version: 1.0
Version: 1.1
Architecture: m68k-amigaos >= 2.0.4
Distribution: Aminet

Expand Down
Loading