Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
b00c3eb
ci: enforce Conventional Commits titles on commits and pull requests
agjs Oct 8, 2026
2739842
feat(scripts): discover packages/* workspaces in check, pre-push and CI
agjs Oct 8, 2026
b323781
feat(ui): bundle budget raises need a recorded reason
agjs Oct 8, 2026
6455f6a
feat(agent): record test-inventory additions during verification
agjs Oct 8, 2026
a2c8931
fix(api): share the Postgres TLS policy between runtime and migrations
agjs Oct 8, 2026
8b5a34e
fix(api): run migrations from their own image and never install at st…
agjs Oct 8, 2026
374eac1
feat(k3s): publish one coordinated production release
agjs Oct 8, 2026
c544354
fix(k3s): migrate only, on a read-only API and migrations root
agjs Oct 8, 2026
fc43ba7
fix(k3s): unblock migrations, scraping, drains and secret leakage
agjs Oct 8, 2026
61ac7b9
fix(api): carry the one-off superuser seed in the migrations image
agjs Oct 8, 2026
7c13750
docs(k3s): describe the coordinated release and the migrate-only Job
agjs Oct 8, 2026
be7a020
docs(maintenance): record the second Tinkercaster report in the feedb…
agjs Oct 8, 2026
e733a29
feat(api): lint-meta rejects vars.CI_RUNNER without the fork guard
agjs Oct 8, 2026
3e4cf43
docs: regenerate the lint-meta catalog for the runner fork guard
agjs Oct 8, 2026
f5e6bb8
feat(k3s): business metrics from CNPG aggregate queries and a Grafana…
agjs Oct 8, 2026
9fc6f1c
feat(site): add apps/site, a static Astro marketing site
agjs Oct 8, 2026
dcf318a
feat(compose): run the public site in dev and route it at the apex in…
agjs Oct 8, 2026
f78c07a
feat(k3s): serve the site at the apex and release it with the other i…
agjs Oct 8, 2026
3b9a177
ci: let a CI_RUNNER variable move jobs to self-hosted runners, never …
agjs Oct 8, 2026
f0cbb17
docs(maintenance): close the site, runner and business-metrics ledger…
agjs Oct 8, 2026
e4032c1
docs: document the public site, coordinated release, business metrics…
agjs Oct 8, 2026
d3ecbf1
docs(k3s): describe the four-image release and the apex/app domain split
agjs Oct 8, 2026
f78fe63
ci: point the k3s validation comment at the k3s README
agjs Oct 8, 2026
50ec8df
test(site): compare parsed origins instead of URL prefixes
agjs Oct 8, 2026
6b41162
fix(deps): pin patched versions for advisories published since September
agjs Oct 8, 2026
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
67 changes: 67 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,53 @@ updates:
- dependency-name: "@astrojs/cloudflare"
update-types: ["version-update:semver-major"]

# Shared workspace packages under packages/. The glob matches nothing when the
# directory is absent, so products without packages/ get no entry in effect.
- package-ecosystem: bun
directories:
- /packages/*
schedule:
interval: weekly
day: monday
time: "09:00"
timezone: Europe/Berlin
open-pull-requests-limit: 4
labels: ["dependencies"]
commit-message:
prefix: "chore(deps)"
include: scope
cooldown:
default-days: 7
groups:
production-dependencies:
dependency-type: "production"
patterns: ["*"]
development-dependencies:
dependency-type: "development"
patterns: ["*"]

- package-ecosystem: bun
directory: /apps/site
schedule:
interval: weekly
day: monday
time: "09:00"
timezone: Europe/Berlin
open-pull-requests-limit: 4
labels: ["dependencies"]
commit-message:
prefix: "chore(deps)"
include: scope
cooldown:
default-days: 7
groups:
production-dependencies:
dependency-type: "production"
patterns: ["*"]
development-dependencies:
dependency-type: "development"
patterns: ["*"]

- package-ecosystem: github-actions
directory: /
schedule:
Expand Down Expand Up @@ -185,6 +232,26 @@ updates:
- dependency-name: "node"
update-types: ["version-update:semver-major"]

- package-ecosystem: docker
directory: /apps/site
schedule:
interval: weekly
day: monday
time: "09:00"
timezone: Europe/Berlin
open-pull-requests-limit: 4
labels: ["dependencies", "docker"]
commit-message:
prefix: "chore(docker)"
groups:
docker-images:
patterns: ["*"]
ignore:
- dependency-name: "oven/bun"
update-types: ["version-update:semver-major"]
- dependency-name: "nginx"
update-types: ["version-update:semver-major"]

- package-ecosystem: docker
directory: /infra/compose/compose
schedule:
Expand Down
6 changes: 4 additions & 2 deletions .github/desired-repo-settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
"delete_branch_on_merge": true,
"allow_squash_merge": true,
"allow_merge_commit": false,
"allow_rebase_merge": false
"allow_rebase_merge": false,
"squash_merge_commit_title": "PR_TITLE"
},
"branch_protection_main": {
"required_status_check_contexts": [
Expand All @@ -23,7 +24,8 @@
"typecheck + lint + test",
"validate",
"yamllint (compose + workflows)",
"agent verification contract"
"agent verification contract",
"pull request title (conventional commit)"
],
"required_approving_review_count": 0,
"dismiss_stale_reviews": true,
Expand Down
6 changes: 5 additions & 1 deletion .github/workflows/agent-verification.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# Known gap: this workflow does not run the API, UI or browser lanes, so it never
# compares observed cases with tools/agent/inventories/*.json. agent:check only
# validates that the committed baselines parse and are non-empty. See
# tools/agent/README.md "CI enforcement".
name: agent-verification
on:
pull_request:
Expand All @@ -24,7 +28,7 @@ permissions:
jobs:
verify:
name: agent verification contract
runs-on: ubuntu-24.04
runs-on: ${{ !github.event.pull_request.head.repo.fork && vars.CI_RUNNER || 'ubuntu-24.04' }}
timeout-minutes: 40
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/apps-api-acl-drift.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ jobs:
defaults:
run:
working-directory: apps/api
runs-on: ubuntu-24.04
runs-on: ${{ !github.event.pull_request.head.repo.fork && vars.CI_RUNNER || 'ubuntu-24.04' }}
timeout-minutes: 5

steps:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/apps-api-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ jobs:
defaults:
run:
working-directory: apps/api
runs-on: ubuntu-24.04
runs-on: ${{ !github.event.pull_request.head.repo.fork && vars.CI_RUNNER || 'ubuntu-24.04' }}
timeout-minutes: 10

services:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/apps-api-openapi-drift.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ permissions:
jobs:
drift:
name: apps/ui OpenAPI schema is fresh
runs-on: ubuntu-24.04
runs-on: ${{ !github.event.pull_request.head.repo.fork && vars.CI_RUNNER || 'ubuntu-24.04' }}
timeout-minutes: 8

# The job always runs (so the required-status-check rule on `main` is
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/apps-api-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ jobs:
defaults:
run:
working-directory: apps/api
runs-on: ubuntu-24.04
runs-on: ${{ !github.event.pull_request.head.repo.fork && vars.CI_RUNNER || 'ubuntu-24.04' }}
timeout-minutes: 20
steps:
- name: Checkout
Expand Down
5 changes: 4 additions & 1 deletion .github/workflows/apps-api-security-deps.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ jobs:
defaults:
run:
working-directory: apps/api
runs-on: ubuntu-24.04
runs-on: ${{ !github.event.pull_request.head.repo.fork && vars.CI_RUNNER || 'ubuntu-24.04' }}
timeout-minutes: 10

env:
Expand All @@ -38,6 +38,9 @@ jobs:
- name: Detect relevant changes
uses: dorny/paths-filter@ceb8a2b8f2d89434be7ff52d3de7ec3738c5cc9d # v4.0.3
id: filter
# An app that was removed from the checkout has no lockfile: skip its scan
# (every later step gates on this filter's output).
if: hashFiles('apps/api/bun.lock') != ''
with:
filters: |
code:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/apps-api-security-sast.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ jobs:
defaults:
run:
working-directory: apps/api
runs-on: ubuntu-24.04
runs-on: ${{ !github.event.pull_request.head.repo.fork && vars.CI_RUNNER || 'ubuntu-24.04' }}
timeout-minutes: 15
container:
image: semgrep/semgrep:1.142.0@sha256:03402a5040a88a570dec58375ef1a19fa777dd61575afdc7d5527ddf308dd765
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/apps-api-security-secrets.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ jobs:
defaults:
run:
working-directory: apps/api
runs-on: ubuntu-24.04
runs-on: ${{ !github.event.pull_request.head.repo.fork && vars.CI_RUNNER || 'ubuntu-24.04' }}
timeout-minutes: 10

env:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/apps-api-security-spec.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ jobs:
defaults:
run:
working-directory: apps/api
runs-on: ubuntu-24.04
runs-on: ${{ !github.event.pull_request.head.repo.fork && vars.CI_RUNNER || 'ubuntu-24.04' }}
timeout-minutes: 15

services:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/apps-docs-linkcheck.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ jobs:
defaults:
run:
working-directory: apps/docs
runs-on: ubuntu-24.04
runs-on: ${{ !github.event.pull_request.head.repo.fork && vars.CI_RUNNER || 'ubuntu-24.04' }}
timeout-minutes: 20
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
Expand Down
5 changes: 4 additions & 1 deletion .github/workflows/apps-docs-security-deps.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ jobs:
defaults:
run:
working-directory: apps/docs
runs-on: ubuntu-24.04
runs-on: ${{ !github.event.pull_request.head.repo.fork && vars.CI_RUNNER || 'ubuntu-24.04' }}
timeout-minutes: 10

env:
Expand All @@ -38,6 +38,9 @@ jobs:
- name: Detect relevant changes
uses: dorny/paths-filter@ceb8a2b8f2d89434be7ff52d3de7ec3738c5cc9d # v4.0.3
id: filter
# An app that was removed from the checkout has no lockfile: skip its scan
# (every later step gates on this filter's output).
if: hashFiles('apps/docs/bun.lock') != ''
with:
filters: |
code:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/apps-docs-security-secrets.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ jobs:
defaults:
run:
working-directory: apps/docs
runs-on: ubuntu-24.04
runs-on: ${{ !github.event.pull_request.head.repo.fork && vars.CI_RUNNER || 'ubuntu-24.04' }}
timeout-minutes: 10

env:
Expand Down
88 changes: 88 additions & 0 deletions .github/workflows/apps-site-release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
name: release

# Builds and publishes the public site's serving image to GitHub Container
# Registry on every push to main (tags: latest + sha-<7>), for the Compose/WUD
# path. Kubernetes never uses these tags: production-release.yml builds the site
# as a candidate and pins its digest together with the API, migrations and UI.
# The build-time public configuration comes from
# infra/k3s/overlays/prod/site-build.env, so a configuration change rebuilds the
# image.

on:
push:
branches: [main]
paths:
- "apps/site/**"
- "infra/k3s/overlays/prod/site-build.env"
- ".github/workflows/apps-site-release.yml"

concurrency:
group: apps-site-release-${{ github.ref }}
cancel-in-progress: false

permissions:
contents: read
packages: write

jobs:
release:
runs-on: ${{ !github.event.pull_request.head.repo.fork && vars.CI_RUNNER || 'ubuntu-24.04' }}
timeout-minutes: 20
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

- name: Load production site configuration
id: config
run: |
set -a
. infra/k3s/overlays/prod/site-build.env
set +a
{
echo "site_url=$SITE_URL"
echo "app_url=$APP_URL"
echo "contact_email=$CONTACT_EMAIL"
echo "analytics_script_url=$ANALYTICS_SCRIPT_URL"
echo "analytics_website_id=$ANALYTICS_WEBSITE_ID"
} >> "$GITHUB_OUTPUT"

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@37fe631027851001ddb9b187196cc803df7f5f0e # v4.3.0

- name: Log in to GHCR
uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Compute image metadata
id: meta
uses: docker/metadata-action@dc802804100637a589fabce1cb79ff13a1411302 # v6.2.0
with:
images: ghcr.io/${{ github.repository_owner }}/${{ github.event.repository.name }}-site
tags: |
type=raw,value=latest,enable={{is_default_branch}}
type=sha,prefix=sha-,format=short
labels: |
org.opencontainers.image.source=${{ github.server_url }}/${{ github.repository }}
org.opencontainers.image.title=BoringStack site
org.opencontainers.image.description=Static marketing site served by nginx

- name: Build and push
uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0
with:
context: apps/site
file: apps/site/Dockerfile
target: production
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
build-args: |
SITE_URL=${{ steps.config.outputs.site_url }}
APP_URL=${{ steps.config.outputs.app_url }}
CONTACT_EMAIL=${{ steps.config.outputs.contact_email }}
ANALYTICS_SCRIPT_URL=${{ steps.config.outputs.analytics_script_url }}
ANALYTICS_WEBSITE_ID=${{ steps.config.outputs.analytics_website_id }}
cache-from: type=gha
cache-to: type=gha,mode=max
Loading
Loading