@@ -48,49 +48,77 @@ func StorePathsFromInstallable(ctx context.Context, installable string, allowIns
48
48
49
49
return nil , err
50
50
}
51
- return parseStorePathFromInstallableOutput (installable , resultBytes )
51
+
52
+ validPaths , err := parseStorePathFromInstallableOutput (resultBytes )
53
+ if err != nil {
54
+ return nil , fmt .Errorf ("failed to parse path-info for %s: %w" , installable , err )
55
+ }
56
+
57
+ return maps .Keys (validPaths ), nil
52
58
}
53
59
54
- // StorePathsAreInStore returns true if the store path is in the store
55
- // It relies on `nix store ls` to check if the store path is in the store
56
- func StorePathsAreInStore (ctx context.Context , storePaths []string ) (bool , error ) {
60
+ // StorePathsAreInStore a map of store paths to whether they are in the store.
61
+ func StorePathsAreInStore (ctx context.Context , storePaths []string ) (map [string ]bool , error ) {
57
62
defer debug .FunctionTimer ().End ()
63
+ if len (storePaths ) == 0 {
64
+ return map [string ]bool {}, nil
65
+ }
66
+ args := append ([]string {"path-info" , "--offline" , "--json" }, storePaths ... )
67
+ cmd := commandContext (ctx , args ... )
68
+ debug .Log ("Running cmd %s" , cmd )
69
+ output , err := cmd .Output ()
70
+ if err != nil {
71
+ return nil , err
72
+ }
73
+
74
+ validPaths , err := parseStorePathFromInstallableOutput (output )
75
+ if err != nil {
76
+ return nil , err
77
+ }
78
+
79
+ result := map [string ]bool {}
58
80
for _ , storePath := range storePaths {
59
- cmd := commandContext (ctx , "store" , "ls" , storePath )
60
- debug .Log ("Running cmd %s" , cmd )
61
- if err := cmd .Run (); err != nil {
62
- if exitErr := (& exec.ExitError {}); errors .As (err , & exitErr ) {
63
- return false , nil
64
- }
65
- return false , err
66
- }
81
+ _ , ok := validPaths [storePath ]
82
+ result [storePath ] = ok
67
83
}
68
- return true , nil
84
+
85
+ return result , nil
86
+ }
87
+
88
+ // Older nix versions (like 2.17) are an array of objects that contain path and valid fields
89
+ type pathInfoLegacy struct {
90
+ Path string `json:"path"`
91
+ Valid bool `json:"valid"`
69
92
}
70
93
71
94
// parseStorePathFromInstallableOutput parses the output of `nix store path-from-installable --json`
72
95
// This function is decomposed out of StorePathFromInstallable to make it testable.
73
- func parseStorePathFromInstallableOutput (installable string , output []byte ) ([]string , error ) {
74
- // Newer nix versions (like 2.20)
96
+ func parseStorePathFromInstallableOutput (output []byte ) (map [string ]any , error ) {
97
+ // Newer nix versions (like 2.20) have output of the form
98
+ // {"<store-path>": {}}
99
+ // if a store path is used as an installable, the keys will be present even if invalid but
100
+ // the values will be null.
75
101
var out1 map [string ]any
76
102
if err := json .Unmarshal (output , & out1 ); err == nil {
77
- return maps .Keys (out1 ), nil
103
+ maps .DeleteFunc (out1 , func (k string , v any ) bool {
104
+ return v == nil
105
+ })
106
+ return out1 , nil
78
107
}
79
108
80
- // Older nix versions (like 2.17)
81
- var out2 []struct {
82
- Path string `json:"path"`
83
- Valid bool `json:"valid"`
84
- }
109
+ var out2 []pathInfoLegacy
110
+
85
111
if err := json .Unmarshal (output , & out2 ); err == nil {
86
- res := [] string {}
112
+ res := map [ string ] any {}
87
113
for _ , outValue := range out2 {
88
- res = append (res , outValue .Path )
114
+ if outValue .Valid {
115
+ res [outValue .Path ] = true
116
+ }
89
117
}
90
118
return res , nil
91
119
}
92
120
93
- return nil , fmt .Errorf ("failed to parse store path from installable (%s) output: %s" , installable , output )
121
+ return nil , fmt .Errorf ("failed to parse path-info output: %s" , output )
94
122
}
95
123
96
124
// DaemonError reports an unsuccessful attempt to connect to the Nix daemon.
0 commit comments