Files
bvandeusen 31217498ce
CI / lint (push) Successful in 3s
CI / unit (push) Successful in 4s
CI / publish (push) Successful in 13s
Add Forgejo CI: ruff lint + pytest unit + gated image publish
Family-consistent CI for StashHandler (mirrors the NhenArchiver Python
sibling, minus the integration lane — StashHandler is SQLite-only with no
service containers).

- .forgejo/workflows/ci.yml: lint (ruff) -> unit (pytest) -> publish.
  publish builds + pushes git.fabledsword.com/bvandeusen/stashhandler with
  the family rule-46 tag scheme (dev->:dev+:c-<sha>, main->:latest+:c-<sha>,
  v* tag->:<version>+:latest). Gated on lint+unit. Needs REGISTRY_TOKEN.
- tests/: starter unit suite — hashing, SQLite dedup, media filter, transfer
  modes.
- pyproject.toml: ruff target + pytest pythonpath/testpaths.
- ci-requirements.md (family rule 39), .dockerignore.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016nyYC9dTQ78SqhpNFtpMvT
2026-06-26 10:44:49 -04:00

101 lines
3.8 KiB
YAML

name: CI
# Family CI lanes (FabledRulebook "CI philosophy", rule 6), mirrored from the
# NhenArchiver Python sibling and adapted for StashHandler:
# - lint: ruff only, no dep install (fast-fail for the common bounce).
# - unit: pytest, no service containers, no DB beyond SQLite tmpfiles.
# - publish: build + push the runtime image to the Forgejo registry, gated on
# the test lanes (family rule 46 tag scheme).
#
# No integration lane: StashHandler is SQLite-only with no service deps
# (family rules 79-82 do not apply).
#
# Requires repo Actions secret REGISTRY_TOKEN -- a Forgejo PAT scoped
# read:package + write:package. The injected GITHUB_TOKEN CANNOT be used: it
# lacks write:package and `docker push` 401s with reqPackageAccess (rule 7).
on:
push:
branches: [dev, main]
# A v* tag publishes an immutable per-version image (e.g. :v26.06.26) and
# refreshes :latest (family rule 46).
tags: ['v*']
jobs:
# Fast-fail lint lane. ruff is pre-installed in ci-python, so this runs with
# NO dependency install and surfaces the common bounce class in seconds.
lint:
runs-on: python-ci
container:
image: git.fabledsword.com/bvandeusen/ci-python:3.14
steps:
- uses: actions/checkout@v4
- name: Ruff lint
run: ruff check .
unit:
runs-on: python-ci
container:
image: git.fabledsword.com/bvandeusen/ci-python:3.14
steps:
- uses: actions/checkout@v4
- name: Install Python deps
# uv: faster wheel resolve than pip on cold caches. Falls back to pip on
# uv-missing runners (older images). blake3 is the optional runtime hash
# backend exercised by the hasher tests.
run: |
if command -v uv >/dev/null 2>&1; then
uv pip install --system -r requirements.txt pytest blake3
else
pip install -r requirements.txt pytest blake3
fi
- name: Pytest (unit)
run: pytest tests/ -v
# Build + push the runtime image. Gated on the test lanes. ci-python ships the
# docker CLI + buildx; act_runner auto-mounts the host docker socket via
# valid_volumes, so we do NOT set container.options (family rule 8).
publish:
needs: [lint, unit]
runs-on: python-ci
container:
image: git.fabledsword.com/bvandeusen/ci-python:3.14
steps:
- uses: actions/checkout@v4
- name: Determine tags
id: tag
run: |
# Three trigger shapes (family rule 46 -- commit-SHA images are the
# rollback unit; :latest tracks main's tip; no separate :main tag):
# refs/tags/v* -> immutable :<version> + refresh :latest
# refs/heads/main -> :latest + :c-<short_sha>
# refs/heads/dev -> :dev + :c-<short_sha>
# POSIX-safe substring (runner shell is dash/BusyBox sh, not bash --
# ${var:0:7} errors with "Bad substitution"; cut works everywhere).
IMG=git.fabledsword.com/bvandeusen/stashhandler
SHORT_SHA=$(printf '%s' "$GITHUB_SHA" | cut -c1-7)
if [ "${GITHUB_REF#refs/tags/}" != "${GITHUB_REF}" ]; then
TAG_NAME="${GITHUB_REF#refs/tags/}"
echo "tags=${IMG}:${TAG_NAME},${IMG}:latest" >> "$GITHUB_OUTPUT"
elif [ "${GITHUB_REF##*/}" = "main" ]; then
echo "tags=${IMG}:latest,${IMG}:c-${SHORT_SHA}" >> "$GITHUB_OUTPUT"
else
echo "tags=${IMG}:dev,${IMG}:c-${SHORT_SHA}" >> "$GITHUB_OUTPUT"
fi
- name: Login to Forgejo registry
uses: docker/login-action@v3
with:
registry: git.fabledsword.com
username: ${{ github.actor }}
password: ${{ secrets.REGISTRY_TOKEN }}
- name: Build and push image
uses: docker/build-push-action@v5
with:
context: .
file: Dockerfile
push: true
tags: ${{ steps.tag.outputs.tags }}