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
This commit is contained in:
@@ -0,0 +1,100 @@
|
||||
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 }}
|
||||
Reference in New Issue
Block a user