Merge remote-tracking branch 'origin/main' into gsd/phase-13-real-lint-gate-eslint
CI / fast-checks (pull_request) Failing after 1m47s
CI / api (pull_request) Successful in 1m0s
CI / harness (pull_request) Successful in 3m30s

# Conflicts:
#	.planning/ROADMAP.md
This commit is contained in:
Lucas Berger
2026-06-11 21:25:14 -04:00
5 changed files with 372 additions and 121 deletions
+7 -1
View File
@@ -303,5 +303,11 @@ jobs:
uses: https://github.com/ChristopherHX/gitea-upload-artifact@v4
with:
name: playwright-traces-${{ github.run_id }}
path: apps/pwa/test-results/
# Upload BOTH the raw traces/screenshots/videos (test-results/) AND the
# navigable HTML report (playwright-report/, built by --reporter=list,html).
# Without the report dir the most useful triage artifact for a remote CI
# failure is built on every run and then discarded at runner teardown (WR-02).
path: |
apps/pwa/test-results/
apps/pwa/playwright-report/
retention-days: 14
+21 -3
View File
@@ -41,6 +41,12 @@ jobs:
- name: Compute image tags
id: tags
run: |
set -euo pipefail
# Fail closed if GITHUB_SHA is empty/unset (Gitea runner env parity is not
# guaranteed across versions). Without this guard SHORT_SHA degrades to ""
# and the immutable tag silently becomes :v1.1- — a valid-but-wrong tag that
# overwrites the milestone pointer and destroys rollback traceability (WR-03).
: "${GITHUB_SHA:?GITHUB_SHA is empty — refusing to build a malformed image tag}"
SHORT_SHA=${GITHUB_SHA:0:7}
MILESTONE="${{ env.MILESTONE }}"
echo "latest=git.bergerhouse.net/luckberg/familysync-api:latest" >> $GITHUB_OUTPUT
@@ -53,8 +59,15 @@ jobs:
# Secret is named REGISTRY_PAT (not GITEA_REGISTRY_PAT): Gitea reserves the GITEA_ prefix
# for secret names, so the GITEA_-prefixed name cannot be created.
- name: Docker login
# Bind the secret through env: so it is never substituted into the rendered
# script body. Read it as $REGISTRY_PAT and pipe with printf '%s' (echo is not
# safe for arbitrary strings — a trailing newline or shell-significant char
# would mangle the password into a confusing `unauthorized`) (WR-05).
env:
REGISTRY_PAT: ${{ secrets.REGISTRY_PAT }}
run: |
echo "${{ secrets.REGISTRY_PAT }}" | \
set -euo pipefail
printf '%s' "$REGISTRY_PAT" | \
docker login git.bergerhouse.net \
--username luckberg \
--password-stdin
@@ -63,13 +76,18 @@ jobs:
# lockfile from the root context; building from apps/api/ would fail to find them.
- name: Build and push
run: |
set -euo pipefail
docker build --target production \
-f apps/api/Dockerfile \
-t ${{ steps.tags.outputs.latest }} \
-t ${{ steps.tags.outputs.sha_tag }} \
.
docker push ${{ steps.tags.outputs.latest }}
docker push ${{ steps.tags.outputs.sha_tag }}
# Push the IMMUTABLE :<milestone>-<sha> tag FIRST. set -euo pipefail stops on
# the first failed push, so :latest is only moved after the immutable,
# rollback-traceable tag has landed — a failed second push can never leave
# :latest advanced without a corresponding rollback tag (WR-04).
docker push ${{ steps.tags.outputs.sha_tag }} # immutable first
docker push ${{ steps.tags.outputs.latest }} # move pointer only after immutable lands
# Always drop the stored credential from the runner after push (defence in depth).
- name: Docker logout