Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
102 changes: 0 additions & 102 deletions .github/workflows/_reusable-axe.yml

This file was deleted.

49 changes: 0 additions & 49 deletions .github/workflows/_reusable-lighthouse.yml

This file was deleted.

84 changes: 73 additions & 11 deletions .github/workflows/all-checks-pass.yml
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
name: All Checks Pass

# Aggregating gate workflow — branch protection requires the `all-checks-pass` status check.
# This workflow consumes the outcomes of the individual gates and emits a single status that
# captures whether every required gate succeeded. CR-C + QA-C may require additional gates;
# add new dependencies to the `needs` block + reference the status check in branch protection.
# Aggregating gate workflow — emits a single status summarising every other check
# run on the same commit.
#
# WHY THIS IS NOT A `needs:` BLOCK: `needs:` only works between jobs in the SAME
# workflow file. The gates this must cover live in other workflows (Islam.wiki CI,
# Quality Gates, Dependency Security Audit, ...), so the only way to aggregate them
# is to read the Checks API for this commit.
#
# HISTORY: this job used to be a bare `echo "all checks passed"` with no `needs:`
# and no API call. It reported success unconditionally, including on commits where
# E2E Tests, Lighthouse CI and axe were all failing. It is the reason a run of main
# with three failing jobs still looked green.

on:
pull_request:
Expand All @@ -15,15 +23,69 @@ concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

permissions:
checks: read
contents: read

jobs:
all-checks-pass:
name: all-checks-pass
runs-on: ubuntu-latest
if: always()
timeout-minutes: 45
steps:
- name: Verify all required gates have run
# NOTE: GitHub branch protection rules consume the *status check name* (job name).
# Failed required checks block merge automatically — this job acts as the
# canonical aggregation status. Add specific `needs:` once each gate runs in
# the same workflow file or use Required Workflows (org-level) to enforce.
run: echo "all checks passed"
- name: Wait for and aggregate all other check runs
env:
GH_TOKEN: ${{ github.token }}
REPO: ${{ github.repository }}
SHA: ${{ github.event.pull_request.head.sha || github.sha }}
SELF: all-checks-pass
run: |
set -euo pipefail

# Poll until every check run on this commit other than ourselves has
# finished. Settle for a couple of rounds so late-registering jobs are
# not missed (matrix legs in particular appear a few seconds apart).
settled=0
for _ in $(seq 1 80); do
runs=$(gh api --paginate \
"repos/$REPO/commits/$SHA/check-runs?per_page=100" \
--jq ".check_runs[] | select(.name != \"$SELF\") | \"\(.status)\t\(.conclusion // \"-\")\t\(.name)\"")

pending=$(printf '%s\n' "$runs" | grep -cv '^completed' || true)

if [ "$pending" -eq 0 ] && [ -n "$runs" ]; then
settled=$((settled + 1))
[ "$settled" -ge 2 ] && break
else
settled=0
fi
sleep 20
done

echo "── Check runs for $SHA ─────────────────────────────"
printf '%s\n' "$runs"
echo "────────────────────────────────────────────────────"

if [ -z "$runs" ]; then
echo "::error::No sibling check runs found for $SHA — refusing to report success."
exit 1
fi

still_running=$(printf '%s\n' "$runs" | grep -v '^completed' || true)
if [ -n "$still_running" ]; then
echo "::error::Timed out waiting for these checks to finish:"
printf '%s\n' "$still_running"
exit 1
fi

# `neutral` and `skipped` are accepted; everything else is a failure.
bad=$(printf '%s\n' "$runs" \
| awk -F'\t' '$2 != "success" && $2 != "neutral" && $2 != "skipped"' || true)

if [ -n "$bad" ]; then
echo "::error::The following checks did not pass:"
printf '%s\n' "$bad"
exit 1
fi

