Skip to content

Commit 421b42f

Browse files
committed
feat(internal/config): add NPM and Pip tool types
Add support for npm and pip to librarian install. Add nodejs.Install to install gapic-generator-typescript, gapic-node-processing, gapic-tools, and synthtool, and wire it into the install command for nodejs. For #4848
1 parent ff29dd7 commit 421b42f

File tree

9 files changed

+239
-11
lines changed

9 files changed

+239
-11
lines changed

.github/workflows/nodejs.yaml

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -40,17 +40,12 @@ jobs:
4040
- name: Display Python version
4141
run: python3 --version
4242
- uses: ./.github/actions/install-protoc
43-
- name: Install gapic-generator-typescript
43+
- name: Install Node.js tools
44+
# TODO(https://github.com/googleapis/librarian/issues/4848): download
45+
# librarian.yaml from google-cloud-node instead of creating a fake one.
4446
run: |
45-
git clone --depth 1 https://github.com/googleapis/google-cloud-node-core.git /tmp/google-cloud-node-core
46-
cd /tmp/google-cloud-node-core/generator/gapic-generator-typescript
47-
npm install
48-
npm run compile
49-
npm link
50-
- name: Install gapic-node-processing and gapic-tools
51-
run: npm install -g gapic-node-processing@0.1.7 gapic-tools@1.0.5
52-
- name: Install synthtool
53-
run: |
54-
pip install gcp-synthtool@git+https://github.com/googleapis/synthtool@5aa438a342707842d11fbbb302c6277fbf9e4655
47+
echo 'language: nodejs' > librarian.yaml
48+
go run ./cmd/librarian install
49+
rm librarian.yaml
5550
- name: Run internal/librarian/nodejs tests and check coverage
5651
run: go run ./tool/cmd/coverage ./internal/librarian/nodejs

internal/config/config.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ type Config struct {
6161
}
6262

