Skip to content

Commit 9274b0b

Browse files
committed
Initial ecosystem implementation for Go.
1 parent 9530a56 commit 9274b0b

File tree

2 files changed

+144
-0
lines changed

2 files changed

+144
-0
lines changed

pkg/runtime/ecosystem.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ func init() {
2121
func() ecosystem { return &ecosys.JavaScript{} },
2222
func() ecosystem { return &ecosys.Rust{} },
2323
func() ecosystem { return &ecosys.DotNet{} },
24+
func() ecosystem { return &ecosys.Golang{} },
2425
}
2526
}
2627

pkg/runtime/ecosystem/golang.go

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
package ecosystem
2+
3+
import (
4+
"os"
5+
"path/filepath"
6+
"sort"
7+
"strings"
8+
9+
"golang.org/x/mod/semver"
10+
11+
"github.com/ActiveState/cli/internal/errs"
12+
"github.com/ActiveState/cli/internal/fileutils"
13+
"github.com/ActiveState/cli/internal/smartlink"
14+
"github.com/ActiveState/cli/internal/unarchiver"
15+
"github.com/ActiveState/cli/pkg/buildplan"
16+
)
17+
18+
type Golang struct {
19+
runtimeDir string
20+
proxyDir string
21+
addedModules map[string][]string
22+
}
23+
24+
func (e *Golang) Init(runtimePath string, buildplan *buildplan.BuildPlan) error {
25+
e.runtimeDir = runtimePath
26+
e.proxyDir = filepath.Join("usr", "goproxy")
27+
err := fileutils.MkdirUnlessExists(filepath.Join(e.runtimeDir, e.proxyDir))
28+
if err != nil {
29+
return errs.Wrap(err, "Unable to create Go proxy directory")
30+
}
31+
e.addedModules = make(map[string][]string)
32+
return nil
33+
}
34+
35+
func (e *Golang) Namespaces() []string {
36+
return []string{"language/golang"}
37+
}
38+
39+
// Unpack the module into the proxy directory.
40+
// We also inject the GOPROXY environment variable into runtime.json to force offline use.
41+
func (e *Golang) Add(artifact *buildplan.Artifact, artifactSrcPath string) ([]string, error) {
42+
installedFiles := []string{}
43+
44+
files, err := fileutils.ListDir(artifactSrcPath, false)
45+
if err != nil {
46+
return nil, errs.Wrap(err, "Unable to read artifact source directory")
47+
}
48+
49+
for _, file := range files {
50+
if file.Name() == "runtime.json" {
51+
err = injectEnvVar(file.AbsolutePath(), "GOPROXY", "file://${INSTALLDIR}/usr/goproxy")
52+
if err != nil {
53+
return nil, errs.Wrap(err, "Unable to add GOPROXY to runtime.json")
54+
}
55+
err = injectEnvVar(file.AbsolutePath(), "GOMODCACHE", "${INSTALLDIR}/usr/goproxy/cache")
56+
if err != nil {
57+
return nil, errs.Wrap(err, "Unable to add GOMODCACHE to runtime.json")
58+
}
59+
continue
60+
}
61+
if !strings.HasSuffix(file.Name(), ".zip") {
62+
continue
63+
}
64+
65+
// The structure of a Go proxy is:
66+
// proxydir
67+
// - example.com
68+
// - mymodule
69+
// - @v
70+
// - list
71+
// - v1.0.0.mod
72+
// - v1.0.0.zip
73+
74+
relativeProxied := filepath.Join(e.proxyDir, artifact.Name())
75+
absProxied := filepath.Join(e.runtimeDir, relativeProxied)
76+
77+
// Create the @v directory if it doesn't already exist.
78+
vDir := filepath.Join(absProxied, "@v")
79+
err = fileutils.MkdirUnlessExists(vDir)
80+
if err != nil {
81+
return nil, errs.Wrap(err, "Could not create proxy module @v directory")
82+
}
83+
84+
// Link/copy the zip file into the @v directory.
85+
err = smartlink.Link(file.AbsolutePath(), filepath.Join(vDir, file.Name()))
86+
87+
// Extract the go.mod from the zip and copy it into the @v directory with a versioned name.
88+
ua := unarchiver.NewZip()
89+
unpackDir := fileutils.TempFilePath("", "")
90+
f, size, err := ua.PrepareUnpacking(file.AbsolutePath(), unpackDir)
91+
if err != nil {
92+
return nil, errs.Wrap(err, "Unable to prepare for unpacking downloaded module")
93+
}
94+
err = ua.Unarchive(f, size, unpackDir)
95+
if err != nil {
96+
return nil, errs.Wrap(err, "Unable to unpack downloaded module")
97+
}
98+
err = fileutils.CopyFile(filepath.Join(unpackDir, artifact.NameAndVersion(), "go.mod"), filepath.Join(vDir, artifact.Version()+".mod"))
99+
if err != nil {
100+
return nil, errs.Wrap(err, "Unable to copy go.mod from unpacked module")
101+
}
102+
err = os.RemoveAll(unpackDir)
103+
if err != nil {
104+
return nil, errs.Wrap(err, "Unable to remove unpacked module")
105+
}
106+
107+
installedFiles = append(installedFiles, relativeProxied)
108+
}
109+
110+
e.addedModules[artifact.Name()] = append(e.addedModules[artifact.Name()], artifact.Version())
111+
112+
return installedFiles, nil
113+
}
114+
115+
func (e *Golang) Remove(artifact *buildplan.Artifact) error {
116+
return nil // TODO: CP-956
117+
}
118+
119+
// Create/update each added module's version list file.
120+
func (e *Golang) Apply() error {
121+
for name, versions := range e.addedModules {
122+
listFile := filepath.Join(e.runtimeDir, e.proxyDir, name, "@v", "list")
123+
if fileutils.FileExists(listFile) {
124+
contents, err := fileutils.ReadFile(listFile)
125+
if err != nil {
126+
return errs.Wrap(err, "Unable to read %s", listFile)
127+
}
128+
for _, version := range strings.Split(string(contents), "\n") {
129+
versions = append(versions, version)
130+
}
131+
}
132+
133+
sort.SliceStable(versions, func(i, j int) bool {
134+
return semver.Compare(versions[i], versions[j]) < 0
135+
})
136+
137+
err := fileutils.WriteFile(listFile, []byte(strings.Join(versions, "\n")))
138+
if err != nil {
139+
return errs.Wrap(err, "Unable to write %s", listFile)
140+
}
141+
}
142+
return nil
143+
}

0 commit comments

Comments
 (0)