Skip to content

Commit ee05006

Browse files
authored
feat(internal/automation): add stage-release command (#2902)
Add a `stage-release` command for automation command set. For #2416
1 parent c828885 commit ee05006

File tree

6 files changed

+186
-7
lines changed

6 files changed

+186
-7
lines changed

cmd/automation/doc.go

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ The commands are:
2626
2727
# generate
2828
29-
The generate command runs a Cloud Build job to generate Cloud Client Libraries.
29+
The generate command triggers a Cloud Build job that runs librarian generate command for every
30+
repository onboarded to Librarian generate automation.
3031
3132
Usage:
3233
@@ -43,8 +44,8 @@ Flags:
4344
4445
# publish-release
4546
46-
The publish-release command runs a Cloud Build job to create a tag on a merged release pull
47-
request.
47+
The publish-release command triggers a Cloud Build job that runs librarian release tag command
48+
for every repository onboarded to Librarian publish-release automation.
4849
4950
Usage:
5051
@@ -55,6 +56,22 @@ Flags:
5556
-project string
5657
Google Cloud Platform project ID (default "cloud-sdk-librarian-prod")
5758
59+
# stage-release
60+
61+
The stage-release command triggers a Cloud Build job that runs librarian release stage command for
62+
every repository onboarded to Librarian stage-release automation.
63+
64+
Usage:
65+
66+
automation stage-release [flags]
67+
68+
Flags:
69+
70+
-project string
71+
Google Cloud Platform project ID (default "cloud-sdk-librarian-prod")
72+
-push
73+
The _PUSH flag (true/false) to Librarian CLI's -push option
74+
5875
# version
5976
6077
Version prints version information for the automation binary.

internal/automation/automation.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ func newAutomationCommand() *cli.Command {
2424
commands := []*cli.Command{
2525
newCmdGenerate(),
2626
newCmdPublishRelease(),
27+
newCmdStageRelease(),
2728
}
2829

2930
return cli.NewCommandSet(
@@ -68,3 +69,21 @@ func newCmdPublishRelease() *cli.Command {
6869

6970
return cmdPublishRelease
7071
}
72+
73+
func newCmdStageRelease() *cli.Command {
74+
cmdStageRelease := &cli.Command{
75+
Short: "stage-release",
76+
UsageLine: "automation stage-release [flags]",
77+
Long: stageLongHelp,
78+
Action: func(ctx context.Context, cmd *cli.Command) error {
79+
runner := newStageRunner(cmd.Config)
80+
return runner.run(ctx)
81+
},
82+
}
83+
84+
cmdStageRelease.Init()
85+
addFlagProject(cmdStageRelease.Flags, cmdStageRelease.Config)
86+
addFlagPush(cmdStageRelease.Flags, cmdStageRelease.Config)
87+
88+
return cmdStageRelease
89+
}

internal/automation/cli.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ var runCommandFn = RunCommand
2727
// Run parses the command line arguments and triggers the specified command.
2828
func Run(ctx context.Context, args []string) error {
2929
// TODO(https://github.com/googleapis/librarian/issues/2889) refactor this function after all commands are migrated.
30-
if len(args) == 0 || args[0] == "version" || args[0] == generateCmdName || args[0] == publishCmdName {
30+
if len(args) == 0 || args[0] == "version" || args[0] == generateCmdName || args[0] == publishCmdName || args[0] == stageCmdName {
3131
cmd := newAutomationCommand()
3232
return cmd.Run(ctx, args)
3333
}

internal/automation/help.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,10 @@ package automation
1717
const (
1818
automationLongHelp = `Automation provides logic to trigger Cloud Build jobs that run Librarian commands for
1919
any repository listed in internal/automation/prod/repositories.yaml.`
20-
generateLongHelp = `The generate command runs a Cloud Build job to generate Cloud Client Libraries.`
21-
publishLongHelp = `The publish-release command runs a Cloud Build job to create a tag on a merged release pull
22-
request.`
20+
generateLongHelp = `The generate command triggers a Cloud Build job that runs librarian generate command for every
21+
repository onboarded to Librarian generate automation.`
22+
publishLongHelp = `The publish-release command triggers a Cloud Build job that runs librarian release tag command
23+
for every repository onboarded to Librarian publish-release automation.`
24+
stageLongHelp = `The stage-release command triggers a Cloud Build job that runs librarian release stage command for
25+
every repository onboarded to Librarian stage-release automation.`
2326
)
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// Copyright 2025 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package automation
16+
17+
import (
18+
"context"
19+
20+
"github.com/googleapis/librarian/internal/config"
21+
)
22+
23+
const (
24+
stageCmdName = "stage-release"
25+
)
26+
27+
type stageRunner struct {
28+
projectID string
29+
push bool
30+
}
31+
32+
func newStageRunner(cfg *config.Config) *stageRunner {
33+
return &stageRunner{
34+
projectID: cfg.Project,
35+
push: cfg.Push,
36+
}
37+
}
38+
39+
func (r *stageRunner) run(ctx context.Context) error {
40+
return runCommandFn(ctx, stageCmdName, r.projectID, r.push, false)
41+
}
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
// Copyright 2025 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package automation
16+
17+
import (
18+
"context"
19+
"errors"
20+
"testing"
21+
22+
"github.com/googleapis/librarian/internal/config"
23+
)
24+
25+
func TestNewStageRunner(t *testing.T) {
26+
t.Parallel()
27+
for _, test := range []struct {
28+
name string
29+
cfg *config.Config
30+
}{
31+
{
32+
name: "create_a_runner",
33+
cfg: &config.Config{
34+
Project: "example-project",
35+
},
36+
},
37+
} {
38+
t.Run(test.name, func(t *testing.T) {
39+
t.Parallel()
40+
runner := newStageRunner(test.cfg)
41+
if runner.projectID != test.cfg.Project {
42+
t.Errorf("newStageRunner() projectID is not set")
43+
}
44+
})
45+
}
46+
}
47+
48+
func TestStageRunnerRun(t *testing.T) {
49+
tests := []struct {
50+
name string
51+
runner *stageRunner
52+
runCommandErr error
53+
wantErr bool
54+
wantCmd string
55+
wantProjectID string
56+
wantPush bool
57+
}{
58+
{
59+
name: "success",
60+
runner: &stageRunner{
61+
projectID: "test-project",
62+
push: true,
63+
},
64+
wantCmd: stageCmdName,
65+
wantProjectID: "test-project",
66+
wantPush: true,
67+
},
68+
{
69+
name: "error from RunCommand",
70+
runner: &stageRunner{},
71+
runCommandErr: errors.New("run command failed"),
72+
wantErr: true,
73+
wantCmd: stageCmdName,
74+
},
75+
}
76+
for _, test := range tests {
77+
t.Run(test.name, func(t *testing.T) {
78+
runCommandFn = func(ctx context.Context, command string, projectId string, push bool, build bool) error {
79+
if command != test.wantCmd {
80+
t.Errorf("runCommandFn() command = %v, want %v", command, test.wantCmd)
81+
}
82+
// Only check other args on success case to avoid nil pointer with empty runner
83+
if test.runCommandErr == nil {
84+
if projectId != test.wantProjectID {
85+
t.Errorf("runCommandFn() projectId = %v, want %v", projectId, test.wantProjectID)
86+
}
87+
if push != test.wantPush {
88+
t.Errorf("runCommandFn() push = %v, want %v", push, test.wantPush)
89+
}
90+
}
91+
return test.runCommandErr
92+
}
93+
94+
if err := test.runner.run(t.Context()); (err != nil) != test.wantErr {
95+
t.Errorf("run() error = %v, wantErr %v", err, test.wantErr)
96+
}
97+
})
98+
}
99+
}

0 commit comments

Comments
 (0)