Skip to content

Commit a9e3af2

Browse files
committed
Add vendor operation for Go, Cargo, Bundler, rebar3, and pip
1 parent 12ad71a commit a9e3af2

9 files changed

Lines changed: 176 additions & 0 deletions

File tree

definitions/bundler.yaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,13 @@ commands:
111111
0: success
112112
1: error
113113

114+
# bundle cache caches .gem files into vendor/cache
115+
vendor:
116+
base: [cache]
117+
exit_codes:
118+
0: success
119+
1: error
120+
114121
capabilities:
115122
- install
116123
- install_frozen
@@ -122,3 +129,4 @@ capabilities:
122129
- outdated
123130
- json_output
124131
- path
132+
- vendor

definitions/cargo.yaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,13 @@ commands:
114114
extract_field: manifest_path
115115
strip_filename: true
116116

117+
# cargo vendor copies crate sources into vendor/
118+
vendor:
119+
base: [vendor]
120+
exit_codes:
121+
0: success
122+
1: error
123+
117124
capabilities:
118125
- install
119126
- install_frozen
@@ -124,5 +131,6 @@ capabilities:
124131
- list
125132
- workspace
126133
- path
134+
- vendor
127135
# No json_output for tree by default
128136
# No native outdated

definitions/gomod.yaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,13 @@ commands:
110110
type: json
111111
field: Dir
112112

113+
# go mod vendor copies all dependencies into vendor/
114+
vendor:
115+
base: [mod, vendor]
116+
exit_codes:
117+
0: success
118+
1: error
119+
113120
capabilities:
114121
- install
115122
- add
@@ -119,4 +126,5 @@ capabilities:
119126
- outdated
120127
- json_output
121128
- path
129+
- vendor
122130
# No add_dev - Go doesn't have dev dependencies

definitions/pip.yaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,13 @@ commands:
8888
type: line_prefix
8989
prefix: "Location: "
9090

91+
# pip download fetches packages into a directory
92+
vendor:
93+
base: [download, -r, requirements.txt, -d, vendor]
94+
exit_codes:
95+
0: success
96+
1: error
97+
9198
capabilities:
9299
- install
93100
- add
@@ -97,3 +104,4 @@ capabilities:
97104
- update
98105
- json_output
99106
- path
107+
- vendor

definitions/rebar3.yaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,16 @@ commands:
7676
type: template
7777
pattern: "_build/default/lib/{package}"
7878

79+
# rebar3 vendor turns dependencies into top-level apps
80+
vendor:
81+
base: [vendor]
82+
exit_codes:
83+
0: success
84+
1: error
85+
7986
capabilities:
8087
- install
8188
- list
8289
- update
8390
- path
91+
- vendor

generic_manager.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,20 @@ func (m *GenericManager) Capabilities() []Capability {
151151
return caps
152152
}
153153

