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
41 changes: 41 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -71,3 +71,44 @@ jobs:
files: coverage.xml
fail_ci_if_error: false
token: ${{ secrets.CODECOV_TOKEN }}

deploy-smoke:
# Catches breakages that the Python-only matrix can't see — e.g.
# PR #6 round 1 shipped a Dockerfile whose runtime stage left the
# venv installed in editable mode pointing at a non-existent /src,
# so `import pypi_winnow_downloads` failed at startup. Lint, type
# checks, and pytest were all green; the bug only surfaced when QA
# ran the container by hand. This job exercises the deploy/
# examples that the docs in deploy/README.md tell users to copy.
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6

- name: Build Docker image
run: docker build -f deploy/docker/Dockerfile -t pypi-winnow-downloads:smoke .

- name: Verify entrypoint resolves (winnow-collect --help)
# Override the Dockerfile's ENTRYPOINT so we can pass --help
# without also passing the hardcoded --config flag (which would
# require a config file the container doesn't ship). Exit 0 from
# --help is the smoke signal that the venv links resolve and
# `import pypi_winnow_downloads` works.
run: docker run --rm --entrypoint /opt/venv/bin/winnow-collect pypi-winnow-downloads:smoke --help

- name: Validate Compose example syntax
env:
# compose.yml.example uses ${BADGE_HOST:?…} so `compose config`
# errors without it. Any non-empty placeholder works.
BADGE_HOST: badges.example.com
run: docker compose -f deploy/docker/compose.yml.example config

- name: Validate Caddyfile example syntax
# Mount the deploy/caddy/ directory into the official caddy:2
# image and run `caddy validate` against the example. Catches
# syntax-level breakage and confirms the Caddyfile parses with
# the version users will most likely run.
run: |
docker run --rm \
-v "${GITHUB_WORKSPACE}/deploy/caddy:/etc/caddy:ro" \
caddy:2 \
caddy validate --config /etc/caddy/Caddyfile.example --adapter caddyfile
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added

- **`deploy-smoke` CI job** in `.github/workflows/ci.yml` exercises the four `deploy/` example artifacts that the Python-only matrix can't reach: builds the multi-stage Dockerfile, runs the container with overridden entrypoint to assert `winnow-collect --help` exits 0 (the bug class that took down PR #6 round 1 — venv installed in editable mode pointing at a non-existent `/src` so `import pypi_winnow_downloads` failed at startup), validates `deploy/docker/compose.yml.example` with `docker compose config` (`BADGE_HOST` substitution), and validates `deploy/caddy/Caddyfile.example` via `caddy validate` inside the official `caddy:2` image. Catches Dockerfile / compose / Caddyfile breakage before users hit it. Skips `systemd-analyze` on the timer because the referenced `.service` declares a binary at `/usr/local/bin/winnow-collect` that doesn't exist on a fresh CI runner. Closes #7.
- **`.github/PULL_REQUEST_TEMPLATE.md`** auto-fills new human-authored PRs with Summary, Test plan, and CHANGELOG sections matching this repo's CI commands (`uv run pytest --cov`, `uv run ruff check src/ tests/`, `uv run ruff format --check src/ tests/`, `uv run mypy src/pypi_winnow_downloads/`). Dependabot bypasses PR templates by design and is handled separately by the new auto-CHANGELOG workflow.
- **`.github/workflows/dependabot-changelog.yml`** auto-appends a `## [Unreleased]` → `### Changed` entry to Dependabot-authored PRs so they satisfy the per-PR CHANGELOG rule without manual intervention. Runs on `pull_request_target`, filters to `dependabot[bot]`, mints a GitHub App installation token via `actions/create-github-app-token`, fetches metadata via `dependabot/fetch-metadata@v3.1.0` (the v3 line fixed empty `prevVersion`/`newVersion` on grouped PRs), and pushes the CHANGELOG commit under the `cmeans-claude-dev[bot]` identity. The App-token push is the load-bearing piece: `secrets.GITHUB_TOKEN`-authored pushes do NOT re-trigger required `pull_request` checks (lint, typecheck, test) under GitHub's anti-loop policy, which would leave Dependabot PRs un-mergeable under the `main-protection` ruleset's required-status-checks rule. Loop guard skips when the last commit is already by the bot; idempotency guard skips when the PR number is already referenced in `CHANGELOG.md`. Operator must configure two repo secrets (`BOT_APP_ID`, `BOT_APP_PRIVATE_KEY`) before the workflow can run. Validated end-to-end on `cmeans/mcp-synology` (PRs #57 + #58 + #60 + #61, the latter being live verification on a real grouped Dependabot bump). Cross-repo playbook lives in awareness `dependabot-pr-hygiene-playbook`.

Expand Down