Files
dc/client/scripts/check-archiver.mjs
T
dc 26c0711bc4
Build / Build-and-ng-test (pull_request) Successful in 5m32s
Lighthouse Checks / lighthouse (pull_request) Successful in 17m17s
Build / Build-and-test-development (pull_request) Successful in 30m5s
ci: exercise the glob path too in the archiver guard
readdir-glob reaches minimatch twice: the directory walk, and the pattern/ignore
matching behind archiver's glob(). The second is where a semantic change shows
up as silently skipped files rather than an error, so the check archives a tree
by pattern with an ignore and asserts what it kept: a.txt in, sub/** out.
2026-10-31 09:39:59 +00:00

75 lines
2.6 KiB
JavaScript

/* Guards the readdir-glob -> minimatch override in client/package.json.
*
* The override forces minimatch 11 where readdir-glob asks for ^10, and
* readdir-glob reaches minimatch on two paths: the directory walk, and the
* pattern/ignore matching behind archiver's glob(). Both are exercised here,
* because a matcher that breaks shows up as files silently skipped rather than
* as an error.
*
* Entries are read from archiver's own 'entry' events, so nothing has to be
* unzipped and the check needs no reader dependency.
*/
import fs from 'node:fs'
import os from 'node:os'
import path from 'node:path'
import { ZipArchive } from 'archiver'
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'dc-arch-'))
const source = path.join(root, 'source')
const cleanup = () => fs.rmSync(root, { recursive: true, force: true })
const fail = (message) => {
console.error(message)
cleanup()
process.exit(1)
}
fs.mkdirSync(source)
fs.writeFileSync(path.join(source, 'a.txt'), 'hello')
fs.mkdirSync(path.join(source, 'sub'))
fs.writeFileSync(path.join(source, 'sub', 'b.txt'), 'world')
fs.writeFileSync(path.join(source, 'sub', 'c.md'), 'ignored by pattern')
let runs = 0
const archiveWith = (configure) =>
new Promise((resolve, reject) => {
const out = path.join(root, `test-${++runs}.zip`)
const sink = fs.createWriteStream(out)
const archive = new ZipArchive({ zlib: { level: 9 } })
const names = []
archive.on('entry', (entry) => names.push(entry.name))
archive.on('error', reject)
sink.on('error', reject)
sink.on('close', () => resolve(names))
archive.pipe(sink)
configure(archive)
archive.finalize()
})
try {
/* the directory walk: every file, nested directories included */
const walked = await archiveWith((archive) => archive.directory(source, false))
const missing = ['a.txt', 'sub/b.txt'].filter((n) => !walked.includes(n))
if (missing.length) {
fail('directory walk missed: ' + missing.join(', ') + ' (got: ' + walked.join(', ') + ')')
}
/* pattern and ignore matching, which is minimatch used directly */
const globbed = await archiveWith((archive) =>
archive.glob('**/*.txt', { cwd: source, ignore: ['sub/**'] })
)
if (!globbed.includes('a.txt')) {
fail('glob did not match a.txt (got: ' + globbed.join(', ') + ')')
}
if (globbed.some((n) => n.startsWith('sub/'))) {
fail('glob ignore did not exclude sub/ (got: ' + globbed.join(', ') + ')')
}
console.log('archiver walk ok: ' + walked.join(', '))
console.log('archiver glob ok: ' + globbed.join(', '))
cleanup()
} catch (err) {
fail('archiver error: ' + err.message)
}