@detergent-software/atk 9.2.0-dev.5 → 9.2.0-dev.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 +30 -0
- package/build/command-manifest.json +3 -3
- package/build/commands/browse.d.ts.map +1 -1
- package/build/commands/browse.js +119 -16
- package/build/commands/browse.js.map +1 -1
- package/build/commands/install.d.ts.map +1 -1
- package/build/commands/install.js +182 -147
- package/build/commands/install.js.map +1 -1
- package/build/commands/uninstall.d.ts.map +1 -1
- package/build/commands/uninstall.js +5 -3
- package/build/commands/uninstall.js.map +1 -1
- package/build/components/BrowseList.d.ts +3 -1
- package/build/components/BrowseList.d.ts.map +1 -1
- package/build/components/BrowseList.js +12 -6
- package/build/components/BrowseList.js.map +1 -1
- package/build/components/HelpBar.d.ts +5 -1
- package/build/components/HelpBar.d.ts.map +1 -1
- package/build/components/HelpBar.js +18 -4
- package/build/components/HelpBar.js.map +1 -1
- package/build/components/InstallConfirmBar.d.ts +4 -2
- package/build/components/InstallConfirmBar.d.ts.map +1 -1
- package/build/components/InstallConfirmBar.js +15 -2
- package/build/components/InstallConfirmBar.js.map +1 -1
- package/build/hooks/useBrowseState.d.ts +31 -1
- package/build/hooks/useBrowseState.d.ts.map +1 -1
- package/build/hooks/useBrowseState.js +99 -15
- package/build/hooks/useBrowseState.js.map +1 -1
- package/build/hooks/useUninstallState.d.ts.map +1 -1
- package/build/hooks/useUninstallState.js +23 -15
- package/build/hooks/useUninstallState.js.map +1 -1
- package/build/lib/format.d.ts +5 -0
- package/build/lib/format.d.ts.map +1 -1
- package/build/lib/format.js +7 -0
- package/build/lib/format.js.map +1 -1
- package/build/lib/install-targets.d.ts +90 -0
- package/build/lib/install-targets.d.ts.map +1 -0
- package/build/lib/install-targets.js +146 -0
- package/build/lib/install-targets.js.map +1 -0
- package/build/lib/installer.js +3 -2
- package/build/lib/installer.js.map +1 -1
- package/build/lib/resolver.d.ts +35 -0
- package/build/lib/resolver.d.ts.map +1 -1
- package/build/lib/resolver.js +65 -21
- package/build/lib/resolver.js.map +1 -1
- package/build/lib/uninstaller.d.ts +29 -1
- package/build/lib/uninstaller.d.ts.map +1 -1
- package/build/lib/uninstaller.js +63 -4
- package/build/lib/uninstaller.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { Box, Text, useApp } from 'ink';
|
|
3
|
-
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
3
|
+
import { Fragment, useCallback, useEffect, useRef, useState } from 'react';
|
|
4
4
|
import { z } from 'zod';
|
|
5
5
|
import { CrossScopeWarning, DryRunBanner, ErrorView, InstallSummary, MultiSelectList, Spinner, } from '../components/index.js';
|
|
6
6
|
import { useCommand } from '../hooks/useCommand.js';
|
|
@@ -10,14 +10,16 @@ import { resolveBundleVersion } from '../lib/bundle-version.js';
|
|
|
10
10
|
import { loadConfig } from '../lib/config.js';
|
|
11
11
|
import { crossScopeNotice, findCrossScopeInstalls } from '../lib/cross-scope.js';
|
|
12
12
|
import { errorGuidance, errorMessage } from '../lib/errors.js';
|
|
13
|
+
import { bundleMembers, installPlannedTargets, isBundleInPlace, planTargetsInstall } from '../lib/install-targets.js';
|
|
13
14
|
import { isInstalledForTool } from '../lib/installed-for-tool.js';
|
|
15
|
+
import { InstallerError } from '../lib/installer.js';
|
|
14
16
|
import { addAssetToLockfile, findInstalledAsset, identityKey, readLockfile, upgradeToDirectInstall, withLockfileErrorHandling, writeLockfile, } from '../lib/lockfile.js';
|
|
15
17
|
import { executeInstallPlanForTools, withoutKeptDependencies } from '../lib/multi-tool-install.js';
|
|
16
18
|
import { addNotice } from '../lib/notices.js';
|
|
17
19
|
import { formatOrgName, isBareOrgScope, parseBundleSpec, resolveActiveOrg } from '../lib/org.js';
|
|
18
20
|
import { renderJson, useNonInteractive } from '../lib/output.js';
|
|
19
21
|
import { argument, option } from '../lib/pastel/index.js';
|
|
20
|
-
import { fetchRegistry, findAsset, findAssetsByName, findBundle,
|
|
22
|
+
import { fetchRegistry, findAsset, findAssetsByName, findBundle, getAssetsByOrg, isDeprecated, isLatestAlias, } from '../lib/registry.js';
|
|
21
23
|
import { resolveBundleInstallPlan, resolveInstallPlan, resolveMultipleInstallPlans, ResolverError, } from '../lib/resolver.js';
|
|
22
24
|
import { scopeLabel } from '../lib/scope.js';
|
|
23
25
|
import { resolvePlacingTools } from '../lib/tool-resolver.js';
|
|
@@ -36,7 +38,7 @@ export const options = z.object({
|
|
|
36
38
|
.default(false)
|
|
37
39
|
.describe(option({
|
|
38
40
|
alias: 'a',
|
|
39
|
-
description: 'Install every asset from an org scope without prompting (for `atk install @org`; required with --json or
|
|
41
|
+
description: 'Install every asset from an org scope without prompting (for `atk install @org`; required with --json, no terminal, or other names)',
|
|
40
42
|
})),
|
|
41
43
|
dryRun: z
|
|
42
44
|
.boolean()
|
|
@@ -119,7 +121,7 @@ export const args = z
|
|
|
119
121
|
})),
|
|
120
122
|
])
|
|
121
123
|
.rest(z.string().describe(argument({
|
|
122
|
-
description: 'More assets to install with the first, as one plan (name@version
|
|
124
|
+
description: 'More assets, bundles or org scopes to install with the first, as one plan (name@version, @org/name, @org with --all)',
|
|
123
125
|
name: 'targets',
|
|
124
126
|
})));
|
|
125
127
|
export default function Install({ args: [rawName, ...more], options: { adopt, all, dryRun, force, json, org, pin, projectRoot, refresh, tool, user, yes }, }) {
|
|
@@ -128,7 +130,7 @@ export default function Install({ args: [rawName, ...more], options: { adopt, al
|
|
|
128
130
|
return _jsx(Sync, { options: { dryRun, force, json, projectRoot, refresh, tool, user } });
|
|
129
131
|
}
|
|
130
132
|
if (more.length > 0) {
|
|
131
|
-
return (_jsx(InstallAssets, { adopt: adopt, dryRun: dryRun, force: force, json: json, org: org, pin: pin, projectRoot: projectRoot, specs: [rawName, ...more], tool: tool, user: user }));
|
|
133
|
+
return (_jsx(InstallAssets, { adopt: adopt, all: all || yes, dryRun: dryRun, force: force, json: json, org: org, pin: pin, projectRoot: projectRoot, specs: [rawName, ...more], tool: tool, user: user }));
|
|
132
134
|
}
|
|
133
135
|
if (isBareOrgScope(rawName)) {
|
|
134
136
|
const orgName = rawName.slice(1);
|
|
@@ -149,20 +151,6 @@ async function bundleAtRequestedVersion(entry, requested, progress) {
|
|
|
149
151
|
}
|
|
150
152
|
return resolution.bundle;
|
|
151
153
|
}
|
|
152
|
-
/**
|
|
153
|
-
* The bundle's members that one of `tools` places, as the registry has them. A member none of them
|
|
154
|
-
* places is left out, since this run can't adopt it, and so is a ref the registry can't find, which
|
|
155
|
-
* the install reports.
|
|
156
|
-
*/
|
|
157
|
-
function bundleMembers(registry, bundle, tools) {
|
|
158
|
-
return (bundle.assets ?? []).flatMap((ref) => {
|
|
159
|
-
const found = findBundleMember(registry, bundle, ref);
|
|
160
|
-
if (!found || !tools.some((t) => placesAsset(t.adapter, found.asset.type, found.versionData.tools)))
|
|
161
|
-
return [];
|
|
162
|
-
const { name, org, type } = found.asset;
|
|
163
|
-
return [{ name, ...(org ? { org } : {}), type }];
|
|
164
|
-
});
|
|
165
|
-
}
|
|
166
154
|
/**
|
|
167
155
|
* What a bundle install says about its members outside the bundle (`kept`) and the members it
|
|
168
156
|
* `adopted`. A member with no origin can be adopted; another bundle's member stays with that bundle.
|
|
@@ -273,16 +261,8 @@ function InstallAsset({ adopt, dryRun, force, json, org: cliOrg, pin, projectRoo
|
|
|
273
261
|
// through to a misleading "asset not found".
|
|
274
262
|
if (!indexBundle && (registry.bundles ?? []).some((b) => b.name === name)) {
|
|
275
263
|
const assetExists = findAsset(registry, name, undefined, undefined, org);
|
|
276
|
-
if (!assetExists)
|
|
277
|
-
|
|
278
|
-
throw new Error(`Bundle '${name}' not found in org '${inlineOrg}'.`);
|
|
279
|
-
}
|
|
280
|
-
const orgScopes = (registry.bundles ?? [])
|
|
281
|
-
.filter((b) => b.name === name && b.org)
|
|
282
|
-
.map((b) => `@${b.org}/${name}`);
|
|
283
|
-
throw new Error(`Bundle '${name}' not found in the registry.` +
|
|
284
|
-
(orgScopes.length > 0 ? ` Did you mean ${orgScopes.join(' or ')}?` : ''));
|
|
285
|
-
}
|
|
264
|
+
if (!assetExists)
|
|
265
|
+
throw otherScopeBundleError(registry, name, inlineOrg);
|
|
286
266
|
}
|
|
287
267
|
// With no active org, `info` opens the only org asset of a bare name, but install doesn't
|
|
288
268
|
// switch scopes on its own: it names the spec that installs that asset.
|
|
@@ -360,26 +340,7 @@ function InstallAsset({ adopt, dryRun, force, json, org: cliOrg, pin, projectRoo
|
|
|
360
340
|
};
|
|
361
341
|
// Bundle installation flow — check if already installed for ALL tools
|
|
362
342
|
if (!force) {
|
|
363
|
-
|
|
364
|
-
// When no configured tool takes any member, fall through so the installer says why.
|
|
365
|
-
const allInstalledForAllTools = tools.every(({ adapter: toolAdapter }) => members.length > 0 &&
|
|
366
|
-
bundleAssets.every((ref) => {
|
|
367
|
-
const found = findBundleMember(registry, bundle, ref);
|
|
368
|
-
if (!found)
|
|
369
|
-
return false;
|
|
370
|
-
// A tool that doesn't take this member (its type, or a hook for other tools) has
|
|
371
|
-
// nothing to check.
|
|
372
|
-
if (!placesAsset(toolAdapter, found.asset.type, found.versionData.tools))
|
|
373
|
-
return true;
|
|
374
|
-
const installed = findInstalledAsset(before, ref.name, ref.type, found.asset.org);
|
|
375
|
-
if (!installed)
|
|
376
|
-
return false;
|
|
377
|
-
if (requiredBundleVersion && !isFromBundleVersion(installed, bundle, requiredBundleVersion)) {
|
|
378
|
-
return false;
|
|
379
|
-
}
|
|
380
|
-
return isInstalledForTool(installed, toolAdapter, projectDir);
|
|
381
|
-
}));
|
|
382
|
-
if (allInstalledForAllTools) {
|
|
343
|
+
if (isBundleInPlace(registry, bundle, before, { members, projectDir, requiredBundleVersion, tools })) {
|
|
383
344
|
// Adopting a bundle that is in place only records its origin on the members with none:
|
|
384
345
|
// their files, versions and pins stay as they are. Another bundle's member stays its.
|
|
385
346
|
const outside = membersOutsideBundle(before, members, bundle);
|
|
@@ -521,13 +482,15 @@ function InstallAsset({ adopt, dryRun, force, json, org: cliOrg, pin, projectRoo
|
|
|
521
482
|
return (_jsxs(Box, { flexDirection: 'column', children: [data.toolResults.map(({ results, skipped, tool: toolName, warnings }) => (_jsx(InstallSummary, { results: results, skipped: skipped, tools: [toolName], warnings: warnings, where: label }, toolName))), _jsx(CrossScopeWarning, { installs: data.crossScope ?? [] })] }));
|
|
522
483
|
}
|
|
523
484
|
/**
|
|
524
|
-
* `atk install a b@1.2.0 @org/c`: the named assets
|
|
525
|
-
*
|
|
526
|
-
*
|
|
527
|
-
*
|
|
528
|
-
* once the install succeeds.
|
|
485
|
+
* `atk install a b@1.2.0 @org/c my-bundle @org --all`: the named assets, the bundles' members and
|
|
486
|
+
* the orgs' assets resolved together (shared dependencies once, one version per asset) and
|
|
487
|
+
* installed as one plan, so a failure rolls the whole run back, as an `@org` install does. Every
|
|
488
|
+
* name is checked before anything is written. An asset already in place for every tool is left out
|
|
489
|
+
* of the plan, and one installed only as a dependency becomes direct once the install succeeds. A
|
|
490
|
+
* bundle is left out when all of it is in place, as when it is installed alone; its members get
|
|
491
|
+
* its origin unless they are named themselves ({@link resolveTargetsInstallPlan}).
|
|
529
492
|
*/
|
|
530
|
-
function InstallAssets({ adopt, dryRun, force, json, org: cliOrg, pin, projectRoot, specs, tool, user, }) {
|
|
493
|
+
function InstallAssets({ adopt, all, dryRun, force, json, org: cliOrg, pin, projectRoot, specs, tool, user, }) {
|
|
531
494
|
const { json: effectiveJson } = useNonInteractive(json);
|
|
532
495
|
const { fields, resolve, resolved } = useResolvedScope({ projectRoot, user });
|
|
533
496
|
const state = useCommand(async (progress) => {
|
|
@@ -535,11 +498,13 @@ function InstallAssets({ adopt, dryRun, force, json, org: cliOrg, pin, projectRo
|
|
|
535
498
|
if (adopt) {
|
|
536
499
|
throw new Error('--adopt records a bundle as the owner of its members, so it works with one bundle only.');
|
|
537
500
|
}
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
}
|
|
501
|
+
// No picker opens halfway through a run, so an org among names takes all its assets, and
|
|
502
|
+
// only when asked to. A dry run shows them all, as `atk install @org --dry-run` does.
|
|
503
|
+
const orgScopes = [...new Set(specs.filter(isBareOrgScope))];
|
|
504
|
+
if (orgScopes.length > 0 && !all && !dryRun) {
|
|
505
|
+
throw new InstallerError(`${orgScopes.join(', ')} among other names installs every asset in ` +
|
|
506
|
+
`${orgScopes.length === 1 ? 'it' : 'them'}, so it needs --all (-a). ` +
|
|
507
|
+
`To choose ${orgScopes.length === 1 ? 'its' : 'their'} assets, install ${orgScopes.length === 1 ? 'it' : 'each'} alone.`, { code: 'selection_required', command: installCommandFor(`${specs.join(' ')} --all`, flags) });
|
|
543
508
|
}
|
|
544
509
|
const { root: projectDir, scope: installScope } = await resolve();
|
|
545
510
|
progress('Loading configuration...');
|
|
@@ -549,33 +514,65 @@ function InstallAssets({ adopt, dryRun, force, json, org: cliOrg, pin, projectRo
|
|
|
549
514
|
progress('Fetching registry...');
|
|
550
515
|
const registry = await fetchRegistry({ force: true });
|
|
551
516
|
const currentLockfile = await readLockfile(projectDir);
|
|
552
|
-
// Every name is checked before anything is resolved or written.
|
|
553
|
-
//
|
|
554
|
-
const bundles = [];
|
|
517
|
+
// Every name is checked before anything is resolved or written. The targets keep the order
|
|
518
|
+
// they were named in, which decides the origin of a member two bundles share.
|
|
555
519
|
const deprecated = [];
|
|
556
|
-
const
|
|
557
|
-
|
|
520
|
+
const emptyOrgs = [];
|
|
521
|
+
const orgs = [];
|
|
522
|
+
const targets = [];
|
|
523
|
+
const deprecationOf = (name, deprecation) => {
|
|
524
|
+
if (deprecation.deprecated && !force) {
|
|
525
|
+
deprecated.push(`"${name}" (${deprecation.reason}${deprecation.replacement ? `; use ${deprecation.replacement}` : ''})`);
|
|
526
|
+
}
|
|
527
|
+
};
|
|
528
|
+
for (const spec of new Set(specs)) {
|
|
529
|
+
if (isBareOrgScope(spec)) {
|
|
530
|
+
const orgName = spec.slice(1);
|
|
531
|
+
const orgAssets = getAssetsByOrg(registry, orgName);
|
|
532
|
+
if (orgAssets.length === 0)
|
|
533
|
+
emptyOrgs.push(spec);
|
|
534
|
+
orgs.push({ count: orgAssets.length, org: orgName });
|
|
535
|
+
for (const { name, type } of orgAssets) {
|
|
536
|
+
targets.push({
|
|
537
|
+
fromOrg: true,
|
|
538
|
+
kind: 'asset',
|
|
539
|
+
name,
|
|
540
|
+
org: orgName,
|
|
541
|
+
spec: formatOrgName(orgName, name),
|
|
542
|
+
type,
|
|
543
|
+
});
|
|
544
|
+
}
|
|
545
|
+
continue;
|
|
546
|
+
}
|
|
558
547
|
const { name: parsedName, version } = parseNameVersion(spec);
|
|
559
548
|
const { name: nameFromOrg, org: inlineOrg } = parseBundleSpec(parsedName);
|
|
560
549
|
const name = inlineOrg ? nameFromOrg : parsedName;
|
|
561
550
|
const org = resolveActiveOrg(inlineOrg ?? cliOrg, currentLockfile.org, config.org);
|
|
562
|
-
const
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
551
|
+
const indexBundle = findBundle(registry, name, inlineOrg);
|
|
552
|
+
if (indexBundle) {
|
|
553
|
+
const bundle = await bundleAtRequestedVersion(indexBundle, version, progress);
|
|
554
|
+
const earlier = targets.find((t) => t.kind === 'bundle' && t.bundle.name === name && t.bundle.org === indexBundle.org);
|
|
555
|
+
if (earlier) {
|
|
556
|
+
// A bundle installs at one version, as an asset does.
|
|
557
|
+
if (earlier.bundle.version !== bundle.version) {
|
|
558
|
+
throw new ResolverError(`Bundle "${formatOrgName(bundle.org, name)}" is named at two versions: ${earlier.bundle.version} ` +
|
|
559
|
+
`(${earlier.spec}) and ${bundle.version} (${spec}). A bundle installs at one version.`);
|
|
560
|
+
}
|
|
561
|
+
continue;
|
|
562
|
+
}
|
|
563
|
+
deprecationOf(name, isDeprecated(registry, name, undefined, indexBundle.org));
|
|
564
|
+
targets.push({ bundle, kind: 'bundle', spec, ...(version ? { version } : {}) });
|
|
567
565
|
continue;
|
|
568
566
|
}
|
|
569
|
-
const
|
|
570
|
-
if (
|
|
571
|
-
|
|
567
|
+
const asset = findAsset(registry, name, undefined, undefined, org)?.asset;
|
|
568
|
+
if (!asset && (registry.bundles ?? []).some((b) => b.name === name)) {
|
|
569
|
+
throw otherScopeBundleError(registry, name, inlineOrg);
|
|
572
570
|
}
|
|
573
|
-
|
|
571
|
+
deprecationOf(name, isDeprecated(registry, name, asset?.type, asset ? asset.org : inlineOrg));
|
|
572
|
+
targets.push({ kind: 'asset', name, ...(org ? { org } : {}), spec, ...(version ? { version } : {}) });
|
|
574
573
|
}
|
|
575
|
-
if (
|
|
576
|
-
throw new ResolverError(
|
|
577
|
-
? `${bundles[0].spec} is a bundle, which installs on its own.`
|
|
578
|
-
: `${bundles.map((b) => b.spec).join(', ')} are bundles, which install on their own.`, { command: installCommandFor(bundles[0].install, flags) });
|
|
574
|
+
if (emptyOrgs.length > 0) {
|
|
575
|
+
throw new ResolverError(`No assets found for ${emptyOrgs.join(', ')}. Nothing was installed.`);
|
|
579
576
|
}
|
|
580
577
|
if (deprecated.length > 0) {
|
|
581
578
|
throw new ResolverError(`Deprecated: ${deprecated.join(', ')}. Nothing was installed.`, {
|
|
@@ -583,54 +580,61 @@ function InstallAssets({ adopt, dryRun, force, json, org: cliOrg, pin, projectRo
|
|
|
583
580
|
});
|
|
584
581
|
}
|
|
585
582
|
// A name every targeted tool already has in place is left out of the plan, as it is for one name.
|
|
586
|
-
const
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
const supportingTools = existing && match ? tools.filter((t) => placesAsset(t.adapter, existing.type, match.versionData.tools)) : [];
|
|
595
|
-
const inPlace = !force &&
|
|
596
|
-
existing !== undefined &&
|
|
597
|
-
supportingTools.length > 0 &&
|
|
598
|
-
supportingTools.every((t) => isInstalledForTool(existing, t.adapter, projectDir)) &&
|
|
599
|
-
(!request.version || request.version === existing.version);
|
|
600
|
-
if (!inPlace) {
|
|
601
|
-
toInstall.push(request);
|
|
602
|
-
continue;
|
|
603
|
-
}
|
|
604
|
-
if (assets.some((a) => identityKey(a) === identityKey(existing)))
|
|
605
|
-
continue;
|
|
606
|
-
const dependency = (existing.installReason ?? 'direct') === 'dependency';
|
|
607
|
-
if (dependency)
|
|
608
|
-
upgrades.push(existing);
|
|
609
|
-
assets.push({
|
|
610
|
-
name: existing.name,
|
|
611
|
-
...(existing.org ? { org: existing.org } : {}),
|
|
612
|
-
outcome: dependency ? 'upgraded-to-direct' : 'already-installed',
|
|
613
|
-
type: existing.type,
|
|
614
|
-
version: existing.version,
|
|
615
|
-
});
|
|
616
|
-
}
|
|
583
|
+
const adoptCommandFor = ({ bundle, version }) => installCommandFor(formatOrgName(bundle.org, bundle.name) + (version ? `@${version}` : ''), {
|
|
584
|
+
adopt: true,
|
|
585
|
+
force: false,
|
|
586
|
+
pin: false,
|
|
587
|
+
projectRoot,
|
|
588
|
+
tool,
|
|
589
|
+
user,
|
|
590
|
+
});
|
|
617
591
|
progress('Resolving dependencies...');
|
|
618
|
-
const
|
|
619
|
-
const
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
592
|
+
const targetsPlan = planTargetsInstall(registry, targets, currentLockfile, tools, { force, projectDir });
|
|
593
|
+
const { assets: report, plan, toInstall } = targetsPlan;
|
|
594
|
+
const bundles = targetsPlan.bundlesInPlace.map((request) => {
|
|
595
|
+
const { bundle } = request;
|
|
596
|
+
return {
|
|
597
|
+
name: bundle.name,
|
|
598
|
+
...(bundle.org ? { org: bundle.org } : {}),
|
|
599
|
+
outcome: 'already-installed',
|
|
600
|
+
version: bundle.version,
|
|
601
|
+
...bundleReport(membersOutsideBundle(currentLockfile, bundleMembers(registry, bundle, tools), bundle), [], {
|
|
602
|
+
adoptCommand: adoptCommandFor(request),
|
|
603
|
+
bundle,
|
|
604
|
+
}),
|
|
605
|
+
};
|
|
606
|
+
});
|
|
629
607
|
const crossScope = (resolvedPlan, installedAt) => crossScopeOf(resolvedPlan, { installedAt, json: effectiveJson, scope: installScope, tools: toolNames });
|
|
608
|
+
// Each bundle in the plan, in the order named, then the ones left out; and the orgs.
|
|
609
|
+
const targetsField = (lockfile, planned) => {
|
|
610
|
+
const installedBundles = toInstall.flatMap((request) => {
|
|
611
|
+
if (request.kind !== 'bundle')
|
|
612
|
+
return [];
|
|
613
|
+
const { bundle } = request;
|
|
614
|
+
const members = bundleMembers(registry, bundle, tools);
|
|
615
|
+
// A dry run writes nothing, so the members it leaves outside the bundle are read from the plan.
|
|
616
|
+
const kept = [
|
|
617
|
+
...membersOutsideBundle(lockfile, members, bundle),
|
|
618
|
+
...(planned ? plannedOutsideBundle(plan, lockfile, members, bundle) : []),
|
|
619
|
+
];
|
|
620
|
+
return [
|
|
621
|
+
{
|
|
622
|
+
name: bundle.name,
|
|
623
|
+
...(bundle.org ? { org: bundle.org } : {}),
|
|
624
|
+
outcome: 'installed',
|
|
625
|
+
version: bundle.version,
|
|
626
|
+
...bundleReport(kept, [], { adoptCommand: adoptCommandFor(request), bundle }),
|
|
627
|
+
},
|
|
628
|
+
];
|
|
629
|
+
});
|
|
630
|
+
const named = [...installedBundles, ...bundles];
|
|
631
|
+
return { ...(named.length > 0 ? { bundles: named } : {}), ...(orgs.length > 0 ? { orgs } : {}) };
|
|
632
|
+
};
|
|
630
633
|
// A dry run says so even when every name is in place, and writes nothing (not even an upgrade).
|
|
631
634
|
if (dryRun) {
|
|
632
635
|
return {
|
|
633
636
|
assets: report,
|
|
637
|
+
...targetsField(currentLockfile, true),
|
|
634
638
|
outcome: 'dry-run-install',
|
|
635
639
|
plan: withoutKeptDependencies(plan, currentLockfile, tools, {
|
|
636
640
|
onProgress: progress,
|
|
@@ -642,28 +646,26 @@ function InstallAssets({ adopt, dryRun, force, json, org: cliOrg, pin, projectRo
|
|
|
642
646
|
};
|
|
643
647
|
}
|
|
644
648
|
return await withLockfileErrorHandling(projectDir, async () => {
|
|
645
|
-
const toolResults =
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
:
|
|
655
|
-
// After the install, so a failed one leaves even the install reasons as they were.
|
|
656
|
-
if (upgrades.length > 0) {
|
|
657
|
-
let lockfile = await readLockfile(projectDir);
|
|
658
|
-
for (const { name, org, type } of upgrades)
|
|
659
|
-
lockfile = upgradeToDirectInstall(lockfile, name, type, org);
|
|
660
|
-
await writeLockfile(projectDir, lockfile);
|
|
649
|
+
const toolResults = await installPlannedTargets(targetsPlan, tools, {
|
|
650
|
+
force,
|
|
651
|
+
onProgress: progress,
|
|
652
|
+
onTool: (toolName) => progress(`Installing for ${toolName}...`),
|
|
653
|
+
pin,
|
|
654
|
+
projectDir,
|
|
655
|
+
scope: installScope,
|
|
656
|
+
});
|
|
657
|
+
if (plan.entries.length === 0) {
|
|
658
|
+
return { assets: report, ...targetsField(currentLockfile, false), outcome: 'already-installed' };
|
|
661
659
|
}
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
660
|
+
return {
|
|
661
|
+
assets: report,
|
|
662
|
+
...targetsField(await readLockfile(projectDir), false),
|
|
663
|
+
outcome: 'installed',
|
|
664
|
+
toolResults,
|
|
665
|
+
...(await crossScope(plan, projectDir)),
|
|
666
|
+
};
|
|
665
667
|
});
|
|
666
|
-
}, [adopt, cliOrg, dryRun, force, pin, specs.join(' '), tool]);
|
|
668
|
+
}, [adopt, all, cliOrg, dryRun, force, pin, specs.join(' '), tool]);
|
|
667
669
|
if (state.status === 'loading') {
|
|
668
670
|
return _jsx(Spinner, { message: state.message });
|
|
669
671
|
}
|
|
@@ -686,10 +688,10 @@ function InstallAssets({ adopt, dryRun, force, json, org: cliOrg, pin, projectRo
|
|
|
686
688
|
return renderJson({ ...data, ...fields });
|
|
687
689
|
}
|
|
688
690
|
if (data.outcome === 'dry-run-install') {
|
|
689
|
-
return (_jsxs(Box, { flexDirection: 'column', children: [_jsx(DryRunBanner, { action: 'install', count: data.plan.totalCount, tools: data.tools, where: label }), _jsx(Text, { children: " " }), _jsx(PlanEntryLines, { plan: data.plan }), _jsx(NamedAssetLines, { assets: data.assets, dryRun: true, where: label }), _jsx(CrossScopeWarning, { installs: data.crossScope ?? [] })] }));
|
|
691
|
+
return (_jsxs(Box, { flexDirection: 'column', children: [_jsx(DryRunBanner, { action: 'install', count: data.plan.totalCount, tools: data.tools, where: label }), _jsx(Text, { children: " " }), _jsx(PlanEntryLines, { plan: data.plan }), _jsx(NamedAssetLines, { assets: data.assets, dryRun: true, where: label }), _jsx(TargetLines, { bundles: data.bundles, dryRun: true, orgs: data.orgs, where: label }), _jsx(CrossScopeWarning, { installs: data.crossScope ?? [] })] }));
|
|
690
692
|
}
|
|
691
693
|
return (_jsxs(Box, { flexDirection: 'column', children: [data.outcome === 'installed' &&
|
|
692
|
-
data.toolResults.map(({ results, skipped, tool: toolName, warnings }) => (_jsx(InstallSummary, { results: results, skipped: skipped, tools: [toolName], warnings: warnings, where: label }, toolName))), _jsx(NamedAssetLines, { assets: data.assets, where: label }), data.outcome === 'installed' && _jsx(CrossScopeWarning, { installs: data.crossScope ?? [] })] }));
|
|
694
|
+
data.toolResults.map(({ results, skipped, tool: toolName, warnings }) => (_jsx(InstallSummary, { results: results, skipped: skipped, tools: [toolName], warnings: warnings, where: label }, toolName))), _jsx(NamedAssetLines, { assets: data.assets, where: label }), _jsx(TargetLines, { bundles: data.bundles, orgs: data.orgs, where: label }), data.outcome === 'installed' && _jsx(CrossScopeWarning, { installs: data.crossScope ?? [] })] }));
|
|
693
695
|
}
|
|
694
696
|
/** `atk install <spec>` with the run's own flags, for a command an error suggests. */
|
|
695
697
|
function installCommandFor(spec, { adopt, dryRun, force, pin, projectRoot, tool, user }) {
|
|
@@ -953,11 +955,6 @@ function InstallOrg({ cliOrg, dryRun, force, json, orgName, pin, projectRoot, sk
|
|
|
953
955
|
}
|
|
954
956
|
return null;
|
|
955
957
|
}
|
|
956
|
-
/** Whether a lockfile asset was installed as part of this bundle (name, org) at this version. */
|
|
957
|
-
function isFromBundleVersion(installed, bundle, version) {
|
|
958
|
-
const origin = installed.bundleOrigin;
|
|
959
|
-
return origin?.name === bundle.name && origin.org === bundle.org && origin.version === version;
|
|
960
|
-
}
|
|
961
958
|
/** A member as a report names it: its spec and type. */
|
|
962
959
|
function memberLabel({ name, org, type }) {
|
|
963
960
|
return `${formatOrgName(org, name)} (${type})`;
|
|
@@ -986,8 +983,46 @@ function NamedAssetLines({ assets, dryRun = false, where, }) {
|
|
|
986
983
|
? `${dryRun ? 'would become' : 'is now'} a direct install${inScope}.`
|
|
987
984
|
: `is already installed${inScope}.`] }, identityKey(a)))) }));
|
|
988
985
|
}
|
|
986
|
+
/**
|
|
987
|
+
* The error for a bundle name that only another scope has, when no asset can claim the name
|
|
988
|
+
* either, instead of a misleading "asset not found".
|
|
989
|
+
*/
|
|
990
|
+
function otherScopeBundleError(registry, name, inlineOrg) {
|
|
991
|
+
if (inlineOrg) {
|
|
992
|
+
return new Error(`Bundle '${name}' not found in org '${inlineOrg}'.`);
|
|
993
|
+
}
|
|
994
|
+
const orgScopes = (registry.bundles ?? []).filter((b) => b.name === name && b.org).map((b) => `@${b.org}/${name}`);
|
|
995
|
+
return new Error(`Bundle '${name}' not found in the registry.` +
|
|
996
|
+
(orgScopes.length > 0 ? ` Did you mean ${orgScopes.join(' or ')}?` : ''));
|
|
997
|
+
}
|
|
989
998
|
/** A plan's entries, each with its type and version, its dependencies marked. */
|
|
990
999
|
function PlanEntryLines({ plan }) {
|
|
991
1000
|
return (_jsx(_Fragment, { children: plan.entries.map((entry) => (_jsxs(Text, { children: [' ', entry.reason === 'dependency' ? (_jsxs(Text, { color: 'cyan', children: [formatOrgName(entry.asset.org, entry.asset.name), ' ', _jsxs(Text, { dimColor: true, children: ["(", entry.asset.type, ") v", entry.version, " \u2014 dependency"] })] })) : (_jsxs(Text, { children: [_jsx(Text, { bold: true, children: formatOrgName(entry.asset.org, entry.asset.name) }), _jsxs(Text, { dimColor: true, children: [' ', "(", entry.asset.type, ") v", entry.version] })] }))] }, identityKey(entry.asset)))) }));
|
|
992
1001
|
}
|
|
1002
|
+
/**
|
|
1003
|
+
* The members not installed yet that the plan installs without the bundle's origin: named on their
|
|
1004
|
+
* own, or with another bundle named before it. A dry run reports them as an install would.
|
|
1005
|
+
*/
|
|
1006
|
+
function plannedOutsideBundle(plan, lockfile, members, bundle) {
|
|
1007
|
+
return members.flatMap((member) => {
|
|
1008
|
+
if (findInstalledAsset(lockfile, member.name, member.type, member.org))
|
|
1009
|
+
return [];
|
|
1010
|
+
const entry = plan.entries.find((e) => identityKey(e.asset) === identityKey(member));
|
|
1011
|
+
const origin = entry?.bundleOrigin;
|
|
1012
|
+
if (!entry || (origin?.name === bundle.name && origin.org === bundle.org))
|
|
1013
|
+
return [];
|
|
1014
|
+
return [{ ...(origin ? { bundle: origin } : {}), ...member }];
|
|
1015
|
+
});
|
|
1016
|
+
}
|
|
1017
|
+
/**
|
|
1018
|
+
* The bundles and orgs a run of several names had among them: what it did with each bundle, the
|
|
1019
|
+
* bundle's members outside it, and how many assets each org brought.
|
|
1020
|
+
*/
|
|
1021
|
+
function TargetLines({ bundles = [], dryRun = false, orgs = [], where, }) {
|
|
1022
|
+
const inScope = where ? ` in ${where}` : '';
|
|
1023
|
+
return (_jsxs(_Fragment, { children: [bundles.map((bundle) => {
|
|
1024
|
+
const spec = formatOrgName(bundle.org, bundle.name);
|
|
1025
|
+
return (_jsxs(Fragment, { children: [bundle.outcome === 'already-installed' ? (_jsxs(Text, { children: [_jsx(Text, { color: 'yellow', children: "!" }), " Bundle ", _jsx(Text, { bold: true, children: spec }), " is already fully installed (v", bundle.version, ")", inScope, "."] })) : (_jsxs(Text, { children: [_jsx(Text, { color: 'green', children: "\u2713" }), " Bundle ", _jsx(Text, { bold: true, children: spec }), " v", bundle.version, ' ', dryRun ? 'would be installed' : 'installed', inScope, "."] })), _jsx(BundleReportLines, { bundleName: spec, dryRun: dryRun, report: bundle })] }, spec));
|
|
1026
|
+
}), orgs.map(({ count, org }) => (_jsxs(Text, { children: [_jsxs(Text, { bold: true, color: 'magenta', children: ["Org: @", org] }), _jsxs(Text, { dimColor: true, children: [' ', "\u2014 all ", count, " asset", count !== 1 ? 's' : ''] })] }, org)))] }));
|
|
1027
|
+
}
|
|
993
1028
|
//# sourceMappingURL=install.js.map
|