Skip to content

Commit c959aea

Browse files
authored
feat(cmd,internal/sideflip): add initial command (#2961)
Create an initial sideflip CLI framework which prints the version command.
1 parent d976b10 commit c959aea

File tree

7 files changed

+265
-0
lines changed

7 files changed

+265
-0
lines changed

cmd/sideflip/main.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
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+
// Command sideflip creates, generates, and release client libraries.
16+
package main
17+
18+
import (
19+
"context"
20+
"log/slog"
21+
"os"
22+
23+
"github.com/googleapis/librarian/internal/sideflip/sideflip"
24+
)
25+
26+
func main() {
27+
ctx := context.Background()
28+
if err := sideflip.Run(ctx, os.Args[1:]...); err != nil {
29+
slog.Error("sideflip command failed", "err", err)
30+
os.Exit(1)
31+
}
32+
}

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ require (
1616
github.com/iancoleman/strcase v0.3.0
1717
github.com/pb33f/libopenapi v0.25.9
1818
github.com/pelletier/go-toml/v2 v2.2.4
19+
github.com/urfave/cli/v3 v3.6.1
1920
github.com/walle/targz v0.0.0-20140417120357-57fe4206da5a
2021
github.com/yuin/goldmark v1.7.13
2122
golang.org/x/exp v0.0.0-20250911091902-df9299821621

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,8 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf
123123
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
124124
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
125125
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
126+
github.com/urfave/cli/v3 v3.6.1 h1:j8Qq8NyUawj/7rTYdBGrxcH7A/j7/G8Q5LhWEW4G3Mo=
127+
github.com/urfave/cli/v3 v3.6.1/go.mod h1:ysVLtOEmg2tOy6PknnYVhDoouyC/6N42TMeoMzskhso=
126128
github.com/walle/targz v0.0.0-20140417120357-57fe4206da5a h1:6cKSHLRphD9Fo1LJlISiulvgYCIafJ3QfKLimPYcAGc=
127129
github.com/walle/targz v0.0.0-20140417120357-57fe4206da5a/go.mod h1:nccQrXCnc5SjsThFLmL7hYbtT/mHJcuolPifzY5vJqE=
128130
github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
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+
// http://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 sideflip provides functionality for onboarding, generating and
16+
// releasing Google Cloud client libraries.
17+
package sideflip
18+
19+
import (
20+
"context"
21+
"fmt"
22+
23+
"github.com/urfave/cli/v3"
24+
)
25+
26+
// Run executes the librarian command with the given arguments.
27+
func Run(ctx context.Context, args ...string) error {
28+
cmd := &cli.Command{
29+
Name: "librarian",
30+
Usage: "manage Google Cloud client libraries",
31+
UsageText: "librarian [command]",
32+
Version: Version(),
33+
Commands: []*cli.Command{
34+
versionCommand(),
35+
},
36+
}
37+
return cmd.Run(ctx, args)
38+
}
39+
40+
// versionCommand prints the version information.
41+
func versionCommand() *cli.Command {
42+
return &cli.Command{
43+
Name: "version",
44+
Usage: "print the version",
45+
UsageText: "librarian version",
46+
Action: func(ctx context.Context, cmd *cli.Command) error {
47+
fmt.Printf("librarian version %s\n", Version())
48+
return nil
49+
},
50+
}
51+
}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
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 sideflip
16+
17+
import (
18+
_ "embed"
19+
"runtime/debug"
20+
"strings"
21+
"time"
22+
)
23+
24+
//go:embed version.txt
25+
var versionString string
26+
27+
// Version return the version information for the binary, which is constructed
28+
// following https://go.dev/ref/mod#versions.
29+
func Version() string {
30+
info, ok := debug.ReadBuildInfo()
31+
if !ok {
32+
return ""
33+
}
34+
return version(info)
35+
}
36+
37+
func version(info *debug.BuildInfo) string {
38+
if info.Main.Version != "" && info.Main.Version != "(devel)" {
39+
return info.Main.Version
40+
}
41+
42+
var revision, at string
43+
for _, s := range info.Settings {
44+
if s.Key == "vcs.revision" {
45+
revision = s.Value
46+
}
47+
if s.Key == "vcs.time" {
48+
at = s.Value
49+
}
50+
}
51+
52+
if revision == "" && at == "" {
53+
return "not available"
54+
}
55+
56+
// Construct the pseudo-version string per
57+
// https://go.dev/ref/mod#pseudo-versions.
58+
var buf strings.Builder
59+
buf.WriteString(strings.TrimSpace(versionString))
60+
if revision != "" {
61+
buf.WriteString("-")
62+
// Per https://go.dev/ref/mod#pseudo-versions, only use the first 12
63+
// letters of the commit hash.
64+
if len(revision) > 12 {
65+
revision = revision[:12]
66+
}
67+
buf.WriteString(revision)
68+
}
69+
if at != "" {
70+
// commit time is of the form 2023-01-25T19:57:54Z
71+
p, err := time.Parse(time.RFC3339, at)
72+
if err == nil {
73+
buf.WriteString("-")
74+
buf.WriteString(p.Format("20060102150405"))
75+
}
76+
}
77+
return buf.String()
78+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
0.2.0
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
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 sideflip
16+
17+
import (
18+
"fmt"
19+
"runtime/debug"
20+
"strings"
21+
"testing"
22+
)
23+
24+
func TestVersion(t *testing.T) {
25+
baseVersion := strings.TrimSpace(versionString)
26+
for _, test := range []struct {
27+
name string
28+
want string
29+
buildinfo *debug.BuildInfo
30+
}{
31+
{
32+
name: "tagged version",
33+
want: "1.2.3",
34+
buildinfo: &debug.BuildInfo{
35+
Main: debug.Module{
36+
Version: "1.2.3",
37+
},
38+
},
39+
},
40+
{
41+
name: "pseudoversion",
42+
want: fmt.Sprintf("%s-123456789000-20230125195754", baseVersion),
43+
buildinfo: &debug.BuildInfo{
44+
Settings: []debug.BuildSetting{
45+
{Key: "vcs.revision", Value: "1234567890001234"},
46+
{Key: "vcs.time", Value: "2023-01-25T19:57:54Z"},
47+
},
48+
},
49+
},
50+
{
51+
name: "pseudoversion only revision",
52+
want: fmt.Sprintf("%s-123456789000", baseVersion),
53+
buildinfo: &debug.BuildInfo{
54+
Settings: []debug.BuildSetting{
55+
{Key: "vcs.revision", Value: "1234567890001234"},
56+
},
57+
},
58+
},
59+
{
60+
name: "pseudoversion only time",
61+
want: fmt.Sprintf("%s-20230102150405", baseVersion),
62+
buildinfo: &debug.BuildInfo{
63+
Settings: []debug.BuildSetting{
64+
{Key: "vcs.time", Value: "2023-01-02T15:04:05Z"},
65+
},
66+
},
67+
},
68+
{
69+
name: "pseudoversion invalid time",
70+
want: fmt.Sprintf("%s-123456789000", baseVersion),
71+
buildinfo: &debug.BuildInfo{
72+
Settings: []debug.BuildSetting{
73+
{Key: "vcs.revision", Value: "123456789000"},
74+
{Key: "vcs.time", Value: "invalid-time"},
75+
},
76+
},
77+
},
78+
{
79+
name: "revision less than 12 chars",
80+
want: fmt.Sprintf("%s-shortrev-20230125195754", baseVersion),
81+
buildinfo: &debug.BuildInfo{
82+
Settings: []debug.BuildSetting{
83+
{Key: "vcs.revision", Value: "shortrev"},
84+
{Key: "vcs.time", Value: "2023-01-25T19:57:54Z"},
85+
},
86+
},
87+
},
88+
{
89+
name: "local development",
90+
want: "not available",
91+
buildinfo: &debug.BuildInfo{},
92+
},
93+
} {
94+
t.Run(test.name, func(t *testing.T) {
95+
if got := version(test.buildinfo); got != test.want {
96+
t.Errorf("got %s; want %s", got, test.want)
97+
}
98+
})
99+
}
100+
}

0 commit comments

Comments
 (0)