Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
50a9c02
ci: add shadow merge queue workflow with configurable settings
igorjs Nov 3, 2025
1613654
chore(workflow): add GITHUB_TOKEN to merge-queue workflow environment
igorjs Nov 3, 2025
dd84a04
fix(workflow): add issues:write permission for dashboard
igorjs Nov 3, 2025
20fdb90
feat: add automatic label and dashboard issue creation
igorjs Nov 3, 2025
7d04f9e
feat: implement dashboard issue pinning and locking
igorjs Nov 3, 2025
79c011e
refactor: rename dashboard label to mq/dashboard for consistency
igorjs Nov 3, 2025
e4ded66
fix: remove duplicate dashboard label creation logic
igorjs Nov 3, 2025
426dc06
feat: create GitHub CLI abstraction layer
igorjs Nov 3, 2025
048e744
refactor: migrate label operations to gh CLI
igorjs Nov 3, 2025
d6fa377
refactor: migrate dashboard/issue operations to gh CLI
igorjs Nov 3, 2025
cd3cef5
refactor: migrate branch operations to gh CLI
igorjs Nov 3, 2025
f15113d
refactor: migrate queue/file content operations to gh CLI
igorjs Nov 3, 2025
0c49514
refactor: complete migration to GitHub CLI
igorjs Nov 4, 2025
39a30d6
build: compile action with gh CLI refactoring
igorjs Nov 4, 2025
5128d58
docs: clean up AI-generated citation artifacts from markdown files
igorjs Nov 4, 2025
d9ff99d
ci(action): update workflow reference and simplify GitHub CLI operations
igorjs Nov 4, 2025
345dd8e
Potential fix for code scanning alert no. 4: Useless regular-expressi…
igorjs Nov 4, 2025
3d50fb5
chore(ci): update dist folder
igorjs Nov 4, 2025
0272395
chore(ci): update dist folder commit configuration
igorjs Nov 4, 2025
cb2f35c
refactor(github-cli): simplify error handling and improve PR data fet…
igorjs Nov 4, 2025
26bd114
docs: enhance README with detailed algorithm, architecture, and FAQ d…
igorjs Nov 4, 2025
3152ce1
feat(workflow): update merge-queue action to live mode with reduced c…
igorjs Nov 4, 2025
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
4 changes: 2 additions & 2 deletions .github/workflows/check-dist.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,10 @@ jobs:
- name: Commit files # commit the new dist folder
if: ${{ steps.diff.outcome == 'failure' }}
run: |
git config --local user.email "igorjs@users.noreply.github.com"
git config --local user.email "2278164+igorjs@users.noreply.github.com
git config --local user.name "GitHub Action CI"
git add ./dist
git commit -m "chore(ci): update dist folder"
git commit --signoff -m "chore(ci): update dist folder"
git push
env:
GITHUB_TOKEN: ${{ github.token }}
41 changes: 41 additions & 0 deletions .github/workflows/merge-queue.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
name: Merge Queue (shadow)
on:
pull_request:
types:
[
opened,
reopened,
ready_for_review,
synchronize,
converted_to_draft,
review_requested,
]
schedule:
- cron: "*/5 * * * *"
workflow_dispatch: {}

permissions:
contents: write
pull-requests: write
statuses: write
issues: write

concurrency:
group: merge-queue
cancel-in-progress: true

jobs:
queue:
runs-on: ubuntu-latest
steps:
- uses: igorjs/gh-actions-merge-queue@main
with:
base_branch: main
mode: live
merge_method: merge
status_context: merge-queue
behind_max_commits: "5"
fastlane_matchers: "^(hotfix|critical|security)/,\\bhotfix\\b,^hotfix:"
dashboard_label: "mq/dashboard"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
42 changes: 22 additions & 20 deletions CODE_OF_CONDUCT.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,33 +2,35 @@

## Our Pledge

We as members, contributors, and leaders pledge to make participation in our community a harassmentfree experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socioeconomic status, nationality, personal appearance, race, caste, color, religion, or sexual identity and orientation【426605577719396†L4-L13】. We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community.
We pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, caste, color, religion, or sexual identity and orientation.

## Our Standards

Examples of behaviour that contributes to a positive environment include demonstrating empathy and kindness toward other people, being respectful of differing opinions, viewpoints, and experiences, giving and gracefully accepting constructive feedback, apologising to those affected by mistakes and learning from the experience, and focusing on what is best for the overall community【771408749191128†L16-L25】.

Examples of unacceptable behaviour include the use of sexualised language or imagery, trolling, insulting or derogatory comments, personal or political attacks, public or private harassment, publishing others’ private information, and other conduct which could reasonably be considered inappropriate in a professional setting【771408749191128†L27-L37】.

