Skip to content

Commit a82ee4b

Browse files
authored
librarian: add publish command and supporting config (#3262)
This change is part 1 of changes to port over `rust-publish` for Rust. - setup skeleton of `librarian publish` command - copy Rust [release related config](https://github.com/googleapis/librarian/blob/main/internal/sidekick/config/release.go) into a new Release struct in config. This is to allow seamless migrate `rust-publish` to librarian for now, may be refactored later. For #3153
1 parent 6a4a57e commit a82ee4b

File tree

4 files changed

+155
-0
lines changed

4 files changed

+155
-0
lines changed

internal/config/config.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,52 @@ type Config struct {
3333
// Libraries contains configuration overrides for libraries that need
3434
// special handling, and differ from default settings.
3535
Libraries []*Library `yaml:"libraries,omitempty"`
36+
37+
// Release holds the configuration parameter for any `${lang}-release` subcommand.
38+
Release *Release `yaml:"release,omitempty"`
39+
}
40+
41+
// Release holds the configuration parameter for publish command.
42+
type Release struct {
43+
// Remote sets the name of the source-of-truth remote for releases, typically `upstream`.
44+
Remote string `yaml:"remote,omitempty"`
45+
46+
// Branch sets the name of the release branch, typically `main`
47+
Branch string `yaml:"branch,omitempty"`
48+
49+
// Tools defines the list of tools to install, indexed by installer.
50+
Tools map[string][]Tool `yaml:"tools,omitempty"`
51+
52+
// Preinstalled tools defines the list of tools that must be pre-installed.
53+
//
54+
// This is indexed by the well-known name of the tool vs. its path, e.g.
55+
// [preinstalled]
56+
// cargo = /usr/bin/cargo
57+
Preinstalled map[string]string `yaml:"preinstalled,omitempty"`
58+
59+
// IgnoredChanges defines globs that are ignored in change analysis.
60+
IgnoredChanges []string `yaml:"ignored_changes,omitempty"`
61+
62+
// An alternative location for the `roots.pem` file. If empty it has no
63+
// effect.
64+
RootsPem string `yaml:"roots_pem,omitempty"`
65+
}
66+
67+
// GetExecutablePath finds the path for a given command, checking for an
68+
// override in the configuration first.
69+
func (r *Release) GetExecutablePath(commandName string) string {
70+
if r != nil && r.Preinstalled != nil {
71+
if exe, ok := r.Preinstalled[commandName]; ok {
72+
return exe
73+
}
74+
}
75+
return commandName
76+
}
77+
78+
// Tool defines the configuration required to install helper tools.
79+
type Tool struct {
80+
Name string `yaml:"name"`
81+
Version string `yaml:"version,omitempty"`
3682
}
3783

3884
// Sources references external source repositories.

internal/config/config_test.go

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,3 +137,54 @@ func TestWrite(t *testing.T) {
137137
t.Errorf("mismatch (-want +got):\n%s", diff)
138138
}
139139
}
140+
141+
func TestReleaseGetExecutablePath(t *testing.T) {
142+
tests := []struct {
143+
name string
144+
releaseConfig *Release
145+
executableName string
146+
want string
147+
}{
148+
{
149+
name: "Preinstalled tool found",
150+
releaseConfig: &Release{
151+
Preinstalled: map[string]string{
152+
"cargo": "/usr/bin/cargo",
153+
"git": "/usr/bin/git",
154+
},
155+
},
156+
executableName: "cargo",
157+
want: "/usr/bin/cargo",
158+
},
159+
{
160+
name: "Preinstalled tool not found",
161+
releaseConfig: &Release{
162+
Preinstalled: map[string]string{
163+
"git": "/usr/bin/git",
164+
},
165+
},
166+
executableName: "cargo",
167+
want: "cargo",
168+
},
169+
{
170+
name: "No preinstalled section",
171+
releaseConfig: &Release{},
172+
executableName: "cargo",
173+
want: "cargo",
174+
},
175+
{
176+
name: "Nil release config",
177+
releaseConfig: nil,
178+
executableName: "cargo",
179+
want: "cargo",
180+
},
181+
}
182+
for _, test := range tests {
183+
t.Run(test.name, func(t *testing.T) {
184+
got := test.releaseConfig.GetExecutablePath(test.executableName)
185+
if diff := cmp.Diff(test.want, got); diff != "" {
186+
t.Errorf("GetExecutablePath() mismatch (-want +got):\n%s", diff)
187+
}
188+
})
189+
}
190+
}

internal/librarian/librarian.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ func Run(ctx context.Context, args ...string) error {
3939
tidyCommand(),
4040
updateCommand(),
4141
versionCommand(),
42+
publishCommand(),
4243
},
4344
}
4445
return cmd.Run(ctx, args)

internal/librarian/publish.go

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
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 librarian
16+
17+
import (
18+
"context"
19+
"fmt"
20+
21+
"github.com/googleapis/librarian/internal/config"
22+
"github.com/googleapis/librarian/internal/yaml"
23+
"github.com/urfave/cli/v3"
24+
)
25+
26+
func publishCommand() *cli.Command {
27+
return &cli.Command{
28+
Name: "publish",
29+
Usage: "publishes client libraries",
30+
UsageText: "librarian publish",
31+
Flags: []cli.Flag{
32+
&cli.BoolFlag{
33+
Name: "dry-run",
34+
Usage: "print commands without executing",
35+
},
36+
&cli.BoolFlag{
37+
Name: "skip-semver-checks",
38+
Usage: "skip semantic versioning checks",
39+
},
40+
},
41+
Action: func(ctx context.Context, cmd *cli.Command) error {
42+
cfg, err := yaml.Read[config.Config](librarianConfigPath)
43+
if err != nil {
44+
return err
45+
}
46+
dryRun := cmd.Bool("dry-run")
47+
skipSemverChecks := cmd.Bool("skip-semver-checks")
48+
return publish(ctx, cfg, dryRun, skipSemverChecks)
49+
},
50+
}
51+
}
52+
53+
func publish(ctx context.Context, cfg *config.Config, dryRun bool, skipSemverChecks bool) error {
54+
// TODO: Not yet implemented.
55+
fmt.Printf("publish not implemented. ctx: %v, cfg: %v, dryRun: %v, skipSemverChecks: %v\n", ctx, cfg, dryRun, skipSemverChecks)
56+
panic("not implemented")
57+
}

0 commit comments

Comments
 (0)