chore: deprecate cherry-picking on release (#186)

## Why this should be merged

Our old strategy of only cherry-picking on release branches made the
`main` branch incompatible with dependent repos.

## How this works

The goal of the old strategy was to avoid cherry-picking on the same
branch to which we would later merge the duplicated, upstream commit.
This can instead be achieved by tracking the cherry-picked commits and
reverting them as part of the geth sync. Since we already do this for
our own changes and a single cherry-pick (see #128), there's no need to
use the two approaches simultaneously.

This PR deletes the cherry-picking mechanism and removes the
release-branch test that enforced its proper usage. It will be followed
up by a series of PRs, one per cherry-pick that would have otherwise
been placed on release branches.

## How this was tested

n/a
This commit is contained in:
Arran Schlosberg 2025-06-10 15:18:42 +01:00 committed by GitHub
parent 7b6ff3e0ff
commit fd03f3ac29
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 4 additions and 210 deletions

View file

@ -7,7 +7,6 @@ replace github.com/ava-labs/libevm => ../../
require (
github.com/ava-labs/libevm v0.0.0-00010101000000-000000000000
github.com/go-git/go-git/v5 v5.14.0
github.com/google/go-cmp v0.7.0
github.com/stretchr/testify v1.10.0
)

View file

@ -1,47 +0,0 @@
#!/usr/bin/env bash
# Copyright 2025 the libevm authors.
#
# The libevm additions to go-ethereum are free software: you can redistribute
# them and/or modify them under the terms of the GNU Lesser General Public License
# as published by the Free Software Foundation, either version 3 of the License,
# or (at your option) any later version.
#
# The libevm additions are distributed in the hope that they will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
# General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with the go-ethereum library. If not, see
# <http://www.gnu.org/licenses/>.
# Usage: run `./cherrypick.sh` on a branch intended to become a release.
#
# Reads the contents of ./cherrypicks, filters out commits that are already
# ancestors of HEAD, and calls `git cherry-pick` with the remaining commit
# hashes.
set -eu;
set -o pipefail;
SELF_DIR=$(dirname "${0}")
# The format of the `cherrypicks` file is guaranteed by a test so we can use simple parsing here.
CHERRY_PICKS=$(< "${SELF_DIR}/cherrypicks" grep -Ev "^#" | awk '{print $1}')
commits=()
for commit in ${CHERRY_PICKS}; do
git merge-base --is-ancestor "${commit}" HEAD && \
echo "Skipping ${commit} already in history" && \
continue;
echo "Cherry-picking ${commit}";
commits+=("${commit}");
done
if [[ -z "${commits[*]// }" ]]; then # $x// removes whitespace
echo "No commits to cherry-pick";
exit 0;
fi
git cherry-pick -S "${commits[@]}";

View file

@ -1,15 +0,0 @@
# Lines starting with # are ignored as comments.
# All other lines MUST have the format [<commit> # <first line of commit message>].
# Commits MUST be in chronological order.
# A test in release_test.go will enforce this / provide the correct lines to copy and paste.
#
# The very first commit is where libevm branched off geth and is included to confirm that it is skipped.
#
2bd6bd01d2e8561dd7fc21b631f4a34ac16627a1 # Merge branch 'master' into release/1.13
99bbbc0277e34fc3a31512a345ba20874ae98e18 # internal/build, rpc: add missing HTTP response body Close() calls (#29223)
1e9bf2a09ed3d82ac1aa69750a556f3ce127721d # core/state: fix bug in statedb.Copy and remove unnecessary preallocation (#29563)
69f815f6f5791e0e48160bdad284773d0ffb1ba9 # params: print time value instead of pointer in ConfigCompatError (#29514)
e4b8058d5a5832cdebdac7da385cf6d829c0d433 # eth/gasprice: add query limit for FeeHistory to defend DDOS attack (#29644)
34b46a2f756da71595ac84eb7f25441f2a5b6ebb # core/state/snapshot: add a missing lock (#30001)
159fb1a1db551c544978dc16a5568a4730b4abf3 # crypto: add IsOnCurve check (#31100)
da71839a270a353bac92e3108e4b74fb0eefec29 # internal/ethapi: fix panic in debug methods (#31157)

View file

@ -22,23 +22,17 @@ import (
"fmt"
"os"
"path/filepath"
"regexp"
"slices"
"sort"
"strings"
"testing"
"time"
"github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/object"
"github.com/google/go-cmp/cmp"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/ava-labs/libevm/params"
_ "embed"
)
func TestMain(m *testing.M) {
@ -46,76 +40,6 @@ func TestMain(m *testing.M) {
os.Exit(m.Run())
}
var (
//go:embed cherrypicks
cherryPicks string
lineFormatRE = regexp.MustCompile(`^([a-fA-F0-9]{40}) # (.*)$`)
)
type parsedLine struct {
hash, commitMsg string
}
func parseCherryPicks(t *testing.T) (rawLines []string, lines []parsedLine) {
t.Helper()
for i, line := range strings.Split(cherryPicks, "\n") {
if line == "" || strings.HasPrefix(line, "#") {
continue
}
switch matches := lineFormatRE.FindStringSubmatch(line); len(matches) {
case 3:
rawLines = append(rawLines, line)
lines = append(lines, parsedLine{
hash: matches[1],
commitMsg: matches[2],
})
default:
t.Errorf("Line %d is improperly formatted: %s", i, line)
}
}
return rawLines, lines
}
func TestCherryPicksFormat(t *testing.T) {
rawLines, lines := parseCherryPicks(t)
if t.Failed() {
t.Fatalf("Required line regexp: %s", lineFormatRE.String())
}
commits := make([]struct {
obj *object.Commit
line parsedLine
}, len(lines))
repo := openGitRepo(t)
for i, line := range lines {
obj, err := repo.CommitObject(plumbing.NewHash(line.hash))
require.NoErrorf(t, err, "%T.CommitObject(%q)", repo, line.hash)
commits[i].obj = obj
commits[i].line = line
}
sort.Slice(commits, func(i, j int) bool {
ci, cj := commits[i].obj, commits[j].obj
return ci.Committer.When.Before(cj.Committer.When)
})
var want []string
for _, c := range commits {
msg := strings.Split(c.obj.Message, "\n")[0]
want = append(
want,
fmt.Sprintf("%s # %s", c.line.hash, msg),
)
}
if diff := cmp.Diff(want, rawLines); diff != "" {
t.Errorf("Commits in `cherrypicks` file out of order or have incorrect commit message(s);\n(-want +got):\n%s", diff)
t.Logf("To fix, copy:\n%s", strings.Join(want, "\n"))
}
}
const (
defaultBranch = "main"
releaseBranchPrefix = "release/"
@ -149,20 +73,8 @@ func TestBranchProperties(t *testing.T) {
// 1. They are named release/v${libevm-version};
// 2. The libevm version's [params.ReleaseType] is appropriate for a release
// branch; and
// 3. The commit history is a "linear fork" off the default branch, with only
// certain allowable commits.
//
// We define a "linear fork" as there being a single ancestral commit at which
// the release branch diverged from the default branch, with no merge commits
// after this divergence:
//
// ______________ main
// \___ release/*
//
// The commits in the release branch that are not in the default branch MUST be:
//
// 1. The cherry-pick commits embedded as [cherryPicks], in order; then
// 2. A single, final commit to change the libevm version.
// 3. The commit history since the default branch is only a single commit, to
// change the version.
//
// testReleaseBranch assumes that the git HEAD currently points at either
// `targetBranch` itself, or at a candidate (i.e. PR source) for said branch.
@ -199,27 +111,10 @@ func testReleaseBranch(t *testing.T, targetBranch string) {
newCommits := linearCommitsSince(t, history, fork)
logCommits(t, "History since fork from default branch", newCommits)
t.Run("cherry_picked_commits", func(t *testing.T) {
_, cherryPick := parseCherryPicks(t)
wantCommits := commitsFromHashes(t, repo, cherryPick, fork)
logCommits(t, "Expected cherry-picks", wantCommits)
if got, want := len(newCommits), len(wantCommits)+1; got != want {
t.Fatalf("Got %d commits since fork from default; want number to be cherry-picked plus one (%d)", got, want)
}
opt := compareCherryPickedCommits()
if diff := cmp.Diff(wantCommits, newCommits[:len(wantCommits)], opt); diff != "" {
t.Fatalf("Cherry-picked commits for release branch (-want +got):\n%s", diff)
}
})
t.Run("final_commit", func(t *testing.T) {
n := len(newCommits)
last := newCommits[n-1]
require.Len(t, newCommits, 1, "Single commit off default branch")
last := newCommits[0]
penultimate := fork
if n >= 2 {
penultimate = newCommits[n-2]
}
lastCommitDiffs, err := object.DiffTree(
treeFromCommit(t, last),
@ -293,25 +188,6 @@ func linearCommitsSince(t *testing.T, iter object.CommitIter, since *object.Comm
return commits
}
func commitsFromHashes(t *testing.T, repo *git.Repository, lines []parsedLine, skipAncestorsOf *object.Commit) []*object.Commit {
t.Helper()
var commits []*object.Commit
for _, l := range lines {
c, err := repo.CommitObject(plumbing.NewHash(l.hash))
require.NoError(t, err)
skip, err := c.IsAncestor(skipAncestorsOf)
require.NoError(t, err)
if skip || c.Hash == skipAncestorsOf.Hash {
continue
}
commits = append(commits, c)
}
return commits
}
func commitMsgFirstLine(c *object.Commit) string {
return strings.Split(c.Message, "\n")[0]
}
@ -323,25 +199,6 @@ func logCommits(t *testing.T, header string, commits []*object.Commit) {
}
}
// compareCherryPickedCommits returns a [cmp.Transformer] that converts
// [object.Commit] instances into structs carrying only the pertinent commit
// properties that remain stable when cherry-picked. Note, however, that this
// does not include the actual diffs induced by cherry-picking.
func compareCherryPickedCommits() cmp.Option {
type comparableCommit struct {
MessageFirstLine, Author string
Authored time.Time
}
return cmp.Transformer("gitCommit", func(c *object.Commit) comparableCommit {
return comparableCommit{
MessageFirstLine: commitMsgFirstLine(c),
Author: c.Author.String(),
Authored: c.Author.When,
}
})
}
func treeFromCommit(t *testing.T, c *object.Commit) *object.Tree {
t.Helper()
tree, err := c.Tree()