Skip to content

Commit aa4addf

Browse files
authored
feat(dry-run): always print publish results in dry run (#185)
1 parent 126874f commit aa4addf

File tree

9 files changed

+156
-49
lines changed

9 files changed

+156
-49
lines changed

.github/workflows/ci-cd.yaml

+15
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,21 @@ jobs:
187187
echo "::error::Expected release type to be 'patch', got '${{ steps.action-publish.outputs.type }}'"
188188
exit 1
189189
190+
- id: action-publish-dry
191+
name: Publish a dry run
192+
uses: ./
193+
with:
194+
registry: http://localhost:4873
195+
package: ${{ steps.setup.outputs.package }}/package.json
196+
token: ${{ steps.setup.outputs.token }}
197+
dry-run: true
198+
199+
- name: Check release output
200+
if: ${{ steps.action-publish-dry.outputs.type }}
201+
run: |
202+
echo "::error::Expected release type to be '', got '${{ steps.action-publish-dry.outputs.type }}'"
203+
exit 1
204+
190205
deploy:
191206
if: ${{ github.ref == 'refs/heads/main' }}
192207
name: Publish

dist/main.js

+19-11
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/main.js.map

+3-3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/__tests__/format-publish-result.test.ts

+22-5
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,20 @@ import { describe, it, expect } from "vitest";
33
import * as subject from "../format-publish-result.js";
44
import type { PackageManifest } from "../read-manifest.js";
55
import type { NormalizedOptions } from "../normalize-options.js";
6-
import type { PublishResult } from "../compare-and-publish/index.js";
6+
import type {
7+
PublishResult,
8+
PublishFile,
9+
} from "../compare-and-publish/index.js";
710

811
describe("formatPublishResult", () => {
912
it("should say if a publish was skipped", () => {
1013
const result = subject.formatPublishResult(
1114
{ name: "cool-package", version: "1.2.3" } as PackageManifest,
12-
{} as NormalizedOptions,
13-
{ id: undefined } as PublishResult
15+
{ dryRun: { value: false } } as NormalizedOptions,
16+
{ id: undefined, files: [] as PublishFile[] } as PublishResult
1417
);
1518

16-
expect(result).toMatch(/cool-package@1\.2\.3.+skipped/);
19+
expect(result).toMatch(/cool-package@1\.2\.3.+already published/);
1720
});
1821

1922
it("should say if a publish was a dry run", () => {
@@ -26,7 +29,21 @@ describe("formatPublishResult", () => {
2629
} as PublishResult
2730
);
2831

29-
expect(result).toMatch(/cool-package@1\.2\.3.+DRY RUN/);
32+
expect(result).toMatch(/DRY RUN/);
33+
});
34+
35+
it("should say if a dry run would have skipped", () => {
36+
const result = subject.formatPublishResult(
37+
{ name: "cool-package", version: "1.2.3" } as PackageManifest,
38+
{ dryRun: { value: true } } as NormalizedOptions,
39+
{
40+
id: undefined,
41+
files: [{ path: "cool-file-1", size: 1 }],
42+
type: undefined,
43+
} as PublishResult
44+
);
45+
46+
expect(result).toMatch(/cool-package@1\.2\.3.+already published/);
3047
});
3148

3249
it("should print files", () => {

src/compare-and-publish/__tests__/compare-and-publish.test.ts

+65-15
Original file line numberDiff line numberDiff line change
@@ -16,23 +16,31 @@ vi.mock("../compare-versions");
1616
vi.mock("../get-arguments");
1717

1818
describe("compareAndPublish", () => {
19-
const manifest = {
20-
packageSpec: ".",
21-
name: "fizzbuzz",
22-
version: "1.2.3",
23-
} as PackageManifest;
24-
2519
const logger = { debug: (message: string) => void message } as Logger;
26-
const normalizedOptions = {
27-
token: "abc123",
28-
ignoreScripts: { value: false },
29-
logger,
30-
} as NormalizedOptions;
3120
const environment: NpmCliEnvironment = { foo: "bar" };
3221
const npmViewResult = { versions: ["0.0.1"], "dist-tags": {} };
33-
const npmPublishResult = { id: "[email protected]", files: [] };
22+
const npmPublishResult = {
23+
24+
files: [{ path: "package.json", size: 42 }],
25+
};
26+
27+
let manifest: PackageManifest;
28+
let normalizedOptions: NormalizedOptions;
3429

3530
beforeEach(() => {
31+
manifest = {
32+
packageSpec: ".",
33+
name: "fizzbuzz",
34+
version: "1.2.3",
35+
} as PackageManifest;
36+
37+
normalizedOptions = {
38+
token: "abc123",
39+
ignoreScripts: { value: false },
40+
dryRun: { value: false },
41+
logger,
42+
} as NormalizedOptions;
43+
3644
when(getViewArguments)
3745
.calledWith("fizzbuzz", normalizedOptions)
3846
.thenReturn(["fizzbuzz"]);
@@ -83,7 +91,24 @@ describe("compareAndPublish", () => {
8391

8492
expect(result).toEqual({
8593
86-
files: [],
94+
files: [{ path: "package.json", size: 42 }],
95+
oldVersion: "0.0.1",
96+
type: "major",
97+
});
98+
});
99+
100+
it("should get versions, compare, and publish in dry run", async () => {
101+
normalizedOptions.dryRun.value = true;
102+
103+
const result = await subject.compareAndPublish(
104+
manifest,
105+
normalizedOptions,
106+
environment
107+
);
108+
109+
expect(result).toEqual({
110+
111+
files: [{ path: "package.json", size: 42 }],
87112
oldVersion: "0.0.1",
88113
type: "major",
89114
});
@@ -118,6 +143,31 @@ describe("compareAndPublish", () => {
118143
);
119144
});
120145

146+
it("should run publish if version exists but dry run", async () => {
147+
normalizedOptions.dryRun.value = true;
148+
149+
when(compareVersions)
150+
.calledWith(
151+
"1.2.3",
152+
{ versions: ["0.0.1"], "dist-tags": {} },
153+
normalizedOptions
154+
)
155+
.thenReturn({ type: undefined, oldVersion: "0.0.1" });
156+
157+
const result = await subject.compareAndPublish(
158+
manifest,
159+
normalizedOptions,
160+
environment
161+
);
162+
163+
expect(result).toEqual({
164+
id: undefined,
165+
files: [{ path: "package.json", size: 42 }],
166+
oldVersion: "0.0.1",
167+
type: undefined,
168+
});
169+
});
170+
121171
it("should handle an E404 from npm view", async () => {
122172
when(callNpmCli<"view">)
123173
.calledWith("view", ["fizzbuzz"], {
@@ -146,7 +196,7 @@ describe("compareAndPublish", () => {
146196

147197
expect(result).toEqual({
148198
149-
files: [],
199+
files: [{ path: "package.json", size: 42 }],
150200
oldVersion: "0.0.1",
151201
type: "major",
152202
});
@@ -260,7 +310,7 @@ describe("compareAndPublish", () => {
260310

261311
expect(result).toEqual({
262312
263-
files: [],
313+
files: [{ path: "package.json", size: 42 }],
264314
oldVersion: "0.0.1",
265315
type: "major",
266316
});

src/compare-and-publish/compare-and-publish.ts

+6-4
Original file line numberDiff line numberDiff line change
@@ -59,10 +59,12 @@ export async function compareAndPublish(
5959
throw viewCall.error;
6060
}
6161

62+
const isDryRun = options.dryRun.value;
6263
const comparison = compareVersions(version, viewCall.successData, options);
63-
const publishCall = comparison.type
64-
? await callNpmCli(PUBLISH, publishArguments, cliOptions)
65-
: { successData: undefined, errorCode: undefined, error: undefined };
64+
const publishCall =
65+
comparison.type ?? isDryRun
66+
? await callNpmCli(PUBLISH, publishArguments, cliOptions)
67+
: { successData: undefined, errorCode: undefined, error: undefined };
6668

6769
if (publishCall.error && publishCall.errorCode !== EPUBLISHCONFLICT) {
6870
throw publishCall.error;
@@ -71,7 +73,7 @@ export async function compareAndPublish(
7173
const { successData: publishData } = publishCall;
7274

7375
return {
74-
id: publishData?.id,
76+
id: isDryRun && !comparison.type ? undefined : publishData?.id,
7577
files: publishData?.files ?? [],
7678
type: publishData ? comparison.type : undefined,
7779
oldVersion: comparison.oldVersion,

src/compare-and-publish/compare-versions.ts

+1-4
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,14 @@ import semverValid from "semver/functions/valid.js";
44

55
import { STRATEGY_ALL } from "../options.js";
66
import type { NormalizedOptions } from "../normalize-options.js";
7-
import type { ReleaseType } from "../results.js";
7+
import { INITIAL, DIFFERENT, type ReleaseType } from "../results.js";
88
import type { NpmViewData } from "../npm/index.js";
99

1010
export interface VersionComparison {
1111
type: ReleaseType | undefined;
1212
oldVersion: string | undefined;
1313
}
1414

15-
const INITIAL = "initial";
16-
const DIFFERENT = "different";
17-
1815
/**
1916
* Compare previously published versions with the package's current version.
2017
*

src/format-publish-result.ts

+24-7
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@ import type { PublishResult } from "./compare-and-publish/index.js";
44
import type { PackageManifest } from "./read-manifest.js";
55
import type { NormalizedOptions } from "./normalize-options.js";
66

7+
const DRY_RUN_BANNER =
8+
"=== DRY RUN === DRY RUN === DRY RUN === DRY RUN === DRY RUN ===";
9+
10+
const CONTENTS_BANNER = "=== Contents ===";
11+
712
/**
813
* Format publish results into a string.
914
*
@@ -17,15 +22,27 @@ export function formatPublishResult(
1722
options: NormalizedOptions,
1823
result: PublishResult
1924
): string {
20-
if (result.id === undefined) {
21-
return `🙅‍♀️ ${manifest.name}@${manifest.version} publish skipped.`;
25+
const lines = [];
26+
27+
lines.push(
28+
result.id === undefined
29+
? `🙅‍♀️ ${manifest.name}@${manifest.version} already published.`
30+
: `📦 ${result.id}`
31+
);
32+
33+
if (result.files.length > 0) {
34+
lines.push("", CONTENTS_BANNER);
35+
}
36+
37+
for (const { path, size } of result.files) {
38+
lines.push(`${formatSize(size)}\t${path}`);
2239
}
2340

24-
return [
25-
`📦 ${result.id}${options.dryRun.value ? " (DRY RUN)" : ""}`,
26-
"=== Contents ===",
27-
...result.files.map(({ path, size }) => `${formatSize(size)}\t${path}`),
28-
].join(os.EOL);
41+
return (
42+
options.dryRun.value
43+
? [DRY_RUN_BANNER, "", ...lines, "", DRY_RUN_BANNER]
44+
: lines
45+
).join(os.EOL);
2946
}
3047

3148
const formatSize = (size: number): string => {

src/results.ts

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import type { ReleaseType as SemverReleaseType } from "semver";
33

44
/** Release type */
55
export type ReleaseType = SemverReleaseType | typeof INITIAL | typeof DIFFERENT;
6+
67
export const INITIAL = "initial";
78
export const DIFFERENT = "different";
89

0 commit comments

Comments
 (0)