name: docker on: # Build + smoke-test on every branch push; registry push is gated per-step to main/dev. push: branches: - '**' pull_request: branches: - main - dev jobs: # --------------------------------------------------------------------------- # 1. Lint: ruff check + format. # --------------------------------------------------------------------------- lint: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: astral-sh/setup-uv@v5 with: enable-cache: true - name: Install dependencies run: uv sync --group dev - name: Run lint run: | uv run ruff check . uv run ruff format --check . # --------------------------------------------------------------------------- # 2. Test: pytest with coverage gate (fails under 80%). # --------------------------------------------------------------------------- test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: astral-sh/setup-uv@v5 with: enable-cache: true - name: Install dependencies run: uv sync --group dev - name: Run tests run: uv run pytest # --------------------------------------------------------------------------- # 3. Build the Docker image, smoke-test it, push to Gitea (push events to # main/dev only), then clean up so nothing lingers on the self-hosted # runner. # --------------------------------------------------------------------------- docker: needs: [lint, test] runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 - name: Compute image name & tags id: meta shell: bash run: | IMAGE="git.hiddenden.cafe/${GITHUB_REPOSITORY,,}" echo "image=${IMAGE}" >> "$GITHUB_OUTPUT" echo "sha_tag=${IMAGE}:sha-${GITHUB_SHA::12}" >> "$GITHUB_OUTPUT" if [ "${GITHUB_REF_NAME}" = "main" ]; then # Production: stable :latest + :main echo "branch_tags=${IMAGE}:latest ${IMAGE}:main" >> "$GITHUB_OUTPUT" else # dev (and any other branch): tag with the branch name echo "branch_tags=${IMAGE}:${GITHUB_REF_NAME}" >> "$GITHUB_OUTPUT" fi - name: Build image shell: bash run: docker build -f docker/Dockerfile -t "${{ steps.meta.outputs.sha_tag }}" . - name: Smoke-test image shell: bash run: | docker run --rm --entrypoint python "${{ steps.meta.outputs.sha_tag }}" \ -c "import familiar" echo "Image imports cleanly." - name: Log in to Gitea Container Registry if: github.event_name == 'push' && (github.ref_name == 'main' || github.ref_name == 'dev') uses: docker/login-action@v3 with: registry: git.hiddenden.cafe username: ${{ github.actor }} # PAT with write:package scope, stored as the REGISTRY_TOKEN secret. # The auto-provided GITEA_TOKEN lacks package-write permission on # this instance, so we use a dedicated token here. password: ${{ secrets.REGISTRY_TOKEN }} - name: Tag & push if: github.event_name == 'push' && (github.ref_name == 'main' || github.ref_name == 'dev') shell: bash run: | for tag in ${{ steps.meta.outputs.branch_tags }} ${{ steps.meta.outputs.sha_tag }}; do docker tag "${{ steps.meta.outputs.sha_tag }}" "$tag" docker push "$tag" echo "Pushed $tag" done # Always runs — removes exactly what this run created, even on failure. # Scoped on purpose: if the runner shares the host Docker daemon, a global # prune would also wipe other homelab services. We never create volumes # here, so only dangling images + build cache are swept. - name: Cleanup if: always() shell: bash run: | docker rmi -f ${{ steps.meta.outputs.sha_tag }} ${{ steps.meta.outputs.branch_tags }} || true docker image prune -f || true docker builder prune -f || true