bulk-release 3.0.5 → 3.1.0

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.
@@ -0,0 +1,83 @@
1
+ import {glob, path, fs} from 'zx-extra'
2
+ import {log} from '../log.js'
3
+ import {parcelChannel} from '../courier/directive.js'
4
+
5
+ const PARCELS_DIR = 'parcels'
6
+
7
+ export const runVerify = async ({cwd, flags}) => {
8
+ const inputDir = typeof flags.verify === 'string' ? flags.verify : PARCELS_DIR
9
+ const contextPath = typeof flags.context === 'string' ? flags.context : path.resolve(cwd, '.zbr-context.json')
10
+ const outputDir = path.resolve(cwd, PARCELS_DIR)
11
+
12
+ log.info(`verifying parcels in ${inputDir} against ${contextPath}`)
13
+
14
+ let context
15
+ try { context = await fs.readJson(contextPath) } catch { context = null }
16
+ if (!context || context.status !== 'proceed') {
17
+ throw new Error(`no valid context at ${contextPath}`)
18
+ }
19
+
20
+ const tars = await glob(path.join(inputDir, 'parcel.*.tar'))
21
+ if (!tars.length) {
22
+ log.info('no parcels to verify')
23
+ return
24
+ }
25
+
26
+ const {sha7, packages: expected} = context
27
+ const errors = []
28
+ const verified = []
29
+
30
+ for (const tarPath of tars) {
31
+ const name = path.basename(tarPath)
32
+
33
+ // sha7 prefix must match
34
+ if (!name.startsWith(`parcel.${sha7}.`)) {
35
+ errors.push(`sha mismatch: ${name}`)
36
+ continue
37
+ }
38
+
39
+ const channel = parcelChannel(name)
40
+ if (!channel) {
41
+ errors.push(`malformed name: ${name}`)
42
+ continue
43
+ }
44
+
45
+ if (channel === 'directive') {
46
+ verified.push(tarPath)
47
+ continue
48
+ }
49
+
50
+ // match to an expected package by tag
51
+ const belongsTo = Object.entries(expected).find(([, pkg]) =>
52
+ pkg.tag && name.includes(`.${pkg.tag}.`)
53
+ )
54
+ if (!belongsTo) {
55
+ errors.push(`unexpected parcel (no matching package): ${name}`)
56
+ continue
57
+ }
58
+
59
+ const [pkgName, pkg] = belongsTo
60
+ if (!pkg.channels.includes(channel)) {
61
+ errors.push(`unexpected channel '${channel}' for ${pkgName}: ${name}`)
62
+ continue
63
+ }
64
+
65
+ verified.push(tarPath)
66
+ }
67
+
68
+ if (errors.length) {
69
+ for (const e of errors) log.error(`verify: ${e}`)
70
+ throw new Error(`parcel verification failed: ${errors.length} error(s)`)
71
+ }
72
+
73
+ // copy verified parcels to output
74
+ if (path.resolve(inputDir) !== outputDir) {
75
+ await fs.ensureDir(outputDir)
76
+ for (const tarPath of verified) {
77
+ await fs.copy(tarPath, path.join(outputDir, path.basename(tarPath)))
78
+ }
79
+ log.info(`${verified.length} parcel(s) verified and copied to ${outputDir}`)
80
+ } else {
81
+ log.info(`${verified.length} parcel(s) verified in place`)
82
+ }
83
+ }
@@ -1,21 +1,17 @@
1
1
  import os from 'node:os'
2
2
  import {createRequire} from 'node:module'
3
- import {$, within, glob, path} from 'zx-extra'
3
+ import {$, within} from 'zx-extra'
4
4
  import {queuefy} from 'queuefy'
5
5
 
6
6
  import {createReport, log} from './log.js'
7
- import {topo, traverseQueue} from './depot/deps.js'
7
+ import {topo} from './depot/deps.js'
8
8
  import {exec} from './depot/exec.js'
9
- import {contextify} from './depot/steps/contextify.js'
10
- import {analyze} from './depot/steps/analyze.js'
11
- import {build} from './depot/steps/build.js'
12
- import {pack} from './depot/steps/pack.js'
13
- import {publish} from './depot/steps/publish.js'
14
- import {clean} from './depot/steps/clean.js'
15
- import {test} from './depot/steps/test.js'
16
- import {deliver, defaultOrder as channels} from './courier/index.js'
9
+ import {defaultOrder as channels} from './courier/index.js'
10
+ import {runReceive} from './modes/receive.js'
11
+ import {runVerify} from './modes/verify.js'
12
+ import {runDeliver} from './modes/deliver.js'
13
+ import {runPack} from './modes/pack.js'
17
14
 
18
- const PARCELS_DIR = 'parcels'
19
15
  const ZBR_VERSION = createRequire(import.meta.url)('../../../../package.json').version
20
16
 
