Skip to content

Commit 905974f

Browse files
authored
Merge pull request #3716 from ActiveState/mitchell/cp-956
Initial support for removing dynamically imported artifacts.
2 parents d29a589 + 3e95cab commit 905974f

File tree

9 files changed

+245
-99
lines changed

9 files changed

+245
-99
lines changed

internal/captain/values.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -155,9 +155,9 @@ func (p *PackageValue) Set(s string) error {
155155
}
156156
switch {
157157
case strings.Contains(s, ":"):
158-
v := strings.Split(s, ":")
159-
p.Namespace = strings.TrimSpace(strings.Join(v[0:len(v)-1], ":"))
160-
p.Name = strings.TrimSpace(v[len(v)-1])
158+
namespace, name, _ := strings.Cut(s, ":")
159+
p.Namespace = strings.TrimSpace(namespace)
160+
p.Name = strings.TrimSpace(name)
161161
case strings.Contains(s, "/"):
162162
v := strings.Split(s, "/")
163163
p.Namespace = strings.TrimSpace(strings.Join(v[0:len(v)-1], "/"))

pkg/runtime/depot.go

Lines changed: 72 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
configMediator "github.com/ActiveState/cli/internal/mediators/config"
2222
"github.com/ActiveState/cli/internal/sliceutils"
2323
"github.com/ActiveState/cli/internal/smartlink"
24+
"github.com/ActiveState/cli/pkg/buildplan"
2425
)
2526

2627
const (
@@ -52,6 +53,11 @@ type artifactInfo struct {
5253
Size int64 `json:"size"`
5354
LastAccessTime int64 `json:"lastAccessTime"`
5455

56+
// These fields are used by ecosystems during Add/Remove/Apply.
57+
Namespace string `json:"namespace,omitempty"`
58+
Name string `json:"name,omitempty"`
59+
Version string `json:"version,omitempty"`
60+
5561
id strfmt.UUID // for convenience when removing stale artifacts; should NOT have json tag
5662
}
5763

@@ -148,9 +154,19 @@ func (d *depot) SetCacheSize(mb int) {
148154
d.cacheSize = int64(mb) * MB
149155
}
150156

151-
func (d *depot) Exists(id strfmt.UUID) bool {
152-
_, ok := d.artifacts[id]
153-
return ok
157+
// Exists returns whether or not an artifact ID exists in the depot, along with any known metadata
158+
// associated with that artifact ID.
159+
// Existence is merely whether a directory with the name of the given ID exists on the filesystem.
160+
// Artifact metadata comes from the depot's cache, and may not exist for installed artifacts that
161+
// predate the cache.
162+
func (d *depot) Exists(id strfmt.UUID) (bool, *artifactInfo) {
163+
if _, ok := d.artifacts[id]; ok {
164+
if artifact, exists := d.config.Cache[id]; exists {
165+
return true, artifact
166+
}
167+
return true, nil
168+
}
169+
return false, nil
154170
}
155171

156172
func (d *depot) Path(id strfmt.UUID) string {
@@ -174,84 +190,80 @@ func (d *depot) Put(id strfmt.UUID) error {
174190
}
175191

176192
// DeployViaLink will take an artifact from the depot and link it to the target path.
177-
func (d *depot) DeployViaLink(id strfmt.UUID, relativeSrc, absoluteDest string) error {
193+
// It should return deployment info to be used for tracking the artifact.
194+
func (d *depot) DeployViaLink(id strfmt.UUID, relativeSrc, absoluteDest string) (*deployment, error) {
178195
d.fsMutex.Lock()
179196
defer d.fsMutex.Unlock()
180197

181-
if !d.Exists(id) {
182-
return errs.New("artifact not found in depot")
198+
if exists, _ := d.Exists(id); !exists {
199+
return nil, errs.New("artifact not found in depot")
183200
}
184201

185202
if err := d.validateVolume(absoluteDest); err != nil {
186-
return errs.Wrap(err, "volume validation failed")
203+
return nil, errs.Wrap(err, "volume validation failed")
187204
}
188205

189206
// Collect artifact meta info
190207
var err error
191208
absoluteDest, err = fileutils.ResolvePath(absoluteDest)
192209
if err != nil {
193-
return errs.Wrap(err, "failed to resolve path")
210+
return nil, errs.Wrap(err, "failed to resolve path")
194211
}
195212

196213
if err := fileutils.MkdirUnlessExists(absoluteDest); err != nil {
197-
return errs.Wrap(err, "failed to create path")
214+
return nil, errs.Wrap(err, "failed to create path")
198215
}
199216

200217
absoluteSrc := filepath.Join(d.Path(id), relativeSrc)
201218
if !fileutils.DirExists(absoluteSrc) {
202-
return errs.New("artifact src does not exist: %s", absoluteSrc)
219+
return nil, errs.New("artifact src does not exist: %s", absoluteSrc)
203220
}
204221

205222
// Copy or link the artifact files, depending on whether the artifact in question relies on file transformations
206223
if err := smartlink.LinkContents(absoluteSrc, absoluteDest); err != nil {
207-
return errs.Wrap(err, "failed to link artifact")
224+
return nil, errs.Wrap(err, "failed to link artifact")
208225
}
209226

210227
files, err := fileutils.ListDir(absoluteSrc, false)
211228
if err != nil {
212-
return errs.Wrap(err, "failed to list files")
229+
return nil, errs.Wrap(err, "failed to list files")
213230
}
214231

215-
// Record deployment to config
216-
err = d.Track(id, &deployment{
232+
return &deployment{
217233
Type: deploymentTypeLink,
218234
Path: absoluteDest,
219235
Files: files.RelativePaths(),
220236
RelativeSrc: relativeSrc,
221-
})
222-
if err != nil {
223-
return errs.Wrap(err, "Could not record artifact use")
224-
}
225-
226-
return nil
237+
}, nil
227238
}
228239

229240
// DeployViaCopy will take an artifact from the depot and copy it to the target path.
230-
func (d *depot) DeployViaCopy(id strfmt.UUID, relativeSrc, absoluteDest string) error {
241+
// It should return deployment info to be used for tracking the artifact.
242+
func (d *depot) DeployViaCopy(id strfmt.UUID, relativeSrc, absoluteDest string) (*deployment, error) {
231243
d.fsMutex.Lock()
232244
defer d.fsMutex.Unlock()
233245

234-
if !d.Exists(id) {
235-
return errs.New("artifact not found in depot")
246+
if exists, _ := d.Exists(id); !exists {
247+
return nil, errs.New("artifact not found in depot")
236248
}
237249

238250
var err error
239251
absoluteDest, err = fileutils.ResolvePath(absoluteDest)
240252
if err != nil {
241-
return errs.Wrap(err, "failed to resolve path")
253+
return nil, errs.Wrap(err, "failed to resolve path")
242254
}
243255

244256
if err := d.validateVolume(absoluteDest); err != nil {
245-
return errs.Wrap(err, "volume validation failed")
257+
return nil, errs.Wrap(err, "volume validation failed")
246258
}
247259

248260
if err := fileutils.MkdirUnlessExists(absoluteDest); err != nil {
249-
return errs.Wrap(err, "failed to create path")
261+
return nil, errs.Wrap(err, "failed to create path")
250262
}
251263

252264
absoluteSrc := filepath.Join(d.Path(id), relativeSrc)
253265
if !fileutils.DirExists(absoluteSrc) {
254-
return errs.New("artifact src does not exist: %s", absoluteSrc)
266+
return nil, errs.New("artifact src does not exist: %s", absoluteSrc)
255267
}
256268

257269
// Copy or link the artifact files, depending on whether the artifact in question relies on file transformations
@@ -260,36 +272,30 @@ func (d *depot) DeployViaCopy(id strfmt.UUID, relativeSrc, absoluteDest string)
260272
if errors.As(err, &errExist) {
261273
logging.Warning("Skipping files that already exist: " + errs.JoinMessage(errExist))
262274
} else {
263-
return errs.Wrap(err, "failed to copy artifact")
275+
return nil, errs.Wrap(err, "failed to copy artifact")
264276
}
265277
}
266278

267279
files, err := fileutils.ListDir(absoluteSrc, false)
268280
if err != nil {
269-
return errs.Wrap(err, "failed to list files")
281+
return nil, errs.Wrap(err, "failed to list files")
270282
}
271283

272-
// Record deployment to config
273-
err = d.Track(id, &deployment{
284+
return &deployment{
274285
Type: deploymentTypeCopy,
275286
Path: absoluteDest,
276287
Files: files.RelativePaths(),
277288
RelativeSrc: relativeSrc,
278-
})
279-
if err != nil {
280-
return errs.Wrap(err, "Could not record artifact use")
281-
}
282-
283-
return nil
289+
}, nil
284290
}
285291

286292
// Track will record an artifact deployment.
287-
// This is automatically called by `DeployVia*()` functions.
288-
// This should be called for ecosystems that handle installation of artifacts.
289-
func (d *depot) Track(id strfmt.UUID, deploy *deployment) error {
293+
func (d *depot) Track(artifact *buildplan.Artifact, deploy *deployment) error {
290294
d.mapMutex.Lock()
291295
defer d.mapMutex.Unlock()
292296

297+
id := artifact.ArtifactID
298+
293299
// Record deployment of this artifact.
294300
if _, ok := d.config.Deployments[id]; !ok {
295301
d.config.Deployments[id] = []deployment{}
@@ -308,6 +314,21 @@ func (d *depot) Track(id strfmt.UUID, deploy *deployment) error {
308314
}
309315
d.config.Cache[id].InUse = true
310316
d.config.Cache[id].LastAccessTime = time.Now().Unix()
317+
318+
// For dynamically imported artifacts, also include artifact metadata.
319+
if artifact != nil {
320+
d.config.Cache[id].Namespace = artifact.Ingredients[0].Namespace
321+
d.config.Cache[id].Name = artifact.Name()
322+
d.config.Cache[id].Version = artifact.Version()
323+
}
324+
325+
return nil
326+
}
327+
328+
func (d *depot) Deployments(id strfmt.UUID) []deployment {
329+
if deployments, ok := d.config.Deployments[id]; ok {
330+
return deployments
331+
}
311332
return nil
312333
}
313334

@@ -317,16 +338,16 @@ func (d *depot) Track(id strfmt.UUID, deploy *deployment) error {
317338
// This is automatically called by the `Undeploy()` function.
318339
// This should be called for ecosystems that handle uninstallation of artifacts.
319340
func (d *depot) Untrack(id strfmt.UUID, path string) {
320-
if _, ok := d.config.Deployments[id]; ok {
321-
d.config.Deployments[id] = sliceutils.Filter(d.config.Deployments[id], func(d deployment) bool { return d.Path != path })
341+
if deployments, ok := d.config.Deployments[id]; ok {
342+
d.config.Deployments[id] = sliceutils.Filter(deployments, func(d deployment) bool { return d.Path != path })
322343
}
323344
}
324345

325346
func (d *depot) Undeploy(id strfmt.UUID, relativeSrc, path string) error {
326347
d.fsMutex.Lock()
327348
defer d.fsMutex.Unlock()
328349

329-
if !d.Exists(id) {
350+
if exists, _ := d.Exists(id); !exists {
330351
return errs.New("artifact not found in depot")
331352
}
332353

@@ -341,7 +362,10 @@ func (d *depot) Undeploy(id strfmt.UUID, relativeSrc, path string) error {
341362
if !ok {
342363
return errs.New("deployment for %s not found in depot", id)
343364
}
344-
deployments = sliceutils.Filter(deployments, func(d deployment) bool { return d.Path == path })
365+
deployments = sliceutils.Filter(deployments, func(d deployment) bool {
366+
equal, _ := fileutils.PathsEqual(d.Path, path)
367+
return equal
368+
})
345369
if len(deployments) != 1 {
346370
return errs.New("no deployment found for %s in depot", path)
347371
}
@@ -445,10 +469,12 @@ func (d *depot) Save() error {
445469
for id := range d.artifacts {
446470
if deployments, ok := d.config.Deployments[id]; !ok || len(deployments) == 0 {
447471
if _, exists := d.config.Cache[id]; !exists {
448-
err := d.Track(id, nil) // create cache entry for previously used artifact
472+
// Create cache entry for previously used artifact.
473+
size, err := fileutils.GetDirSize(d.Path(id))
449474
if err != nil {
450-
return errs.Wrap(err, "Could not update depot cache with previously used artifact")
475+
return errs.Wrap(err, "Could not get artifact size on disk")
451476
}
477+
d.config.Cache[id] = &artifactInfo{Size: size, id: id}
452478
}
453479
d.config.Cache[id].InUse = false
454480
logging.Debug("Artifact '%s' is no longer in use", id.String())

pkg/runtime/ecosystem.go

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ type ecosystem interface {
99
Init(runtimePath string, buildplan *buildplan.BuildPlan) error
1010
Namespaces() []string
1111
Add(artifact *buildplan.Artifact, artifactSrcPath string) ([]string, error)
12-
Remove(artifact *buildplan.Artifact) error
12+
Remove(name, version string, installedFiles []string) error
1313
Apply() error
1414
}
1515

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

39-
func namespacesMatchesEcosystem(namespaces []string, e ecosystem) bool {
40-
for _, namespace := range e.Namespaces() {
41-
for _, n := range namespaces {
42-
if n == namespace {
43-
return true
44-
}
39+
func namespaceMatchesEcosystem(namespace string, e ecosystem) bool {
40+
for _, n := range e.Namespaces() {
41+
if n == namespace {
42+
return true
4543
}
4644
}
4745
return false
@@ -56,12 +54,11 @@ func filterEcosystemMatchingArtifact(artifact *buildplan.Artifact, ecosystems []
5654
return nil
5755
}
5856

59-
func filterEcosystemsMatchingNamespaces(ecosystems []ecosystem, namespaces []string) []ecosystem {
60-
result := []ecosystem{}
57+
func filterEcosystemMatchingNamespace(ecosystems []ecosystem, namespace string) ecosystem {
6158
for _, ecosystem := range ecosystems {
62-
if namespacesMatchesEcosystem(namespaces, ecosystem) {
63-
result = append(result, ecosystem)
59+
if namespaceMatchesEcosystem(namespace, ecosystem) {
60+
return ecosystem
6461
}
6562
}
66-
return result
63+
return nil
6764
}

pkg/runtime/ecosystem/dotnet.go

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,8 +105,17 @@ func (e *DotNet) Add(artifact *buildplan.Artifact, artifactSrcPath string) ([]st
105105
return installedFiles, nil
106106
}
107107

108-
func (e *DotNet) Remove(artifact *buildplan.Artifact) error {
109-
return nil // TODO: CP-956
108+
func (e *DotNet) Remove(name, version string, installedFiles []string) (rerr error) {
109+
for _, dir := range installedFiles {
110+
if !fileutils.DirExists(dir) {
111+
continue
112+
}
113+
err := os.RemoveAll(dir)
114+
if err != nil {
115+
rerr = errs.Pack(rerr, errs.Wrap(err, "Unable to remove directory for '%s': %s", name, dir))
116+
}
117+
}
118+
return rerr
110119
}
111120

112121
func (e *DotNet) Apply() error {

0 commit comments

Comments
 (0)