@detergent-software/atk 8.9.0-dev.21 → 8.9.0-dev.23
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 +18 -0
- package/README.md +1 -1
- package/build/commands/publish.js +1 -1
- package/build/commands/publish.js.map +1 -1
- package/build/lib/publish-flow.d.ts +2 -3
- package/build/lib/publish-flow.d.ts.map +1 -1
- package/build/lib/publish-flow.js +165 -149
- package/build/lib/publish-flow.js.map +1 -1
- package/build/lib/publisher.d.ts +14 -1
- package/build/lib/publisher.d.ts.map +1 -1
- package/build/lib/publisher.js +27 -10
- package/build/lib/publisher.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import { writeFile } from 'node:fs/promises';
|
|
2
2
|
import { join, resolve } from 'node:path';
|
|
3
3
|
import { compare as semverCompare, valid as semverValid } from 'semver';
|
|
4
|
-
import { loadAllAdapters } from './adapter.js';
|
|
4
|
+
import { loadAllAdapters, resolveInstalledPaths } from './adapter.js';
|
|
5
5
|
import { isApiError } from './api-client.js';
|
|
6
6
|
import { getCredential } from './auth.js';
|
|
7
7
|
import { errorGuidance, errorMessage } from './errors.js';
|
|
8
|
-
import { detectCompatibleTools, getSignedInUserName } from './init.js';
|
|
8
|
+
import { detectCompatibleTools, getEntrypointName, getSignedInUserName } from './init.js';
|
|
9
9
|
import { findInstalledAsset, readLockfile } from './lockfile.js';
|
|
10
10
|
import { diffManifests, fetchExistingManifest } from './manifest-diff.js';
|
|
11
11
|
import { parseOrgFromName } from './org.js';
|
|
12
12
|
import { findProjectRoot } from './paths.js';
|
|
13
|
-
import { buildPublishPlan, buildStagingArea, bumpVersion, checkRegistryVersion, detectAssetFromPath, detectPublishType, executePublish, findSiblingDependencies, isOrgScopedTarget, loadPublishBatch, loadPublishEntries, mapPublishError, missingDependencies, resolvePlanDependencies, unknownToolsError, updateManifestField, updateManifestVersion, validatePublishTarget, } from './publisher.js';
|
|
13
|
+
import { buildPublishPlan, buildStagingArea, bumpVersion, checkRegistryVersion, detectAssetFromPath, detectPublishType, executePublish, filledManifestFields, findSiblingDependencies, isOrgScopedTarget, loadPublishBatch, loadPublishEntries, mapPublishError, missingDependencies, readManifestFile, resolvePlanDependencies, unknownToolsError, updateManifestField, updateManifestVersion, validatePublishTarget, } from './publisher.js';
|
|
14
14
|
import { clearCache, fetchAssetFileBytes, fetchAssetReadme, fetchRegistry, findAsset } from './registry.js';
|
|
15
15
|
import { parseReviewersInput, validateGithubUsername } from './reviewers.js';
|
|
16
16
|
import { stageInstalledFiles } from './staging.js';
|
|
@@ -162,6 +162,64 @@ async function firstFailure(edits) {
|
|
|
162
162
|
}
|
|
163
163
|
return undefined;
|
|
164
164
|
}
|
|
165
|
+
/**
|
|
166
|
+
* The metadata step of a raw publish, for a raw folder or an installed asset, whose `source`
|
|
167
|
+
* gives its org and any description of its own: the signed-in author, the description from
|
|
168
|
+
* --description or the source, the tags from --tags, and the tools from --tools or detection.
|
|
169
|
+
* An interactive run goes on to ask for what is missing; a non-interactive one stages the
|
|
170
|
+
* manifest now, or fails without a description or tools.
|
|
171
|
+
*/
|
|
172
|
+
async function gatherRawMetadata(state, ctx, detection, source) {
|
|
173
|
+
const [signedIn, tools] = await Promise.all([
|
|
174
|
+
getSignedInUserName(),
|
|
175
|
+
state.flags.toolsFlag ? splitList(state.flags.toolsFlag) : detectCompatibleTools(detection.assetType),
|
|
176
|
+
]);
|
|
177
|
+
if (ctx.cancelled())
|
|
178
|
+
return;
|
|
179
|
+
const author = signedIn ?? '';
|
|
180
|
+
const description = state.flags.descriptionFlag?.trim() || source.description;
|
|
181
|
+
if (!state.nonInteractive) {
|
|
182
|
+
ctx.dispatch({
|
|
183
|
+
authorValue: author,
|
|
184
|
+
...(description ? { description } : {}),
|
|
185
|
+
orgValue: source.org ?? '',
|
|
186
|
+
rawDetection: detection,
|
|
187
|
+
toolsDetected: tools,
|
|
188
|
+
type: 'RAW_DETECT_DONE',
|
|
189
|
+
});
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
if (!description) {
|
|
193
|
+
ctx.dispatch({
|
|
194
|
+
message: state.flags.fromInstalled
|
|
195
|
+
? `description required: provide --description (the lockfile has none for '${detection.name}')`
|
|
196
|
+
: 'description required: provide --description or include in manifest.json',
|
|
197
|
+
type: 'ERROR',
|
|
198
|
+
});
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
if (tools.length === 0) {
|
|
202
|
+
ctx.dispatch({
|
|
203
|
+
message: `--tools <comma-separated> required: no tools detected for type ${detection.assetType}`,
|
|
204
|
+
type: 'ERROR',
|
|
205
|
+
});
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
const stagingDir = await buildStagingArea(detection, {
|
|
209
|
+
author,
|
|
210
|
+
description,
|
|
211
|
+
...(source.org ? { org: source.org } : {}),
|
|
212
|
+
tags: splitList(state.flags.tagsFlag),
|
|
213
|
+
tools,
|
|
214
|
+
});
|
|
215
|
+
ctx.stage(stagingDir);
|
|
216
|
+
if (ctx.cancelled())
|
|
217
|
+
return;
|
|
218
|
+
const detected = await detectPublishType(stagingDir);
|
|
219
|
+
if (ctx.cancelled())
|
|
220
|
+
return;
|
|
221
|
+
ctx.dispatch({ rawDetection: detection, target: detected, type: 'RAW_DETECT_DONE_NON_INTERACTIVE' });
|
|
222
|
+
}
|
|
165
223
|
function stepFor(state) {
|
|
166
224
|
switch (state.phase) {
|
|
167
225
|
case 'building-plan':
|
|
@@ -226,10 +284,10 @@ function toErrorAction(err, override) {
|
|
|
226
284
|
}
|
|
227
285
|
/**
|
|
228
286
|
* Write what the run changed in memory to the manifests on disk: the root's bump and each
|
|
229
|
-
* entry's, and the tags and tools the
|
|
230
|
-
*
|
|
231
|
-
*
|
|
232
|
-
* other files are still written.
|
|
287
|
+
* entry's, and the tags and tools the publish sent on top of the root's manifest (from flags,
|
|
288
|
+
* prompts or detection). Only once the publish has opened its PR, so a cancelled or failed one
|
|
289
|
+
* leaves every manifest as it was. A file whose write fails takes none of its later edits and
|
|
290
|
+
* comes back as one warning naming the fields it lacks; the other files are still written.
|
|
233
291
|
*/
|
|
234
292
|
async function writeManifestEdits(state) {
|
|
235
293
|
const target = state.target;
|
|
@@ -241,19 +299,14 @@ async function writeManifestEdits(state) {
|
|
|
241
299
|
files.set(path, [...(files.get(path) ?? []), { field, value, write }]);
|
|
242
300
|
};
|
|
243
301
|
const version = (dir, file, value) => edit(dir, file, 'version', value, () => updateManifestVersion(dir, file, value));
|
|
244
|
-
const
|
|
302
|
+
const manifestFile = target.type === 'asset' ? 'manifest.json' : 'bundle.json';
|
|
245
303
|
if (state.versionCheck?.status === 'needs-bump' && state.resolvedVersion) {
|
|
246
|
-
version(target.sourceDir,
|
|
304
|
+
version(target.sourceDir, manifestFile, state.resolvedVersion);
|
|
247
305
|
}
|
|
248
|
-
//
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
field('tags', tags);
|
|
253
|
-
}
|
|
254
|
-
if (state.selectedPromptTools.length > 0) {
|
|
255
|
-
field('tools', tools);
|
|
256
|
-
}
|
|
306
|
+
// A manifest that can't be read now takes no field edits; its version edit, if any, warns.
|
|
307
|
+
const written = await readManifestFile(target).catch(() => undefined);
|
|
308
|
+
for (const [name, value] of Object.entries(written ? filledManifestFields(written, target) : {})) {
|
|
309
|
+
edit(target.sourceDir, manifestFile, name, value, () => updateManifestField(target.sourceDir, manifestFile, name, value));
|
|
257
310
|
}
|
|
258
311
|
for (const entry of state.entries) {
|
|
259
312
|
if (entry.versionCheck?.status === 'needs-bump' && entry.resolvedVersion) {
|
|
@@ -266,7 +319,7 @@ async function writeManifestEdits(state) {
|
|
|
266
319
|
if (!failed)
|
|
267
320
|
continue;
|
|
268
321
|
const missing = edits.slice(failed.index);
|
|
269
|
-
// Only the version matters to the next publish: it
|
|
322
|
+
// Only the version matters to the next publish: it fills missing tags and tools again.
|
|
270
323
|
const again = missing.some((e) => e.field === 'version') ? ' before you publish it again' : '';
|
|
271
324
|
unwritten.push(`The PR is open, but ${path} wasn't updated (${errorMessage(failed.error)}): set ${missing.map((e) => `"${e.field}" to ${JSON.stringify(e.value)}`).join(', ')}${again}.`);
|
|
272
325
|
}
|
|
@@ -275,29 +328,45 @@ async function writeManifestEdits(state) {
|
|
|
275
328
|
// ---------------------------------------------------------------------------
|
|
276
329
|
// Steps, one per phase that waits on work
|
|
277
330
|
// ---------------------------------------------------------------------------
|
|
331
|
+
/**
|
|
332
|
+
* The installed asset's files, at their source paths, through a raw publish's metadata step,
|
|
333
|
+
* with the version, entrypoint, dependencies, org and description its lockfile entry records.
|
|
334
|
+
*/
|
|
278
335
|
const resolvingInstalled = {
|
|
279
336
|
async run(state, ctx) {
|
|
280
337
|
const source = await findInstalledSource(state, ctx);
|
|
281
338
|
if (!source)
|
|
282
339
|
return;
|
|
283
340
|
const { adapter, installed, projectRoot } = source;
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
341
|
+
const { files } = resolveInstalledPaths(installed, adapter, projectRoot);
|
|
342
|
+
// An older lockfile entry has no entrypoint: the type's usual one when it was installed, else its first file.
|
|
343
|
+
const usual = getEntrypointName(installed.type, installed.name);
|
|
344
|
+
const entrypoint = installed.entrypoint ?? (files.some((file) => file.sourcePath === usual) ? usual : files[0]?.sourcePath);
|
|
345
|
+
const dependencies = (installed.dependencies ?? []).map(({ name, type, version }) => ({
|
|
346
|
+
name,
|
|
347
|
+
type,
|
|
348
|
+
...(version ? { version } : {}),
|
|
349
|
+
}));
|
|
350
|
+
const detection = {
|
|
351
|
+
assetType: installed.type,
|
|
352
|
+
...(dependencies.length > 0 ? { dependencies } : {}),
|
|
353
|
+
...(entrypoint ? { entrypoint } : {}),
|
|
354
|
+
files: files.map((file) => ({
|
|
355
|
+
absolutePath: join(projectRoot, file.installedPath),
|
|
356
|
+
relativePath: file.sourcePath,
|
|
357
|
+
})),
|
|
294
358
|
name: installed.name,
|
|
295
|
-
|
|
296
|
-
type: installed.type,
|
|
359
|
+
sourceDir: projectRoot,
|
|
297
360
|
version: installed.version,
|
|
298
361
|
};
|
|
299
|
-
|
|
300
|
-
|
|
362
|
+
// A --with folder that is missing, a bundle or a repeat fails here, before any prompt, as in detecting.
|
|
363
|
+
if (state.flags.withPaths?.length) {
|
|
364
|
+
const entries = await loadPublishEntries(state.flags.withPaths, projectRoot);
|
|
365
|
+
if (ctx.cancelled())
|
|
366
|
+
return;
|
|
367
|
+
ctx.dispatch({ entries, type: 'ENTRIES_LOADED' });
|
|
368
|
+
}
|
|
369
|
+
await gatherRawMetadata(state, ctx, detection, { description: installed.description, org: installed.org });
|
|
301
370
|
},
|
|
302
371
|
};
|
|
303
372
|
const resolvingUpdate = {
|
|
@@ -396,66 +465,11 @@ const detecting = {
|
|
|
396
465
|
const detection = await detectAssetFromPath(state.resolvedPath, adapter, projectRoot);
|
|
397
466
|
if (ctx.cancelled())
|
|
398
467
|
return;
|
|
399
|
-
//
|
|
400
|
-
const author = (await getSignedInUserName()) ?? '';
|
|
401
|
-
if (ctx.cancelled())
|
|
402
|
-
return;
|
|
468
|
+
// No manifest exists yet, so the project's org and the flags and prompts give the metadata.
|
|
403
469
|
const lockfile = await readLockfile(projectRoot);
|
|
404
470
|
if (ctx.cancelled())
|
|
405
471
|
return;
|
|
406
|
-
|
|
407
|
-
if (ctx.cancelled())
|
|
408
|
-
return;
|
|
409
|
-
// Non-interactive mode (raw publish — no manifest exists yet, so the only
|
|
410
|
-
// sources for description are --description flag or exit-1; tags come from
|
|
411
|
-
// --tags or default to empty). Tool detection runs for both modes above.
|
|
412
|
-
if (!state.nonInteractive) {
|
|
413
|
-
// Interactive mode — gather metadata from the user
|
|
414
|
-
ctx.dispatch({
|
|
415
|
-
authorValue: author,
|
|
416
|
-
orgValue: lockfile.org ?? '',
|
|
417
|
-
rawDetection: detection,
|
|
418
|
-
toolsDetected: tools,
|
|
419
|
-
type: 'RAW_DETECT_DONE',
|
|
420
|
-
});
|
|
421
|
-
return;
|
|
422
|
-
}
|
|
423
|
-
if (!state.flags.descriptionFlag) {
|
|
424
|
-
ctx.dispatch({
|
|
425
|
-
message: 'description required: provide --description or include in manifest.json',
|
|
426
|
-
type: 'ERROR',
|
|
427
|
-
});
|
|
428
|
-
return;
|
|
429
|
-
}
|
|
430
|
-
// Tools resolution: --tools flag -> detected (already populated above)
|
|
431
|
-
let toolsList;
|
|
432
|
-
if (state.flags.toolsFlag) {
|
|
433
|
-
toolsList = splitList(state.flags.toolsFlag);
|
|
434
|
-
}
|
|
435
|
-
else if (tools.length > 0) {
|
|
436
|
-
toolsList = tools;
|
|
437
|
-
}
|
|
438
|
-
else {
|
|
439
|
-
ctx.dispatch({
|
|
440
|
-
message: `--tools <comma-separated> required: no tools detected for type ${detection.assetType}`,
|
|
441
|
-
type: 'ERROR',
|
|
442
|
-
});
|
|
443
|
-
return;
|
|
444
|
-
}
|
|
445
|
-
const stagingDir = await buildStagingArea(detection, {
|
|
446
|
-
author,
|
|
447
|
-
description: state.flags.descriptionFlag,
|
|
448
|
-
...(lockfile.org ? { org: lockfile.org } : {}),
|
|
449
|
-
tags: splitList(state.flags.tagsFlag),
|
|
450
|
-
tools: toolsList,
|
|
451
|
-
});
|
|
452
|
-
ctx.stage(stagingDir);
|
|
453
|
-
if (ctx.cancelled())
|
|
454
|
-
return;
|
|
455
|
-
const detected = await detectPublishType(stagingDir);
|
|
456
|
-
if (ctx.cancelled())
|
|
457
|
-
return;
|
|
458
|
-
ctx.dispatch({ rawDetection: detection, target: detected, type: 'RAW_DETECT_DONE_NON_INTERACTIVE' });
|
|
472
|
+
await gatherRawMetadata(state, ctx, detection, { org: lockfile.org });
|
|
459
473
|
},
|
|
460
474
|
};
|
|
461
475
|
/** The `building-staging` sub-phase of `gathering-metadata`: the manifest from the prompts' answers. */
|
|
@@ -536,14 +550,13 @@ const validating = {
|
|
|
536
550
|
},
|
|
537
551
|
};
|
|
538
552
|
/**
|
|
539
|
-
* In interactive mode, loads adapters for the multi-select prompt.
|
|
540
|
-
*
|
|
541
|
-
*
|
|
542
|
-
* checking-registry.
|
|
553
|
+
* In interactive mode without --tools, loads adapters for the multi-select prompt. Otherwise
|
|
554
|
+
* resolves tools via manifest -> --tools flag -> detectCompatibleTools(assetType) -> exit 1,
|
|
555
|
+
* then dispatches RESOLVE_TOOLS_NONINTERACTIVE to advance past the prompt.
|
|
543
556
|
*/
|
|
544
557
|
const loadingTools = {
|
|
545
558
|
async run(state, ctx) {
|
|
546
|
-
if (!state.nonInteractive) {
|
|
559
|
+
if (!state.nonInteractive && !state.flags.toolsFlag) {
|
|
547
560
|
const adapters = await loadAllAdapters();
|
|
548
561
|
if (ctx.cancelled())
|
|
549
562
|
return;
|
|
@@ -825,19 +838,23 @@ export function publishReducer(state, action) {
|
|
|
825
838
|
case 'PUBLISH_PROGRESS':
|
|
826
839
|
return { ...state, progressMessage: action.message };
|
|
827
840
|
case 'RAW_DETECT_DONE':
|
|
841
|
+
// Ask only for what neither a flag nor the installed asset gave.
|
|
828
842
|
return {
|
|
829
843
|
...state,
|
|
830
844
|
authorValue: action.authorValue,
|
|
831
|
-
|
|
845
|
+
descriptionValue: action.description ?? '',
|
|
846
|
+
isRawPublish: !state.flags.fromInstalled,
|
|
847
|
+
metadataSubPhase: action.description === undefined ? 'description' : metadataAfterDescription(state),
|
|
832
848
|
orgValue: action.orgValue,
|
|
833
849
|
phase: 'gathering-metadata',
|
|
834
850
|
rawDetection: action.rawDetection,
|
|
851
|
+
tagsValue: state.flags.tagsFlag ?? '',
|
|
835
852
|
toolsDetected: action.toolsDetected,
|
|
836
853
|
};
|
|
837
854
|
case 'RAW_DETECT_DONE_NON_INTERACTIVE':
|
|
838
855
|
return {
|
|
839
856
|
...state,
|
|
840
|
-
isRawPublish:
|
|
857
|
+
isRawPublish: !state.flags.fromInstalled,
|
|
841
858
|
phase: 'validating',
|
|
842
859
|
rawDetection: action.rawDetection,
|
|
843
860
|
target: action.target,
|
|
@@ -857,34 +874,32 @@ export function publishReducer(state, action) {
|
|
|
857
874
|
}
|
|
858
875
|
else {
|
|
859
876
|
// needs-bump
|
|
860
|
-
if (state.
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
};
|
|
869
|
-
nextPhase = 'building-plan';
|
|
870
|
-
}
|
|
871
|
-
catch {
|
|
872
|
-
errorMessage = 'Failed to compute bumped version.';
|
|
873
|
-
nextPhase = 'error';
|
|
874
|
-
}
|
|
875
|
-
}
|
|
876
|
-
else if (isBatch(state.flags)) {
|
|
877
|
-
// advanceToPlan lists every asset of the batch that needs --bump, once the entries are checked.
|
|
877
|
+
if (state.flags.bump && state.target) {
|
|
878
|
+
// --bump applies in any run; a successful publish writes it to the manifest
|
|
879
|
+
try {
|
|
880
|
+
bumpedVersion = bumpVersion(versionCheck.latestVersion, state.flags.bump);
|
|
881
|
+
bumpedTarget = {
|
|
882
|
+
...state.target,
|
|
883
|
+
data: { ...state.target.data, version: bumpedVersion },
|
|
884
|
+
};
|
|
878
885
|
nextPhase = 'building-plan';
|
|
879
886
|
}
|
|
880
|
-
|
|
881
|
-
errorMessage =
|
|
887
|
+
catch {
|
|
888
|
+
errorMessage = 'Failed to compute bumped version.';
|
|
882
889
|
nextPhase = 'error';
|
|
883
890
|
}
|
|
884
891
|
}
|
|
885
|
-
else {
|
|
892
|
+
else if (!state.nonInteractive) {
|
|
886
893
|
nextPhase = 'bump-prompt';
|
|
887
894
|
}
|
|
895
|
+
else if (isBatch(state.flags)) {
|
|
896
|
+
// advanceToPlan lists every asset of the batch that needs --bump, once the entries are checked.
|
|
897
|
+
nextPhase = 'building-plan';
|
|
898
|
+
}
|
|
899
|
+
else {
|
|
900
|
+
errorMessage = `--bump <patch|minor|major> required: registry has v${versionCheck.latestVersion} but manifest is not newer`;
|
|
901
|
+
nextPhase = 'error';
|
|
902
|
+
}
|
|
888
903
|
}
|
|
889
904
|
const checked = {
|
|
890
905
|
...state,
|
|
@@ -907,16 +922,11 @@ export function publishReducer(state, action) {
|
|
|
907
922
|
: undefined;
|
|
908
923
|
return {
|
|
909
924
|
...state,
|
|
910
|
-
|
|
925
|
+
// An interactive run's --tools stood in for the tools prompt; the reviewer prompt may still follow.
|
|
926
|
+
phase: state.nonInteractive ? 'checking-registry' : phaseAfterMetadata(updatedTarget),
|
|
911
927
|
target: updatedTarget,
|
|
912
928
|
};
|
|
913
929
|
}
|
|
914
|
-
case 'RESOLVED_INSTALLED':
|
|
915
|
-
return {
|
|
916
|
-
...state,
|
|
917
|
-
phase: 'detecting',
|
|
918
|
-
resolvedPath: action.resolvedPath,
|
|
919
|
-
};
|
|
920
930
|
case 'RESOLVED_UPDATE':
|
|
921
931
|
return {
|
|
922
932
|
...state,
|
|
@@ -999,7 +1009,7 @@ export function publishReducer(state, action) {
|
|
|
999
1009
|
return {
|
|
1000
1010
|
...state,
|
|
1001
1011
|
descriptionValue: trimmed,
|
|
1002
|
-
metadataSubPhase:
|
|
1012
|
+
metadataSubPhase: metadataAfterDescription(state),
|
|
1003
1013
|
};
|
|
1004
1014
|
}
|
|
1005
1015
|
case 'SUBMIT_PROMPT_TAGS': {
|
|
@@ -1107,19 +1117,21 @@ export function publishReducer(state, action) {
|
|
|
1107
1117
|
validation: action.validation,
|
|
1108
1118
|
};
|
|
1109
1119
|
}
|
|
1110
|
-
//
|
|
1111
|
-
//
|
|
1112
|
-
|
|
1113
|
-
|
|
1120
|
+
// A manifest without tags takes them from --tags right here, in any run: a non-interactive
|
|
1121
|
+
// one without the flag publishes none, an interactive one asks. Manifest tags always win.
|
|
1122
|
+
const tagsFromFlag = !hasTags && (state.nonInteractive || state.flags.tagsFlag !== undefined);
|
|
1123
|
+
let nextTarget = state.target;
|
|
1124
|
+
if (tagsFromFlag && nextTarget) {
|
|
1125
|
+
const flagTags = splitList(state.flags.tagsFlag);
|
|
1126
|
+
nextTarget = {
|
|
1127
|
+
...nextTarget,
|
|
1128
|
+
data: { ...nextTarget.data, tags: flagTags.length > 0 ? flagTags : undefined },
|
|
1129
|
+
};
|
|
1130
|
+
}
|
|
1131
|
+
// Non-interactive path: either route to loading-tools (effect resolves tools async) or
|
|
1132
|
+
// directly to checking-registry. The reviewer prompt never applies here — non-interactive
|
|
1133
|
+
// mode always skips it.
|
|
1114
1134
|
if (state.nonInteractive) {
|
|
1115
|
-
let nextTarget = state.target;
|
|
1116
|
-
if (!hasTags && nextTarget) {
|
|
1117
|
-
const flagTags = splitList(state.flags.tagsFlag);
|
|
1118
|
-
nextTarget = {
|
|
1119
|
-
...nextTarget,
|
|
1120
|
-
data: { ...nextTarget.data, tags: flagTags.length > 0 ? flagTags : undefined },
|
|
1121
|
-
};
|
|
1122
|
-
}
|
|
1123
1135
|
let nextPhase;
|
|
1124
1136
|
if (!hasTools) {
|
|
1125
1137
|
// The loading-tools effect detects whether we're non-interactive and
|
|
@@ -1138,7 +1150,7 @@ export function publishReducer(state, action) {
|
|
|
1138
1150
|
};
|
|
1139
1151
|
}
|
|
1140
1152
|
let nextPhase;
|
|
1141
|
-
if (!hasTags) {
|
|
1153
|
+
if (!hasTags && !tagsFromFlag) {
|
|
1142
1154
|
nextPhase = 'prompting-tags';
|
|
1143
1155
|
}
|
|
1144
1156
|
else if (!hasTools) {
|
|
@@ -1151,6 +1163,7 @@ export function publishReducer(state, action) {
|
|
|
1151
1163
|
...state,
|
|
1152
1164
|
phase: nextPhase,
|
|
1153
1165
|
reviewerValue,
|
|
1166
|
+
target: nextTarget,
|
|
1154
1167
|
validation: action.validation,
|
|
1155
1168
|
};
|
|
1156
1169
|
}
|
|
@@ -1161,7 +1174,7 @@ export function publishReducer(state, action) {
|
|
|
1161
1174
|
/**
|
|
1162
1175
|
* The step before `building-plan`, once the root asset's own checks are done:
|
|
1163
1176
|
* check the entries not yet checked, then settle each entry that needs a bump
|
|
1164
|
-
* (the bump prompt
|
|
1177
|
+
* (`--bump`; without it, the bump prompt, or an error when non-interactive), then plan.
|
|
1165
1178
|
*/
|
|
1166
1179
|
function advanceToPlan(state) {
|
|
1167
1180
|
if (state.entries.some((entry) => !entry.versionCheck)) {
|
|
@@ -1191,18 +1204,17 @@ function advanceToPlan(state) {
|
|
|
1191
1204
|
if (entry?.versionCheck?.status !== 'needs-bump') {
|
|
1192
1205
|
return { ...state, bumpEntry: undefined, phase: 'building-plan' };
|
|
1193
1206
|
}
|
|
1207
|
+
if (state.flags.bump) {
|
|
1208
|
+
return bumpEntryAt(state, index, state.flags.bump);
|
|
1209
|
+
}
|
|
1194
1210
|
if (!state.nonInteractive) {
|
|
1195
1211
|
return { ...state, bumpEntry: index, bumpSelection: '', phase: 'bump-prompt' };
|
|
1196
1212
|
}
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
phase: 'error',
|
|
1203
|
-
};
|
|
1204
|
-
}
|
|
1205
|
-
return bumpEntryAt(state, index, state.flags.bump);
|
|
1213
|
+
return {
|
|
1214
|
+
...state,
|
|
1215
|
+
errorMessage: `--bump <patch|minor|major> required: registry has v${entry.versionCheck.latestVersion} of ${entry.target.assetType}:${entry.target.data.name} (from ${entry.label}) but its manifest is not newer`,
|
|
1216
|
+
phase: 'error',
|
|
1217
|
+
};
|
|
1206
1218
|
}
|
|
1207
1219
|
/**
|
|
1208
1220
|
* Bump the entry at `index` from its registry version, then go on toward the
|
|
@@ -1221,6 +1233,10 @@ function bumpEntryAt(state, index, bump) {
|
|
|
1221
1233
|
}
|
|
1222
1234
|
return advanceToPlan({ ...state, entries: replaceEntry(state.entries, index, withVersion(entry, version)) });
|
|
1223
1235
|
}
|
|
1236
|
+
/** A raw publish's step after its description: the tags prompt, unless --tags answered it. */
|
|
1237
|
+
function metadataAfterDescription(state) {
|
|
1238
|
+
return state.flags.tagsFlag === undefined ? 'tags' : 'building-staging';
|
|
1239
|
+
}
|
|
1224
1240
|
/**
|
|
1225
1241
|
* Phase to enter once the tags/tools metadata prompts are resolved: org-scoped
|
|
1226
1242
|
* targets get the reviewer prompt, everything else proceeds to the registry check.
|