auklet 0.0.26 → 0.0.28
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/README.md +20 -1
- package/dist/publish/api/gitApi.js +1 -1
- package/dist/publish/api/pnpmApi.d.ts +7 -0
- package/dist/publish/api/pnpmApi.js +39 -5
- package/dist/publish/api/publishHookApi.d.ts +2 -2
- package/dist/publish/cli.js +1 -1
- package/dist/publish/publishRunner.d.ts +0 -2
- package/dist/publish/publishRunner.js +12 -17
- package/dist/publish/runner/packagePublisher.js +1 -1
- package/dist/publish/runner/publishFailureReporter.d.ts +1 -2
- package/dist/publish/runner/publishFailureReporter.js +15 -13
- package/dist/publish/runner/publishPreflight.d.ts +4 -1
- package/dist/publish/runner/publishPreflight.js +22 -2
- package/dist/publish/runner/publishSummaryReporter.d.ts +24 -4
- package/dist/publish/runner/publishSummaryReporter.js +122 -77
- package/dist/publish/runner/publishTargetError.js +1 -1
- package/dist/publish/runner/versionWriter.js +14 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -122,10 +122,28 @@ auklet CLI overrides.
|
|
|
122
122
|
Publish controls stay on CLI flags:
|
|
123
123
|
|
|
124
124
|
```bash
|
|
125
|
+
auk publish
|
|
125
126
|
auk publish --filter @scope/ui
|
|
126
127
|
auk publish --version patch --dry-run
|
|
127
128
|
auk publish --no-format
|
|
128
129
|
auk publish --otp 123456
|
|
130
|
+
auk owner add alice
|
|
131
|
+
auk owner add alice --filter @scope/ui --otp 123456
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Target selection:
|
|
135
|
+
|
|
136
|
+
```text
|
|
137
|
+
auk publish
|
|
138
|
+
├─ --filter <pattern> publish matching workspace packages
|
|
139
|
+
└─ no --filter publish the current package
|
|
140
|
+
└─ private monorepo root -> fail; use --filter
|
|
141
|
+
|
|
142
|
+
auk owner add <user...>
|
|
143
|
+
├─ --filter <pattern> add owners to matching workspace packages
|
|
144
|
+
├─ --package <name> add owners to explicit npm packages
|
|
145
|
+
└─ no selector add owners to the current package
|
|
146
|
+
└─ private package -> fail
|
|
129
147
|
```
|
|
130
148
|
|
|
131
149
|
`--no-format` disables auklet's publish output formatter for that run. It is not
|
|
@@ -134,7 +152,8 @@ Before writing versions, auklet checks npm authentication from each target
|
|
|
134
152
|
package directory. Package-local `.npmrc` files and
|
|
135
153
|
`package.json#publishConfig.registry` are respected.
|
|
136
154
|
`--otp` is forwarded to `pnpm publish` for npm accounts or organizations that
|
|
137
|
-
require publish 2FA
|
|
155
|
+
require publish 2FA, and to `pnpm owner add` for owner management 2FA. In CI,
|
|
156
|
+
prefer an npm automation token.
|
|
138
157
|
|
|
139
158
|
## Configuration
|
|
140
159
|
|
|
@@ -10,7 +10,7 @@ export async function isGitRepository(cwd) {
|
|
|
10
10
|
return !result.exitCode && result.stdout.trim() === 'true';
|
|
11
11
|
}
|
|
12
12
|
export async function getGitShortHash(cwd) {
|
|
13
|
-
const result = await runGit(['rev-parse', '--short=
|
|
13
|
+
const result = await runGit(['rev-parse', '--short=7', 'HEAD'], cwd);
|
|
14
14
|
if (result.exitCode)
|
|
15
15
|
return null;
|
|
16
16
|
return result.stdout.trim() || null;
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import type { WorkspacePackage } from '#auklet/publish/types';
|
|
2
2
|
export declare class NpmPublishAuthenticationError extends Error {
|
|
3
|
+
readonly packageRoot: string;
|
|
3
4
|
constructor(packageRoot: string);
|
|
4
5
|
}
|
|
6
|
+
export declare class NpmPackageVersionExistsError extends Error {
|
|
7
|
+
constructor(packageName: string, version: string, registry?: string);
|
|
8
|
+
}
|
|
5
9
|
export declare function ensurePnpm(): Promise<string>;
|
|
6
10
|
export declare function readPnpmWorkspacePackages(root: string): Promise<WorkspacePackage[]>;
|
|
7
11
|
export declare function runPnpmBuild(packageRoot: string): Promise<void>;
|
|
@@ -10,6 +14,9 @@ export declare function runPnpmWhoami(packageRoot: string, options?: {
|
|
|
10
14
|
packageName?: string;
|
|
11
15
|
registry?: string;
|
|
12
16
|
}): Promise<string>;
|
|
17
|
+
export declare function hasPublishedPackageVersion(packageRoot: string, packageName: string, version: string, options?: {
|
|
18
|
+
registry?: string;
|
|
19
|
+
}): Promise<boolean>;
|
|
13
20
|
export declare function runPnpmOwnerAdd(packageName: string, user: string, options: {
|
|
14
21
|
cwd: string;
|
|
15
22
|
otp?: string;
|
|
@@ -4,9 +4,16 @@ import semver from 'semver';
|
|
|
4
4
|
import { readPnpmWorkspacePackageInfo } from '#auklet/workspace/packages';
|
|
5
5
|
const supportedPnpmRange = '>=10.0.0';
|
|
6
6
|
export class NpmPublishAuthenticationError extends Error {
|
|
7
|
+
packageRoot;
|
|
7
8
|
constructor(packageRoot) {
|
|
8
|
-
super(
|
|
9
|
-
|
|
9
|
+
super('npm publish requires additional authentication.');
|
|
10
|
+
this.packageRoot = packageRoot;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
export class NpmPackageVersionExistsError extends Error {
|
|
14
|
+
constructor(packageName, version, registry) {
|
|
15
|
+
const location = registry ? ` at ${registry}` : '';
|
|
16
|
+
super(`[publish] package ${packageName}@${version}${location} already exists.`);
|
|
10
17
|
}
|
|
11
18
|
}
|
|
12
19
|
const runPnpm = async (args, options = {}) => {
|
|
@@ -64,7 +71,7 @@ export async function runPnpmPublish(packageRoot, args) {
|
|
|
64
71
|
if (hasNpmAuthChallenge(result)) {
|
|
65
72
|
throw new NpmPublishAuthenticationError(packageRoot);
|
|
66
73
|
}
|
|
67
|
-
throw new Error(
|
|
74
|
+
throw new Error('pnpm publish failed.');
|
|
68
75
|
}
|
|
69
76
|
}
|
|
70
77
|
export async function runPnpmWhoami(packageRoot, options = {}) {
|
|
@@ -82,6 +89,19 @@ export async function runPnpmWhoami(packageRoot, options = {}) {
|
|
|
82
89
|
}
|
|
83
90
|
return String(result.stdout ?? '').trim();
|
|
84
91
|
}
|
|
92
|
+
export async function hasPublishedPackageVersion(packageRoot, packageName, version, options = {}) {
|
|
93
|
+
const args = ['view', `${packageName}@${version}`, 'version'];
|
|
94
|
+
if (options.registry)
|
|
95
|
+
args.push('--registry', options.registry);
|
|
96
|
+
const result = await runPnpm(args, {
|
|
97
|
+
cwd: packageRoot,
|
|
98
|
+
});
|
|
99
|
+
if (!result.exitCode)
|
|
100
|
+
return String(result.stdout ?? '').trim() === version;
|
|
101
|
+
if (isPackageNotFound(result))
|
|
102
|
+
return false;
|
|
103
|
+
throw new Error(`[publish] failed to check published version for ${packageName}@${version}.`);
|
|
104
|
+
}
|
|
85
105
|
export async function runPnpmOwnerAdd(packageName, user, options) {
|
|
86
106
|
const args = ['owner', 'add', user, packageName];
|
|
87
107
|
if (options.otp)
|
|
@@ -120,9 +140,12 @@ const writeProcessOutput = (result) => {
|
|
|
120
140
|
const stdout = String(result.stdout ?? '');
|
|
121
141
|
const stderr = String(result.stderr ?? '');
|
|
122
142
|
if (stdout)
|
|
123
|
-
process.stdout.write(stdout);
|
|
143
|
+
process.stdout.write(withTrailingLineBreak(stdout));
|
|
124
144
|
if (stderr)
|
|
125
|
-
process.stderr.write(stderr);
|
|
145
|
+
process.stderr.write(withTrailingLineBreak(stderr));
|
|
146
|
+
};
|
|
147
|
+
const withTrailingLineBreak = (value) => {
|
|
148
|
+
return value.endsWith('\n') ? value : `${value}\n`;
|
|
126
149
|
};
|
|
127
150
|
const isNpmAuthChallenge = (output) => {
|
|
128
151
|
return [
|
|
@@ -133,3 +156,14 @@ const isNpmAuthChallenge = (output) => {
|
|
|
133
156
|
'ENEEDAUTH',
|
|
134
157
|
].some((text) => output.includes(text));
|
|
135
158
|
};
|
|
159
|
+
const isPackageNotFound = (result) => {
|
|
160
|
+
const output = [result.stdout, result.stderr]
|
|
161
|
+
.map((value) => String(value ?? ''))
|
|
162
|
+
.join('\n');
|
|
163
|
+
return [
|
|
164
|
+
'E404',
|
|
165
|
+
'404 Not Found',
|
|
166
|
+
'is not in this registry',
|
|
167
|
+
'No match found for version',
|
|
168
|
+
].some((text) => output.includes(text));
|
|
169
|
+
};
|
|
@@ -3,8 +3,8 @@ type RunPublishHookOptions = {
|
|
|
3
3
|
status: HookStatus;
|
|
4
4
|
plan: PublishPlan;
|
|
5
5
|
result?: HookResult;
|
|
6
|
-
failedTarget?: PublishTarget;
|
|
7
|
-
error?: unknown;
|
|
6
|
+
failedTarget?: PublishTarget | null;
|
|
7
|
+
error?: unknown | null;
|
|
8
8
|
};
|
|
9
9
|
export declare function runPublishHook(options: RunPublishHookOptions): Promise<void>;
|
|
10
10
|
export {};
|
package/dist/publish/cli.js
CHANGED
|
@@ -36,8 +36,8 @@ export async function runPublishCli(args) {
|
|
|
36
36
|
dryRun: argv['dry-run'] === true,
|
|
37
37
|
format: argv.format !== false,
|
|
38
38
|
otp: stringOption(argv.otp),
|
|
39
|
-
ignoreScripts: argv['ignore-scripts'] === true,
|
|
40
39
|
allowDirty: argv['allow-dirty'] === true,
|
|
40
|
+
ignoreScripts: argv['ignore-scripts'] === true,
|
|
41
41
|
}).run();
|
|
42
42
|
}
|
|
43
43
|
export async function runOwnerCli(args) {
|
|
@@ -5,11 +5,9 @@ export declare class PublishRunner {
|
|
|
5
5
|
private readonly preflight;
|
|
6
6
|
private readonly versions;
|
|
7
7
|
private readonly logger;
|
|
8
|
-
private readonly summaryLogger;
|
|
9
8
|
private readonly options;
|
|
10
9
|
constructor(options: PublishOptions);
|
|
11
10
|
run(): Promise<void>;
|
|
12
|
-
private preparePlan;
|
|
13
11
|
private publishWithPlan;
|
|
14
12
|
private handleFailure;
|
|
15
13
|
private runAfterPublishSuccess;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createScopedAukletLogger } from '#auklet/logger';
|
|
2
2
|
import { runPublishHook } from '#auklet/publish/api/publishHookApi';
|
|
3
3
|
import { runPackageBuilds, validateBuildScript, } from '#auklet/publish/runner/packageBuilder';
|
|
4
4
|
import { formatPublishOutputs } from '#auklet/publish/runner/publishOutputFormatter';
|
|
@@ -16,21 +16,23 @@ export class PublishRunner {
|
|
|
16
16
|
preflight;
|
|
17
17
|
versions;
|
|
18
18
|
logger;
|
|
19
|
-
summaryLogger;
|
|
20
19
|
options;
|
|
21
20
|
constructor(options) {
|
|
22
21
|
this.options = options;
|
|
23
22
|
this.logger = createScopedAukletLogger('publish');
|
|
24
|
-
this.summaryLogger = createAukletLogger();
|
|
25
23
|
this.git = new ReleaseGitController(this.options, this.logger);
|
|
26
24
|
this.publisher = new PackagePublisher(this.options, this.logger);
|
|
27
25
|
this.preflight = new PublishPreflight(this.options, this.logger);
|
|
28
26
|
this.versions = new VersionWriter(this.options, this.logger);
|
|
29
27
|
}
|
|
30
28
|
async run() {
|
|
31
|
-
const plan = await this.
|
|
29
|
+
const plan = await resolvePublishPlan(this.options, this.logger);
|
|
32
30
|
let failureHookEnabled = false;
|
|
33
31
|
try {
|
|
32
|
+
validateBuildScript(plan.targets);
|
|
33
|
+
await this.git.checkBeforePublish(plan);
|
|
34
|
+
await this.preflight.verifyBeforeBuild(plan);
|
|
35
|
+
this.versions.logDryRunPlan(plan);
|
|
34
36
|
await runPublishHook({ status: 'beforeBuild', plan });
|
|
35
37
|
failureHookEnabled = true;
|
|
36
38
|
this.versions.writeBeforeBuild(plan);
|
|
@@ -42,7 +44,7 @@ export class PublishRunner {
|
|
|
42
44
|
}
|
|
43
45
|
catch (error) {
|
|
44
46
|
if (!failureHookEnabled) {
|
|
45
|
-
reportPublishSummary(
|
|
47
|
+
reportPublishSummary({
|
|
46
48
|
plan,
|
|
47
49
|
status: 'failure',
|
|
48
50
|
error,
|
|
@@ -53,14 +55,6 @@ export class PublishRunner {
|
|
|
53
55
|
}
|
|
54
56
|
await this.runAfterPublishSuccess(plan);
|
|
55
57
|
}
|
|
56
|
-
async preparePlan() {
|
|
57
|
-
const plan = await resolvePublishPlan(this.options, this.logger);
|
|
58
|
-
validateBuildScript(plan.targets);
|
|
59
|
-
await this.git.checkBeforePublish(plan);
|
|
60
|
-
await this.preflight.verifyAuthentication(plan);
|
|
61
|
-
this.versions.logDryRunPlan(plan);
|
|
62
|
-
return plan;
|
|
63
|
-
}
|
|
64
58
|
async publishWithPlan(plan) {
|
|
65
59
|
if (plan.dryRun) {
|
|
66
60
|
await this.preflight.run(plan);
|
|
@@ -71,7 +65,7 @@ export class PublishRunner {
|
|
|
71
65
|
await this.publisher.run(plan);
|
|
72
66
|
}
|
|
73
67
|
async handleFailure(plan, error) {
|
|
74
|
-
const failedTarget = error instanceof PublishTargetError ? error.target :
|
|
68
|
+
const failedTarget = error instanceof PublishTargetError ? error.target : null;
|
|
75
69
|
try {
|
|
76
70
|
await runPublishHook({
|
|
77
71
|
status: 'afterPublish',
|
|
@@ -85,9 +79,9 @@ export class PublishRunner {
|
|
|
85
79
|
this.logger.error?.(hookError);
|
|
86
80
|
}
|
|
87
81
|
if (error instanceof PublishTargetError) {
|
|
88
|
-
reportPublishFailure(error, plan.version
|
|
82
|
+
reportPublishFailure(error, plan.version);
|
|
89
83
|
}
|
|
90
|
-
reportPublishSummary(
|
|
84
|
+
reportPublishSummary({
|
|
91
85
|
plan,
|
|
92
86
|
status: 'failure',
|
|
93
87
|
error,
|
|
@@ -101,9 +95,10 @@ export class PublishRunner {
|
|
|
101
95
|
plan,
|
|
102
96
|
result: 'success',
|
|
103
97
|
});
|
|
104
|
-
reportPublishSummary(
|
|
98
|
+
reportPublishSummary({
|
|
105
99
|
plan,
|
|
106
100
|
status: 'success',
|
|
101
|
+
error: null,
|
|
107
102
|
});
|
|
108
103
|
}
|
|
109
104
|
}
|
|
@@ -27,6 +27,6 @@ export function logAuthenticationError(logger, error) {
|
|
|
27
27
|
if (!(error instanceof NpmPublishAuthenticationError))
|
|
28
28
|
return;
|
|
29
29
|
logger.error('npm publish requires additional authentication.');
|
|
30
|
-
logger.error('If publish 2FA is enabled, retry with
|
|
30
|
+
logger.error('If publish 2FA is enabled, retry with `--otp <code>`.');
|
|
31
31
|
logger.error('For CI, use an npm automation token.');
|
|
32
32
|
}
|
|
@@ -1,3 +1,2 @@
|
|
|
1
1
|
import { PublishTargetError } from '#auklet/publish/runner/publishTargetError';
|
|
2
|
-
|
|
3
|
-
export declare function reportPublishFailure(error: PublishTargetError, version: string, logger: AukletLogger): void;
|
|
2
|
+
export declare function reportPublishFailure(error: PublishTargetError, version: string): void;
|
|
@@ -1,14 +1,16 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
1
|
+
import { createAukletLogger } from '#auklet/logger';
|
|
2
|
+
export function reportPublishFailure(error, version) {
|
|
3
|
+
const logger = createAukletLogger();
|
|
4
|
+
const noteBody = logger.colors.rgb(184, 140, 40);
|
|
5
|
+
const body = [
|
|
6
|
+
noteBody(' Some packages were already published before the failure.'),
|
|
7
|
+
'',
|
|
8
|
+
...error.publishedTargets.map((target) => noteBody(` published ${target.packageName}@${version}`)),
|
|
9
|
+
noteBody(` failed ${error.target.packageName}@${version}`),
|
|
10
|
+
];
|
|
11
|
+
logger.newline();
|
|
12
|
+
logger.note({
|
|
13
|
+
title: logger.colors.yellow(logger.colors.bold('Partial publish detected')),
|
|
14
|
+
body,
|
|
15
|
+
});
|
|
11
16
|
}
|
|
12
|
-
const formatPublishedTarget = (logger, packageName, version) => {
|
|
13
|
-
return ['- ', logger.package(packageName), '@', logger.version(version)];
|
|
14
|
-
};
|
|
@@ -6,6 +6,9 @@ export declare class PublishPreflight {
|
|
|
6
6
|
private readonly pnpm;
|
|
7
7
|
constructor(options: PublishOptions, logger: AukletLogger);
|
|
8
8
|
run(plan: PublishPlan): Promise<void>;
|
|
9
|
-
|
|
9
|
+
verifyBeforeBuild(plan: PublishPlan): Promise<void>;
|
|
10
|
+
private verifyAuthentication;
|
|
10
11
|
private verifyPnpmPublishDryRun;
|
|
12
|
+
private verifyPackageVersions;
|
|
13
|
+
private logExistingVersion;
|
|
11
14
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { isPlainObject, isString } from 'aidly';
|
|
2
2
|
import { PnpmPublishApi } from '#auklet/publish/api/pnpmPublishApi';
|
|
3
|
-
import { runPnpmWhoami } from '#auklet/publish/api/pnpmApi';
|
|
3
|
+
import { hasPublishedPackageVersion, NpmPackageVersionExistsError, runPnpmWhoami, } from '#auklet/publish/api/pnpmApi';
|
|
4
4
|
import { logAuthenticationError } from '#auklet/publish/runner/packagePublisher';
|
|
5
5
|
import { PublishTargetError } from '#auklet/publish/runner/publishTargetError';
|
|
6
6
|
export class PublishPreflight {
|
|
@@ -14,9 +14,13 @@ export class PublishPreflight {
|
|
|
14
14
|
async run(plan) {
|
|
15
15
|
await this.verifyPnpmPublishDryRun(plan);
|
|
16
16
|
}
|
|
17
|
-
async
|
|
17
|
+
async verifyBeforeBuild(plan) {
|
|
18
18
|
if (plan.dryRun)
|
|
19
19
|
return;
|
|
20
|
+
await this.verifyAuthentication(plan);
|
|
21
|
+
await this.verifyPackageVersions(plan);
|
|
22
|
+
}
|
|
23
|
+
async verifyAuthentication(plan) {
|
|
20
24
|
const checked = new Set();
|
|
21
25
|
for (const target of plan.targets) {
|
|
22
26
|
const registry = getPublishRegistry(target.packageJson.publishConfig);
|
|
@@ -45,6 +49,22 @@ export class PublishPreflight {
|
|
|
45
49
|
}
|
|
46
50
|
}
|
|
47
51
|
}
|
|
52
|
+
async verifyPackageVersions(plan) {
|
|
53
|
+
for (const target of plan.targets) {
|
|
54
|
+
const registry = getPublishRegistry(target.packageJson.publishConfig);
|
|
55
|
+
const exists = await hasPublishedPackageVersion(target.packageRoot, target.packageName, target.publishVersion, { registry });
|
|
56
|
+
if (!exists)
|
|
57
|
+
continue;
|
|
58
|
+
this.logExistingVersion(target, registry);
|
|
59
|
+
throw new PublishTargetError(target, 'preflight', new NpmPackageVersionExistsError(target.packageName, target.publishVersion, registry), []);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
logExistingVersion(target, registry) {
|
|
63
|
+
this.logger.error('package version already exists: ', this.logger.package(target.packageName), '@', this.logger.version(target.publishVersion));
|
|
64
|
+
if (registry) {
|
|
65
|
+
this.logger.error('registry: ', this.logger.url(registry));
|
|
66
|
+
}
|
|
67
|
+
}
|
|
48
68
|
}
|
|
49
69
|
const getPublishRegistry = (publishConfig) => {
|
|
50
70
|
if (!isPlainObject(publishConfig))
|
|
@@ -1,10 +1,30 @@
|
|
|
1
|
-
import type { AukletLogger } from '#auklet/logger';
|
|
2
1
|
import type { PublishPlan } from '#auklet/publish/types';
|
|
3
2
|
type PublishSummaryStatus = 'success' | 'failure';
|
|
4
|
-
type PublishSummaryOptions = {
|
|
3
|
+
export type PublishSummaryOptions = {
|
|
5
4
|
plan: PublishPlan;
|
|
6
5
|
status: PublishSummaryStatus;
|
|
7
|
-
error
|
|
6
|
+
error: unknown | null;
|
|
8
7
|
};
|
|
9
|
-
export declare function reportPublishSummary(
|
|
8
|
+
export declare function reportPublishSummary(options: PublishSummaryOptions): void;
|
|
9
|
+
export declare class PublishSummaryReporter {
|
|
10
|
+
private readonly options;
|
|
11
|
+
private readonly logger;
|
|
12
|
+
private publishedCount;
|
|
13
|
+
private failedTarget;
|
|
14
|
+
private publishError;
|
|
15
|
+
private publishedTargets;
|
|
16
|
+
private publishedTargetRoots;
|
|
17
|
+
constructor(options: PublishSummaryOptions);
|
|
18
|
+
report(): void;
|
|
19
|
+
private getSummaryTitle;
|
|
20
|
+
private getSummaryBody;
|
|
21
|
+
private getFailureReasonLine;
|
|
22
|
+
private getFailureMessage;
|
|
23
|
+
private getSummaryDetails;
|
|
24
|
+
private getVersionChangeTasks;
|
|
25
|
+
private getVersionChangeTaskStatus;
|
|
26
|
+
private getTargetKey;
|
|
27
|
+
private formatVersionChange;
|
|
28
|
+
private formatSummaryValue;
|
|
29
|
+
}
|
|
10
30
|
export {};
|
|
@@ -1,85 +1,130 @@
|
|
|
1
|
+
import { createAukletLogger } from '#auklet/logger';
|
|
1
2
|
import { PublishTargetError } from '#auklet/publish/runner/publishTargetError';
|
|
2
|
-
export function reportPublishSummary(
|
|
3
|
-
|
|
4
|
-
const failedTarget = publishError?.target;
|
|
5
|
-
const publishedTargets = publishError?.publishedTargets ?? [];
|
|
6
|
-
const publishedTargetRoots = new Set(publishedTargets.map((target) => getTargetKey(target)));
|
|
7
|
-
const publishedCount = options.status === 'success' && !options.plan.dryRun
|
|
8
|
-
? options.plan.targets.length
|
|
9
|
-
: publishedTargets.length;
|
|
10
|
-
logger.newline();
|
|
11
|
-
logger.raw(logger.colors.gray('-'.repeat(56)));
|
|
12
|
-
logger.newline();
|
|
13
|
-
logger.result({
|
|
14
|
-
title: logger.colors.bold(getSummaryTitle(options.status, options.plan)),
|
|
15
|
-
status: options.status === 'success' ? 'success' : 'error',
|
|
16
|
-
body: getSummaryBody(options.status, options.plan, failedTarget),
|
|
17
|
-
details: {
|
|
18
|
-
mode: formatSummaryValue(logger, options.plan.dryRun ? 'dry-run' : 'publish'),
|
|
19
|
-
packages: formatSummaryValue(logger, options.plan.targets.length),
|
|
20
|
-
published: formatSummaryValue(logger, publishedCount),
|
|
21
|
-
version: logger.version(options.plan.version),
|
|
22
|
-
},
|
|
23
|
-
});
|
|
24
|
-
logger.newline();
|
|
25
|
-
logger.tasks({
|
|
26
|
-
tasks: getVersionChangeTasks(logger, {
|
|
27
|
-
targets: options.plan.targets,
|
|
28
|
-
status: options.status,
|
|
29
|
-
dryRun: options.plan.dryRun,
|
|
30
|
-
failedTarget,
|
|
31
|
-
publishedTargetRoots,
|
|
32
|
-
}),
|
|
33
|
-
});
|
|
34
|
-
logger.newline();
|
|
3
|
+
export function reportPublishSummary(options) {
|
|
4
|
+
new PublishSummaryReporter(options).report();
|
|
35
5
|
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
6
|
+
export class PublishSummaryReporter {
|
|
7
|
+
options;
|
|
8
|
+
logger;
|
|
9
|
+
publishedCount = 0;
|
|
10
|
+
failedTarget;
|
|
11
|
+
publishError;
|
|
12
|
+
publishedTargets = [];
|
|
13
|
+
publishedTargetRoots = new Set();
|
|
14
|
+
constructor(options) {
|
|
15
|
+
this.options = options;
|
|
16
|
+
this.logger = createAukletLogger();
|
|
17
|
+
this.publishError =
|
|
18
|
+
options.error instanceof PublishTargetError ? options.error : null;
|
|
19
|
+
this.publishedTargets = this.publishError
|
|
20
|
+
? this.publishError.publishedTargets
|
|
21
|
+
: [];
|
|
22
|
+
this.failedTarget = this.publishError ? this.publishError.target : null;
|
|
23
|
+
this.publishedTargetRoots = new Set(this.publishedTargets.map((target) => this.getTargetKey(target)));
|
|
24
|
+
this.publishedCount =
|
|
25
|
+
options.status === 'success' && !options.plan.dryRun
|
|
26
|
+
? options.plan.targets.length
|
|
27
|
+
: this.publishedTargets.length;
|
|
28
|
+
}
|
|
29
|
+
report() {
|
|
30
|
+
this.logger.newline();
|
|
31
|
+
this.logger.raw(this.logger.colors.gray('-'.repeat(56)));
|
|
32
|
+
this.logger.newline();
|
|
33
|
+
this.logger.result({
|
|
34
|
+
title: this.logger.colors.bold(this.getSummaryTitle()),
|
|
35
|
+
status: this.options.status === 'success' ? 'success' : 'error',
|
|
36
|
+
body: this.getSummaryBody(),
|
|
37
|
+
details: this.getSummaryDetails(),
|
|
38
|
+
});
|
|
39
|
+
this.logger.newline();
|
|
40
|
+
this.logger.tasks({
|
|
41
|
+
tasks: this.getVersionChangeTasks(),
|
|
42
|
+
});
|
|
43
|
+
this.logger.newline();
|
|
44
|
+
}
|
|
45
|
+
getSummaryTitle() {
|
|
46
|
+
if (this.options.status === 'failure')
|
|
47
|
+
return 'Publish failed';
|
|
48
|
+
return this.options.plan.dryRun
|
|
49
|
+
? 'Publish dry-run complete'
|
|
50
|
+
: 'Publish complete';
|
|
51
|
+
}
|
|
52
|
+
getSummaryBody() {
|
|
53
|
+
if (this.options.status === 'success') {
|
|
54
|
+
return [
|
|
55
|
+
this.options.plan.dryRun
|
|
56
|
+
? 'Preflight completed without publishing packages.'
|
|
57
|
+
: 'All selected packages were published.',
|
|
58
|
+
];
|
|
59
|
+
}
|
|
60
|
+
if (this.failedTarget) {
|
|
61
|
+
return [
|
|
62
|
+
['Failed at ', this.logger.package(this.failedTarget.packageName), '.'],
|
|
63
|
+
...this.getFailureReasonLine(),
|
|
64
|
+
];
|
|
65
|
+
}
|
|
43
66
|
return [
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
: 'All selected packages were published.',
|
|
67
|
+
'Publish stopped before package publishing completed.',
|
|
68
|
+
...this.getFailureReasonLine(),
|
|
47
69
|
];
|
|
48
70
|
}
|
|
49
|
-
|
|
50
|
-
|
|
71
|
+
getFailureReasonLine() {
|
|
72
|
+
const message = this.getFailureMessage();
|
|
73
|
+
if (!message)
|
|
74
|
+
return [];
|
|
75
|
+
return [
|
|
76
|
+
[this.logger.colors.bold(this.logger.colors.red(`Reason: ${message}`))],
|
|
77
|
+
];
|
|
78
|
+
}
|
|
79
|
+
getFailureMessage() {
|
|
80
|
+
const error = this.publishError
|
|
81
|
+
? this.publishError.originalError
|
|
82
|
+
: this.options.error;
|
|
83
|
+
return error instanceof Error ? error.message : null;
|
|
84
|
+
}
|
|
85
|
+
getSummaryDetails() {
|
|
86
|
+
return {
|
|
87
|
+
mode: this.formatSummaryValue(this.options.plan.dryRun ? 'dry-run' : 'publish'),
|
|
88
|
+
packages: this.formatSummaryValue(this.options.plan.targets.length),
|
|
89
|
+
published: this.formatSummaryValue(this.publishedCount),
|
|
90
|
+
version: this.logger.version(this.options.plan.version),
|
|
91
|
+
};
|
|
51
92
|
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
93
|
+
getVersionChangeTasks() {
|
|
94
|
+
return this.options.plan.targets.map((target) => ({
|
|
95
|
+
title: [
|
|
96
|
+
this.logger.package(target.packageName),
|
|
97
|
+
' ',
|
|
98
|
+
...this.formatVersionChange(target.version, target.publishVersion),
|
|
99
|
+
],
|
|
100
|
+
status: this.getVersionChangeTaskStatus(target),
|
|
101
|
+
}));
|
|
102
|
+
}
|
|
103
|
+
getVersionChangeTaskStatus(target) {
|
|
104
|
+
if (this.options.plan.dryRun)
|
|
105
|
+
return 'skipped';
|
|
106
|
+
if (this.options.status === 'success')
|
|
107
|
+
return 'success';
|
|
108
|
+
if (this.failedTarget &&
|
|
109
|
+
this.getTargetKey(target) === this.getTargetKey(this.failedTarget)) {
|
|
110
|
+
return 'error';
|
|
111
|
+
}
|
|
112
|
+
if (this.publishedTargetRoots.has(this.getTargetKey(target))) {
|
|
113
|
+
return 'success';
|
|
114
|
+
}
|
|
66
115
|
return 'skipped';
|
|
67
|
-
if (options.status === 'success')
|
|
68
|
-
return 'success';
|
|
69
|
-
if (options.failedTarget &&
|
|
70
|
-
getTargetKey(target) === getTargetKey(options.failedTarget)) {
|
|
71
|
-
return 'error';
|
|
72
116
|
}
|
|
73
|
-
|
|
74
|
-
return
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
}
|
|
117
|
+
getTargetKey(target) {
|
|
118
|
+
return `${target.packageRoot}:${target.packageName}`;
|
|
119
|
+
}
|
|
120
|
+
formatVersionChange(from, to) {
|
|
121
|
+
return [
|
|
122
|
+
this.logger.version(from),
|
|
123
|
+
this.logger.colors.gray(' -> '),
|
|
124
|
+
this.logger.version(to),
|
|
125
|
+
];
|
|
126
|
+
}
|
|
127
|
+
formatSummaryValue(value) {
|
|
128
|
+
return this.logger.colors.bold(this.logger.colors.cyan(String(value)));
|
|
129
|
+
}
|
|
130
|
+
}
|
|
@@ -5,7 +5,7 @@ export class PublishTargetError extends Error {
|
|
|
5
5
|
originalError;
|
|
6
6
|
publishedTargets;
|
|
7
7
|
constructor(target, phase, originalError, publishedTargets) {
|
|
8
|
-
super(
|
|
8
|
+
super(`${phase} failed for ${target.packageName} at ${path.relative(process.cwd(), target.packageRoot) || target.packageRoot}.`);
|
|
9
9
|
this.target = target;
|
|
10
10
|
this.phase = phase;
|
|
11
11
|
this.originalError = originalError;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { readPackageJson, writePackageJson, } from '#auklet/publish/api/packageJsonApi';
|
|
2
|
+
import { createAukletLogger } from '#auklet/logger';
|
|
2
3
|
export class VersionWriter {
|
|
3
4
|
options;
|
|
4
5
|
logger;
|
|
@@ -48,7 +49,19 @@ export class VersionWriter {
|
|
|
48
49
|
}
|
|
49
50
|
logWrittenVersionFailure(plan) {
|
|
50
51
|
if (!plan.dryRun && this.options.version) {
|
|
51
|
-
|
|
52
|
+
const logger = createAukletLogger();
|
|
53
|
+
const noteBody = logger.colors.rgb(184, 140, 40);
|
|
54
|
+
logger.newline();
|
|
55
|
+
logger.note({
|
|
56
|
+
title: logger.colors.yellow(logger.colors.bold('Version files may have changed')),
|
|
57
|
+
body: [
|
|
58
|
+
[noteBody(' package.json versions may have been written.')],
|
|
59
|
+
[
|
|
60
|
+
noteBody(' Auklet will not roll them back; check publish output before retrying.'),
|
|
61
|
+
],
|
|
62
|
+
],
|
|
63
|
+
});
|
|
64
|
+
logger.newline();
|
|
52
65
|
}
|
|
53
66
|
}
|
|
54
67
|
writeVersions(plan) {
|