Skip to content

Commit

Permalink
Improve enforcement of linear commit history
Browse files Browse the repository at this point in the history
Use the GitHub API to check commit parents in BASE and HEAD
branches and ensure we are following a linear history.

HEAD should not have any merge commits, so reject any HEAD branches
where one or more commits have multiple parents.

The PR merge commit must include the HEAD commit as a parent,
otherwise reject the versioning job.

Change-type: minor
Signed-off-by: Kyle Harding <[email protected]>
  • Loading branch information
klutchell committed Dec 9, 2024
1 parent 41d15f7 commit fdb8e1a
Show file tree
Hide file tree
Showing 2 changed files with 95 additions and 20 deletions.
46 changes: 37 additions & 9 deletions .github/workflows/flowzone.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

69 changes: 58 additions & 11 deletions flowzone.yml
Original file line number Diff line number Diff line change
Expand Up @@ -571,6 +571,56 @@
Please consult the documentation for more information."
exit 1
- &rejectNonLinearHead
# This can happen when a PR is updated with a merge commit from main, rather than rebased on main.
# In the GitHub UI this is represented by the "Update with merge commit" option vs "Update with rebase".
# https://github.com/actions/github-script
# https://octokit.github.io/rest.js/v21/#pulls-list-commits
name: Reject HEAD branches containing merge commits
if: github.event.pull_request.state == 'open'
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7
with:
github-token: ${{ github.token }}
result-encoding: json
script: |
const { data: commits } = await github.rest.pulls.listCommits({
...context.repo,
pull_number: context.payload.pull_request.number
});
if (commits.some(({ parents }) => parents.length > 1)) {
throw new Error('Non-linear history detected - merge commit(s) identified in HEAD branch');
}
- &rejectNonLinearMerge
# This can happen if multiple PRs are merged at the same time, typically because
# one PR is rebased on another PR before merging, where the merge commit in the BASE
# branch does not include the tip of the HEAD branch as a parent.
# https://github.com/actions/github-script
# https://octokit.github.io/rest.js/v21/#repos-get-commit
name: Reject merge commits that do not include the HEAD commit as a parent
# This check passes when the PR is open too, but only on pull_request events and not pull_request_target.
# So only check on merged PRs where the HEAD sha should always be a parent of the merge commit.
if: github.event.pull_request.merged
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7
with:
github-token: ${{ github.token }}
result-encoding: json
script: |
console.debug(`Merge commit SHA: ${context.sha}`)
console.debug(`HEAD commit SHA: ${context.payload.pull_request.head.sha}`)
const { data: commit } = await github.rest.repos.getCommit({
...context.repo,
ref: context.sha
});
console.debug('Commit parents: %s', JSON.stringify(commit.parents, null, 2))
if (!commit.parents.some(({ sha }) => sha === context.payload.pull_request.head.sha)) {
throw new Error('Non-linear history detected - HEAD branch commit is not a parent of the merge commit');
}
- &setupBuildx # https://github.com/docker/setup-buildx-action
name: Setup buildx
id: setup_buildx
Expand Down Expand Up @@ -1078,6 +1128,7 @@ jobs:
- *rejectExternalPullRequest
- *rejectInternalPullRequestTarget
- *rejectMissingSecrets

- <<: *getGitHubAppToken
with:
<<: *getGitHubAppTokenWith
Expand Down Expand Up @@ -1116,7 +1167,9 @@ jobs:
<<: *rootWorkingDirectory

permissions: {}
permissions:
# Required to inspect commits in branches for linear history
contents: read

outputs:
tag: ${{ steps.versionist.outputs.tag || steps.git_describe.outputs.tag }}
Expand All @@ -1137,6 +1190,10 @@ jobs:
<<: *gitHubCliEnvironment

steps:

- *rejectNonLinearHead
- *rejectNonLinearMerge

- <<: *getGitHubAppToken
with:
<<: *getGitHubAppTokenWith
Expand All @@ -1163,16 +1220,6 @@ jobs:
- <<: *checkoutEventSha
if: github.event.pull_request.state != 'open'

# fail on merge commits (ones with more than one parent)
- name: Reject merge commits
if: github.event.pull_request.state == 'open'
run: |
if [ "$(git cat-file -p ${{ github.event.pull_request.head.sha || github.event.head_commit.id }} | grep -c '^parent ')" -gt 1 ]
then
echo "::error::Latest commit appears to be a merge, which is currently unsupported. Try a rebase instead."
exit 1
fi
# The current commit sha is needed as the parent sha for the versioned commit
# and/or as default tag & semver if versioning is disabled.
- *describeGitState
Expand Down

0 comments on commit fdb8e1a

Please sign in to comment.