6363
// Release holds the configuration parameter for publish command.
64+
//
65+
// TODO(https://github.com/googleapis/librarian/issues/4910): delete Release.
6466
type Release struct {
6567
// IgnoredChanges defines globs that are ignored in change analysis.
6668
IgnoredChanges []string `yaml:"ignored_changes,omitempty"`
@@ -128,6 +130,12 @@ type Source struct {
128130
type Tools struct {
129131
// Cargo defines tools to install via cargo.
130132
Cargo []*CargoTool `yaml:"cargo,omitempty"`
133+
134+
// NPM defines tools to install via npm.
135+
NPM []*NPMTool `yaml:"npm,omitempty"`
136+
137+
// Pip defines tools to install via pip.
138+
Pip []*PipTool `yaml:"pip,omitempty"`
131139
}
132140

133141
// CargoTool defines a tool to install via cargo.
@@ -139,6 +147,36 @@ type CargoTool struct {
139147
Version string `yaml:"version"`
140148
}
141149

150+
// NPMTool defines a tool to install via npm.
151+
type NPMTool struct {
152+
// Name is the npm package name.
153+
Name string `yaml:"name"`
154+
155+
// Version is the version to install.
156+
Version string `yaml:"version"`
157+
158+
// Package is the URL or path of the package to install.
159+
Package string `yaml:"package,omitempty"`
160+
161+
// Checksum is the SHA256 checksum of the package.
162+
Checksum string `yaml:"checksum,omitempty"`
163+
164+
// Build defines the commands to run to build the tool after installation.
165+
Build []string `yaml:"build,omitempty"`
166+
}
167+
168+
// PipTool defines a tool to install via pip.
169+
type PipTool struct {
170+
// Name is the pip package name.
171+
Name string `yaml:"name"`
172+
173+
// Version is the version to install.
174+
Version string `yaml:"version"`
175+
176+
// Package is the pip install specifier (e.g., "pkg@git+https://...").
177+
Package string `yaml:"package,omitempty"`
178+
}
179+
142180
// Default contains default settings for all libraries.
143181
type Default struct {
144182
// Keep lists files and directories to preserve during regeneration.

internal/config/config_test.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,32 @@ func TestNodejsRead(t *testing.T) {
271271
SHA256: "81e6057ffd85154af5268c2c3c8f2408745ca0f7fa03d43c68f4847f31eb5f98",
272272
},
273273
},
274+
Tools: &Tools{
275+
NPM: []*NPMTool{
276+
{
277+
Name: "gapic-generator-typescript",
278+
Version: "76ba85a7f55c6e82943008b4eceb07a0f58b39e1",
279+
Package: "https://github.com/googleapis/google-cloud-node-core/archive/76ba85a7f55c6e82943008b4eceb07a0f58b39e1.tar.gz",
280+
Checksum: "9561a116203761bad63bf1a0abc7a4a0db67608683c3d67b45abfc394df612ac",
281+
Build: []string{"npm install", "npm run compile", "npm link"},
282+
},
283+
{
284+
Name: "gapic-node-processing",
285+
Version: "0.1.7",
286+
},
287+
{
288+
Name: "gapic-tools",
289+
Version: "1.0.5",
290+
},
291+
},
292+
Pip: []*PipTool{
293+
{
294+
Name: "synthtool",
295+
Version: "5aa438a342707842d11fbbb302c6277fbf9e4655",
296+
Package: "gcp-synthtool@git+https://github.com/googleapis/synthtool@5aa438a342707842d11fbbb302c6277fbf9e4655",
297+
},
298+
},
299+
},
274300
Default: &Default{
275301
Output: "packages",
276302
Keep: []string{"CHANGELOG.md"},

internal/config/testdata/nodejs/librarian.yaml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,24 @@ sources:
33
googleapis:
44
commit: 9fcfbea0aa5b50fa22e190faceb073d74504172b
55
sha256: 81e6057ffd85154af5268c2c3c8f2408745ca0f7fa03d43c68f4847f31eb5f98
6+
tools:
7+
npm:
8+
- name: gapic-generator-typescript
9+
version: "76ba85a7f55c6e82943008b4eceb07a0f58b39e1"
10+
package: "https://github.com/googleapis/google-cloud-node-core/archive/76ba85a7f55c6e82943008b4eceb07a0f58b39e1.tar.gz"
11+
checksum: "9561a116203761bad63bf1a0abc7a4a0db67608683c3d67b45abfc394df612ac"
12+
build:
13+
- "npm install"
14+
- "npm run compile"
15+
- "npm link"
16+
- name: gapic-node-processing
17+
version: "0.1.7"
18+
- name: gapic-tools
19+
version: "1.0.5"
20+
pip:
21+
- name: synthtool
22+
version: "5aa438a342707842d11fbbb302c6277fbf9e4655"
23+
package: "gcp-synthtool@git+https://github.com/googleapis/synthtool@5aa438a342707842d11fbbb302c6277fbf9e4655"
624
default:
725
output: packages
826
keep:

internal/librarian/librarian.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
"github.com/googleapis/librarian/internal/command"
2525
"github.com/googleapis/librarian/internal/config"
2626
"github.com/googleapis/librarian/internal/librarian/golang"
27+
"github.com/googleapis/librarian/internal/librarian/nodejs"
2728
"github.com/googleapis/librarian/internal/librarian/rust"
2829
"github.com/googleapis/librarian/internal/yaml"
2930
"github.com/urfave/cli/v3"
@@ -77,6 +78,8 @@ func installCommand() *cli.Command {
7778
switch cfg.Language {
7879
case config.LanguageGo:
7980
return golang.Install(ctx)
81+
case config.LanguageNodejs:
82+
return nodejs.Install(ctx)
8083
case config.LanguageRust:
8184
return rust.Install(ctx, cfg.Tools)
8285
default:
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
// Copyright 2026 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 nodejs
16+
17+
import (
18+
"context"
19+
"fmt"
20+
"os"
21+
22+
"github.com/googleapis/librarian/internal/command"
23+
)
24+
25+
// TODO(https://github.com/googleapis/librarian/issues/4848): read tool
26+
// versions from google-cloud-node/librarian.yaml instead of hardcoding them.
27+
const (
28+
// TODO(https://github.com/googleapis/librarian/issues/4904): use google-cloud-node.
29+
gapicGeneratorTypescriptRepo = "https://github.com/googleapis/google-cloud-node-core.git"
30+
gapicNodeProcessingPkg = "gapic-node-processing@0.1.7"
31+
gapicToolsPkg = "gapic-tools@1.0.5"
32+
synthtoolPkg = "gcp-synthtool@git+https://github.com/googleapis/synthtool@5aa438a342707842d11fbbb302c6277fbf9e4655"
33+
)
34+
35+
// Install installs Node.js tool dependencies.
36+
func Install(ctx context.Context) error {
37+
if err := installGapicGeneratorTypescript(ctx); err != nil {
38+
return fmt.Errorf("installing gapic-generator-typescript: %w", err)
39+
}
40+
if err := command.Run(ctx, "npm", "install", "-g", gapicNodeProcessingPkg, gapicToolsPkg); err != nil {
41+
return fmt.Errorf("installing npm tools: %w", err)
42+
}
43+
if err := command.Run(ctx, "pip", "install", synthtoolPkg); err != nil {
44+
return fmt.Errorf("installing synthtool: %w", err)
45+
}
46+
return nil
47+
}
48+
49+
func installGapicGeneratorTypescript(ctx context.Context) error {
50+
dir, err := os.MkdirTemp("", "gapic-generator-typescript-*")
51+
if err != nil {
52+
return err
53+
}
54+
defer os.RemoveAll(dir)
55+
if err := command.Run(ctx, "git", "clone", "--depth", "1", gapicGeneratorTypescriptRepo, dir); err != nil {
56+
return err
57+
}
58+
genDir := dir + "/generator/gapic-generator-typescript"
59+
if err := command.RunInDir(ctx, genDir, "npm", "install"); err != nil {
60+
return err
61+
}
62+
if err := command.RunInDir(ctx, genDir, "npm", "run", "compile"); err != nil {
63+
return err
64+
}
65+
return command.RunInDir(ctx, genDir, "npm", "link")
66+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// Copyright 2026 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 nodejs
16+
17+
import (
18+
"os"
19+
"path/filepath"
20+
"testing"
21+
)
22+
23+
func TestInstall(t *testing.T) {
24+
bin := t.TempDir()
25+
// The fake git stub creates the directory structure that
26+
// installGapicGeneratorTypescript expects after cloning.
27+
gitStub := `#!/bin/sh
28+
for last; do true; done
29+
mkdir -p "$last/generator/gapic-generator-typescript"
30+
`
31+
if err := os.WriteFile(filepath.Join(bin, "git"), []byte(gitStub), 0o755); err != nil {
32+
t.Fatal(err)
33+
}
34+
if err := os.WriteFile(filepath.Join(bin, "npm"), []byte("#!/bin/sh\n"), 0o755); err != nil {
35+
t.Fatal(err)
36+
}
37+
if err := os.WriteFile(filepath.Join(bin, "pip"), []byte("#!/bin/sh\n"), 0o755); err != nil {
38+
t.Fatal(err)
39+
}
40+
t.Setenv("PATH", bin+":"+os.Getenv("PATH"))
41+
42+
if err := Install(t.Context()); err != nil {
43+
t.Fatal(err)
44+
}
45+
}

internal/librarian/tidy.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,12 @@ func formatConfig(cfg *config.Config) *config.Config {
195195
slices.SortFunc(cfg.Tools.Cargo, func(a, b *config.CargoTool) int {
196196
return strings.Compare(a.Name, b.Name)
197197
})
198+
slices.SortFunc(cfg.Tools.NPM, func(a, b *config.NPMTool) int {
199+
return strings.Compare(a.Name, b.Name)
200+
})
201+
slices.SortFunc(cfg.Tools.Pip, func(a, b *config.PipTool) int {
202+
return strings.Compare(a.Name, b.Name)
203+
})
198204
}
199205
if cfg.Default != nil && cfg.Default.Rust != nil {
200206
slices.SortFunc(cfg.Default.Rust.PackageDependencies, func(a, b *config.RustPackageDependency) int {

internal/librarian/tidy_test.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,15 @@ func TestFormatConfig(t *testing.T) {
109109
{Name: "taplo-cli", Version: "0.10.0"},
110110
{Name: "cargo-semver-checks", Version: "0.46.0"},
111111
},
112+
NPM: []*config.NPMTool{
113+
{Name: "gapic-tools", Version: "1.0.5"},
114+
{Name: "gapic-generator-typescript", Version: "1.0.0"},
115+
{Name: "gapic-node-processing", Version: "0.1.7"},
116+
},
117+
Pip: []*config.PipTool{
118+
{Name: "synthtool", Version: "abc123"},
119+
{Name: "nox", Version: "2024.1.1"},
120+
},
112121
},
113122
Default: &config.Default{
114123
Rust: &config.RustDefault{
@@ -208,6 +217,28 @@ func TestFormatConfig(t *testing.T) {
208217
t.Errorf("mismatch (-want +got):\n%s", diff)
209218
}
210219
})
220+
221+
t.Run("sorts npm tools by name", func(t *testing.T) {
222+
want := []string{"gapic-generator-typescript", "gapic-node-processing", "gapic-tools"}
223+
var got []string
224+
for _, tool := range cfg.Tools.NPM {
225+
got = append(got, tool.Name)
226+
}
227+
if diff := cmp.Diff(want, got); diff != "" {
228+
t.Errorf("mismatch (-want +got):\n%s", diff)
229+
}
230+
})
231+
232+
t.Run("sorts pip tools by name", func(t *testing.T) {
233+
want := []string{"nox", "synthtool"}
234+
var got []string
235+
for _, tool := range cfg.Tools.Pip {
236+
got = append(got, tool.Name)
237+
}
238+
if diff := cmp.Diff(want, got); diff != "" {
239+
t.Errorf("mismatch (-want +got):\n%s", diff)
240+
}
241+
})
211242
}
212243

213244
func TestTidyCommand(t *testing.T) {

0 commit comments

Comments
 (0)