diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..6dbd2dd --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,47 @@ +# Dependabot configuration. +# +# This repo had NO dependency watching until 2026-09-09, which is the +# same gap that produced the Sentinel AI agent's three production +# breakages in two days: mcp 2.x renaming a symbol, Starlette removing +# on_startup, and a ceiling added to stop the second one silently +# pinning the service to a Starlette line with 7 advisories. All three +# surfaced on a rebuild of a repo nobody had rebuilt since June. +# +# `pip-audit --strict` in the Test workflow catches a *known advisory* +# in what is already pinned. It does nothing about a lockfile quietly +# ageing out of support. That is what this is for. +# +# NO AUTO-MERGE WORKFLOW HERE, DELIBERATELY. +# +# This repo now deploys to Fly on push to master. GitHub does not +# trigger `on: push` workflows for commits pushed with GITHUB_TOKEN, so +# an auto-merge action would land a bump on master WITHOUT deploying it +# — master and production drift apart, and nothing reports an error. +# That is a real, observed bug in Sentinel-Command (four frontend bumps +# merged and never shipped on 2026-09-09). A human merge triggers the +# deploy normally, so these PRs are reviewed and merged by hand until +# that repo's PAT fix is proven. +version: 2 +updates: + # Python (uv) — pyproject.toml + uv.lock live at the repo root. + - package-ecosystem: "uv" + directory: "/" + schedule: + interval: "weekly" + day: "monday" + open-pull-requests-limit: 5 + commit-message: + prefix: "build(deps)" + labels: + - "dependencies" + + # GitHub Actions — pinned action versions age out the same way. + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + day: "monday" + open-pull-requests-limit: 5 + labels: + - "dependencies" + - "ci" diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 5e1247a..5d25613 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,10 +1,15 @@ -name: Test +name: Test & Deploy -# Deploy automation is deferred deliberately (see the Sentinel Command -# plan doc, Phase 1: "fine to start with a manual `fly deploy` for the -# very first cut given a human should be watching the first-ever deploy -# of new infrastructure anyway; automate once phase 1 is stable"). This -# workflow only covers lint + dependency audit + tests. +# Tests on every push and PR; deploys to Fly on pushes to master. +# +# Deploy automation was deferred so a human could watch the first-ever +# deploy of new infrastructure, with a note to "automate once phase 1 is +# stable". Phase 1 is stable, and leaving it deferred created a worse +# problem than it avoided: fly.toml became a file that did nothing. A +# scale-to-zero change merged with CI green on 2026-09-09 and never +# reached Fly — the app kept running always-on until someone noticed and +# deployed by hand. Config that silently doesn't apply is more dangerous +# than no config. on: push: @@ -68,3 +73,36 @@ jobs: env: TEST_DATABASE_URL: ${{ matrix.test_database_url }} run: uv run pytest -v + + deploy: + name: Deploy to Fly.io + runs-on: ubuntu-latest + # Waits on BOTH matrix legs (sqlite and postgres) — this service runs + # the same code against either dialect, so a green sqlite run alone + # is not evidence the deploy is safe. + needs: test + # Push-only: a PR runs the tests above but never ships. + if: github.event_name == 'push' + # Serialize so two quick pushes don't race on the machine update. + concurrency: + group: deploy-sentinel-license + cancel-in-progress: true + steps: + - uses: actions/checkout@v7 + + - uses: superfly/flyctl-actions/setup-flyctl@master + + # --strategy immediate because this app mounts a volume + # (sentinel_license_data). The default rolling strategy tries to + # stand up a parallel machine first and errors on the volume's + # single attachment slot. Sentinel-Sync has no volume and so needs + # no override; Command Center has the same constraint and the same + # flag. + # + # --ha=false because Fly otherwise provisions TWO machines, which + # this service does not need and which the volume cannot serve + # anyway. + - name: flyctl deploy + run: flyctl deploy --remote-only --strategy immediate --ha=false --yes + env: + FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}