echo "All $(printf '%s\n' "$runs" | wc -l | tr -d ' ') sibling checks passed."
12 changes: 8 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,12 @@ jobs:
e2e-tests:
name: E2E Tests
runs-on: ubuntu-latest
continue-on-error: true # TODO(P2-E5): wire CI services — app needs NEXTAUTH_SECRET + DB to boot
# This job is BLOCKING. It previously carried continue-on-error, so it failed
# on every run while "Islam.wiki CI" still reported success — the failure was
# invisible in `gh run list`. The old comment blamed NEXTAUTH_SECRET + a DB,
# but this app is Astro, not Next.js, and needs neither: Playwright's
# webServer pointed at `pnpm start` (= `astro preview`), which the
# @astrojs/vercel adapter does not implement, so the server never bound.
strategy:
matrix:
node: ['22']
Expand All @@ -69,9 +74,8 @@ jobs:
cache-dependency-path: 'web/pnpm-lock.yaml'
- run: pnpm install --frozen-lockfile
- run: pnpm exec playwright install --with-deps chromium
- run: pnpm build
env:
NEXT_PUBLIC_SENTRY_DSN: ''
# No `pnpm build` step: Playwright's webServer runs `astro dev`, which does
# not read build output. Build validity is covered by the `build` job.
- run: pnpm test:e2e --project=chromium
env:
CI: true
Expand Down
31 changes: 19 additions & 12 deletions .github/workflows/quality-gates.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,12 @@ concurrency:
jobs:
lighthouse:
name: Lighthouse CI
# TODO(P2-E5): wire CI services — lhci needs a running Next.js server + Chromium sandbox.
# CHROME_INTERSTITIAL_ERROR fires because the server is not ready before lhci connects.
# Port corrected to 3041 (matches pnpm start in package.json).
# Re-enable as hard failure (remove continue-on-error) once CI services are wired.
continue-on-error: true
# This job is BLOCKING. It previously carried continue-on-error, which made the
# whole workflow report success while the job failed on every single run —
# the failure was invisible in `gh run list`. The underlying cause was not
# "CI services need wiring": `pnpm start` is `astro preview`, which the
# @astrojs/vercel adapter does not implement, so no server ever bound.
# lighthouserc.cjs now uses staticDistDir instead of starting a server.
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
Expand Down Expand Up @@ -53,10 +54,12 @@ jobs:

axe:
name: axe WCAG 2.1 AA
# TODO(P2-E5): wire CI services — axe wait-on times out because pnpm start requires env vars
# (NEXTAUTH_SECRET etc.) that are not available without CI services.
# Re-enable as hard failure (remove continue-on-error) once CI services are wired.
continue-on-error: true
# This job is BLOCKING. It previously carried continue-on-error and failed on
# every run while the workflow still reported success. The old comment blamed
# missing NEXTAUTH_SECRET, but this app is Astro, not Next.js — wait-on timed
# out because `pnpm start` (= `astro preview`) is unsupported by the
# @astrojs/vercel adapter and exited instantly, on a port (3041) that belonged
# to the abandoned web/astro/ scaffold rather than to this app.
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
Expand All @@ -76,11 +79,15 @@ jobs:
- name: Build
working-directory: web
run: pnpm build
# `astro dev`, NOT `astro preview` — the Vercel adapter has no preview
# command, so `pnpm start` exits immediately and wait-on always times out.
# The dev server serves SSR routes under any adapter. Port 3040 matches
# `pnpm dev` in web/package.json.
- name: Start dev server
working-directory: web
run: pnpm start &
run: pnpm dev &
- name: Wait for server
run: npx wait-on http://localhost:3041 -t 60000
run: npx wait-on http://localhost:3040 -t 120000
# @axe-core/cli bundles a `chromedriver` pinned to the latest Chrome stable,
# which drifts out of sync with the runner's installed Chrome. When they
# disagree the session comes up broken and axe fails with
Expand All @@ -101,7 +108,7 @@ jobs:
working-directory: web
run: |
axe \
http://localhost:3041 \
http://localhost:3040 \
--exit \
--tags wcag2a,wcag2aa,wcag21a,wcag21aa \
--chromedriver-path /usr/local/bin/chromedriver \
Expand Down
Loading
Loading