Skip to content

Conversation

@bthomee
Copy link
Collaborator

@bthomee bthomee commented Oct 14, 2025

High Level Overview of Change

This PR adds a script to squash all commits in a PR using a provided commit message, and then force-pushes it to the corresponding branch.

To support this new functionality, a MergeQueueCI and a SkipRunCI label are introduced to skip running the CI pipelines which do and do not allow the PR to be merged, respectively. The merge queue pipeline is moved to a separate workflow, as the check to confirm that a PR was squashed with the script made keeping it in the PR workflow complicated.

Context of Change

We would like to enable a merge queue in our repository to increase developer velocity. For all intents and purposes, the merge queue will process all PRs that have been added to the queue within a certain time period, and then merge them one by one while ensuring they are brought up to date before doing so. All code is built and tested as usual, and if a failure occurs the affected PR is removed from the queue; this also happens in case of conflicts.

Currently, GitHub does not permit specifying a commit message when using a merge queue, which means that it will provide the default commit message: title = the PR title, description = the list of individual commits followed by a list of authors. We don't like this default message, and would like to adjust it. As it turns out, squashing all commits in a PR, providing the desired commit message, and then force-pushing it to the branch gets us there (except for the list of authors, which are still added; we can live with this).

Although the process to do this is not complicated, there are several steps to be followed. This PR therefore has created a script to make this process as seamless as possible. The script:

  • Checks out the PR and gathers some details such as source and target branch, username, repo owner name.
  • Avoids source and target branches being out of date / inconsistent by ensuring both contain the latest upstream code.
  • Squashes all commits and adds the provided commit title and description.
  • Adds a label to the PR that will skip running the CI pipeline (except for the required 'success' job).
  • Prints out the commands to run to actually perform the force-push.

The script also works for forks when maintainers have been granted permission to push to them (which is generally needed anyway to allow maintainers to update the branch with the latest changes from the develop branch).

This change skips running the CI pipelines when pushing the updated commit message, because once added to the merge queue the CI pipelines are run again anyway, which must succeed for the PR to be actually merged.

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Refactor (non-breaking change that only restructures code)
  • Performance (increase or change in throughput and/or latency)
  • Tests (you added tests for code that already exists, or your new feature included in this PR)
  • Documentation update
  • Chore (no impact to binary, e.g. .gitignore, formatting, dropping support for older tooling)
  • Release

@bthomee bthomee requested a review from Bronek October 14, 2025 18:50
@codecov
Copy link

codecov bot commented Oct 15, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 79.5%. Comparing base (afb6e0e) to head (b251d73).
⚠️ Report is 6 commits behind head on develop.

Additional details and impacted files

Impacted file tree graph

@@           Coverage Diff           @@
##           develop   #5888   +/-   ##
=======================================
  Coverage     79.5%   79.5%           
=======================================
  Files          816     817    +1     
  Lines        72185   72209   +24     
  Branches      8297    8275   -22     
=======================================
+ Hits         57372   57393   +21     
- Misses       14813   14816    +3     

see 12 files with indirect coverage changes

Impacted file tree graph

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@bthomee bthomee requested a review from ximinez October 16, 2025 14:52
@bthomee
Copy link
Collaborator Author

bthomee commented Oct 17, 2025

@ximinez I made a couple of changes to simplify the logic.

  1. Handling the merge_queue event is moved to a separate on-queue.yml file.
  • This avoids the complication of selectively running the check-commit-message job and having the passed job take it into account when it ran.
  1. A new merge-queue-check job only performs a no-op, but since it will run when the label has been applied, I'm hoping that the passed job will be "successfully skipped", allowing the PR to be merged (once approvals have been given).
  • We'll see if that actually works out once you've given your approval.

Comment on lines 128 to 134
merge-queue-check:
if: ${{ contains(github.event.pull_request.labels.*.name, 'MergeQueueCI') }}
runs-on: ubuntu-latest
steps:
- name: No-op
run: true

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is necessary. If the flag is set, should-run will be skipped, which causes all the things that depend on it to be skipped, which includes build-test and passed. Skipped counts as a success.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understood that:

If job 3 depends on job 2, and job 2 depends on job 1, then:

  • if job 1 is skipped via if-statement, then jobs 2 and 3 will be skipped, but job 3 is "failingly skipped", i.e. you can't merge if it is required.
  • if job 1 runs but job 2 is skipped, then job 3 will be "successfully skipped", because a direct dependency was skipped, allowing the merge if job 3 is required.

But perhaps there's more nuance to it.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understood that:

If job 3 depends on job 2, and job 2 depends on job 1, then:

* if job 1 is skipped via if-statement, then jobs 2 and 3 will be skipped, but job 3 is "failingly skipped", i.e. you can't merge if it is required.

* if job 1 runs but job 2 is skipped, then job 3 will be "successfully skipped", because a direct dependency was skipped, allowing the merge if job 3 is required.

But perhaps there's more nuance to it.

I think that all jobs in both scenarios will could as "successfully skipped". That's why we can merge PRs that don't affect any of the files in the changes step.

https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/managing-protected-branches/about-protected-branches#require-status-checks-before-merging

Required status checks must have a successful, skipped, or neutral status before collaborators can make changes to a protected branch. Required status checks can be checks or commit statuses. For more information, see About status checks.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I've removed it but wonder if it will then do what I want it to do..

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I've removed it but wonder if it will then do what I want it to do..

Let's try it and see what happens. If it breaks, you can give me a big "I told you so", and resurrect this job.

gh pr checkout "${pr}"
git merge ${target} --no-edit

# TODO: check for conflicts and abort if there are any.
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Bronek do you have a good command I can use for this? Possibly to combine with the git merge command above that will fail gracefully/detectably if merging cannot be done automatically.

Comment on lines +24 to +26
# the 'SkipRunCI' and 'MergeQueueCI' labels. The former two labels will result
# in the PR not being allowed to be merged, whereas the latter label will
# allow it to be merged without running the full CI suite, because the merge
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ximinez Right now I think the DraftRunCI, SkipRunCI and MergeQueueCI labels will behave identically, with all allowing the PR to either be merged or not to be merged (I'd need to run some tests to confirm which one it is).

I'll have to do some more pondering over how to achieve what is described in the comment.

Comment on lines +43 to +44
if [[ "${COMMIT_TITLE}" =~ ^[^:]+:[^:]+ ]]; then
if ! [[ "${COMMIT_TITLE}" =~ ^(build|chore|docs|fix|perf|refactor|test):[^:]+ ]]; then
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're doing more or less the same thing in Clio already.
Could we not reimplement this in a different way in rippled?
https://github.com/XRPLF/clio/blob/develop/.github/workflows/check-pr-title.yml

So, move Clio's implementation in XRPLF/actions, and then reuse it in Clio and rippled

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants