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.
- package/CHANGELOG.md +9 -0
- package/README.md +146 -24
- package/package.json +1 -1
- package/src/main/js/config.js +3 -2
- package/src/main/js/post/api/gh.js +11 -0
- package/src/main/js/post/api/git.js +19 -0
- package/src/main/js/post/courier/channels/changelog.js +8 -1
- package/src/main/js/post/courier/channels/gh-pages.js +8 -1
- package/src/main/js/post/courier/channels/gh-release.js +11 -1
- package/src/main/js/post/courier/channels/git-tag.js +25 -0
- package/src/main/js/post/courier/channels/meta.js +8 -1
- package/src/main/js/post/courier/channels/npm.js +27 -14
- package/src/main/js/post/courier/directive.js +81 -0
- package/src/main/js/post/courier/index.js +130 -7
- package/src/main/js/post/courier/parcel.js +15 -2
- package/src/main/js/post/courier/semaphore.js +31 -0
- package/src/main/js/post/courier/seniority.js +19 -0
- package/src/main/js/post/depot/context.js +45 -0
- package/src/main/js/post/depot/reconcile.js +50 -0
- package/src/main/js/post/depot/steps/contextify.js +2 -1
- package/src/main/js/post/depot/steps/pack.js +3 -1
- package/src/main/js/post/depot/steps/publish.js +1 -11
- package/src/main/js/post/modes/deliver.js +24 -0
- package/src/main/js/post/modes/pack.js +71 -0
- package/src/main/js/post/modes/receive.js +71 -0
- package/src/main/js/post/modes/verify.js +83 -0
- package/src/main/js/post/release.js +12 -86
- package/src/main/js/post/tar.js +1 -1
- package/src/test/js/utils/mock.js +1 -1
|
@@ -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
|
|
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
|
|
7
|
+
import {topo} from './depot/deps.js'
|
|
8
8
|
import {exec} from './depot/exec.js'
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
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
|
|
30
|
-
|
|
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
|
-
|
|
60
|
-
|
|
61
|
-
|
|
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})
|
package/src/main/js/post/tar.js
CHANGED
|
@@ -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,
|
|
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 = () => {
|