• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

CyberDuck79 / duckfile / 18097442731

29 Sep 2025 12:46PM UTC coverage: 79.573% (+0.04%) from 79.529%
18097442731

Pull #70

github

CyberDuck79
refactor: extract writeMetadataFile helper to reduce metadata writing duplication

- Add writeMetadataFile() helper function for consistent JSON metadata writing
- Replace duplicate json.Marshal + os.WriteFile patterns in remote.go
- Reduces code duplication in template and remote metadata handling
Pull Request #70: Phase 2: Implement separated targets and remotes configurations

592 of 686 new or added lines in 9 files covered. (86.3%)

24 existing lines in 5 files now uncovered.

2758 of 3466 relevant lines covered (79.57%)

11.37 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

54.29
/internal/run/clean.go
1
package run
2

3
import (
4
        "os"
5
        "path/filepath"
6
        "strings"
7

8
        "github.com/CyberDuck79/duckfile/internal/config"
9
        "github.com/CyberDuck79/duckfile/internal/log"
10
)
11

12
// Clean removes cached objects and per-target working dirs.
13
// If targetName is empty, purge everything. Otherwise, clean only that target’s cache
14
// and its currently referenced object.
15
func Clean(cfg *config.DuckConf, targetName string) error {
5✔
16
        if strings.TrimSpace(targetName) == "" { // clean all
5✔
17
                log.Infof("🧹 clean all")
×
18
                for name, t := range cfg.Targets {
×
19
                        _ = cleanOne(name, t)
×
20
                }
×
21
                return os.RemoveAll(filepath.Join(".duck", "objects"))
×
22
        }
23
        key, t, err := searchTarget(cfg, targetName)
5✔
24
        if err != nil {
5✔
25
                return err
×
26
        }
×
27
        log.Infof("🧽 clean %q", key)
5✔
28
        return cleanOne(key, t)
5✔
29
}
30

31
func cleanOne(targetName string, t config.Target) error {
5✔
32
        // Compute template paths to identify cache keys for this target
5✔
33
        vars := map[string]any{} // Use empty vars for cache key computation
5✔
34
        paths, err := computeTemplatePaths(targetName, t, vars)
5✔
35
        if err != nil {
5✔
NEW
36
                log.Warnf("failed to compute paths for target %s: %v", targetName, err)
×
NEW
37
                // Fall back to basic cleanup
×
NEW
38
                return cleanOneBasic(targetName, t)
×
NEW
39
        }
×
40

41
        // Clean rendered cache (if symlink exists)
42
        base := getBaseTemplateName(t.Template.Path)
5✔
43
        cacheDir := getTargetCacheDir(targetName)
5✔
44
        linkPath := t.RenderedPath
5✔
45
        if linkPath == "" {
10✔
46
                linkPath = filepath.Join(cacheDir, base)
5✔
47
        }
5✔
48
        if fi, err := os.Lstat(linkPath); err == nil && (fi.Mode()&os.ModeSymlink) != 0 {
10✔
49
                if renderedKey := detectRenderedKeyFromSymlink(linkPath); renderedKey != "" {
10✔
50
                        renderedDir := filepath.Join(".duck", "objects", "rendered", renderedKey)
5✔
51
                        log.Debugf("remove rendered object %s", renderedKey)
5✔
52
                        _ = os.RemoveAll(renderedDir)
5✔
53
                }
5✔
54
                _ = os.Remove(linkPath)
5✔
55
                log.Infof("🗂️ removed %s", linkPath)
5✔
56
        }
57

58
        // Clean template cache specific to this target
59
        templateDir := paths.templateDir
5✔
60
        if _, err := os.Stat(templateDir); err == nil {
10✔
61
                log.Debugf("remove template cache %s", paths.templateKey)
5✔
62
                _ = os.RemoveAll(templateDir)
5✔
63
        }
5✔
64

65
        // Clean remote cache - but be careful as it might be shared
66
        // For now, we'll be conservative and only clean if no other targets use it
67
        if shouldCleanRemoteCache(t, paths.remoteKey) {
5✔
NEW
68
                remoteDir := paths.remoteDir
×
NEW
69
                if _, err := os.Stat(remoteDir); err == nil {
×
NEW
70
                        log.Debugf("remove remote cache %s", paths.remoteKey)
×
NEW
71
                        _ = os.RemoveAll(remoteDir)
×
NEW
72
                }
×
73
        }
74

75
        // Clean target directory
76
        log.Debugf("remove target dir %s", cacheDir)
5✔
77
        return os.RemoveAll(cacheDir)
5✔
78
}
79

80
// cleanOneBasic provides fallback cleanup when path computation fails
NEW
81
func cleanOneBasic(targetName string, t config.Target) error {
×
NEW
82
        base := getBaseTemplateName(t.Template.Path)
×
NEW
83
        cacheDir := getTargetCacheDir(targetName)
×
UNCOV
84
        linkPath := t.RenderedPath
×
UNCOV
85
        if linkPath == "" {
×
UNCOV
86
                linkPath = filepath.Join(cacheDir, base)
×
UNCOV
87
        }
×
UNCOV
88
        if fi, err := os.Lstat(linkPath); err == nil && (fi.Mode()&os.ModeSymlink) != 0 {
×
UNCOV
89
                if renderedKey := detectRenderedKeyFromSymlink(linkPath); renderedKey != "" {
×
UNCOV
90
                        log.Debugf("remove rendered object %s", renderedKey)
×
UNCOV
91
                        _ = os.RemoveAll(filepath.Join(".duck", "objects", "rendered", renderedKey))
×
UNCOV
92
                }
×
UNCOV
93
                _ = os.Remove(linkPath)
×
UNCOV
94
                log.Infof("🗂️ removed %s", linkPath)
×
95
        }
UNCOV
96
        log.Debugf("remove target dir %s", cacheDir)
×
UNCOV
97
        return os.RemoveAll(cacheDir)
×
98
}
99

100
// shouldCleanRemoteCache determines if a remote cache can be safely removed
101
// For now, we'll be conservative and avoid removing shared remote caches during single target clean
102
func shouldCleanRemoteCache(t config.Target, remoteKey string) bool {
5✔
103
        // Conservative approach: don't clean remote cache for single target clean
5✔
104
        // Remote caches are shared and should only be cleaned during "clean all"
5✔
105
        return false
5✔
106
}
5✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc