All checks were successful
PR build (required check) / changes (pull_request) Successful in 8s
shell-lint / shellcheck (pull_request) Successful in 7s
secrets-guard / encrypted (pull_request) Successful in 10s
PR build (required check) / build-backend (pull_request) Has been skipped
PR build (required check) / build-frontend (pull_request) Has been skipped
PR build (required check) / validate-observability (pull_request) Has been skipped
PR build (required check) / gate (pull_request) Successful in 3s
Forgejo cannot scope merge styles per branch — they live on the repo's pull-request unit, and BranchProtection carries no merge-style or linear-history field in 9.0.3. So the chain is enforced repo-wide by allowing exactly two styles: squash and fast-forward-only. Merge commits and rebase merges are off. Squash belongs on the short-lived hop. Squashing dev into main would put a commit on main that does not have dev's commits as ancestors, freezing merge-base(dev, main); every later promotion would then list all commits since that stale point and re-apply changes main already has. Fast-forwarding the promotions instead means a promotion creates no commit at all, so the three branches keep identical history rather than merely identical trees, and `release ⊆ main ⊆ dev` holds permanently. Rebase-updates stay enabled. They are a different setting from rebase merges, and BRANCHING.md is right that the merge queue 403s without them. Also adds `release` to pr-build.yml's pull_request branches. This is step one of the order BRANCHING.md prescribes for closing the missing check on release: teach the workflow to fire there, confirm on a real PR that the context appears, and only then require it. Requiring a context nothing produces is what makes a production promotion unmergeable with a 405 that names no cause.
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, release]
|
|
|
|
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; }
|