Some checks failed
Check merge_group trigger on required workflows / Required workflows have merge_group trigger (pull_request) Successful in 9s
CodeQL / Analyze (${{ matrix.language }}) (go) (pull_request) Successful in 8s
CodeQL / Analyze (${{ matrix.language }}) (javascript-typescript) (pull_request) Successful in 10s
Block internal-flavored paths / Block forbidden paths (pull_request) Successful in 19s
CodeQL / Analyze (${{ matrix.language }}) (python) (pull_request) Successful in 10s
E2E API Smoke Test / detect-changes (pull_request) Successful in 21s
CI / Detect changes (pull_request) Successful in 31s
E2E Staging Canvas (Playwright) / detect-changes (pull_request) Successful in 20s
Retarget main PRs to staging / Retarget to staging (pull_request) Has been skipped
Lint curl status-code capture / Scan workflows for curl status-capture pollution (pull_request) Successful in 16s
Handlers Postgres Integration / detect-changes (pull_request) Successful in 19s
Harness Replays / detect-changes (pull_request) Successful in 21s
Runtime PR-Built Compatibility / detect-changes (pull_request) Successful in 23s
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 24s
E2E Staging Canvas (Playwright) / Canvas tabs E2E (pull_request) Successful in 26s
E2E API Smoke Test / E2E API Smoke Test (pull_request) Successful in 30s
Handlers Postgres Integration / Handlers Postgres Integration (pull_request) Successful in 24s
CI / Shellcheck (E2E scripts) (pull_request) Successful in 37s
Harness Replays / Harness Replays (pull_request) Successful in 23s
Runtime PR-Built Compatibility / PR-built wheel + import smoke (pull_request) Successful in 15s
CI / Python Lint & Test (pull_request) Successful in 8m27s
CI / Canvas (Next.js) (pull_request) Failing after 9m59s
CI / Canvas Deploy Reminder (pull_request) Has been skipped
CI / Platform (Go) (pull_request) Successful in 12m57s
Part of the post-#66 sweep to remove `gh` CLI dependencies that fail
silently against Gitea (which exposes /api/v1 only — no GraphQL → 405,
no /api/v3 → 404). Class D covers `gh api` REST passthroughs that
either have a Gitea v1 equivalent at a different path/shape or no
equivalent at all.
Three files in this class, each with a different fix shape because
each underlying Gitea capability is different:
`auto-promote-on-e2e.yml` (compute SHA ancestry):
Old: `gh api repos/.../compare/A...B` returning `.status`
(ahead|behind|identical|diverged).
Gitea: `/api/v1/repos/.../compare/A...B` accepts only branch / tag
refs — full commit SHAs return `BaseNotExist`. So even a
"translate the URL" rewrite would fail. Verified empirically
2026-05-07: branches/tags work, SHAs don't.
Fix: Add `actions/checkout@v6 fetch-depth=200` + use `git merge-
base --is-ancestor` locally. Exact same four-bucket semantics
(ahead | behind | diverged | error), zero cross-host API
dependency. Same pattern PR #66 used for auto-sync. The 200-
commit depth comfortably covers any realistic divergence
between :latest and a candidate retag (promotes are minutes
apart, not hundreds of commits).
`ci.yml` (canvas-deploy-reminder commit comment):
Old: `gh api -X POST repos/.../commits/{sha}/comments` posting a
deploy-reminder body for the operator.
Gitea: NO commit-comments endpoint exists — `/repos/.../commits/
{sha}/comments` returns 404 (verified 2026-05-07). Gitea only
exposes `/commits/{sha}/statuses` for commit-level surface,
which is the wrong shape for a free-form reminder.
Fix: Drop the API call. Write the reminder body to
`$GITHUB_STEP_SUMMARY` instead. The reminder is entirely
operator-facing and is just as discoverable on the run summary
page (which an operator naturally lands on when they need to
action a deploy). Commit comments were a stale UI artefact of
the GitHub era, not a load-bearing automation surface.
Permission: drop `contents: write` (no longer needed) → `read`,
smallest scope per least-privilege.
`check-merge-group-trigger.yml` (merge_group: trigger linter):
Old: `gh api .../branches/staging/protection/required_status_checks`
reading the contexts list, then walking workflow files.
Gitea: branch-protection API is at /api/v1/repos/.../branch_
protections/{name} (different path) with `status_check_
contexts` (different field name) — but the entire workflow
only existed to lint that workflows producing a required
check declare a `merge_group:` trigger, which is needed
because GitHub's merge queue dead-locks at AWAITING_CHECKS
when the trigger is missing. Gitea has NO merge queue, NO
gh-readonly-queue/... ref shape, NO merge_group event
semantics. The dead-lock pattern this linter catches cannot
occur on Gitea by construction.
Fix: Convert to no-op stub (same pattern as the CodeQL stub
landed in PR #51). Workflow name + trigger surface preserved
so any external referrer (none confirmed via the 2026-05-07
branch-protection audit) keeps resolving. Re-enable path
documented in the file header for if/when Gitea grows a
merge queue.
curl invocation pattern: `curl --fail-with-body -sS` (NOT `-fsS` —
the two short-fail flags are mutually exclusive in modern curl).
Token model: workflows continue to use act_runner's GITHUB_TOKEN
where they still need API access (`auto-promote-on-e2e.yml`'s
checkout uses the runner's default token; `ci.yml` no longer needs
any API auth for the deploy-reminder step; `check-merge-group-
trigger.yml` no longer makes any API calls).
Verification:
- YAML syntax validates for all three files.
- Live curl against Gitea confirms `/compare/A...B` accepts branch
refs (200, total_commits=N) and refuses full SHAs (404,
BaseNotExist) — justifying the local-git approach.
- `/repos/.../commits/{sha}/comments` confirmed 404 on Gitea.
- `git merge-base --is-ancestor` exit-code semantics match the
GitHub compare API status semantics exactly: ahead = current is
ancestor of target; behind = target is ancestor of current;
diverged = neither.
Closes part of #75. Class A landed in #80; class F (gh run list →
no Gitea workflow-runs API at all) lands in a separate PR.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
107 lines
4.5 KiB
YAML
107 lines
4.5 KiB
YAML
name: Check merge_group trigger on required workflows
|
|
|
|
# Stub workflow — Gitea Actions has no merge queue (post-2026-05-06
|
|
# SCM migration off GitHub).
|
|
#
|
|
# Why this is a stub, not a real linter:
|
|
#
|
|
# 1. The original workflow existed exclusively to lint that any
|
|
# workflow producing a required status check on the staging branch
|
|
# also declares a `merge_group:` trigger. Without that trigger,
|
|
# GitHub's merge queue dead-locks at AWAITING_CHECKS forever
|
|
# because the required check can't fire on `gh-readonly-queue/...`
|
|
# refs (a GitHub-merge-queue-specific ref shape).
|
|
#
|
|
# 2. Gitea has no merge queue. There is no `gh-readonly-queue/...`
|
|
# ref shape on Gitea. There is no `merge_group` event type in the
|
|
# Gitea Actions trigger surface. The deadlock pattern this linter
|
|
# catches CANNOT occur on the new SCM, by construction.
|
|
#
|
|
# 3. The original linter additionally called
|
|
# `gh api repos/.../branches/staging/protection/required_status_checks`,
|
|
# which on Gitea returns 404 — Gitea's branch-protection API lives
|
|
# at `/repos/{owner}/{repo}/branch_protections/{name}` (different
|
|
# path), and the response shape uses `status_check_contexts`
|
|
# instead of `contexts`. So the linter would have failed to fetch
|
|
# the required-check list even if Gitea had a merge queue.
|
|
#
|
|
# What this stub preserves:
|
|
#
|
|
# - Workflow name `Check merge_group trigger on required workflows`
|
|
# is unchanged so any external surface that referenced it (none
|
|
# confirmed via branch-protection audit 2026-05-07) keeps resolving.
|
|
# - Trigger surface (pull_request, push, merge_group). The
|
|
# merge_group: trigger is a no-op on Gitea (the event never fires)
|
|
# but kept declared so the workflow file itself stays a faithful
|
|
# subscriber to anything that DID fire it on the GitHub side, in
|
|
# case we ever migrate back.
|
|
#
|
|
# Re-enabling real linting (future work):
|
|
#
|
|
# - If/when Gitea grows a merge queue (none on the roadmap as of
|
|
# 2026-05-07), reinstate the linter using
|
|
# `/api/v1/repos/.../branch_protections/{branch}` to read the
|
|
# required-check list (.status_check_contexts), then walk
|
|
# workflow files for the appropriate Gitea-shape trigger.
|
|
# - Alternative invariant worth checking on Gitea today: that any
|
|
# workflow producing a status_check_context declared in branch
|
|
# protection has at least one non-skipped path through to a
|
|
# terminal step (the "required check name needs a job that always
|
|
# runs" invariant from saved memory
|
|
# feedback_branch_protection_check_name_parity). That linter
|
|
# exists separately as `branch-protection-drift.yml` so this stub
|
|
# doesn't need to also cover it.
|
|
#
|
|
# Until merge-queue semantics return to this fleet, the stub keeps
|
|
# the workflow surface visible in Gitea's Actions UI so the next
|
|
# operator notices it's a stub instead of a missing surface, and
|
|
# emits success so the surrounding chain isn't artificially red.
|
|
#
|
|
# Issue tracking: post-#66 sweep tracked in #75 (class D — `gh api`
|
|
# REST passthroughs that have no Gitea v1 equivalent).
|
|
|
|
on:
|
|
pull_request:
|
|
paths:
|
|
- '.github/workflows/**.yml'
|
|
- '.github/workflows/**.yaml'
|
|
push:
|
|
branches: [staging, main]
|
|
paths:
|
|
- '.github/workflows/**.yml'
|
|
- '.github/workflows/**.yaml'
|
|
# Self-listen on merge_group is structurally a no-op on Gitea; kept
|
|
# declared for parity with the GitHub-era contract.
|
|
merge_group:
|
|
types: [checks_requested]
|
|
|
|
jobs:
|
|
check:
|
|
name: Required workflows have merge_group trigger
|
|
runs-on: ubuntu-latest
|
|
permissions:
|
|
contents: read
|
|
steps:
|
|
- name: Stub — Gitea has no merge queue
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
cat <<'EOF'
|
|
merge_group-trigger linter is a no-op on Gitea Actions.
|
|
|
|
Reason: Gitea has no merge queue. The dead-lock pattern this
|
|
linter catches (required check missing merge_group: trigger
|
|
→ AWAITING_CHECKS forever on gh-readonly-queue refs) cannot
|
|
occur on this SCM by construction.
|
|
|
|
Adjacent live linters that DO catch real Gitea-shape failure
|
|
modes:
|
|
- branch-protection-drift.yml — guards the protection rules
|
|
- check-name parity gate (PR #56, issue #144) — guards
|
|
that path-filtered workflows still emit required checks
|
|
|
|
Issue: #75 (post-#66 gh-CLI sweep, class D — gh api REST
|
|
calls that have no Gitea v1 equivalent).
|
|
EOF
|
|
echo "::notice::merge_group-trigger linter is a stub on Gitea — see workflow file header for context."
|