## Enforcement Responsibilities

Community leaders are responsible for clarifying and enforcing our standards of acceptable behaviour. They have the right to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned with this Code of Conduct, and will communicate reasons for moderation decisions when appropriate【771408749191128†L39-L49】.

## Scope

This Code of Conduct applies within all community spaces and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official email address, posting via an official social media account, or acting as an appointed representative at an online or offline event【771408749191128†L51-L58】.
Examples of behavior that contributes to a positive environment:
- Demonstrating empathy and kindness
- Being respectful of differing opinions and experiences
- Giving and accepting constructive feedback gracefully
- Accepting responsibility and learning from mistakes
- Focusing on what is best for the community

Examples of unacceptable behavior:
- Use of sexualized language or imagery
- Trolling, insulting, or derogatory comments
- Personal or political attacks
- Public or private harassment
- Publishing others' private information without permission
- Other conduct inappropriate in a professional setting

## Enforcement

Instances of abusive, harassing, or otherwise unacceptable behaviour may be reported to the maintainers at **[INSERT CONTACT METHOD]**. All complaints will be reviewed and investigated promptly and fairly【771408749191128†L59-L66】. The maintainers are obligated to respect the privacy and security of the reporter of any incident.

Enforcement follows these community impact guidelines:
Instances of unacceptable behavior may be reported to the maintainers. All complaints will be reviewed and investigated promptly and fairly.

1. **Correction** – A private, written warning and request for apology for a first violation【771408749191128†L74-L82】.
2. **Warning** – A warning with consequences for continued behaviour and a specified period of no interaction with those enforcing the Code【771408749191128†L83-L93】.
3. **Temporary Ban** – A temporary ban from public interaction for serious violations【771408749191128†L95-L103】.
4. **Permanent Ban** – A permanent ban from the community for sustained or extreme violations【771408749191128†L106-L113】.
Community impact guidelines:
1. **Correction** – Private warning and request for apology
2. **Warning** – Warning with consequences for continued behavior
3. **Temporary Ban** – Temporary ban from community interaction
4. **Permanent Ban** – Permanent ban for sustained violations

## Attribution

