Skip to content

Commit c4f08da

Browse files
authored
feat(librarian): change publish/tag library flags to release-commit (#4141)
Instead of specifying the library that should be used to find a release commit, after this change the release commit is specified directly. This is a lot less confusing, as otherwise there was an implication that *only* that library would be published/tagged. Fixes #4053.
1 parent 1e3d487 commit c4f08da

7 files changed

Lines changed: 116 additions & 114 deletions

File tree

cmd/librarian/doc.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -169,12 +169,12 @@ USAGE:
169169
170170
OPTIONS:
171171
172-
--execute fully publish (default is to only perform a dry run)
173-
--library string library to find a release commit for; default finds latest release commit for any library
174-
--dry-run print commands without executing (legacy Rust-only flag)
175-
--dry-run-keep-going print commands without executing, don't stop on error (legacy Rust-only flag)
176-
--skip-semver-checks skip semantic versioning checks (legacy Rust-only flag)
177-
--help, -h show help
172+
--execute fully publish (default is to only perform a dry run)
173+
--release-commit string the release commit to publish; default finds latest release commit
174+
--dry-run print commands without executing (legacy Rust-only flag)
175+
--dry-run-keep-going print commands without executing, don't stop on error (legacy Rust-only flag)
176+
--skip-semver-checks skip semantic versioning checks (legacy Rust-only flag)
177+
--help, -h show help
178178
179179
GLOBAL OPTIONS:
180180
@@ -192,8 +192,8 @@ USAGE:
192192
193193
OPTIONS:
194194
195-
--library string library to find a release commit for; default finds latest release commit for any library
196-
--help, -h show help
195+
--release-commit string the release commit to tag; default finds latest release commit
196+
--help, -h show help
197197
198198
GLOBAL OPTIONS:
199199

internal/librarian/bump.go

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ import (
1818
"context"
1919
"errors"
2020
"fmt"
21-
"slices"
2221
"strings"
2322

2423
"github.com/googleapis/librarian/internal/command"
@@ -309,12 +308,12 @@ func findReleasedLibraries(cfgBefore, cfgAfter *config.Config) ([]string, error)
309308
}
310309

311310
// findLatestReleaseCommitHash finds the latest (most recent) commit hash
312-
// which released the library named by libraryName, or which released any libraries
313-
// if libraryName is empty. (See findReleasedLibraries for the definition of what it
314-
// means for a commit to release a library.) Importantly, it does this *without*
315-
// using tags, as it's used in circumstances where the full release process has
316-
// not yet been completed (e.g. to find which commit *should* be tagged).
317-
func findLatestReleaseCommitHash(ctx context.Context, gitExe, libraryName string) (string, error) {
311+
// which released any libraries. (See findReleasedLibraries for the definition
312+
// of what it means for a commit to release a library.) Importantly, it does
313+
// this *without* using tags, as it's used in circumstances where the full
314+
// release process has not yet been completed (e.g. to find which commit
315+
// *should* be tagged).
316+
func findLatestReleaseCommitHash(ctx context.Context, gitExe string) (string, error) {
318317
commits, err := git.FindCommitsForPath(ctx, gitExe, librarianConfigPath)
319318
if err != nil {
320319
return "", err
@@ -342,7 +341,7 @@ func findLatestReleaseCommitHash(ctx context.Context, gitExe, libraryName string
342341
if err != nil {
343342
return "", err
344343
}
345-
if len(released) > 0 && (libraryName == "" || slices.Contains(released, libraryName)) {
344+
if len(released) > 0 {
346345
return candidateCommit, nil
347346
}
348347
}

internal/librarian/bump_test.go

Lines changed: 4 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -915,12 +915,11 @@ func TestFindLatestReleaseCommitHash(t *testing.T) {
915915
for _, test := range []struct {
916916
name string
917917
setup func(cfg *config.Config)
918-
libraryName string
919918
wantCommitCount int
920919
wantCommitIndex int // Commit index in the log: HEAD=0, HEAD~=1 etc
921920
}{
922921
{
923-
name: "HEAD commit releases, match any release",
922+
name: "HEAD commit releases",
924923
setup: func(cfg *config.Config) {
925924
// 2 commits in addition to the two in Setup:
926925
// - Chore commit with a modified readme
@@ -933,7 +932,7 @@ func TestFindLatestReleaseCommitHash(t *testing.T) {
933932
wantCommitIndex: 0,
934933
},
935934
{
936-
name: "HEAD~ commit, match any release",
935+
name: "HEAD~ commit",
937936
setup: func(cfg *config.Config) {
938937
// 3 commits in addition to the two in Setup:
939938
// - Chore commit with a modified readme
@@ -947,26 +946,6 @@ func TestFindLatestReleaseCommitHash(t *testing.T) {
947946
wantCommitCount: 5,
948947
wantCommitIndex: 1,
949948
},
950-
{
951-
name: "match specific library",
952-
setup: func(cfg *config.Config) {
953-
// 4 commits in addition to the two in Setup:
954-
// - Chore commit with a modified readme
955-
// - Release commit with the first library version bumped
956-
// - Chore commit with another modified readme
957-
// - Release commit with the second library version bumped
958-
// (We're looking for the first library, so effectively HEAD~2)
959-
writeReadmeAndCommit(t, "modified readme")
960-
cfg.Libraries[0].Version = "1.1.0"
961-
writeConfigAndCommit(t, cfg)
962-
writeReadmeAndCommit(t, "modified readme again")
963-
cfg.Libraries[1].Version = "1.3.0"
964-
writeConfigAndCommit(t, cfg)
965-
},
966-
libraryName: sample.Lib1Name,
967-
wantCommitCount: 6,
968-
wantCommitIndex: 2,
969-
},
970949
} {
971950
t.Run(test.name, func(t *testing.T) {
972951
cfg := &config.Config{
@@ -988,7 +967,7 @@ func TestFindLatestReleaseCommitHash(t *testing.T) {
988967
if test.wantCommitCount != len(commits) {
989968
t.Fatalf("expected setup to create %d commits; got %d", test.wantCommitCount, len(commits))
990969
}
991-
got, err := findLatestReleaseCommitHash(t.Context(), "git", test.libraryName)
970+
got, err := findLatestReleaseCommitHash(t.Context(), "git")
992971
if err != nil {
993972
t.Fatal(err)
994973
}
@@ -1005,7 +984,6 @@ func TestFindLatestReleaseCommitHash_Error(t *testing.T) {
1005984
for _, test := range []struct {
1006985
name string
1007986
setup func(cfg *config.Config)
1008-
libraryName string
1009987
wantReleaseCommitNotFound bool
1010988
}{
1011989
{
@@ -1017,24 +995,6 @@ func TestFindLatestReleaseCommitHash_Error(t *testing.T) {
1017995
},
1018996
wantReleaseCommitNotFound: true,
1019997
},
1020-
{
1021-
name: "no library with given name",
1022-
setup: func(cfg *config.Config) {
1023-
cfg.Libraries[0].Version = "1.1.0"
1024-
writeConfigAndCommit(t, cfg)
1025-
},
1026-
libraryName: "nonexistent",
1027-
wantReleaseCommitNotFound: true,
1028-
},
1029-
{
1030-
name: "release, but not for the specified library",
1031-
setup: func(cfg *config.Config) {
1032-
cfg.Libraries[0].Version = "1.1.0"
1033-
writeConfigAndCommit(t, cfg)
1034-
},
1035-
libraryName: sample.Lib2Name,
1036-
wantReleaseCommitNotFound: true,
1037-
},
1038998
{
1039999
name: "invalid release",
10401000
setup: func(cfg *config.Config) {
@@ -1080,7 +1040,7 @@ func TestFindLatestReleaseCommitHash_Error(t *testing.T) {
10801040
}
10811041
testhelper.Setup(t, opts)
10821042
test.setup(cfg)
1083-
got, err := findLatestReleaseCommitHash(t.Context(), "git", test.libraryName)
1043+
got, err := findLatestReleaseCommitHash(t.Context(), "git")
10841044
if err == nil {
10851045
t.Errorf("expected error; succeeded with hash %s", got)
10861046
}

internal/librarian/publish.go

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,8 @@ func publishCommand() *cli.Command {
3737
Usage: "fully publish (default is to only perform a dry run)",
3838
},
3939
&cli.StringFlag{
40-
Name: "library",
41-
Usage: "library to find a release commit for; default finds latest release commit for any library",
40+
Name: "release-commit",
41+
Usage: "the release commit to publish; default finds latest release commit",
4242
},
4343
&cli.BoolFlag{
4444
Name: "dry-run",
@@ -61,7 +61,7 @@ func publishCommand() *cli.Command {
6161
if cfg.Language == languageRust {
6262
return legacyRustPublish(ctx, cfg, cmd)
6363
}
64-
return publish(ctx, cfg, cmd.String("library"), cmd.Bool("execute"))
64+
return publish(ctx, cfg, cmd.String("release-commit"), cmd.Bool("execute"))
6565
},
6666
}
6767
}
@@ -81,22 +81,25 @@ func legacyRustPublish(ctx context.Context, cfg *config.Config, cmd *cli.Command
8181
// at HEAD, just to find the git executable to use, after which it finds the
8282
// release commit to publish. The configuration at the release commit is used
8383
// for all further operations (and the repo will be checked out at that commit).
84-
// The library flag allows a user to identify a specific release to publish, in
85-
// case of overlapping releases being performed. The execute flag says whether to
86-
// actually publish (true) or just perform a dry run (false).
87-
func publish(ctx context.Context, cfg *config.Config, library string, execute bool) error {
84+
// The releaseCommit flag allows a user to identify a specific release commit to
85+
// publish, in case of overlapping releases being performed. The execute flag
86+
// says whether to actually publish (true) or just perform a dry run (false).
87+
func publish(ctx context.Context, cfg *config.Config, releaseCommit string, execute bool) error {
8888
gitExe := "git"
8989
if cfg.Release != nil {
9090
gitExe = command.GetExecutablePath(cfg.Release.Preinstalled, "git")
9191
}
9292
if err := git.AssertGitStatusClean(ctx, gitExe); err != nil {
9393
return err
9494
}
95-
releaseCommitHash, err := findLatestReleaseCommitHash(ctx, gitExe, library)
96-
if err != nil {
97-
return err
95+
var err error
96+
if releaseCommit == "" {
97+
releaseCommit, err = findLatestReleaseCommitHash(ctx, gitExe)
98+
if err != nil {
99+
return err
100+
}
98101
}
99-
if err := git.Checkout(ctx, gitExe, releaseCommitHash); err != nil {
102+
if err := git.Checkout(ctx, gitExe, releaseCommit); err != nil {
100103
return err
101104
}
102105
// Reload the config after checking out the release commit.
@@ -121,6 +124,9 @@ func publish(ctx context.Context, cfg *config.Config, library string, execute bo
121124
if err != nil {
122125
return err
123126
}
127+
if len(librariesToPublish) == 0 {
128+
return fmt.Errorf("error publishing %s: %w", releaseCommit, errNoLibrariesAtReleaseCommit)
129+
}
124130

125131
switch cfg.Language {
126132
case languageFake:

internal/librarian/publish_test.go

Lines changed: 39 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,14 @@
1515
package librarian
1616

1717
import (
18+
"errors"
1819
"fmt"
1920
"os"
2021
"testing"
2122

2223
"github.com/google/go-cmp/cmp"
2324
"github.com/googleapis/librarian/internal/config"
25+
"github.com/googleapis/librarian/internal/git"
2426
"github.com/googleapis/librarian/internal/sample"
2527
"github.com/googleapis/librarian/internal/testhelper"
2628
)
@@ -29,11 +31,11 @@ func TestPublish(t *testing.T) {
2931
// Each test starts (before setup) with Lib1Name with a version of 1.0.0 and
3032
// Lib2Name with a version of 1.2.0.
3133
for _, test := range []struct {
32-
name string
33-
setup func(cfg *config.Config)
34-
library string
35-
execute bool
36-
want string
34+
name string
35+
setup func(cfg *config.Config)
36+
releaseCommit string
37+
execute bool
38+
want string
3739
}{
3840
{
3941
name: "publish Lib1Name and Lib2Name",
@@ -63,23 +65,26 @@ func TestPublish(t *testing.T) {
6365
want: fmt.Sprintf("libraries=%s; execute=false", sample.Lib1Name),
6466
},
6567
{
66-
name: "publish Lib1Name, specified in flags, with a later release of Lib2Name ignored",
68+
name: "publish Lib1Name due to release commit specified in flags, with a later release of Lib2Name ignored",
6769
setup: func(cfg *config.Config) {
6870
cfg.Libraries[0].Version = "1.1.0"
6971
writeConfigAndCommit(t, cfg)
7072
cfg.Libraries[1].Version = "1.3.0"
7173
writeConfigAndCommit(t, cfg)
7274
},
73-
library: sample.Lib1Name,
74-
want: fmt.Sprintf("libraries=%s; execute=false", sample.Lib1Name),
75+
// Fortunately this doesn't have to be an actual commit, just a
76+
// committish, so we don't need to know the hash of the HEAD~
77+
// commit when creating the test data.
78+
releaseCommit: "HEAD~",
79+
want: fmt.Sprintf("libraries=%s; execute=false", sample.Lib1Name),
7580
},
7681
} {
7782
t.Run(test.name, func(t *testing.T) {
7883
cfg := sample.Config()
7984
cfg.Libraries[1].Version = "1.2.0"
8085
testhelper.Setup(t, testhelper.SetupOptions{Config: cfg})
8186
test.setup(cfg)
82-
if err := publish(t.Context(), cfg, test.library, test.execute); err != nil {
87+
if err := publish(t.Context(), cfg, test.releaseCommit, test.execute); err != nil {
8388
t.Fatal(err)
8489
}
8590
got, err := os.ReadFile(fakePublishedFile)
@@ -95,9 +100,10 @@ func TestPublish(t *testing.T) {
95100

96101
func TestPublish_Error(t *testing.T) {
97102
for _, test := range []struct {
98-
name string
99-
setup func(cfg *config.Config)
100-
library string
103+
name string
104+
setup func(cfg *config.Config)
105+
releaseCommit string
106+
wantErr error
101107
}{
102108
{
103109
name: "custom tool specified for git and doesn't exist",
@@ -111,6 +117,7 @@ func TestPublish_Error(t *testing.T) {
111117
}
112118
writeConfigAndCommit(t, cfg)
113119
},
120+
// Can't easily check this error.
114121
},
115122
{
116123
name: "repo is dirty",
@@ -122,6 +129,7 @@ func TestPublish_Error(t *testing.T) {
122129
t.Fatal(err)
123130
}
124131
},
132+
wantErr: git.ErrGitStatusUnclean,
125133
},
126134
{
127135
name: "language isn't supported",
@@ -131,30 +139,42 @@ func TestPublish_Error(t *testing.T) {
131139
cfg.Language = "unsupported-for-publish"
132140
writeConfigAndCommit(t, cfg)
133141
},
142+
// Can't easily check this error.
134143
},
135144
{
136145
name: "no release commit",
137146
setup: func(cfg *config.Config) {
138147
},
139148
},
140149
{
141-
name: "no release commit for specified library",
150+
name: "specified release commit is invalid",
142151
setup: func(cfg *config.Config) {
143-
cfg.Libraries[1].Version = "1.3.0"
144-
writeConfigAndCommit(t, cfg)
145152
},
146-
library: sample.Lib1Name,
153+
releaseCommit: "not a commit",
154+
// Can't easily check this error
155+
},
156+
{
157+
name: "release commit does not release anything",
158+
setup: func(cfg *config.Config) {
159+
writeFileAndCommit(t, "README.txt", []byte("Just a readme"), "Modified config")
160+
},
161+
releaseCommit: "HEAD",
162+
wantErr: errNoLibrariesAtReleaseCommit,
147163
},
148164
} {
149165
t.Run(test.name, func(t *testing.T) {
150166
cfg := sample.Config()
151167
cfg.Libraries[1].Version = "1.2.0"
152168
testhelper.Setup(t, testhelper.SetupOptions{Config: cfg})
153169
test.setup(cfg)
154-
err := publish(t.Context(), cfg, test.library, false)
170+
err := publish(t.Context(), cfg, test.releaseCommit, false)
155171
if err == nil {
156-
t.Errorf("publish(): expected error, got none")
172+
t.Fatal("expected error, got nil")
157173
}
174+
if test.wantErr != nil && !errors.Is(err, test.wantErr) {
175+
t.Errorf("expected %v, got %v", test.wantErr, err)
176+
}
177+
158178
})
159179
}
160180
}
@@ -170,7 +190,7 @@ func TestPublishCommand(t *testing.T) {
170190
cfg.Libraries[1].Version = "1.3.0"
171191
writeConfigAndCommit(t, cfg)
172192

173-
if err := Run(t.Context(), "librarian", "publish", "--library", sample.Lib1Name, "--execute"); err != nil {
193+
if err := Run(t.Context(), "librarian", "publish", "--release-commit", "HEAD~", "--execute"); err != nil {
174194
t.Fatal(err)
175195
}
176196
want := fmt.Sprintf("libraries=%s; execute=true", sample.Lib1Name)

0 commit comments

Comments
 (0)