154+
func (m *GenericManager) Vendor(ctx context.Context) (*Result, error) {
155+
input := CommandInput{
156+
Args: map[string]string{},
157+
Flags: map[string]any{},
158+
}
159+
160+
cmd, err := m.translator.BuildCommand(m.def.Name, "vendor", input)
161+
if err != nil {
162+
return nil, err
163+
}
164+
165+
return m.runner.Run(ctx, m.dir, cmd...)
166+
}
167+
154168
func (m *GenericManager) Path(ctx context.Context, pkg string) (*PathResult, error) {
155169
input := CommandInput{
156170
Args: map[string]string{

generic_manager_test.go

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,63 @@ func TestGenericManager_Path_NoPathCommand(t *testing.T) {
305305
}
306306
}
307307

308+
func TestGenericManager_Vendor(t *testing.T) {
309+
def := &definitions.Definition{
310+
Name: "gomod",
311+
Binary: "go",
312+
Commands: map[string]definitions.Command{
313+
"vendor": {
314+
Base: []string{"mod", "vendor"},
315+
},
316+
},
317+
Capabilities: []string{"vendor"},
318+
}
319+
320+
runner := NewMockRunner()
321+
runner.Results = []*Result{{
322+
ExitCode: 0,
323+
Stdout: "",
324+
}}
325+
326+
mgr := newTestManager(def, runner)
327+
result, err := mgr.Vendor(context.Background())
328+
if err != nil {
329+
t.Fatalf("Vendor failed: %v", err)
330+
}
331+
332+
if result.ExitCode != 0 {
333+
t.Errorf("got exit code %d, want 0", result.ExitCode)
334+
}
335+
336+
if len(runner.Captured) != 1 {
337+
t.Fatalf("expected 1 command, got %d", len(runner.Captured))
338+
}
339+
expected := []string{"go", "mod", "vendor"}
340+
if !slicesEqual(runner.Captured[0], expected) {
341+
t.Errorf("got command %v, want %v", runner.Captured[0], expected)
342+
}
343+
}
344+
345+
func TestGenericManager_Vendor_NoCommand(t *testing.T) {
346+
def := &definitions.Definition{
347+
Name: "testpkg",
348+
Binary: "testpkg",
349+
Commands: map[string]definitions.Command{
350+
"install": {
351+
Base: []string{"install"},
352+
},
353+
},
354+
Capabilities: []string{"install"},
355+
}
356+
357+
runner := NewMockRunner()
358+
mgr := newTestManager(def, runner)
359+
_, err := mgr.Vendor(context.Background())
360+
if err == nil {
361+
t.Error("expected error for missing vendor command, got nil")
362+
}
363+
}
364+
308365
func slicesEqual(a, b []string) bool {
309366
if len(a) != len(b) {
310367
return false

manager.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ type Manager interface {
1616
Outdated(ctx context.Context) (*Result, error)
1717
Update(ctx context.Context, pkg string) (*Result, error)
1818
Path(ctx context.Context, pkg string) (*PathResult, error)
19+
Vendor(ctx context.Context) (*Result, error)
1920

2021
Supports(cap Capability) bool
2122
Capabilities() []Capability
@@ -80,6 +81,7 @@ const (
8081
CapSBOMCycloneDX
8182
CapSBOMSPDX
8283
CapPath
84+
CapVendor
8385
)
8486

8587
var capabilityNames = map[Capability]string{
@@ -99,6 +101,7 @@ var capabilityNames = map[Capability]string{
99101
CapSBOMCycloneDX: "sbom_cyclonedx",
100102
CapSBOMSPDX: "sbom_spdx",
101103
CapPath: "path",
104+
CapVendor: "vendor",
102105
}
103106

104107
func (c Capability) String() string {

translator_test.go

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3294,3 +3294,65 @@ func TestRebar3Path(t *testing.T) {
32943294
t.Errorf("got %v, want %v", cmd, expected)
32953295
}
32963296
}
3297+
3298+
// --- vendor tests ---
3299+
3300+
func TestGomodVendor(t *testing.T) {
3301+
tr := loadTranslator(t)
3302+
cmd, err := tr.BuildCommand("gomod", "vendor", CommandInput{})
3303+
if err != nil {
3304+
t.Fatalf("BuildCommand failed: %v", err)
3305+
}
3306+
expected := []string{"go", "mod", "vendor"}
3307+
if !reflect.DeepEqual(cmd, expected) {
3308+
t.Errorf("got %v, want %v", cmd, expected)
3309+
}
3310+
}
3311+
3312+
func TestCargoVendor(t *testing.T) {
3313+
tr := loadTranslator(t)
3314+
cmd, err := tr.BuildCommand("cargo", "vendor", CommandInput{})
3315+
if err != nil {
3316+
t.Fatalf("BuildCommand failed: %v", err)
3317+
}
3318+
expected := []string{"cargo", "vendor"}
3319+
if !reflect.DeepEqual(cmd, expected) {
3320+
t.Errorf("got %v, want %v", cmd, expected)
3321+
}
3322+
}
3323+
3324+
func TestBundlerVendor(t *testing.T) {
3325+
tr := loadTranslator(t)
3326+
cmd, err := tr.BuildCommand("bundler", "vendor", CommandInput{})
3327+
if err != nil {
3328+
t.Fatalf("BuildCommand failed: %v", err)
3329+
}
3330+
expected := []string{"bundle", "cache"}
3331+
if !reflect.DeepEqual(cmd, expected) {
3332+
t.Errorf("got %v, want %v", cmd, expected)
3333+
}
3334+
}
3335+
3336+
func TestRebar3Vendor(t *testing.T) {
3337+
tr := loadTranslator(t)
3338+
cmd, err := tr.BuildCommand("rebar3", "vendor", CommandInput{})
3339+
if err != nil {
3340+
t.Fatalf("BuildCommand failed: %v", err)
3341+
}
3342+
expected := []string{"rebar3", "vendor"}
3343+
if !reflect.DeepEqual(cmd, expected) {
3344+
t.Errorf("got %v, want %v", cmd, expected)
3345+
}
3346+
}
3347+
3348+
func TestPipVendor(t *testing.T) {
3349+
tr := loadTranslator(t)
3350+
cmd, err := tr.BuildCommand("pip", "vendor", CommandInput{})
3351+
if err != nil {
3352+
t.Fatalf("BuildCommand failed: %v", err)
3353+
}
3354+
expected := []string{"pip", "download", "-r", "requirements.txt", "-d", "vendor"}
3355+
if !reflect.DeepEqual(cmd, expected) {
3356+
t.Errorf("got %v, want %v", cmd, expected)
3357+
}
3358+
}

0 commit comments

Comments
 (0)