One root workflow set replaces the four repos' copies (deleted -- root-only is where Forgejo reads them, and dead copies are a trap): per-domain build-push with explicit image paths (emi/thermograph/backend|frontend; the old github.repository-derived path collides in a monorepo), path-filtered per-domain beta/prod/dev deploys, a domain-input reusable build check, a single always-reporting PR gate (path-filtered required checks deadlock auto-merge), a new infra-sync pipeline (host checkout + secrets render on infra/** pushes), and ports of secrets-guard / ops-cron / observability-validate to monorepo paths.
98 lines
3.3 KiB
YAML
98 lines
3.3 KiB
YAML
name: PR build (required check)
|
|
|
|
# Monorepo port of the app repos' pr-build.yml: ONE workflow, ONE stable
|
|
# required-check name (`gate`), any number of domains. A `changes` job diffs
|
|
# the PR against its base; per-domain jobs run only for domains the PR
|
|
# touches; `gate` reduces their results into the single status branch
|
|
# protection requires.
|
|
#
|
|
# Deliberately NOT path-filtered at the workflow level: a path-filtered
|
|
# required check that never fires leaves the PR stuck "expected -- waiting for
|
|
# status" forever, so auto-merge never proceeds. An always-running gate whose
|
|
# domain jobs skip cleanly gives the same compute savings without the
|
|
# deadlock.
|
|
#
|
|
# One-time web UI setup (Settings -> Branches, rules for `dev` and `main`):
|
|
# -> enable "Enable Status Check", list this workflow's "gate" job
|
|
# -> merge style: squash
|
|
# Then on a ready PR: "Auto merge when checks succeed".
|
|
|
|
on:
|
|
pull_request:
|
|
branches: [dev, main]
|
|
|
|
concurrency:
|
|
group: pr-build-${{ github.event.pull_request.number }}
|
|
cancel-in-progress: true
|
|
|
|
jobs:
|
|
changes:
|
|
runs-on: docker
|
|
outputs:
|
|
backend: ${{ steps.diff.outputs.backend }}
|
|
frontend: ${{ steps.diff.outputs.frontend }}
|
|
observability: ${{ steps.diff.outputs.observability }}
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
with:
|
|
# The PR base commit isn't reachable from a depth-1 clone; full
|
|
# history keeps the diff computable however far the branch diverged.
|
|
fetch-depth: 0
|
|
|
|
- name: Detect changed domains
|
|
id: diff
|
|
run: |
|
|
set -euo pipefail
|
|
base="${{ github.event.pull_request.base.sha }}"
|
|
changed=$(git diff --name-only "$base" "$GITHUB_SHA")
|
|
echo "changed files:"; printf '%s\n' "$changed"
|
|
for d in backend frontend observability; do
|
|
if printf '%s\n' "$changed" | grep -q "^$d/"; then
|
|
echo "$d=true" >> "$GITHUB_OUTPUT"
|
|
else
|
|
echo "$d=false" >> "$GITHUB_OUTPUT"
|
|
fi
|
|
done
|
|
|
|
build-backend:
|
|
needs: changes
|
|
if: needs.changes.outputs.backend == 'true'
|
|
uses: ./.forgejo/workflows/build.yml
|
|
with:
|
|
domain: backend
|
|
|
|
build-frontend:
|
|
needs: changes
|
|
if: needs.changes.outputs.frontend == 'true'
|
|
uses: ./.forgejo/workflows/build.yml
|
|
with:
|
|
domain: frontend
|
|
|
|
validate-observability:
|
|
needs: changes
|
|
if: needs.changes.outputs.observability == 'true'
|
|
uses: ./.forgejo/workflows/observability-validate.yml
|
|
|
|
gate:
|
|
name: gate
|
|
# always(): skipped domain jobs (nothing changed there) must not skip the
|
|
# gate itself -- the required check has to report on EVERY PR.
|
|
if: always()
|
|
needs: [changes, build-backend, build-frontend, validate-observability]
|
|
runs-on: docker
|
|
steps:
|
|
- name: Reduce domain results
|
|
run: |
|
|
set -euo pipefail
|
|
ok=1
|
|
for pair in \
|
|
"backend=${{ needs.build-backend.result }}" \
|
|
"frontend=${{ needs.build-frontend.result }}" \
|
|
"observability=${{ needs.validate-observability.result }}"; do
|
|
echo "$pair"
|
|
case "${pair#*=}" in
|
|
success|skipped) ;;
|
|
*) ok=0 ;;
|
|
esac
|
|
done
|
|
[ "$ok" = 1 ] || { echo "a changed domain's check failed"; exit 1; }
|