Skip to content

Commit e475c96

Browse files
committed
Add transitive dependency support
1 parent bf26b85 commit e475c96

File tree

1 file changed

+86
-11
lines changed

1 file changed

+86
-11
lines changed

pack.go

Lines changed: 86 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,23 @@
11
package main
22

33
import (
4+
"errors"
45
"io/ioutil"
56
"log"
67
"os"
78
"os/exec"
89
"path/filepath"
10+
"strings"
11+
"unicode"
912

1013
"github.com/go-sharp/color"
1114
)
1215

1316
type PackCmd struct {
14-
Module []string `short:"m" long:"module" description:"Modules to pack (github.com/jessevdk/go-flags or github.com/jessevdk/go-flags@v1.4.0)"`
15-
ModFile string `short:"g" long:"go-mod-file" description:"Pack all dependencies specified in go.mod file."`
16-
Output string `short:"o" long:"out" description:"Output file name of the zip archive." default:"gop_dependencies.zip"`
17+
Module []string `short:"m" long:"module" description:"Modules to pack (github.com/jessevdk/go-flags or github.com/jessevdk/go-flags@v1.4.0)"`
18+
ModFile string `short:"g" long:"go-mod-file" description:"Pack all dependencies specified in go.mod file."`
19+
Output string `short:"o" long:"out" description:"Output file name of the zip archive." default:"gop_dependencies.zip"`
20+
DoTransitive bool `short:"t" long:"transitive" description:"Ensure all transitive dependencies are included."`
1721
}
1822

1923
// Execute will be called for the last active (sub)command. The
@@ -53,21 +57,20 @@ func (p *PackCmd) Execute(args []string) error {
5357

5458
for _, m := range p.Module {
5559
verboseF("adding module: %v\n", color.BlueString(m))
56-
cmd := exec.Command(commonOpts.GoBinPath, "get", m)
57-
cmd.Dir = workDir
58-
cmd.Env = append(os.Environ(), "GOMODCACHE="+modCache)
59-
if output, err := cmd.CombinedOutput(); err != nil {
60+
if output, err := getGoCommand(workDir, modCache, "get", m).CombinedOutput(); err != nil {
6061
log.Printf("failed to add module: %v\n", color.RedString(m))
6162
verboseF("%v: \n%s", color.RedString("error"), output)
6263
}
6364
}
65+
66+
}
67+
68+
if p.DoTransitive {
69+
p.addTransitive(workDir, modCache)
6470
}
6571

6672
log.Println("download all dependencies")
67-
cmd := exec.Command(commonOpts.GoBinPath, "mod", "download", "all")
68-
cmd.Dir = workDir
69-
cmd.Env = append(os.Environ(), "GOMODCACHE="+modCache)
70-
if err := cmd.Run(); err != nil {
73+
if err := getGoCommand(workDir, modCache, "mod", "download", "all").Run(); err != nil {
7174
log.Fatalln("failed to download dependencies:", color.RedString(err.Error()))
7275
}
7376

@@ -78,3 +81,75 @@ func (p *PackCmd) Execute(args []string) error {
7881
log.Println("archive created:", color.GreenString(p.Output))
7982
return nil
8083
}
84+
85+
func (p *PackCmd) addTransitive(workDir, modCache string) {
86+
hasMore := false
87+
88+
for {
89+
output, err := getGoCommand(workDir, modCache, "mod", "graph").Output()
90+
if err != nil {
91+
log.Println("failed to add transitive dependencies:", color.RedString(err.Error()))
92+
return
93+
}
94+
95+
deps := strings.Split(string(output), "\n")
96+
if len(deps) == 0 {
97+
return
98+
}
99+
100+
for _, dep := range deps {
101+
mods := strings.Split(dep, " ")
102+
mod := strings.Trim(mods[len(mods)-1], " ")
103+
104+
if mod == "" || folderExists(filepath.Join(modCache, moduleNameToCaseInsensitive(mod))) {
105+
continue
106+
}
107+
108+
verboseF("adding transitive module: %v\n", color.BlueString(mod))
109+
if output, err := getGoCommand(workDir, modCache, "get", mod).CombinedOutput(); err != nil {
110+
log.Printf("failed to add module: %v\n", color.RedString(mod))
111+
verboseF("%v: \n%s", color.RedString("error"), output)
112+
}
113+
hasMore = true
114+
}
115+
116+
if hasMore {
117+
hasMore = false
118+
continue
119+
}
120+
break
121+
}
122+
123+
}
124+
125+
func getGoCommand(workDir, modCache string, args ...string) *exec.Cmd {
126+
cmd := exec.Command(commonOpts.GoBinPath, args...)
127+
cmd.Dir = workDir
128+
cmd.Env = append(os.Environ(), "GOMODCACHE="+modCache)
129+
130+
return cmd
131+
}
132+
133+
func folderExists(name string) bool {
134+
if _, err := os.Stat(name); errors.Is(err, os.ErrNotExist) {
135+
return false
136+
}
137+
138+
return true
139+
}
140+
141+
func moduleNameToCaseInsensitive(name string) string {
142+
name = filepath.ToSlash(name)
143+
var modName []rune
144+
145+
for _, v := range name {
146+
if unicode.IsUpper(v) {
147+
modName = append(modName, '!', unicode.ToLower(v))
148+
continue
149+
}
150+
151+
modName = append(modName, v)
152+
}
153+
154+
return string(modName)
155+
}

0 commit comments

Comments
 (0)