[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-81470":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":9,"htmlUrl":10,"language":11,"languages":10,"totalLinesOfCode":10,"stars":12,"forks":13,"watchers":14,"openIssues":13,"contributorsCount":13,"subscribersCount":13,"size":13,"stars1d":13,"stars7d":14,"stars30d":15,"stars90d":13,"forks30d":13,"starsTrendScore":13,"compositeScore":16,"rankGlobal":10,"rankLanguage":10,"license":17,"archived":18,"fork":18,"defaultBranch":19,"hasWiki":18,"hasPages":18,"topics":20,"createdAt":10,"pushedAt":10,"updatedAt":22,"readmeContent":23,"aiSummary":24,"trendingCount":13,"starSnapshotCount":13,"syncStatus":15,"lastSyncTime":25,"discoverSource":26},81470,"automerge-gate","pkgdeps\u002Fautomerge-gate","pkgdeps","A single required status check that gates Enable Auto Merge on every CI run that lands on a PR. Merge GateKeeper.","",null,"TypeScript",33,0,1,2,40.7,"MIT License",false,"main",[21],"github-actions","2026-06-12 04:01:33","# automerge-gate\n\nA single required check that gates **Enable Auto Merge** on every CI run that lands on a PR.\n\n## Why\n\nGitHub's branch protection \u002F rulesets ask you to list each required status check by name. That list is fragile:\n\n- Renovate \u002F Dependabot bring in checks from external GitHub Apps that come and go.\n- Monorepos use path filters, so a workflow may be skipped on some PRs and present on others.\n- Adding a new workflow file means rewriting the ruleset.\n\nautomerge-gate replaces that list with **one aggregated check**. You register only that single check (`automerge-gate\u002Fall-passed`) as the required check in your ruleset. When a maintainer clicks Enable Auto Merge, the action waits for every check on the PR — across workflow files, across GitHub Apps — then reports the verdict (in private mode, as a commit status on the head SHA; in public mode, via the gate job's exit code). GitHub's native auto-merge takes the PR from there.\n\n- Related: [Is it possible to require all GitHub Actions tasks to pass without enumerating them? · community · Discussion #26733](https:\u002F\u002Fgithub.com\u002Forgs\u002Fcommunity\u002Fdiscussions\u002F26733)\n- Migrating from merge-gatekeeper? See the [migration guide](docs\u002Fmigration-from-merge-gatekeeper.md).\n\n## How it works\n\nautomerge-gate ships in two modes, with different gate-signal mechanics. Pick the one that matches your repository's fork-PR posture (see [Usage](#usage) for the trade-offs).\n\n### Private mode\n\nCost-optimized: PRs without merge intent skip polling so runner minutes are saved. The action writes the aggregated verdict as a commit status via the legacy Commit Status API.\n\n```mermaid\nsequenceDiagram\n    participant U as Maintainer\n    participant A as automerge-gate (action)\n    participant PR as Pull Request\n\n    U->>PR: open \u002F push\n    Note over A: action skips (no merge intent)\n    Note over PR: required check at \"Expected — Waiting for status to be reported\" → merge blocked\n\n    U->>PR: Enable Auto Merge or Approve\n    A->>A: poll every other check on the PR\n\n    alt all checks pass\n        A->>PR: POST commit status → success\n        PR->>PR: GitHub auto-merge → merged\n    else any check fails\n        A->>PR: POST commit status → failure\n        Note over PR: merge blocked\n    end\n```\n\n1. A PR is opened. There's no merge intent yet, so the action skips without writing a status. The required check stays at GitHub's default `Expected — Waiting for status to be reported`, which keeps the PR blocked.\n2. The maintainer clicks **Enable Auto Merge**, _or_ a reviewer with write access submits an **Approve** review. The action enters polling mode and watches every other check on the PR.\n3. The action polls every other check on the PR, applying any `ignore-checks` filters.\n4. After polling, the action writes the aggregated verdict (`state: success` or `failure`) as a commit status on the head SHA, keyed by the configured `context`. GitHub's required-check evaluation looks up the same `(SHA, context)` pair, so the verdict turns the required check green or red immediately.\n5. GitHub's native auto-merge fires when the required check turns green.\n\nIf Auto Merge is already enabled when you push a new commit, the gate re-evaluates the new SHA automatically — no need to disable→enable. Commit status is keyed by `(SHA, context)`, so there's no per-SHA cleanup: each push targets a fresh SHA whose status starts blank until the gate posts a verdict.\n\nIf your team uses an Approve review to mean \"looks good\" rather than \"ready to merge\", remove `pull_request_review` from the workflow's `on:` triggers. The gate then only enters polling mode when Auto Merge is explicitly enabled.\n\n### Public mode\n\nFork-aware: `GITHUB_TOKEN` is read-only on fork PRs, so the gate signal is the gate **job's** own auto-created check_run conclusion. The job runs on every triggering event and always polls; there is no skip path.\n\n```mermaid\nsequenceDiagram\n    participant PR as Pull Request\n    participant J as gate job\n    participant A as automerge-gate (action)\n\n    PR->>J: workflow triggered (always)\n    Note over J: job's check_run = required-check context (job name matches)\n    J->>A: action starts → polls every other check on the PR\n\n    alt all checks pass\n        A->>J: exit 0\n        J->>PR: job's check_run → success\n        PR->>PR: GitHub auto-merge → merged\n    else any check fails\n        A->>J: exit non-zero\n        J->>PR: job's check_run → failure\n        Note over PR: merge blocked\n    end\n```\n\n1. The PR is opened (or pushed to). The workflow always triggers. GitHub Actions auto-creates a check_run named after the gate job (e.g. `automerge-gate\u002Fall-passed`) — that check_run is the required-check signal.\n2. The action polls every other check on the PR, applying any `ignore-checks` filters.\n3. After polling, the action exits 0 (success) or non-zero (failure). The job's check_run conclusion follows the exit code, and GitHub treats it as the required check's verdict.\n4. GitHub's native auto-merge fires when the required check turns green.\n\nThe action does not write its own check_run in this mode (the JOB's auto-created one is the gate). Read-only `checks: read` permission is sufficient; add `actions: read` if `ignore-checks` uses a `workflow` rule (the action resolves run-to-workflow paths via the Actions API).\n\nNote: GitHub rulesets only support AND across required checks (no OR \u002F conditional logic), so this action is the place where \"all of these checks across workflows must pass\" is expressed as a single check.\n\n## Usage\n\nSetup is five steps: pick a mode, add the workflow file, register the required check in the ruleset, allow auto-merge in repository settings, then click Enable Auto Merge on a PR. Run them in order — Step 5 won't show the button until Step 4 is done, and the required check from Step 3 only gates merges once a workflow run reports it.\n\n### Step 1: Pick a mode\n\nChoose based on whether your repository accepts external fork PRs.\n\n- Private mode (cost-optimized) — internal-only repos that do not receive external fork PRs. The action writes the aggregated verdict as a commit status via the legacy Commit Status API. PRs without merge intent skip polling entirely so runner minutes are saved.\n- Public mode (fork-aware) — repos that accept fork PRs. `GITHUB_TOKEN` is read-only on fork PRs, so the gate signal is the gate job's own check_run conclusion. The job runs on every triggering event and always polls.\n\n|                               | private                            | public                 |\n| ----------------------------- | ---------------------------------- | ---------------------- |\n| `pull_request_review` trigger | yes                                | no                     |\n| Job `name:`                   | (default)                          | matches required check |\n| Permissions                   | `statuses: write` + `checks: read` (+ `actions: read` for `workflow` rules) | `checks: read` (+ `actions: read` for `workflow` rules) |\n| API write of aggregate        | yes (commit status)                | no (job exit code)     |\n| Skip on no merge intent       | yes (saves runner minutes)         | no (always polls)      |\n\n### Step 2: Add the workflow file\n\nCreate `.github\u002Fworkflows\u002Fautomerge-gate.yaml` using the YAML for the mode picked in Step 1.\n\n#### Private mode\n\n```yaml\nname: automerge-gate\n\non:\n  pull_request:\n    types: [opened, synchronize, reopened, auto_merge_enabled]\n  pull_request_review:\n    types: [submitted]\n\nconcurrency:\n  group: ${{ github.workflow }}-${{ github.ref }}\n  cancel-in-progress: true\n\njobs:\n  gate:\n    if: >-\n      github.event_name != 'pull_request_review' ||\n      github.event.review.state == 'approved'\n    runs-on: ubuntu-latest\n    timeout-minutes: 10\n    permissions:\n      statuses: write\n      checks: read\n      pull-requests: read\n      actions: read\n    steps:\n      - uses: pkgdeps\u002Fautomerge-gate@v4.1.0\n        with:\n          gate-mode: 'private'\n          context: 'automerge-gate\u002Fall-passed'\n```\n\nThe `pull_request_review.state == 'approved'` clause filters out non-Approve review submissions (`commented`, `changes_requested`) at the job level, so the runner doesn't even spin up for those. GitHub's `on:` block can filter activity types but not review state, so the filter has to live in the job's `if:`.\n\nWhen a `synchronize`, `opened`, or `reopened` event fires without an active merge intent (no Auto Merge enabled, no sticky write-permission Approve), the action exits cleanly without writing a status. The required check stays at GitHub's `Expected` state and merge stays blocked, but no polling burns runner minutes.\n\n#### Public mode\n\n```yaml\nname: automerge-gate\n\non:\n  pull_request:\n    types: [opened, synchronize, reopened, auto_merge_enabled]\n\nconcurrency:\n  group: ${{ github.workflow }}-${{ github.ref }}\n  cancel-in-progress: true\n\njobs:\n  gate:\n    name: automerge-gate\u002Fall-passed # must match the required check in your ruleset\n    runs-on: ubuntu-latest\n    timeout-minutes: 10\n    permissions:\n      checks: read\n      pull-requests: read\n      actions: read\n    steps:\n      - uses: pkgdeps\u002Fautomerge-gate@v4.1.0\n        with:\n          gate-mode: 'public'\n```\n\nIn public mode, the job's `name:` _is_ the required-check context — GitHub Actions creates a check_run with that name when the job starts, and its conclusion is what the required check evaluates. The action polls every triggering event (no skip path) because the read-only token can't write a \"waiting\" signal anyway, and a skipped job would let an unfinished PR slip through merge.\n\n### Step 3: Register the required check\n\nOpen Settings → Rules → Rulesets (or Branches → Branch protection) and add a rule that requires the check `automerge-gate\u002Fall-passed`.\n\n> [!WARNING]\n> The autocomplete only lists checks that have already run on this repo, so the dropdown is empty on first setup. Type `automerge-gate\u002Fall-passed` by hand — both rulesets and branch protection accept any name. Once the gate runs on a PR, it shows up in the dropdown.\n\nThis single required check is now the only thing standing between a PR and merge. Any check that lands on the PR — Renovate, Codecov, your own workflows — gets aggregated into it.\n\n### Step 4: Allow auto-merge\n\nOpen Settings → General → Pull Requests and tick **Allow auto-merge**. Without this the _Enable Auto Merge_ button doesn't show up on PRs, so Step 5 has nothing to click.\n\n### Step 5: Enable Auto Merge on a PR\n\n1. Get the PR ready (review, fix, etc.).\n2. Click **Enable Auto Merge**.\n3. The gate job runs, polls every check on the PR, then exits with `success` (or fails the job on aggregated failure).\n4. On success, GitHub's native auto-merge fires immediately and merges the PR. On failure, auto-merge is blocked; fix and push again — as long as Auto Merge stays enabled, the gate re-evaluates the new SHA on every push.\n\n> [!IMPORTANT]\n> The action does **not** expose a timeout input. The job-level `timeout-minutes` is the only bound on how long the polling loop runs, and you should treat it as part of the action's configuration. There are no two timeouts to keep in sync — just one. If your CI runs longer than 10 minutes, raise `timeout-minutes` accordingly.\n\n## Inputs\n\n| name                    | required | default                     | description                                                                                                                                                                                                                                                                                                          |\n| ----------------------- | -------- | --------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `gate-mode`             | **yes**  | (none)                      | `private` \u002F `public`. `private` = action writes the aggregated commit status via the legacy Commit Status API (token needs `statuses: write` + `checks: read`). `public` = gate signal is the JOB's own check_run conclusion; the job's `name:` must match the required-check context (token can be `checks: read`). Either mode additionally needs `actions: read` when `ignore-checks` contains a `workflow` rule. |\n| `context`               | no       | `automerge-gate\u002Fall-passed` | Aggregated commit status context. **`gate-mode: private` only** — must match the required check in your ruleset. Ignored when `gate-mode: public` (the job name is the signal).                                                                                                                                      |\n| `poll-interval-seconds` | no       | `30`                        | How often to re-fetch check status                                                                                                                                                                                                                                                                                   |\n| `ignore-checks`         | no       | `[]`                        | JSONC array of rules to exclude check_runs from aggregation. Each rule is `{ app?, workflow?, name? }`; fields are AND-evaluated and every field is a glob (`*` \u002F `?`). See [Examples](#examples).                                                                                                                   |\n| `token`                 | no       | `${{ github.token }}`       | GitHub token used to read checks and (when permitted) write the aggregated commit status                                                                                                                                                                                                                             |\n\nThere is **no `timeout-seconds` input on purpose** — timeout is delegated entirely to the job's `timeout-minutes` so there's a single source of truth. See the IMPORTANT note in the Usage section above.\n\n### Examples\n\n#### `ignore-checks`\n\n`ignore-checks` is a JSONC array of rules. Each rule is an object with optional fields:\n\n| field      | matches against                             | notes                                                                                                                                                                                       |\n| ---------- | ------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `app`      | The originating GitHub App's slug           | Internally the action reads the slug from the check_run's parent check_suite. See [Discovering what to ignore](#discovering-what-to-ignore) below for the inspection command.               |\n| `workflow` | Basename of the workflow file               | GitHub Actions only; third-party Checks (no workflow file) never match a rule with `workflow` set. **Requires the workflow's token to have `actions: read` permission** — the action resolves each run's workflow path via the Actions API, and without that scope the lookup returns `null` so the rule never matches. |\n| `name`     | `check_run.name`                            | This is `jobs.\u003Ckey>.name` (or `jobs.\u003Ckey>` if `name:` is omitted), **not** the `\u003Cworkflow> \u002F \u003Cjob>` string the UI shows.                                                                    |\n\nWithin a single rule, all present fields must match (AND); absent fields are wildcards. Every field is a glob — `*` matches any run of characters, `?` matches one character. Across rules, the action excludes a check_run if **any** rule matches.\n\nJSONC means standard JSON plus `\u002F\u002F` \u002F `\u002F* *\u002F` comments and trailing commas — so this is legal:\n\n```yaml\nwith:\n  ignore-checks: |\n    [\n      { \"app\": \"dependabot\" },                                  \u002F\u002F ignore everything from dependabot\n      { \"app\": \"renovate\" },\n      { \"name\": \"optional-*\" },                                 \u002F\u002F glob across all workflows \u002F apps\n      { \"app\": \"xcode-cloud\", \"name\": \"Build *\" },              \u002F\u002F only xcode-cloud's build jobs\n      { \"workflow\": \"ci-go.yaml\", \"name\": \"lint\" },             \u002F\u002F only ci-go.yaml's lint job (GitHub Actions)\n    ]\n```\n\n#### Discovering what to ignore\n\nThe schema is shaped to mirror `gh api ... | jq` output, so you can inspect a PR's checks and paste the rows you want to ignore directly into `ignore-checks`.\n\n```bash\ngh api --paginate --slurp \"repos\u002F{owner}\u002F{repo}\u002Fcommits\u002F{sha}\u002Fcheck-runs\" \\\n  | jq '[.[].check_runs[] | { app: .app.slug, name } | with_entries(select(.value != null))]'\n```\n\n`--slurp` cannot be combined with `gh`'s built-in `--jq`, so the pipe to an external `jq` is intentional. `--slurp` collects all paginated pages into one array; the `jq` expression then flattens `.check_runs[]` across pages so the result is a single rule list rather than one array per page.\n\nThis emits one `{ app, name }` row per check_run. Pick the rows you want to silence and paste them into `ignore-checks` as-is — each row is already a valid rule:\n\n```yaml\nwith:\n  ignore-checks: |\n    [\n      { \"app\": \"github-actions\", \"name\": \"optional-flaky\" },\n      { \"app\": \"xcode-cloud\", \"name\": \"Build (release)\" }\n    ]\n```\n\nWhen the same `name` repeats across rows, those are separate check_runs from different workflows whose job names happen to collide — a common monorepo pattern:\n\n```json\n[\n  { \"app\": \"github-actions\", \"name\": \"check\" },\n  { \"app\": \"github-actions\", \"name\": \"check\" },\n  { \"app\": \"github-actions\", \"name\": \"gate\" },\n  { \"app\": \"github-actions\", \"name\": \"check\" }\n]\n```\n\nTo ignore one of them but not the others, add a `workflow` field to disambiguate. The `workflow` field is not in the inspection command above, but you can join in the workflow path via `check_suite_id` with a second `gh api` call against `\u002Factions\u002Fruns?head_sha={sha}`:\n\n```bash\nSHA=...\nruns=$(gh api --paginate --slurp \"repos\u002F{owner}\u002F{repo}\u002Factions\u002Fruns?head_sha=$SHA\" \\\n  | jq 'map(.workflow_runs[]) | map({ (.check_suite_id|tostring): (.path | split(\"\u002F\") | last) }) | add')\n\ngh api --paginate --slurp \"repos\u002F{owner}\u002F{repo}\u002Fcommits\u002F$SHA\u002Fcheck-runs\" \\\n  | jq --argjson runs \"$runs\" '[\n      .[].check_runs[]\n      | { app: .app.slug, workflow: $runs[.check_suite.id|tostring], name }\n      | with_entries(select(.value != null))\n    ]'\n```\n\n`workflow` is `null` (and therefore dropped from the row) for third-party Checks that don't originate from a GitHub Actions workflow. The second call requires the same `actions: read` token scope that the action itself uses for `workflow` rules. Paste the disambiguating row into `ignore-checks`:\n\n```yaml\nwith:\n  ignore-checks: |\n    [\n      { \"app\": \"github-actions\", \"workflow\": \"ci-go.yaml\", \"name\": \"check\" }\n    ]\n```\n\nThe command covers **check_runs only** — the data source `ignore-checks` filters against. It does not include legacy commit statuses (`\u002Fcommits\u002F{sha}\u002Fstatus`) or PR reviews (`\u002Fpulls\u002F{n}\u002Freviews`). automerge-gate likewise reads only check_runs (plus, in `gate-mode: private`, PR reviews for the approval signal); legacy commit statuses are not evaluated. Signals such as Copilot Code Review surface as PR reviews and never appear in `\u002Fcheck-runs`.\n\n#### Tune polling interval for fast CI\n\n```yaml\n- uses: pkgdeps\u002Fautomerge-gate@v4.1.0\n  with:\n    gate-mode: 'private'\n    poll-interval-seconds: '10'\n```\n\n## Outputs\n\n| name                | description                                    |\n| ------------------- | ---------------------------------------------- |\n| `state`             | `success` \u002F `failure` \u002F `skipped`              |\n| `total-checks`      | Number of check_runs observed before filtering |\n| `evaluated-checks`  | Number of check_runs after filters             |\n| `completed-checks`  | Number of completed check_runs after filters   |\n| `polled-iterations` | Number of polling iterations performed         |\n\n## Limitations\n\n- **Merge queue (`merge_group`)** is not supported.\n- **Dead runner \u002F job timeout**: the polling job can be killed before it finishes (its `timeout-minutes` fires, the runner dies, and so on). The gate job's check_run then ends as `failure` or `cancelled`. The required check stays red and merge stays blocked. To retry, disable Auto Merge and enable it again.\n- **CIs that only write legacy commit statuses**: GitHub has two CI reporting APIs. Modern CIs (GitHub Actions, Cloudflare Pages, Codecov) use the check_run \u002F check_suite API. Some older or self-hosted CIs (Atlantis, some Jenkins setups) only use the legacy commit-status API. This action only reads the check_run \u002F check_suite side, so a CI that only writes legacy commit statuses is not aggregated. If you depend on such a CI, add it as a separate required check in your ruleset alongside `automerge-gate\u002Fall-passed`.\n\n## Versioning\n\nReleases are published as **immutable semver tags** (`v1.0.0`, `v1.1.0`, ...). There is intentionally no moving major tag (`v4`) — pin a fixed version in your workflow and let Renovate \u002F Dependabot open PRs when a new version ships. This eliminates the supply-chain risk of a moving tag being silently rewritten.\n\n## License\n\nMIT\n","automerge-gate 是一个用于在每次CI运行时对拉取请求启用自动合并的单一必需状态检查工具。其核心功能是通过一个聚合检查（`automerge-gate\u002Fall-passed`）替代传统的逐个列出所需状态检查的方法，从而简化了分支保护规则的配置过程。该工具采用TypeScript编写，并以GitHub Action的形式提供服务，支持私有和公开两种模式，其中私有模式更注重成本优化。适用于需要频繁更新CI流程或使用外部GitHub应用（如Renovate、Dependabot）的项目，尤其是大型单体仓库中，能够有效避免因新增工作流文件而必须重写规则集的问题。","2026-06-11 04:05:11","CREATED_QUERY"]