Skip to content

Commit

Permalink
Merge #11277
Browse files Browse the repository at this point in the history
11277: ci: block non-linear merges r=AaronFriel a=AaronFriel

This ensures commit history is linear, enabling customer-owned forks of the pulumi CLI to more easily maintain their fork. Reverse merges into PR branches result in a more complex process for them and for us.

To test this PR, I based my PR branch off an older commit from the target branch and added a merge commit. That resulted in the check failing:
https://github.com/pulumi/pulumi/actions/runs/3416559764/jobs/5686840393

> Checking merge commit efb7be0  for non-linear history
> Main branch parent is: 83c9dfc Merge #11262
> PR branch parents are d9460b9
> Checking: d9460b9 Merge remote-tracking branch 'origin/master' into friel/block-reverse-merge
> Error: Non-linear history, PR contains a merge d9460b9. Remove this by rebasing on the target.
> Error: Detected non-linear history.
> Error: Process completed with exit code 1.

Fixes #10903

The script used by this lint contains several "tests" and useful diagnostics. Example outputs:

```shell
$ ./scripts/git-linear-history-check.sh f033d9d
Checking merge commit f033d9d for non-linear history
Main branch parent is: 0797f29 Merge #10817
PR branch parents are 110dd76 ffbb03c cdf8f20 396650a
Checking: 110dd76 ci: Pin yarn lockfile for security & dependency scanning
Checking: ffbb03c ci: Build binary with .exe extension on Windows
Checking: cdf8f20 ci: Remove several test skips, check if unnecessary
Checking: 396650a ci: Re-enable Windows tests with temp dir
✅ Commit history is linear.
```

```shell
$ ./scripts/git-linear-history-check.sh 0f3e536 
Checking merge commit 0f3e536 for non-linear history
Main branch parent is: bc704af Merge #10703 #10717
PR branch parents are 9065d7c 22f2989 9f5ec4a
Checking: 9065d7c refactored defaultServiceLoop into its own method
::error::Non-linear history, PR contains a merge fa09da6. Remove this by rebasing on the target.
::error::Non-linear history, PR contains a merge 536f3d6. Remove this by rebasing on the target.
Checking: 22f2989 ci: Fix package parallelism assignment
Checking: 9f5ec4a Add missing `ProgramTestOptions` overrides in `With`
::error::Detected non-linear history.
```

The `::error::` messages should appear in GitHub Actions logs at the top level (the workflow level) as well as in the detailed output of the action. 

Co-authored-by: Aaron Friel <mayreply@aaronfriel.com>
  • Loading branch information
bors[bot] and AaronFriel committed Nov 8, 2022
2 parents 362523f + d6e03eb commit dbb138d
Show file tree
Hide file tree
Showing 3 changed files with 129 additions and 0 deletions.
12 changes: 12 additions & 0 deletions .github/workflows/ci-lint.yml
Expand Up @@ -125,3 +125,15 @@ jobs:
go-version: ${{ fromJson(inputs.version-set).go }}
- run: |
make lint_actions
linear-history:
name: linear-history
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
# This should be the merge commit of the PR or candidate merge commit in the staging branch for `bors`.
ref: ${{ inputs.ref }}
fetch-depth: 0
- name: Check for non-linear history
run: ./scripts/git-linear-history-check.sh
3 changes: 3 additions & 0 deletions bors.toml
Expand Up @@ -6,3 +6,6 @@ timeout-sec = 7200 # two hours, in seconds
status = [
'bors-ok',
]
pr_status = [
'CI / lint / linear-history'
]
114 changes: 114 additions & 0 deletions scripts/git-linear-history-check.sh
@@ -0,0 +1,114 @@
#!/usr/bin/env bash

# This script is designed to run against a merge commit, either produced during PR checks or a
# `bors` octopus merge commit. A non-zero exit status is returned if either of the following is true
# of the COMMITISH (see below)
# * If COMMITISH is not a merge commit (i.e.: has 0 or 1 parents.)
# * If COMMITISH is a merge commit and any of the parents have a non-linear history with the common ancestor
# of the target commit (first parent).

# usage: ./scripts/git-linear-history-check.sh [COMMITISH]
#
# COMMITISH: a commit to use, if not provided, HEAD is used.
#

