Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions internal/captain/values.go
Original file line number Diff line number Diff line change
Expand Up @@ -155,9 +155,9 @@ func (p *PackageValue) Set(s string) error {
}
switch {
case strings.Contains(s, ":"):
v := strings.Split(s, ":")
p.Namespace = strings.TrimSpace(strings.Join(v[0:len(v)-1], ":"))
p.Name = strings.TrimSpace(v[len(v)-1])
namespace, name, _ := strings.Cut(s, ":")
p.Namespace = strings.TrimSpace(namespace)
p.Name = strings.TrimSpace(name)
case strings.Contains(s, "/"):
v := strings.Split(s, "/")
p.Namespace = strings.TrimSpace(strings.Join(v[0:len(v)-1], "/"))
Expand Down
28 changes: 22 additions & 6 deletions pkg/runtime/depot.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
configMediator "github.com/ActiveState/cli/internal/mediators/config"
"github.com/ActiveState/cli/internal/sliceutils"
"github.com/ActiveState/cli/internal/smartlink"
"github.com/ActiveState/cli/pkg/buildplan"
)

const (
Expand Down Expand Up @@ -52,6 +53,11 @@ type artifactInfo struct {
Size int64 `json:"size"`
LastAccessTime int64 `json:"lastAccessTime"`

// These fields are used by ecosystems during Add/Remove/Apply.
Namespace string `json:"namespace,omitempty"`
Name string `json:"name,omitempty"`
Version string `json:"version,omitempty"`

id strfmt.UUID // for convenience when removing stale artifacts; should NOT have json tag
}

Expand Down Expand Up @@ -218,7 +224,7 @@ func (d *depot) DeployViaLink(id strfmt.UUID, relativeSrc, absoluteDest string)
Path: absoluteDest,
Files: files.RelativePaths(),
RelativeSrc: relativeSrc,
})
}, nil)
if err != nil {
return errs.Wrap(err, "Could not record artifact use")
}
Expand Down Expand Up @@ -275,7 +281,7 @@ func (d *depot) DeployViaCopy(id strfmt.UUID, relativeSrc, absoluteDest string)
Path: absoluteDest,
Files: files.RelativePaths(),
RelativeSrc: relativeSrc,
})
}, nil)
if err != nil {
return errs.Wrap(err, "Could not record artifact use")
}
Expand All @@ -286,7 +292,9 @@ func (d *depot) DeployViaCopy(id strfmt.UUID, relativeSrc, absoluteDest string)
// Track will record an artifact deployment.
// This is automatically called by `DeployVia*()` functions.
// This should be called for ecosystems that handle installation of artifacts.
func (d *depot) Track(id strfmt.UUID, deploy *deployment) error {
// The artifact parameter is only necessary for tracking dynamically imported artifacts after being
// added by an ecosystem.
func (d *depot) Track(id strfmt.UUID, deploy *deployment, artifact *buildplan.Artifact) error {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
func (d *depot) Track(id strfmt.UUID, deploy *deployment, artifact *buildplan.Artifact) error {
func (d *depot) Track(artifact *buildplan.Artifact, deploy *deployment) error {

Could you try to make this the behaviour? The reason this was using id was to keep it as dumb as possible. Obviously for artifacts that ship has now sailed, I'd prefer we're consistent then in so far as that is possible.

This also means updating the Deploy functions. The real question mark here is going to be the Save() function, which far as I can tell is "abusing" the Track() function in order to clean up the cache. I don't recall specifically what the exact behaviour is there nor can I quickly grok it, but I would suggest spending some time there to try and see if we can remove its reliance on Track(). If this turns into a deep rabbit hole please push back.

d.mapMutex.Lock()
defer d.mapMutex.Unlock()

Expand All @@ -308,6 +316,14 @@ func (d *depot) Track(id strfmt.UUID, deploy *deployment) error {
}
d.config.Cache[id].InUse = true
d.config.Cache[id].LastAccessTime = time.Now().Unix()

// For dynamically imported artifacts, also include artifact metadata.
if artifact != nil {
d.config.Cache[id].Namespace = artifact.Ingredients[0].Namespace
d.config.Cache[id].Name = artifact.Name()
d.config.Cache[id].Version = artifact.Version()
}

return nil
}

Expand All @@ -317,8 +333,8 @@ func (d *depot) Track(id strfmt.UUID, deploy *deployment) error {
// This is automatically called by the `Undeploy()` function.
// This should be called for ecosystems that handle uninstallation of artifacts.
func (d *depot) Untrack(id strfmt.UUID, path string) {
if _, ok := d.config.Deployments[id]; ok {
d.config.Deployments[id] = sliceutils.Filter(d.config.Deployments[id], func(d deployment) bool { return d.Path != path })
if deployments, ok := d.config.Deployments[id]; ok {
d.config.Deployments[id] = sliceutils.Filter(deployments, func(d deployment) bool { return d.Path != path })
}
}

Expand Down Expand Up @@ -445,7 +461,7 @@ func (d *depot) Save() error {
for id := range d.artifacts {
if deployments, ok := d.config.Deployments[id]; !ok || len(deployments) == 0 {
if _, exists := d.config.Cache[id]; !exists {
err := d.Track(id, nil) // create cache entry for previously used artifact
err := d.Track(id, nil, nil) // create cache entry for previously used artifact
if err != nil {
return errs.Wrap(err, "Could not update depot cache with previously used artifact")
}
Expand Down
21 changes: 9 additions & 12 deletions pkg/runtime/ecosystem.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ type ecosystem interface {
Init(runtimePath string, buildplan *buildplan.BuildPlan) error
Namespaces() []string
Add(artifact *buildplan.Artifact, artifactSrcPath string) ([]string, error)
Remove(artifact *buildplan.Artifact) error
Remove(name, version string, installedFiles []string) error
Apply() error
}

Expand All @@ -36,12 +36,10 @@ func artifactMatchesEcosystem(a *buildplan.Artifact, e ecosystem) bool {
return false
}

func namespacesMatchesEcosystem(namespaces []string, e ecosystem) bool {
for _, namespace := range e.Namespaces() {
for _, n := range namespaces {
if n == namespace {
return true
}
func namespaceMatchesEcosystem(namespace string, e ecosystem) bool {
for _, n := range e.Namespaces() {
if n == namespace {
return true
}
}
return false
Expand All @@ -56,12 +54,11 @@ func filterEcosystemMatchingArtifact(artifact *buildplan.Artifact, ecosystems []
return nil
}

func filterEcosystemsMatchingNamespaces(ecosystems []ecosystem, namespaces []string) []ecosystem {
result := []ecosystem{}
func filterEcosystemMatchingNamespace(ecosystems []ecosystem, namespace string) ecosystem {
for _, ecosystem := range ecosystems {
if namespacesMatchesEcosystem(namespaces, ecosystem) {
result = append(result, ecosystem)
if namespaceMatchesEcosystem(namespace, ecosystem) {
return ecosystem
}
}
return result
return nil
}
13 changes: 11 additions & 2 deletions pkg/runtime/ecosystem/dotnet.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,17 @@ func (e *DotNet) Add(artifact *buildplan.Artifact, artifactSrcPath string) ([]st
return installedFiles, nil
}

func (e *DotNet) Remove(artifact *buildplan.Artifact) error {
return nil // TODO: CP-956
func (e *DotNet) Remove(name, version string, installedFiles []string) (rerr error) {
for _, dir := range installedFiles {
if !fileutils.DirExists(dir) {
continue
}
err := os.RemoveAll(dir)
if err != nil {
rerr = errs.Pack(rerr, errs.Wrap(err, "Unable to remove directory for '%s': %s", name, dir))
}
}
return rerr
}

func (e *DotNet) Apply() error {
Expand Down
62 changes: 57 additions & 5 deletions pkg/runtime/ecosystem/golang.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,17 @@ import (

"github.com/ActiveState/cli/internal/errs"
"github.com/ActiveState/cli/internal/fileutils"
"github.com/ActiveState/cli/internal/sliceutils"
"github.com/ActiveState/cli/internal/smartlink"
"github.com/ActiveState/cli/internal/unarchiver"
"github.com/ActiveState/cli/pkg/buildplan"
)

type Golang struct {
runtimeDir string
proxyDir string
addedModuleVersions map[string][]string
runtimeDir string
proxyDir string
addedModuleVersions map[string][]string
removedModuleVersions map[string][]string
}

func (e *Golang) Init(runtimePath string, buildplan *buildplan.BuildPlan) error {
Expand All @@ -29,6 +31,7 @@ func (e *Golang) Init(runtimePath string, buildplan *buildplan.BuildPlan) error
return errs.Wrap(err, "Unable to create Go proxy directory")
}
e.addedModuleVersions = make(map[string][]string)
e.removedModuleVersions = make(map[string][]string)
return nil
}

Expand Down Expand Up @@ -135,8 +138,28 @@ func (e *Golang) Add(artifact *buildplan.Artifact, artifactSrcPath string) (_ []
return installedFiles, nil
}

func (e *Golang) Remove(artifact *buildplan.Artifact) error {
return nil // TODO: CP-956
// Remove a module's .mod and .zip files from the filesystem proxy.
func (e *Golang) Remove(name, version string, installedFiles []string) (rerr error) {
for _, proxyDir := range installedFiles {
modFile := filepath.Join(proxyDir, "@v", version+".mod")
if fileutils.TargetExists(modFile) {
err := os.Remove(modFile)
if err != nil {
rerr = errs.Pack(rerr, errs.Wrap(err, "Unable to remove mod file for '%s': %s", name, modFile))
}
}

zipFile := filepath.Join(proxyDir, "@v", version+".zip")
if fileutils.TargetExists(zipFile) {
err := os.Remove(zipFile)
if err != nil {
rerr = errs.Pack(rerr, errs.Wrap(err, "Unable to remove zip file for '%s': %s", name, zipFile))
}
}

e.removedModuleVersions[name] = append(e.removedModuleVersions[name], version)
}
return rerr
}

// Create/update each added module's version list file.
Expand Down Expand Up @@ -165,5 +188,34 @@ func (e *Golang) Apply() error {
return errs.Wrap(err, "Unable to write %s", listFile)
}
}

for name, versions := range e.removedModuleVersions {
listFile := filepath.Join(e.runtimeDir, e.proxyDir, name, "@v", "list")
if !fileutils.FileExists(listFile) {
continue
}

// Read known versions and remove the ones being removed.
contents, err := fileutils.ReadFile(listFile)
if err != nil {
return errs.Wrap(err, "Unable to read %s", listFile)
}
knownVersions := strings.Split(string(contents), "\n")
knownVersions = sliceutils.Filter(knownVersions, func(version string) bool {
for _, v := range versions {
if v == version {
return false
}
}
return true
})

// Write the remaining versions.
err = fileutils.WriteFile(listFile, []byte(strings.Join(knownVersions, "\n")))
if err != nil {
return errs.Wrap(err, "Unable to write %s", listFile)
}
}

return nil
}
14 changes: 12 additions & 2 deletions pkg/runtime/ecosystem/java.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package ecosystem

import (
"encoding/json"
"os"
"path/filepath"
"strings"

Expand Down Expand Up @@ -59,8 +60,17 @@ func (e *Java) Add(artifact *buildplan.Artifact, artifactSrcPath string) ([]stri
return installedFiles, nil
}

func (e *Java) Remove(artifact *buildplan.Artifact) error {
return nil // TODO: CP-956
func (e *Java) Remove(name, version string, installedFiles []string) (rerr error) {
for _, file := range installedFiles {
if !fileutils.TargetExists(file) {
continue
}
err := os.Remove(file)
if err != nil {
rerr = errs.Pack(rerr, errs.Wrap(err, "Unable to remove installed file for '%s': %s", name, file))
}
}
return rerr
}

func (e *Java) Apply() error {
Expand Down
42 changes: 29 additions & 13 deletions pkg/runtime/ecosystem/javascript.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,14 @@ import (
const nodeModulesDir = "usr/lib/node_modules"

type JavaScript struct {
runtimePath string
packages []string
runtimePath string
installPackages []string
uninstallPackages []string
}

func (e *JavaScript) Init(runtimePath string, buildplan *buildplan.BuildPlan) error {
e.runtimePath = runtimePath
e.packages = []string{}
e.installPackages = []string{}
return nil
}

Expand Down Expand Up @@ -56,33 +57,48 @@ func (e *JavaScript) Add(artifact *buildplan.Artifact, artifactSrcPath string) (
packageName = file.Name()[:i]
}
}
e.packages = append(e.packages, file.AbsolutePath())
e.installPackages = append(e.installPackages, file.AbsolutePath())
}
installedDir := filepath.Join(nodeModulesDir, packageName) // Apply() will install here
return []string{installedDir}, nil
}

func (e *JavaScript) Remove(artifact *buildplan.Artifact) error {
return nil // TODO: CP-956
func (e *JavaScript) Remove(name, version string, installedFiles []string) error {
e.uninstallPackages = append(e.uninstallPackages, name)
return nil
}

func (e *JavaScript) Apply() error {
if len(e.packages) == 0 {
if len(e.installPackages) == 0 && len(e.uninstallPackages) == 0 {
return nil // nothing to do
}

binDir := filepath.Join(e.runtimePath, "usr", "bin")
args := []string{"install", "-g", "--offline"} // do not install to current directory
for _, arg := range e.packages {
args = append(args, arg)
installArgs := []string{"install", "-g", "--offline"} // do not install to current directory
for _, arg := range e.installPackages {
installArgs = append(installArgs, arg)
}
uninstallArgs := []string{"uninstall", "-g", "--no-save"} // do not remove from current directory
for _, arg := range e.uninstallPackages {
uninstallArgs = append(uninstallArgs, arg)
}
env := []string{
fmt.Sprintf("PATH=%s%s%s", binDir, string(os.PathListSeparator), os.Getenv("PATH")),
fmt.Sprintf("NPM_CONFIG_PREFIX=%s", filepath.Join(e.runtimePath, "usr")),
}
_, stderr, err := osutils.ExecSimple(filepath.Join(binDir, "npm"), args, env)
if err != nil {
return errs.Wrap(err, "Error running npm: %s", stderr)

if len(e.installPackages) > 0 {
_, stderr, err := osutils.ExecSimple(filepath.Join(binDir, "npm"), installArgs, env)
if err != nil {
return errs.Wrap(err, "Error running npm install: %s", stderr)
}
}

if len(e.uninstallPackages) > 0 {
_, stderr, err := osutils.ExecSimple(filepath.Join(binDir, "npm"), uninstallArgs, env)
if err != nil {
return errs.Wrap(err, "Error running npm uninstall: %s", stderr)
}
}
return nil
}
13 changes: 11 additions & 2 deletions pkg/runtime/ecosystem/rust.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,17 @@ func (e *Rust) Add(artifact *buildplan.Artifact, artifactSrcPath string) ([]stri
return installedFiles, nil
}

func (e *Rust) Remove(artifact *buildplan.Artifact) error {
return nil // TODO: CP-956
func (e *Rust) Remove(name, version string, installedFiles []string) (rerr error) {
for _, dir := range installedFiles {
if !fileutils.DirExists(dir) {
continue
}
err := os.RemoveAll(dir)
if err != nil {
rerr = errs.Pack(rerr, errs.Wrap(err, "Unable to remove directory for '%s': %s", name, dir))
}
}
return rerr
}

// configFileContents replaces the crates.io source with our vendored crates.
Expand Down
Loading
Loading