zx-bulk-release 3.1.6 → 3.1.7
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
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
## [3.1.7](https://github.com/semrel-extra/zx-bulk-release/compare/v3.1.6...v3.1.7) (2026-04-13)
|
|
2
|
+
|
|
3
|
+
### Fixes & improvements
|
|
4
|
+
* perf: apply traverseQueue to delivery phase ([6f1ba58](https://github.com/semrel-extra/zx-bulk-release/commit/6f1ba58885e7739b23de1d895f80ea4680645347))
|
|
5
|
+
* perf: let `--verify` accept out dir ([80015bb](https://github.com/semrel-extra/zx-bulk-release/commit/80015bb067306892bbcc969c5ee2700a256b22f4))
|
|
6
|
+
|
|
1
7
|
## [3.1.6](https://github.com/semrel-extra/zx-bulk-release/compare/v3.1.5...v3.1.6) (2026-04-13)
|
|
2
8
|
|
|
3
9
|
### Fixes & improvements
|
package/package.json
CHANGED
package/src/main/js/cli.js
CHANGED
|
@@ -15,7 +15,7 @@ Modes:
|
|
|
15
15
|
(no flags) All-in-one: analyze, build, test, pack, deliver
|
|
16
16
|
--receive Analyze & preflight, write zbr-context.json. Run BEFORE deps install
|
|
17
17
|
--pack [dir] Build, test, pack tars to dir [default: parcels]
|
|
18
|
-
--verify [
|
|
18
|
+
--verify [in:out] Validate parcels against context, copy to out dir [default: parcels]
|
|
19
19
|
--deliver [dir] Deliver parcels through channels [default: parcels]
|
|
20
20
|
|
|
21
21
|
Options:
|
|
@@ -2,6 +2,7 @@ import {$, tempy, within, path, semver, fs} from 'zx-extra'
|
|
|
2
2
|
import {unpackTar} from '../tar.js'
|
|
3
3
|
import {log} from '../log.js'
|
|
4
4
|
import {pool} from '../../util.js'
|
|
5
|
+
import {traverseQueue} from '../depot/deps.js'
|
|
5
6
|
import {scanDirectives, invalidateOrphans} from '../parcel/directive.js'
|
|
6
7
|
import {tryLock, unlock, signalRebuild} from './semaphore.js'
|
|
7
8
|
import gitTag from './channels/git-tag.js'
|
|
@@ -160,43 +161,38 @@ const deliverParcel = async (tarPath, channelName, pkgName, version, env, {dryRu
|
|
|
160
161
|
return res === 'duplicate' ? 'duplicate' : 'ok'
|
|
161
162
|
}
|
|
162
163
|
|
|
163
|
-
const
|
|
164
|
+
const deliverPkg = async (pkgName, pkg, tarMap, env, {dryRun, cwd}) => {
|
|
164
165
|
const entries = []
|
|
165
166
|
const conflicts = []
|
|
166
167
|
const skipped = []
|
|
167
168
|
|
|
168
|
-
|
|
169
|
-
const pkg = directive.packages[pkgName]
|
|
170
|
-
if (!pkg) continue
|
|
171
|
-
|
|
172
|
-
let pkgConflict = false
|
|
169
|
+
let pkgConflict = false
|
|
173
170
|
|
|
174
|
-
|
|
175
|
-
|
|
171
|
+
for (const step of pkg.deliver) {
|
|
172
|
+
if (pkgConflict) break
|
|
176
173
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
174
|
+
const results = await Promise.all(step.map(async (channelName) => {
|
|
175
|
+
const parcelName = (pkg.parcels || []).find(p => p.includes(`.${channelName}.`))
|
|
176
|
+
const tarPath = parcelName && tarMap.get(parcelName)
|
|
177
|
+
if (!tarPath) return 'missing'
|
|
181
178
|
|
|
182
|
-
|
|
183
|
-
|
|
179
|
+
return deliverParcel(tarPath, channelName, pkgName, pkg.version, env, {dryRun, cwd})
|
|
180
|
+
}))
|
|
184
181
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
182
|
+
for (let i = 0; i < step.length; i++) {
|
|
183
|
+
const r = results[i]
|
|
184
|
+
if (r === 'skip') skipped.push({channelName: step[i], pkg: pkgName})
|
|
185
|
+
else if (r !== 'missing' && r !== 'already') entries.push({channel: step[i], name: pkgName, version: pkg.version})
|
|
186
|
+
}
|
|
190
187
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
}
|
|
188
|
+
if (results.includes('conflict')) {
|
|
189
|
+
pkgConflict = true
|
|
190
|
+
conflicts.push(pkgName)
|
|
191
|
+
for (const p of pkg.parcels || []) {
|
|
192
|
+
const tp = tarMap.get(p)
|
|
193
|
+
if (!tp) continue
|
|
194
|
+
const c = await fs.readFile(tp, 'utf8').catch(() => null)
|
|
195
|
+
if (c !== 'released') await fs.writeFile(tp, 'conflict')
|
|
200
196
|
}
|
|
201
197
|
}
|
|
202
198
|
}
|
|
@@ -204,6 +200,25 @@ const deliverDirective = async (directive, tarMap, env, {dryRun, cwd}) => {
|
|
|
204
200
|
return {entries, conflicts, skipped}
|
|
205
201
|
}
|
|
206
202
|
|
|
203
|
+
const deliverDirective = async (directive, tarMap, env, {dryRun, cwd}) => {
|
|
204
|
+
const entries = []
|
|
205
|
+
const conflicts = []
|
|
206
|
+
const skipped = []
|
|
207
|
+
|
|
208
|
+
const prev = new Map(Object.entries(directive.prev || {}))
|
|
209
|
+
|
|
210
|
+
await traverseQueue({queue: directive.queue, prev, cb: async (pkgName) => {
|
|
211
|
+
const pkg = directive.packages[pkgName]
|
|
212
|
+
if (!pkg) return
|
|
213
|
+
const r = await deliverPkg(pkgName, pkg, tarMap, env, {dryRun, cwd})
|
|
214
|
+
entries.push(...r.entries)
|
|
215
|
+
conflicts.push(...r.conflicts)
|
|
216
|
+
skipped.push(...r.skipped)
|
|
217
|
+
}})
|
|
218
|
+
|
|
219
|
+
return {entries, conflicts, skipped}
|
|
220
|
+
}
|
|
221
|
+
|
|
207
222
|
// --- Main entry point ---
|
|
208
223
|
|
|
209
224
|
export const deliver = async (tars, env = process.env, {concurrency = 4, dryRun = false, cwd} = {}) => {
|
|
@@ -10,8 +10,8 @@ import {publish} from '../depot/steps/publish.js'
|
|
|
10
10
|
import {clean} from '../depot/steps/clean.js'
|
|
11
11
|
import {test} from '../depot/steps/test.js'
|
|
12
12
|
import {preflight} from '../depot/reconcile.js'
|
|
13
|
-
import {buildDirective, PARCELS_DIR} from '../parcel/index.js'
|
|
14
13
|
import {CONTEXT_FILE, readContext} from '../depot/context.js'
|
|
14
|
+
import {buildDirective, PARCELS_DIR} from '../parcel/index.js'
|
|
15
15
|
|
|
16
16
|
export const runPack = async ({cwd, env, flags}, ctx) => {
|
|
17
17
|
const {report, packages, queue, prev} = ctx
|
|
@@ -4,9 +4,9 @@ import {PARCELS_DIR, verifyParcels} from '../parcel/index.js'
|
|
|
4
4
|
import {CONTEXT_FILE, readContext} from '../depot/context.js'
|
|
5
5
|
|
|
6
6
|
export const runVerify = async ({cwd, flags}) => {
|
|
7
|
-
const inputDir = typeof flags.verify === 'string' ? flags.verify :
|
|
7
|
+
const [inputDir = PARCELS_DIR, outputDir_ = PARCELS_DIR] = typeof flags.verify === 'string' ? flags.verify.split(':') : []
|
|
8
|
+
const outputDir = path.resolve(cwd, outputDir_)
|
|
8
9
|
const contextPath = typeof flags.context === 'string' ? flags.context : path.resolve(cwd, CONTEXT_FILE)
|
|
9
|
-
const outputDir = path.resolve(cwd, PARCELS_DIR)
|
|
10
10
|
|
|
11
11
|
log.info(`verifying parcels in ${inputDir} against ${contextPath}`)
|
|
12
12
|
|
|
@@ -17,6 +17,7 @@ export const buildDirective = async (ctx, packedPkgs, outputDir) => {
|
|
|
17
17
|
const {sha, timestamp} = packedPkgs[0].ctx.git
|
|
18
18
|
const packages = {}
|
|
19
19
|
const parcels = []
|
|
20
|
+
const packedNames = new Set(packedPkgs.map(p => p.name))
|
|
20
21
|
|
|
21
22
|
for (const pkg of packedPkgs) {
|
|
22
23
|
const pkgParcels = (pkg.tars || []).map(t => path.basename(t))
|
|
@@ -29,12 +30,21 @@ export const buildDirective = async (ctx, packedPkgs, outputDir) => {
|
|
|
29
30
|
parcels.push(...pkgParcels)
|
|
30
31
|
}
|
|
31
32
|
|
|
33
|
+
// Store dependency graph for parallel delivery in topo order
|
|
34
|
+
const prev = {}
|
|
35
|
+
if (ctx.prev) {
|
|
36
|
+
for (const name of packedNames) {
|
|
37
|
+
const deps = (ctx.prev.get(name) || []).filter(d => packedNames.has(d))
|
|
38
|
+
if (deps.length) prev[name] = deps
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
32
42
|
const sha7 = sha.slice(0, 7)
|
|
33
43
|
const manifest = {
|
|
34
44
|
channel: 'directive',
|
|
35
45
|
sha, timestamp: Number(timestamp),
|
|
36
46
|
queue: packedPkgs.map(p => p.name),
|
|
37
|
-
packages, parcels,
|
|
47
|
+
packages, parcels, prev,
|
|
38
48
|
}
|
|
39
49
|
|
|
40
50
|
const tarPath = path.join(outputDir, `parcel.${sha7}.directive.${timestamp}.tar`)
|