cacache 16.1.3 → 17.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/lib/content/read.js +1 -76
- package/lib/content/rm.js +2 -4
- package/lib/content/write.js +5 -9
- package/lib/entry-index.js +21 -95
- package/lib/get.js +0 -55
- package/lib/index.js +0 -3
- package/lib/rm.js +5 -5
- package/lib/util/glob.js +7 -0
- package/lib/util/move-file.js +1 -1
- package/lib/util/tmp.js +3 -10
- package/lib/verify.js +22 -27
- package/package.json +16 -17
- package/lib/util/fix-owner.js +0 -145
package/README.md
CHANGED
|
@@ -601,7 +601,7 @@ See: [options](#tmp-options)
|
|
|
601
601
|
|
|
602
602
|
```javascript
|
|
603
603
|
cacache.tmp.withTmp(cache, dir => {
|
|
604
|
-
return fs.
|
|
604
|
+
return fs.writeFile(path.join(dir, 'blablabla'), 'blabla contents', { encoding: 'utf8' })
|
|
605
605
|
}).then(() => {
|
|
606
606
|
// `dir` no longer exists
|
|
607
607
|
})
|
package/lib/content/read.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const fs = require('
|
|
3
|
+
const fs = require('fs/promises')
|
|
4
4
|
const fsm = require('fs-minipass')
|
|
5
5
|
const ssri = require('ssri')
|
|
6
6
|
const contentPath = require('./path')
|
|
@@ -46,24 +46,6 @@ const readPipeline = (cpath, size, sri, stream) => {
|
|
|
46
46
|
return stream
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
module.exports.sync = readSync
|
|
50
|
-
|
|
51
|
-
function readSync (cache, integrity, opts = {}) {
|
|
52
|
-
const { size } = opts
|
|
53
|
-
return withContentSriSync(cache, integrity, (cpath, sri) => {
|
|
54
|
-
const data = fs.readFileSync(cpath, { encoding: null })
|
|
55
|
-
if (typeof size === 'number' && size !== data.length) {
|
|
56
|
-
throw sizeError(size, data.length)
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
if (ssri.checkData(data, sri)) {
|
|
60
|
-
return data
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
throw integrityError(sri, cpath)
|
|
64
|
-
})
|
|
65
|
-
}
|
|
66
|
-
|
|
67
49
|
module.exports.stream = readStream
|
|
68
50
|
module.exports.readStream = readStream
|
|
69
51
|
|
|
@@ -88,7 +70,6 @@ function readStream (cache, integrity, opts = {}) {
|
|
|
88
70
|
}
|
|
89
71
|
|
|
90
72
|
module.exports.copy = copy
|
|
91
|
-
module.exports.copy.sync = copySync
|
|
92
73
|
|
|
93
74
|
function copy (cache, integrity, dest) {
|
|
94
75
|
return withContentSri(cache, integrity, (cpath, sri) => {
|
|
@@ -96,12 +77,6 @@ function copy (cache, integrity, dest) {
|
|
|
96
77
|
})
|
|
97
78
|
}
|
|
98
79
|
|
|
99
|
-
function copySync (cache, integrity, dest) {
|
|
100
|
-
return withContentSriSync(cache, integrity, (cpath, sri) => {
|
|
101
|
-
return fs.copyFileSync(cpath, dest)
|
|
102
|
-
})
|
|
103
|
-
}
|
|
104
|
-
|
|
105
80
|
module.exports.hasContent = hasContent
|
|
106
81
|
|
|
107
82
|
async function hasContent (cache, integrity) {
|
|
@@ -130,34 +105,6 @@ async function hasContent (cache, integrity) {
|
|
|
130
105
|
}
|
|
131
106
|
}
|
|
132
107
|
|
|
133
|
-
module.exports.hasContent.sync = hasContentSync
|
|
134
|
-
|
|
135
|
-
function hasContentSync (cache, integrity) {
|
|
136
|
-
if (!integrity) {
|
|
137
|
-
return false
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
return withContentSriSync(cache, integrity, (cpath, sri) => {
|
|
141
|
-
try {
|
|
142
|
-
const stat = fs.statSync(cpath)
|
|
143
|
-
return { size: stat.size, sri, stat }
|
|
144
|
-
} catch (err) {
|
|
145
|
-
if (err.code === 'ENOENT') {
|
|
146
|
-
return false
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
if (err.code === 'EPERM') {
|
|
150
|
-
/* istanbul ignore else */
|
|
151
|
-
if (process.platform !== 'win32') {
|
|
152
|
-
throw err
|
|
153
|
-
} else {
|
|
154
|
-
return false
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
})
|
|
159
|
-
}
|
|
160
|
-
|
|
161
108
|
async function withContentSri (cache, integrity, fn) {
|
|
162
109
|
const sri = ssri.parse(integrity)
|
|
163
110
|
// If `integrity` has multiple entries, pick the first digest
|
|
@@ -201,28 +148,6 @@ async function withContentSri (cache, integrity, fn) {
|
|
|
201
148
|
}
|
|
202
149
|
}
|
|
203
150
|
|
|
204
|
-
function withContentSriSync (cache, integrity, fn) {
|
|
205
|
-
const sri = ssri.parse(integrity)
|
|
206
|
-
// If `integrity` has multiple entries, pick the first digest
|
|
207
|
-
// with available local data.
|
|
208
|
-
const algo = sri.pickAlgorithm()
|
|
209
|
-
const digests = sri[algo]
|
|
210
|
-
if (digests.length <= 1) {
|
|
211
|
-
const cpath = contentPath(cache, digests[0])
|
|
212
|
-
return fn(cpath, digests[0])
|
|
213
|
-
} else {
|
|
214
|
-
let lastErr = null
|
|
215
|
-
for (const meta of digests) {
|
|
216
|
-
try {
|
|
217
|
-
return withContentSriSync(cache, meta, fn)
|
|
218
|
-
} catch (err) {
|
|
219
|
-
lastErr = err
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
throw lastErr
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
|
|
226
151
|
function sizeError (expected, found) {
|
|
227
152
|
/* eslint-disable-next-line max-len */
|
|
228
153
|
const err = new Error(`Bad data size: expected inserted data to be ${expected} bytes, but got ${found} instead`)
|
package/lib/content/rm.js
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const
|
|
4
|
-
|
|
3
|
+
const fs = require('fs/promises')
|
|
5
4
|
const contentPath = require('./path')
|
|
6
5
|
const { hasContent } = require('./read')
|
|
7
|
-
const rimraf = util.promisify(require('rimraf'))
|
|
8
6
|
|
|
9
7
|
module.exports = rm
|
|
10
8
|
|
|
@@ -12,7 +10,7 @@ async function rm (cache, integrity) {
|
|
|
12
10
|
const content = await hasContent(cache, integrity)
|
|
13
11
|
// ~pretty~ sure we can't end up with a content lacking sri, but be safe
|
|
14
12
|
if (content && content.sri) {
|
|
15
|
-
await
|
|
13
|
+
await fs.rm(contentPath(cache, content.sri), { recursive: true, force: true })
|
|
16
14
|
return true
|
|
17
15
|
} else {
|
|
18
16
|
return false
|
package/lib/content/write.js
CHANGED
|
@@ -1,17 +1,14 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const events = require('events')
|
|
4
|
-
const util = require('util')
|
|
5
4
|
|
|
6
5
|
const contentPath = require('./path')
|
|
7
|
-
const
|
|
8
|
-
const fs = require('@npmcli/fs')
|
|
6
|
+
const fs = require('fs/promises')
|
|
9
7
|
const moveFile = require('../util/move-file')
|
|
10
8
|
const Minipass = require('minipass')
|
|
11
9
|
const Pipeline = require('minipass-pipeline')
|
|
12
10
|
const Flush = require('minipass-flush')
|
|
13
11
|
const path = require('path')
|
|
14
|
-
const rimraf = util.promisify(require('rimraf'))
|
|
15
12
|
const ssri = require('ssri')
|
|
16
13
|
const uniqueFilename = require('unique-filename')
|
|
17
14
|
const fsm = require('fs-minipass')
|
|
@@ -40,7 +37,7 @@ async function write (cache, data, opts = {}) {
|
|
|
40
37
|
return { integrity: sri, size: data.length }
|
|
41
38
|
} finally {
|
|
42
39
|
if (!tmp.moved) {
|
|
43
|
-
await
|
|
40
|
+
await fs.rm(tmp.target, { recursive: true, force: true })
|
|
44
41
|
}
|
|
45
42
|
}
|
|
46
43
|
}
|
|
@@ -111,7 +108,7 @@ async function handleContent (inputStream, cache, opts) {
|
|
|
111
108
|
return res
|
|
112
109
|
} finally {
|
|
113
110
|
if (!tmp.moved) {
|
|
114
|
-
await
|
|
111
|
+
await fs.rm(tmp.target, { recursive: true, force: true })
|
|
115
112
|
}
|
|
116
113
|
}
|
|
117
114
|
}
|
|
@@ -152,7 +149,7 @@ async function pipeToTmp (inputStream, cache, tmpTarget, opts) {
|
|
|
152
149
|
|
|
153
150
|
async function makeTmp (cache, opts) {
|
|
154
151
|
const tmpTarget = uniqueFilename(path.join(cache, 'tmp'), opts.tmpPrefix)
|
|
155
|
-
await
|
|
152
|
+
await fs.mkdir(path.dirname(tmpTarget), { recursive: true })
|
|
156
153
|
return {
|
|
157
154
|
target: tmpTarget,
|
|
158
155
|
moved: false,
|
|
@@ -163,10 +160,9 @@ async function moveToDestination (tmp, cache, sri, opts) {
|
|
|
163
160
|
const destination = contentPath(cache, sri)
|
|
164
161
|
const destDir = path.dirname(destination)
|
|
165
162
|
|
|
166
|
-
await
|
|
163
|
+
await fs.mkdir(destDir, { recursive: true })
|
|
167
164
|
await moveFile(tmp.target, destination)
|
|
168
165
|
tmp.moved = true
|
|
169
|
-
await fixOwner.chownr(cache, destination)
|
|
170
166
|
}
|
|
171
167
|
|
|
172
168
|
function sizeError (expected, found) {
|
package/lib/entry-index.js
CHANGED
|
@@ -1,21 +1,23 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const util = require('util')
|
|
4
3
|
const crypto = require('crypto')
|
|
5
|
-
const
|
|
4
|
+
const {
|
|
5
|
+
appendFile,
|
|
6
|
+
mkdir,
|
|
7
|
+
readFile,
|
|
8
|
+
readdir,
|
|
9
|
+
rm,
|
|
10
|
+
writeFile,
|
|
11
|
+
} = require('fs/promises')
|
|
6
12
|
const Minipass = require('minipass')
|
|
7
13
|
const path = require('path')
|
|
8
14
|
const ssri = require('ssri')
|
|
9
15
|
const uniqueFilename = require('unique-filename')
|
|
10
16
|
|
|
11
17
|
const contentPath = require('./content/path')
|
|
12
|
-
const fixOwner = require('./util/fix-owner')
|
|
13
18
|
const hashToSegments = require('./util/hash-to-segments')
|
|
14
19
|
const indexV = require('../package.json')['cache-version'].index
|
|
15
20
|
const moveFile = require('@npmcli/move-file')
|
|
16
|
-
const _rimraf = require('rimraf')
|
|
17
|
-
const rimraf = util.promisify(_rimraf)
|
|
18
|
-
rimraf.sync = _rimraf.sync
|
|
19
21
|
|
|
20
22
|
module.exports.NotFoundError = class NotFoundError extends Error {
|
|
21
23
|
constructor (cache, key) {
|
|
@@ -66,7 +68,7 @@ async function compact (cache, key, matchFn, opts = {}) {
|
|
|
66
68
|
|
|
67
69
|
const setup = async () => {
|
|
68
70
|
const target = uniqueFilename(path.join(cache, 'tmp'), opts.tmpPrefix)
|
|
69
|
-
await
|
|
71
|
+
await mkdir(path.dirname(target), { recursive: true })
|
|
70
72
|
return {
|
|
71
73
|
target,
|
|
72
74
|
moved: false,
|
|
@@ -75,24 +77,17 @@ async function compact (cache, key, matchFn, opts = {}) {
|
|
|
75
77
|
|
|
76
78
|
const teardown = async (tmp) => {
|
|
77
79
|
if (!tmp.moved) {
|
|
78
|
-
return
|
|
80
|
+
return rm(tmp.target, { recursive: true, force: true })
|
|
79
81
|
}
|
|
80
82
|
}
|
|
81
83
|
|
|
82
84
|
const write = async (tmp) => {
|
|
83
|
-
await
|
|
84
|
-
await
|
|
85
|
+
await writeFile(tmp.target, newIndex, { flag: 'wx' })
|
|
86
|
+
await mkdir(path.dirname(bucket), { recursive: true })
|
|
85
87
|
// we use @npmcli/move-file directly here because we
|
|
86
88
|
// want to overwrite the existing file
|
|
87
89
|
await moveFile(tmp.target, bucket)
|
|
88
90
|
tmp.moved = true
|
|
89
|
-
try {
|
|
90
|
-
await fixOwner.chownr(cache, bucket)
|
|
91
|
-
} catch (err) {
|
|
92
|
-
if (err.code !== 'ENOENT') {
|
|
93
|
-
throw err
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
91
|
}
|
|
97
92
|
|
|
98
93
|
// write the file atomically
|
|
@@ -124,7 +119,7 @@ async function insert (cache, key, integrity, opts = {}) {
|
|
|
124
119
|
metadata,
|
|
125
120
|
}
|
|
126
121
|
try {
|
|
127
|
-
await
|
|
122
|
+
await mkdir(path.dirname(bucket), { recursive: true })
|
|
128
123
|
const stringified = JSON.stringify(entry)
|
|
129
124
|
// NOTE - Cleverness ahoy!
|
|
130
125
|
//
|
|
@@ -134,44 +129,13 @@ async function insert (cache, key, integrity, opts = {}) {
|
|
|
134
129
|
//
|
|
135
130
|
// Thanks to @isaacs for the whiteboarding session that ended up with
|
|
136
131
|
// this.
|
|
137
|
-
await
|
|
138
|
-
await fixOwner.chownr(cache, bucket)
|
|
132
|
+
await appendFile(bucket, `\n${hashEntry(stringified)}\t${stringified}`)
|
|
139
133
|
} catch (err) {
|
|
140
134
|
if (err.code === 'ENOENT') {
|
|
141
135
|
return undefined
|
|
142
136
|
}
|
|
143
137
|
|
|
144
138
|
throw err
|
|
145
|
-
// There's a class of race conditions that happen when things get deleted
|
|
146
|
-
// during fixOwner, or between the two mkdirfix/chownr calls.
|
|
147
|
-
//
|
|
148
|
-
// It's perfectly fine to just not bother in those cases and lie
|
|
149
|
-
// that the index entry was written. Because it's a cache.
|
|
150
|
-
}
|
|
151
|
-
return formatEntry(cache, entry)
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
module.exports.insert.sync = insertSync
|
|
155
|
-
|
|
156
|
-
function insertSync (cache, key, integrity, opts = {}) {
|
|
157
|
-
const { metadata, size } = opts
|
|
158
|
-
const bucket = bucketPath(cache, key)
|
|
159
|
-
const entry = {
|
|
160
|
-
key,
|
|
161
|
-
integrity: integrity && ssri.stringify(integrity),
|
|
162
|
-
time: Date.now(),
|
|
163
|
-
size,
|
|
164
|
-
metadata,
|
|
165
|
-
}
|
|
166
|
-
fixOwner.mkdirfix.sync(cache, path.dirname(bucket))
|
|
167
|
-
const stringified = JSON.stringify(entry)
|
|
168
|
-
fs.appendFileSync(bucket, `\n${hashEntry(stringified)}\t${stringified}`)
|
|
169
|
-
try {
|
|
170
|
-
fixOwner.chownr.sync(cache, bucket)
|
|
171
|
-
} catch (err) {
|
|
172
|
-
if (err.code !== 'ENOENT') {
|
|
173
|
-
throw err
|
|
174
|
-
}
|
|
175
139
|
}
|
|
176
140
|
return formatEntry(cache, entry)
|
|
177
141
|
}
|
|
@@ -198,27 +162,6 @@ async function find (cache, key) {
|
|
|
198
162
|
}
|
|
199
163
|
}
|
|
200
164
|
|
|
201
|
-
module.exports.find.sync = findSync
|
|
202
|
-
|
|
203
|
-
function findSync (cache, key) {
|
|
204
|
-
const bucket = bucketPath(cache, key)
|
|
205
|
-
try {
|
|
206
|
-
return bucketEntriesSync(bucket).reduce((latest, next) => {
|
|
207
|
-
if (next && next.key === key) {
|
|
208
|
-
return formatEntry(cache, next)
|
|
209
|
-
} else {
|
|
210
|
-
return latest
|
|
211
|
-
}
|
|
212
|
-
}, null)
|
|
213
|
-
} catch (err) {
|
|
214
|
-
if (err.code === 'ENOENT') {
|
|
215
|
-
return null
|
|
216
|
-
} else {
|
|
217
|
-
throw err
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
|
|
222
165
|
module.exports.delete = del
|
|
223
166
|
|
|
224
167
|
function del (cache, key, opts = {}) {
|
|
@@ -227,18 +170,7 @@ function del (cache, key, opts = {}) {
|
|
|
227
170
|
}
|
|
228
171
|
|
|
229
172
|
const bucket = bucketPath(cache, key)
|
|
230
|
-
return
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
module.exports.delete.sync = delSync
|
|
234
|
-
|
|
235
|
-
function delSync (cache, key, opts = {}) {
|
|
236
|
-
if (!opts.removeFully) {
|
|
237
|
-
return insertSync(cache, key, null, opts)
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
const bucket = bucketPath(cache, key)
|
|
241
|
-
return rimraf.sync(bucket)
|
|
173
|
+
return rm(bucket, { recursive: true, force: true })
|
|
242
174
|
}
|
|
243
175
|
|
|
244
176
|
module.exports.lsStream = lsStream
|
|
@@ -304,14 +236,7 @@ async function ls (cache) {
|
|
|
304
236
|
module.exports.bucketEntries = bucketEntries
|
|
305
237
|
|
|
306
238
|
async function bucketEntries (bucket, filter) {
|
|
307
|
-
const data = await
|
|
308
|
-
return _bucketEntries(data, filter)
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
module.exports.bucketEntries.sync = bucketEntriesSync
|
|
312
|
-
|
|
313
|
-
function bucketEntriesSync (bucket, filter) {
|
|
314
|
-
const data = fs.readFileSync(bucket, 'utf8')
|
|
239
|
+
const data = await readFile(bucket, 'utf8')
|
|
315
240
|
return _bucketEntries(data, filter)
|
|
316
241
|
}
|
|
317
242
|
|
|
@@ -331,10 +256,11 @@ function _bucketEntries (data, filter) {
|
|
|
331
256
|
let obj
|
|
332
257
|
try {
|
|
333
258
|
obj = JSON.parse(pieces[1])
|
|
334
|
-
} catch (
|
|
335
|
-
//
|
|
336
|
-
return
|
|
259
|
+
} catch (_) {
|
|
260
|
+
// eslint-ignore-next-line no-empty-block
|
|
337
261
|
}
|
|
262
|
+
// coverage disabled here, no need to test with an entry that parses to something falsey
|
|
263
|
+
// istanbul ignore else
|
|
338
264
|
if (obj) {
|
|
339
265
|
entries.push(obj)
|
|
340
266
|
}
|
|
@@ -394,7 +320,7 @@ function formatEntry (cache, entry, keepAll) {
|
|
|
394
320
|
}
|
|
395
321
|
|
|
396
322
|
function readdirOrEmpty (dir) {
|
|
397
|
-
return
|
|
323
|
+
return readdir(dir).catch((err) => {
|
|
398
324
|
if (err.code === 'ENOENT' || err.code === 'ENOTDIR') {
|
|
399
325
|
return []
|
|
400
326
|
}
|
package/lib/get.js
CHANGED
|
@@ -53,61 +53,6 @@ async function getDataByDigest (cache, key, opts = {}) {
|
|
|
53
53
|
}
|
|
54
54
|
module.exports.byDigest = getDataByDigest
|
|
55
55
|
|
|
56
|
-
function getDataSync (cache, key, opts = {}) {
|
|
57
|
-
const { integrity, memoize, size } = opts
|
|
58
|
-
const memoized = memo.get(cache, key, opts)
|
|
59
|
-
|
|
60
|
-
if (memoized && memoize !== false) {
|
|
61
|
-
return {
|
|
62
|
-
metadata: memoized.entry.metadata,
|
|
63
|
-
data: memoized.data,
|
|
64
|
-
integrity: memoized.entry.integrity,
|
|
65
|
-
size: memoized.entry.size,
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
const entry = index.find.sync(cache, key, opts)
|
|
69
|
-
if (!entry) {
|
|
70
|
-
throw new index.NotFoundError(cache, key)
|
|
71
|
-
}
|
|
72
|
-
const data = read.sync(cache, entry.integrity, {
|
|
73
|
-
integrity: integrity,
|
|
74
|
-
size: size,
|
|
75
|
-
})
|
|
76
|
-
const res = {
|
|
77
|
-
metadata: entry.metadata,
|
|
78
|
-
data: data,
|
|
79
|
-
size: entry.size,
|
|
80
|
-
integrity: entry.integrity,
|
|
81
|
-
}
|
|
82
|
-
if (memoize) {
|
|
83
|
-
memo.put(cache, entry, res.data, opts)
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
return res
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
module.exports.sync = getDataSync
|
|
90
|
-
|
|
91
|
-
function getDataByDigestSync (cache, digest, opts = {}) {
|
|
92
|
-
const { integrity, memoize, size } = opts
|
|
93
|
-
const memoized = memo.get.byDigest(cache, digest, opts)
|
|
94
|
-
|
|
95
|
-
if (memoized && memoize !== false) {
|
|
96
|
-
return memoized
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
const res = read.sync(cache, digest, {
|
|
100
|
-
integrity: integrity,
|
|
101
|
-
size: size,
|
|
102
|
-
})
|
|
103
|
-
if (memoize) {
|
|
104
|
-
memo.put.byDigest(cache, digest, res, opts)
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
return res
|
|
108
|
-
}
|
|
109
|
-
module.exports.sync.byDigest = getDataByDigestSync
|
|
110
|
-
|
|
111
56
|
const getMemoizedStream = (memoized) => {
|
|
112
57
|
const stream = new Minipass()
|
|
113
58
|
stream.on('newListener', function (ev, cb) {
|
package/lib/index.js
CHANGED
|
@@ -17,15 +17,12 @@ module.exports.ls.stream = index.lsStream
|
|
|
17
17
|
|
|
18
18
|
module.exports.get = get
|
|
19
19
|
module.exports.get.byDigest = get.byDigest
|
|
20
|
-
module.exports.get.sync = get.sync
|
|
21
|
-
module.exports.get.sync.byDigest = get.sync.byDigest
|
|
22
20
|
module.exports.get.stream = get.stream
|
|
23
21
|
module.exports.get.stream.byDigest = get.stream.byDigest
|
|
24
22
|
module.exports.get.copy = get.copy
|
|
25
23
|
module.exports.get.copy.byDigest = get.copy.byDigest
|
|
26
24
|
module.exports.get.info = get.info
|
|
27
25
|
module.exports.get.hasContent = get.hasContent
|
|
28
|
-
module.exports.get.hasContent.sync = get.hasContent.sync
|
|
29
26
|
|
|
30
27
|
module.exports.put = put
|
|
31
28
|
module.exports.put.stream = put.stream
|
package/lib/rm.js
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const
|
|
4
|
-
|
|
3
|
+
const { rm } = require('fs/promises')
|
|
4
|
+
const glob = require('./util/glob.js')
|
|
5
5
|
const index = require('./entry-index')
|
|
6
6
|
const memo = require('./memoization')
|
|
7
7
|
const path = require('path')
|
|
8
|
-
const rimraf = util.promisify(require('rimraf'))
|
|
9
8
|
const rmContent = require('./content/rm')
|
|
10
9
|
|
|
11
10
|
module.exports = entry
|
|
@@ -25,7 +24,8 @@ function content (cache, integrity) {
|
|
|
25
24
|
|
|
26
25
|
module.exports.all = all
|
|
27
26
|
|
|
28
|
-
function all (cache) {
|
|
27
|
+
async function all (cache) {
|
|
29
28
|
memo.clearMemoized()
|
|
30
|
-
|
|
29
|
+
const paths = await glob(path.join(cache, '*(content-*|index-*)'), { silent: true, nosort: true })
|
|
30
|
+
return Promise.all(paths.map((p) => rm(p, { recursive: true, force: true })))
|
|
31
31
|
}
|
package/lib/util/glob.js
ADDED
package/lib/util/move-file.js
CHANGED
package/lib/util/tmp.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const
|
|
4
|
-
|
|
5
|
-
const fixOwner = require('./fix-owner')
|
|
3
|
+
const { withTempDir } = require('@npmcli/fs')
|
|
4
|
+
const fs = require('fs/promises')
|
|
6
5
|
const path = require('path')
|
|
7
6
|
|
|
8
7
|
module.exports.mkdir = mktmpdir
|
|
@@ -23,11 +22,5 @@ function withTmp (cache, opts, cb) {
|
|
|
23
22
|
cb = opts
|
|
24
23
|
opts = {}
|
|
25
24
|
}
|
|
26
|
-
return
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
module.exports.fix = fixtmpdir
|
|
30
|
-
|
|
31
|
-
function fixtmpdir (cache) {
|
|
32
|
-
return fixOwner(cache, path.join(cache, 'tmp'))
|
|
25
|
+
return withTempDir(path.join(cache, 'tmp'), cb, opts)
|
|
33
26
|
}
|
package/lib/verify.js
CHANGED
|
@@ -1,20 +1,21 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const
|
|
4
|
-
|
|
3
|
+
const {
|
|
4
|
+
mkdir,
|
|
5
|
+
readFile,
|
|
6
|
+
rm,
|
|
7
|
+
stat,
|
|
8
|
+
truncate,
|
|
9
|
+
writeFile,
|
|
10
|
+
} = require('fs/promises')
|
|
5
11
|
const pMap = require('p-map')
|
|
6
12
|
const contentPath = require('./content/path')
|
|
7
|
-
const fixOwner = require('./util/fix-owner')
|
|
8
|
-
const fs = require('@npmcli/fs')
|
|
9
13
|
const fsm = require('fs-minipass')
|
|
10
|
-
const glob =
|
|
14
|
+
const glob = require('./util/glob.js')
|
|
11
15
|
const index = require('./entry-index')
|
|
12
16
|
const path = require('path')
|
|
13
|
-
const rimraf = util.promisify(require('rimraf'))
|
|
14
17
|
const ssri = require('ssri')
|
|
15
18
|
|
|
16
|
-
const globify = pattern => pattern.split('\\').join('/')
|
|
17
|
-
|
|
18
19
|
const hasOwnProperty = (obj, key) =>
|
|
19
20
|
Object.prototype.hasOwnProperty.call(obj, key)
|
|
20
21
|
|
|
@@ -77,9 +78,7 @@ async function markEndTime (cache, opts) {
|
|
|
77
78
|
|
|
78
79
|
async function fixPerms (cache, opts) {
|
|
79
80
|
opts.log.silly('verify', 'fixing cache permissions')
|
|
80
|
-
await
|
|
81
|
-
// TODO - fix file permissions too
|
|
82
|
-
await fixOwner.chownr(cache, cache)
|
|
81
|
+
await mkdir(cache, { recursive: true })
|
|
83
82
|
return null
|
|
84
83
|
}
|
|
85
84
|
|
|
@@ -90,7 +89,7 @@ async function fixPerms (cache, opts) {
|
|
|
90
89
|
// 2. Mark each integrity value as "live"
|
|
91
90
|
// 3. Read entire filesystem tree in `content-vX/` dir
|
|
92
91
|
// 4. If content is live, verify its checksum and delete it if it fails
|
|
93
|
-
// 5. If content is not marked as live,
|
|
92
|
+
// 5. If content is not marked as live, rm it.
|
|
94
93
|
//
|
|
95
94
|
async function garbageCollect (cache, opts) {
|
|
96
95
|
opts.log.silly('verify', 'garbage collecting content')
|
|
@@ -107,7 +106,7 @@ async function garbageCollect (cache, opts) {
|
|
|
107
106
|
indexStream.on('end', resolve).on('error', reject)
|
|
108
107
|
})
|
|
109
108
|
const contentDir = contentPath.contentDir(cache)
|
|
110
|
-
const files = await glob(
|
|
109
|
+
const files = await glob(path.join(contentDir, '**'), {
|
|
111
110
|
follow: false,
|
|
112
111
|
nodir: true,
|
|
113
112
|
nosort: true,
|
|
@@ -139,8 +138,8 @@ async function garbageCollect (cache, opts) {
|
|
|
139
138
|
} else {
|
|
140
139
|
// No entries refer to this content. We can delete.
|
|
141
140
|
stats.reclaimedCount++
|
|
142
|
-
const s = await
|
|
143
|
-
await
|
|
141
|
+
const s = await stat(f)
|
|
142
|
+
await rm(f, { recursive: true, force: true })
|
|
144
143
|
stats.reclaimedSize += s.size
|
|
145
144
|
}
|
|
146
145
|
return stats
|
|
@@ -153,7 +152,7 @@ async function garbageCollect (cache, opts) {
|
|
|
153
152
|
async function verifyContent (filepath, sri) {
|
|
154
153
|
const contentInfo = {}
|
|
155
154
|
try {
|
|
156
|
-
const { size } = await
|
|
155
|
+
const { size } = await stat(filepath)
|
|
157
156
|
contentInfo.size = size
|
|
158
157
|
contentInfo.valid = true
|
|
159
158
|
await ssri.checkStream(new fsm.ReadStream(filepath), sri)
|
|
@@ -165,7 +164,7 @@ async function verifyContent (filepath, sri) {
|
|
|
165
164
|
throw err
|
|
166
165
|
}
|
|
167
166
|
|
|
168
|
-
await
|
|
167
|
+
await rm(filepath, { recursive: true, force: true })
|
|
169
168
|
contentInfo.valid = false
|
|
170
169
|
}
|
|
171
170
|
return contentInfo
|
|
@@ -211,13 +210,13 @@ async function rebuildIndex (cache, opts) {
|
|
|
211
210
|
}
|
|
212
211
|
|
|
213
212
|
async function rebuildBucket (cache, bucket, stats, opts) {
|
|
214
|
-
await
|
|
213
|
+
await truncate(bucket._path)
|
|
215
214
|
// This needs to be serialized because cacache explicitly
|
|
216
215
|
// lets very racy bucket conflicts clobber each other.
|
|
217
216
|
for (const entry of bucket) {
|
|
218
217
|
const content = contentPath(cache, entry.integrity)
|
|
219
218
|
try {
|
|
220
|
-
await
|
|
219
|
+
await stat(content)
|
|
221
220
|
await index.insert(cache, entry.key, entry.integrity, {
|
|
222
221
|
metadata: entry.metadata,
|
|
223
222
|
size: entry.size,
|
|
@@ -236,22 +235,18 @@ async function rebuildBucket (cache, bucket, stats, opts) {
|
|
|
236
235
|
|
|
237
236
|
function cleanTmp (cache, opts) {
|
|
238
237
|
opts.log.silly('verify', 'cleaning tmp directory')
|
|
239
|
-
return
|
|
238
|
+
return rm(path.join(cache, 'tmp'), { recursive: true, force: true })
|
|
240
239
|
}
|
|
241
240
|
|
|
242
|
-
function writeVerifile (cache, opts) {
|
|
241
|
+
async function writeVerifile (cache, opts) {
|
|
243
242
|
const verifile = path.join(cache, '_lastverified')
|
|
244
243
|
opts.log.silly('verify', 'writing verifile to ' + verifile)
|
|
245
|
-
|
|
246
|
-
return fs.writeFile(verifile, `${Date.now()}`)
|
|
247
|
-
} finally {
|
|
248
|
-
fixOwner.chownr.sync(cache, verifile)
|
|
249
|
-
}
|
|
244
|
+
return writeFile(verifile, `${Date.now()}`)
|
|
250
245
|
}
|
|
251
246
|
|
|
252
247
|
module.exports.lastRun = lastRun
|
|
253
248
|
|
|
254
249
|
async function lastRun (cache) {
|
|
255
|
-
const data = await
|
|
250
|
+
const data = await readFile(path.join(cache, '_lastverified'), { encoding: 'utf8' })
|
|
256
251
|
return new Date(+data)
|
|
257
252
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cacache",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "17.0.1",
|
|
4
4
|
"cache-version": {
|
|
5
5
|
"content": "2",
|
|
6
6
|
"index": "5"
|
|
@@ -12,9 +12,6 @@
|
|
|
12
12
|
"lib/"
|
|
13
13
|
],
|
|
14
14
|
"scripts": {
|
|
15
|
-
"preversion": "npm test",
|
|
16
|
-
"postversion": "npm publish",
|
|
17
|
-
"prepublishOnly": "git push origin --follow-tags",
|
|
18
15
|
"test": "tap",
|
|
19
16
|
"snap": "tap",
|
|
20
17
|
"coverage": "tap",
|
|
@@ -48,37 +45,39 @@
|
|
|
48
45
|
],
|
|
49
46
|
"license": "ISC",
|
|
50
47
|
"dependencies": {
|
|
51
|
-
"@npmcli/fs": "^
|
|
52
|
-
"@npmcli/move-file": "^
|
|
53
|
-
"chownr": "^2.0.0",
|
|
48
|
+
"@npmcli/fs": "^3.0.0",
|
|
49
|
+
"@npmcli/move-file": "^3.0.0",
|
|
54
50
|
"fs-minipass": "^2.1.0",
|
|
55
51
|
"glob": "^8.0.1",
|
|
56
|
-
"infer-owner": "^1.0.4",
|
|
57
52
|
"lru-cache": "^7.7.1",
|
|
58
53
|
"minipass": "^3.1.6",
|
|
59
54
|
"minipass-collect": "^1.0.2",
|
|
60
55
|
"minipass-flush": "^1.0.5",
|
|
61
56
|
"minipass-pipeline": "^1.2.4",
|
|
62
|
-
"mkdirp": "^1.0.4",
|
|
63
57
|
"p-map": "^4.0.0",
|
|
64
58
|
"promise-inflight": "^1.0.1",
|
|
65
|
-
"
|
|
66
|
-
"ssri": "^9.0.0",
|
|
59
|
+
"ssri": "^10.0.0",
|
|
67
60
|
"tar": "^6.1.11",
|
|
68
|
-
"unique-filename": "^
|
|
61
|
+
"unique-filename": "^3.0.0"
|
|
69
62
|
},
|
|
70
63
|
"devDependencies": {
|
|
71
|
-
"@npmcli/eslint-config": "^
|
|
72
|
-
"@npmcli/template-oss": "
|
|
64
|
+
"@npmcli/eslint-config": "^4.0.0",
|
|
65
|
+
"@npmcli/template-oss": "4.5.1",
|
|
73
66
|
"tap": "^16.0.0"
|
|
74
67
|
},
|
|
75
68
|
"engines": {
|
|
76
|
-
"node": "^
|
|
69
|
+
"node": "^14.17.0 || ^16.13.0 || >=18.0.0"
|
|
77
70
|
},
|
|
78
71
|
"templateOSS": {
|
|
79
72
|
"//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.",
|
|
80
73
|
"windowsCI": false,
|
|
81
|
-
"version": "
|
|
74
|
+
"version": "4.5.1"
|
|
82
75
|
},
|
|
83
|
-
"author": "GitHub Inc."
|
|
76
|
+
"author": "GitHub Inc.",
|
|
77
|
+
"tap": {
|
|
78
|
+
"nyc-arg": [
|
|
79
|
+
"--exclude",
|
|
80
|
+
"tap-snapshots/**"
|
|
81
|
+
]
|
|
82
|
+
}
|
|
84
83
|
}
|
package/lib/util/fix-owner.js
DELETED
|
@@ -1,145 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
const util = require('util')
|
|
4
|
-
|
|
5
|
-
const chownr = util.promisify(require('chownr'))
|
|
6
|
-
const mkdirp = require('mkdirp')
|
|
7
|
-
const inflight = require('promise-inflight')
|
|
8
|
-
const inferOwner = require('infer-owner')
|
|
9
|
-
|
|
10
|
-
// Memoize getuid()/getgid() calls.
|
|
11
|
-
// patch process.setuid/setgid to invalidate cached value on change
|
|
12
|
-
const self = { uid: null, gid: null }
|
|
13
|
-
const getSelf = () => {
|
|
14
|
-
if (typeof self.uid !== 'number') {
|
|
15
|
-
self.uid = process.getuid()
|
|
16
|
-
const setuid = process.setuid
|
|
17
|
-
process.setuid = (uid) => {
|
|
18
|
-
self.uid = null
|
|
19
|
-
process.setuid = setuid
|
|
20
|
-
return process.setuid(uid)
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
if (typeof self.gid !== 'number') {
|
|
24
|
-
self.gid = process.getgid()
|
|
25
|
-
const setgid = process.setgid
|
|
26
|
-
process.setgid = (gid) => {
|
|
27
|
-
self.gid = null
|
|
28
|
-
process.setgid = setgid
|
|
29
|
-
return process.setgid(gid)
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
module.exports.chownr = fixOwner
|
|
35
|
-
|
|
36
|
-
async function fixOwner (cache, filepath) {
|
|
37
|
-
if (!process.getuid) {
|
|
38
|
-
// This platform doesn't need ownership fixing
|
|
39
|
-
return
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
getSelf()
|
|
43
|
-
if (self.uid !== 0) {
|
|
44
|
-
// almost certainly can't chown anyway
|
|
45
|
-
return
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
const { uid, gid } = await inferOwner(cache)
|
|
49
|
-
|
|
50
|
-
// No need to override if it's already what we used.
|
|
51
|
-
if (self.uid === uid && self.gid === gid) {
|
|
52
|
-
return
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
return inflight('fixOwner: fixing ownership on ' + filepath, () =>
|
|
56
|
-
chownr(
|
|
57
|
-
filepath,
|
|
58
|
-
typeof uid === 'number' ? uid : self.uid,
|
|
59
|
-
typeof gid === 'number' ? gid : self.gid
|
|
60
|
-
).catch((err) => {
|
|
61
|
-
if (err.code === 'ENOENT') {
|
|
62
|
-
return null
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
throw err
|
|
66
|
-
})
|
|
67
|
-
)
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
module.exports.chownr.sync = fixOwnerSync
|
|
71
|
-
|
|
72
|
-
function fixOwnerSync (cache, filepath) {
|
|
73
|
-
if (!process.getuid) {
|
|
74
|
-
// This platform doesn't need ownership fixing
|
|
75
|
-
return
|
|
76
|
-
}
|
|
77
|
-
const { uid, gid } = inferOwner.sync(cache)
|
|
78
|
-
getSelf()
|
|
79
|
-
if (self.uid !== 0) {
|
|
80
|
-
// almost certainly can't chown anyway
|
|
81
|
-
return
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
if (self.uid === uid && self.gid === gid) {
|
|
85
|
-
// No need to override if it's already what we used.
|
|
86
|
-
return
|
|
87
|
-
}
|
|
88
|
-
try {
|
|
89
|
-
chownr.sync(
|
|
90
|
-
filepath,
|
|
91
|
-
typeof uid === 'number' ? uid : self.uid,
|
|
92
|
-
typeof gid === 'number' ? gid : self.gid
|
|
93
|
-
)
|
|
94
|
-
} catch (err) {
|
|
95
|
-
// only catch ENOENT, any other error is a problem.
|
|
96
|
-
if (err.code === 'ENOENT') {
|
|
97
|
-
return null
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
throw err
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
module.exports.mkdirfix = mkdirfix
|
|
105
|
-
|
|
106
|
-
async function mkdirfix (cache, p, cb) {
|
|
107
|
-
// we have to infer the owner _before_ making the directory, even though
|
|
108
|
-
// we aren't going to use the results, since the cache itself might not
|
|
109
|
-
// exist yet. If we mkdirp it, then our current uid/gid will be assumed
|
|
110
|
-
// to be correct if it creates the cache folder in the process.
|
|
111
|
-
await inferOwner(cache)
|
|
112
|
-
try {
|
|
113
|
-
const made = await mkdirp(p)
|
|
114
|
-
if (made) {
|
|
115
|
-
await fixOwner(cache, made)
|
|
116
|
-
return made
|
|
117
|
-
}
|
|
118
|
-
} catch (err) {
|
|
119
|
-
if (err.code === 'EEXIST') {
|
|
120
|
-
await fixOwner(cache, p)
|
|
121
|
-
return null
|
|
122
|
-
}
|
|
123
|
-
throw err
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
module.exports.mkdirfix.sync = mkdirfixSync
|
|
128
|
-
|
|
129
|
-
function mkdirfixSync (cache, p) {
|
|
130
|
-
try {
|
|
131
|
-
inferOwner.sync(cache)
|
|
132
|
-
const made = mkdirp.sync(p)
|
|
133
|
-
if (made) {
|
|
134
|
-
fixOwnerSync(cache, made)
|
|
135
|
-
return made
|
|
136
|
-
}
|
|
137
|
-
} catch (err) {
|
|
138
|
-
if (err.code === 'EEXIST') {
|
|
139
|
-
fixOwnerSync(cache, p)
|
|
140
|
-
return null
|
|
141
|
-
} else {
|
|
142
|
-
throw err
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
}
|