21
17
  export const run = async ({cwd = process.cwd(), env: _env, flags = {}} = {}) => within(async () => {
@@ -26,83 +22,13 @@ export const run = async ({cwd = process.cwd(), env: _env, flags = {}} = {}) =>
26
22
  log.secret(env.GH_TOKEN, env.GITHUB_TOKEN, env.NPM_TOKEN)
27
23
  log.info(`zx-bulk-release@${ZBR_VERSION}`)
28
24
 
29
- return flags.deliver
30
- ? runDeliver({env, flags})
31
- : runPipeline({cwd, env, flags})
32
- })
33
-
34
- // --deliver [dir]
35
- const runDeliver = async ({env, flags}) => {
36
- const dir = typeof flags.deliver === 'string' ? flags.deliver : PARCELS_DIR
37
- const report = createReport({flags})
38
-
39
- $.memo = new Map()
40
- $.report = report
41
-
42
- report.setStatus('inspecting')
43
-
44
- const tars = await glob(path.join(dir, 'parcel.*.tar'))
45
- if (!tars.length) return report.setStatus('success').log(`no parcels in ${dir}`)
25
+ if (flags.verify) return runVerify({cwd, flags})
26
+ if (flags.deliver) return runDeliver({env, flags})
46
27
 
47
- report.setStatus('delivering').log(`parcels: ${tars.length}`)
48
- const result = await deliver(tars, env, {dryRun: flags.dryRun})
49
- report.set('delivery', result).setStatus('success')
50
-
51
- for (const {channel, name, version} of result.entries)
52
- log.info(`${channel} ${name}@${version}`)
53
- log.info(`done: ${result.delivered} delivered, ${result.skipped} skipped`)
54
- }
55
-
56
- // full pipeline (legacy / --pack)
57
- const runPipeline = async ({cwd, env, flags}) => {
58
28
  const ctx = await createContext({flags, env, cwd})
59
- const {report, packages, queue, prev} = ctx
60
-
61
- const forEachPkg = (cb) => traverseQueue({queue, prev, cb: (name) => within(async () => {
62
- $.scope = name
63
- await contextify(packages[name], ctx)
64
- return cb(packages[name])
65
- })})
66
-
67
- report
68
- .log('queue:', queue)
69
- .log('graphs', ctx.graphs)
70
-
71
- try {
72
- await forEachPkg(async (pkg) => {
73
- report.setStatus('analyzing', pkg.name)
74
- await analyze(pkg)
75
- report.set({
76
- config: pkg.config,
77
- version: pkg.version,
78
- prevVersion: pkg.latest.tag?.version || pkg.manifest.version,
79
- releaseType: pkg.releaseType,
80
- tag: pkg.tag,
81
- }, pkg.name)
82
- })
83
-
84
- report.setStatus('pending')
85
-
86
- await forEachPkg(async (pkg) => {
87
- if (!pkg.releaseType) { pkg.skipped = true; return report.setStatus('skipped', pkg.name) }
88
- if (flags.build !== false) { report.setStatus('building', pkg.name); await build(pkg) }
89
- if (flags.test !== false) { report.setStatus('testing', pkg.name); await test(pkg) }
90
- if (flags.dryRun || flags.publish === false) return report.setStatus('success', pkg.name)
91
-
92
- report.setStatus('packing', pkg.name); await pack(pkg)
93
- if (flags.pack) return report.setStatus('packed', pkg.name)
94
-
95
- report.setStatus('publishing', pkg.name); await publish(pkg)
96
- report.setStatus('success', pkg.name)
97
- })
98
- } catch (e) {
99
- report.error(e, e.stack).set('error', e).setStatus('failure')
100
- throw e
101
- } finally {
102
- await clean(ctx)
103
- }
104
- report.setStatus('success').log('Great success!')
105
- }
29
+ if (flags.receive) return runReceive({cwd, env, flags}, ctx)
30
+ return runPack({cwd, env, flags}, ctx)
31
+ })
106
32
 
107
33
  export const createContext = async ({flags, env, cwd}) => {
108
34
  const {packages, queue, root, prev, graphs} = await topo({cwd, flags})
@@ -26,7 +26,7 @@ export const packTar = async (tarPath, manifest, files = []) => {
26
26
  export const hashFile = async (filePath) => {
27
27
  const hash = crypto.createHash('sha1')
28
28
  await pipeline(createReadStream(filePath), hash)
29
- return hash.digest('hex').slice(0, 8)
29
+ return hash.digest('hex').slice(0, 6)
30
30
  }
31
31
 
32
32
  const addDir = async (pack, prefix, dirPath) => {
@@ -112,7 +112,7 @@ export const makeCtx = (overrides = {}) => ({
112
112
  report: overrides.report ?? makeReport(),
113
113
  channels: overrides.channels ?? [],
114
114
  run: overrides.run ?? (async () => {}),
115
- git: {sha: 'abc1234567890', root: tmpDir, ...overrides.git},
115
+ git: {sha: 'abc1234567890', root: tmpDir, timestamp: '1700000000', ...overrides.git},
116
116
  })
117
117
 
118
118
  export const makeReport = () => {