# ## Non-linear merge example
#
# Merge commit hash: fc7c341c38c006f96ac288ebfcf5ce18b8e31a48
#
# PR commits: https://github.com/pulumi/pulumi/pull/11095/commits
#
# This commit is a single merge commit produced by `bors`. The 2nd parent of the commit is the PR
# commit HEAD and the commit history shows a merge commit. This script should log errors and exit
# with a 1 on this commit.

# ## Linear merge example
#
# Merge commit hash: 2a98a6e4dc36524fde5d33f2b5bdca0521441c72
#
# PR commits: https://github.com/pulumi/pulumi/pull/11261/commits
#
# This is a regular PR merge, containing a single commit. This script should not log any errors on
# this commit.


# ## Non-linear octopus merge example
#
# Merge commit hash: 0f3e53688fe04ec18180ba87f6915c454023ddf9
#
# PR commits:
# 1. https://github.com/pulumi/pulumi/pull/10687/commits
# 2. https://github.com/pulumi/pulumi/pull/10729/commits
# 3. https://github.com/pulumi/pulumi/pull/10740/commits
#
# This octopus merge has 3 PRs as parents. The first of which (#10687) has non-linear commit
# history. This script should log two errors and exit with a 1.


# ## Linear octopus merge example
#
# Merge commit hash: f033d9de02a633ad386b09d6dfff810ffe7ddea5
#
# PR commits:
# 1. https://github.com/pulumi/pulumi/pull/10815/commits
# 2. https://github.com/pulumi/pulumi/pull/10821/commits
# 3. https://github.com/pulumi/pulumi/pull/10822/commits
# 4. https://github.com/pulumi/pulumi/pull/10823/commits
#
# This octopus merge has 4 PRs as parents, all of which have linear commit history. This script
# should not log any errors on this commit.


# ## Initial commit example
#
# Commit hash: 86f6117640ebaaffb8689e241a668218b24f4690
#
# This commit has no parents, and should exit with a 1.


# ## Single parent (non-merge commit) example
#
# Commit hash: 1f861c5132a738216c69398ae600e1998c4e436b
#
# This commit has only a single parent, and should exit with a 1.

COMMITISH="${1:-"HEAD"}"
MERGED_BRANCH_COMMIT="$(git rev-parse "${COMMITISH}")"

>&2 echo "Checking merge commit ${MERGED_BRANCH_COMMIT} for non-linear history"

# git rev-list - list the merged branch commit followed by all of its parents, separated by spaces
# cut - remove the first line
# tr - replace spaces with newlines to turn this into an array
PARENTS_RAW=$(git rev-list --no-commit-header --parents -n 1 "${MERGED_BRANCH_COMMIT}" | cut -d' ' -f2- | tr ' ' '\n')

readarray -t PARENTS <<<"${PARENTS_RAW}" # split into array on newlines, -t strips newlines
if [ "${#PARENTS[@]}" -le "1" ]; then
>&2 echo "::error::Input commit ${COMMITISH} is not a merge commit, this script must run against a merge commit."
exit 1
fi

# First parent in bors & github PR merge commits is always the target branch's HEAD commit:
TARGET_BRANCH_HEAD="${PARENTS[0]}"
>&2 echo "Main branch parent is: $(git log --oneline -n 1 "${TARGET_BRANCH_HEAD}")"
# Subsequent parents are from PR branches:
PR_BRANCH_HEADS=( "${PARENTS[@]:1}" )
>&2 echo "PR branch parents are ${PR_BRANCH_HEADS[*]}"

HAS_MERGE_COMMIT=false
for PR_COMMIT in "${PR_BRANCH_HEADS[@]}"; do
>&2 echo "Checking: $(git log --oneline -n 1 "${PR_COMMIT}")"
# Find the common parent of the target branch and PR branch
MERGE_COMMITS_IN_PR=$(git rev-list "${TARGET_BRANCH_HEAD}..${PR_COMMIT}" --merges | cut -d' ' -f2-)
for MERGE_COMMIT in ${MERGE_COMMITS_IN_PR}; do
>&2 echo "::error::Non-linear history, PR contains a merge ${MERGE_COMMIT}. Remove this by rebasing on the target."
HAS_MERGE_COMMIT=true
done
done

if ${HAS_MERGE_COMMIT}; then
>&2 echo "::error::Detected non-linear history."
exit 1
fi

>&2 echo "✅ Commit history is linear."

0 comments on commit dbb138d

Please sign in to comment.