This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.1【771408749191128†L116-L119】, available at https://www.contributor-covenant.org/version/2/1/code_of_conduct.html.
Adapted from the [Contributor Covenant](https://www.contributor-covenant.org), version 2.1.
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ Thank you for considering contributing to this project! The merge queue action i

## Code of Conduct

This project and all contributors are expected to abide by our Code of Conduct. It outlines behaviours that contribute to a positive environment and those that are unacceptable【426605577719396†L16-L39】. Instances of abusive or harassing behaviour may be reported to the maintainers.
This project and all contributors are expected to abide by our [Code of Conduct](CODE_OF_CONDUCT.md). Instances of abusive or harassing behavior may be reported to the maintainers.

## Support

Expand Down
211 changes: 198 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,41 @@ This repository provides a GitHub composite action that implements a **merge que
* **Shadow vs. live mode** – Shadow mode tests your configuration without merging; live mode performs actual merges.
* **No additional services required** – Uses the built‑in `GITHUB_TOKEN` only; no enterprise features or external CI are required.

## How It Works

The merge queue action maintains a FIFO queue of approved pull requests and processes them sequentially:

1. **Queue Discovery**: Scans all open PRs and adds approved, non-draft PRs to the queue
2. **Candidate Selection**: Picks the next PR from the queue (or a fastlane PR if available)
3. **Staging**: Creates a temporary branch merging the PR with the current base branch
4. **Testing**: CI runs tests on the staged merge (via required status checks)
5. **Merging**: If tests pass and base hasn't moved, merges the PR (in live mode)
6. **Cleanup**: Removes PR from queue and deletes staging branch

**Key Benefit**: Every PR is tested against the latest base branch before merging, preventing integration issues without requiring "update branch before merge" in GitHub settings.

See [docs/ALGORITHM.md](docs/ALGORITHM.md) for detailed algorithm documentation.

## Installation

Add the action to your workflow by referencing the tag in your repository. Make sure your repository’s **Settings → Branch protection** requires the custom status set in `status_context` (default `merge‑queue`) and disables **“Require branches to be up to date with base”**.
Add the action to your workflow by referencing the tag in your repository. Make sure your repository's **Settings → Branch protection** requires the custom status set in `status_context` (default `merge‑queue`) and disables **"Require branches to be up to date with base"**.

### Required Permissions

The action requires the following GitHub token permissions:

* **contents: write** – Create and update queue staging branches
* **pull-requests: write** – Update PR statuses and branches
* **statuses: write** – Set commit statuses for queue validation
* **issues: write** – Create/update dashboard issue, create labels, pin and lock dashboard

### Auto-Created Resources

On first run, the action automatically creates:

* **Labels** – `mq/dashboard`, `mq/queued`, `mq/staging`, `mq/testing`, `mq/conflict`, `mq/fastlane`, `mq/hold`, `mq/ready`, and `mq/failed`
* **Dashboard Issue** – A pinned and locked issue displaying the current queue state (when `enable_queue_tracking` is enabled)
* **State Branch** – A branch to persist queue state between workflow runs

### Example: shadow mode

Expand All @@ -29,6 +61,7 @@ permissions:
contents: write
pull-requests: write
statuses: write
issues: write

concurrency:
group: merge-queue
Expand Down Expand Up @@ -79,24 +112,176 @@ This action exposes a number of inputs to customise behaviour:
* **merge_method** – `merge` (preserves the tested tree) or `squash`.
* **clean_queue** – Whether to delete the temporary queue branches (default `true`).

## Branch protection
## Branch Protection

1. Protect your base branch (`master` or `main`).
2. Require pull request reviews as you normally do.
3. Add the status context specified in your workflow (default `merge‑queue`) as the only required status check.
4. Turn **off** “Require branches to be up to date before merging.”
5. Allow **merge commits** (recommended) or configure your merge method to squash.
Configure branch protection rules to work with the merge queue:

For more information on branch protection and security policies, see the GitHub documentation【162389469722289†L471-L485】.
1. **Protect your base branch** (`master` or `main`) – Standard GitHub branch protection
2. **Require pull request reviews** – Use your normal review requirements (e.g., 1-2 approvals, CODEOWNERS)
3. **Add the status context as the only required status check** – Set `merge‑queue` (or your custom `status_context`) as required. This ensures PRs can only merge after passing queue staging.
4. **Turn OFF "Require branches to be up to date before merging"** – The queue handles this automatically by testing PRs against the latest base during staging. Enabling this setting would force unnecessary branch updates.
5. **Allow merge commits** (recommended) – Or configure your merge method to match your `merge_method` setting (squash/rebase)

## License
**Why this configuration?** The merge queue replaces GitHub's "require branches to be up to date" feature with a more efficient approach. Instead of forcing every PR to update its branch before merge (which can cause a cascade of updates), the queue tests each PR's merge result on a staging branch. This provides the same safety guarantee (no untested code reaches main) with fewer branch updates and faster throughput.

## Common Use Cases

### 1. Hotfix Fast Lane
Use the fastlane to bypass the queue for urgent fixes:

```yaml
fastlane_matchers: "^hotfix/,^security/,\\bURGENT\\b"
```

PRs with branches like `hotfix/critical-bug` or titles containing "URGENT" will be processed immediately, jumping ahead of the regular queue.

### 2. Auto-Update Stale Branches
Automatically update PR branches that have fallen behind:

```yaml
behind_max_commits: "50"
```

When a PR is more than 50 commits behind the base branch, the action will trigger GitHub's "update branch" operation before staging. Set to `0` to disable.

### 3. Squash Merge Strategy
Use squash commits for a linear history:

```yaml
merge_method: squash
mode: live
```

All commits in the PR will be squashed into a single commit when merged. Note that squash changes the commit SHA, but the queue has already tested the merge result.

### 4. Multiple Base Branches
Run separate queues for different branches (e.g., `main` and `develop`):

```yaml
# .github/workflows/merge-queue-main.yml
- uses: igorjs/gh-actions-merge-queue@v1
with:
base_branch: main
queue_branch: merge-queue/main-staging
state_branch: merge-queue/main-state

# .github/workflows/merge-queue-develop.yml
- uses: igorjs/gh-actions-merge-queue@v1
with:
base_branch: develop
queue_branch: merge-queue/develop-staging
state_branch: merge-queue/develop-state
```

Each workflow manages its own queue independently with separate concurrency groups.

### 5. Shadow Mode Testing
Test your merge queue configuration without actually merging PRs:

```yaml
mode: shadow
```

The action will stage PRs and set commit statuses, but won't perform actual merges. Use this to validate your setup before going live.

### 6. High-Frequency Queue Processing
Process the queue more frequently for faster turnaround:

This project is licensed under the MIT License. The MIT License allows free use, modification, and distribution of the software, provided that the copyright notice and license text appear in all copies and substantial portions of the software【16404071374199†L4-L16】.
```yaml
on:
schedule:
- cron: "*/3 * * * *" # Every 3 minutes
```

More frequent runs mean shorter wait times, but higher CI resource usage. Balance based on your team's needs.

### 7. Monorepo with Path Filters
Only trigger the queue for changes to specific paths:

```yaml
on:
pull_request:
types: [opened, reopened, ready_for_review, synchronize]
paths:
- 'services/api/**'
- 'shared/**'
```

Combine with GitHub's path filtering to run separate queues for different parts of a monorepo.

## Troubleshooting

### Queue Not Processing PRs

**Symptom**: Approved PRs remain in "pending" status

**Common causes**:
- Workflow not triggering frequently enough (check `schedule` cron)
- Concurrency group blocking new runs (check Actions tab)
- PR doesn't meet eligibility criteria (not approved, is draft, or has conflicts)
- Branch protection not configured correctly (missing required status check)

**Solution**: Check workflow runs in the Actions tab for errors, verify the PR is approved and not a draft, and ensure branch protection requires the `merge-queue` status check.

### Merge Conflicts

**Symptom**: PR shows "merge-queue: failure" with conflict comment

**Cause**: PR branch conflicts with current base branch

## Code of Conduct
**Solution**: Rebase or merge the base branch into your PR branch:
```bash
git fetch origin
git rebase origin/main # or: git merge origin/main
git push --force-with-lease
```

### Base Moved During Test

**Symptom**: PR status shows "pending" with "Base moved during test; will retry"

**Cause**: Another PR merged while this PR was being staged/tested

**Solution**: Wait for the next workflow run (usually 3-5 minutes). The PR will be automatically re-tested against the new base. This ensures all PRs are tested against the very latest code.

### Stale Branches Not Auto-Updating

**Symptom**: PRs far behind base aren't being updated automatically

**Common causes**:
- `behind_max_commits` is set to `0` (disabled)
- PR is from a fork (requires maintainer to update)
- Insufficient permissions

**Solution**: Set `behind_max_commits: "100"` (or desired threshold) in your workflow. For fork PRs, maintainers must update manually or ask contributors to rebase.

### Dashboard Issue Not Created

**Symptom**: No dashboard issue appears

**Common causes**:
- Missing `issues: write` permission in workflow
- `enable_queue_tracking` is `false`

**Solution**: Add `issues: write` to workflow permissions and ensure `enable_queue_tracking: true` (default).

### Queue Stuck on Failing PR

**Symptom**: Queue won't progress because the first PR keeps failing CI

**Cause**: The action doesn't automatically skip failing PRs

**Solution**:
1. PR author should fix the failing tests and push updates
2. Or close/un-approve the PR to remove it from the queue
3. Or manually edit the queue file on the state branch to remove the PR number

For more detailed troubleshooting, see [docs/FAQ.md](docs/FAQ.md).

## License

Participation in this project is governed by a Code of Conduct that aims to create a welcoming and inclusive environment. The code is adapted from the Contributor Covenant v2.1 and outlines behaviours that foster a positive community and those that are unacceptable【426605577719396†L4-L27】. Please review it in [`CODE_OF_CONDUCT.md`](CODE_OF_CONDUCT.md).
MIT License - see [LICENSE](LICENSE) file.

## Security

To report a security vulnerability, please see [`SECURITY.md`](SECURITY.md) for instructions on responsible disclosure.
To report a security vulnerability, see [SECURITY.md](SECURITY.md).
2 changes: 1 addition & 1 deletion SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,6 @@ If you believe you have found a security vulnerability, please report it respons
* Email the maintainers at **oss@mail.igorjs.io**, or
* Use GitHub’s **Report a vulnerability** feature to open a private security advisory.

Please do not disclose security issues publicly until we have had a chance to review and fix them. We commit to respond within three business days and will work with you to coordinate disclosure and a fix. GitHub recommends linking to your security policy from your README【162389469722289†L471-L485】; doing so helps reporters find the correct procedure quickly.
Please do not disclose security issues publicly until we have had a chance to review and fix them. We commit to respond within three business days and will work with you to coordinate disclosure and a fix.

Thank you for helping to keep this project secure.
5 changes: 2 additions & 3 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -116,10 +116,9 @@ inputs:
dashboard_label:
description: >-
Optional existing label to attach to the dashboard issue. The label
must exist in the repository; if it doesn’t the issue will be created
without labels.
will be created automatically if it doesn't exist.
required: false
default: "merge-queue-dashboard"
default: "mq/dashboard"
dashboard_pin:
description: >-
Whether to pin the dashboard issue when created or updated. Requires
Expand Down
2 changes: 1 addition & 1 deletion biome.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
"formatter": {
"quoteStyle": "double",
"semicolons": "always",
"trailingCommas": "es5"
"trailingCommas": "all"
}
},
"assist": {
Expand Down
6 changes: 3 additions & 3 deletions dist/index.js

Large diffs are not rendered by default.

Loading