Skip to content

Commit b8d9d33

Browse files
committed
feat(dry-run): print publish results in dry run, even if publish would be skipped
1 parent 126874f commit b8d9d33

File tree

9 files changed

+144
-54
lines changed

9 files changed

+144
-54
lines changed

.github/workflows/ci-cd.yaml

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

README.md

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -133,17 +133,17 @@ npm-publish exposes several output variables, which you can use in later steps o
133133
+ run: echo "Version changed!"
134134
```
135135

136-
| Name | Type | Description |
137-
| ------------- | ------- | ------------------------------------------------------------------------------------------------------------- |
138-
| `id` | string | Package identifier of the release: `${name}@${version}` or empty if no release. |
139-
| `type` | string | [Semver release type][], `initial` if first release, `different` if other change, or empty if no release. |
140-
| `name` | string | Name of the package. |
141-
| `version` | string | Version of the package. |
142-
| `old-version` | string | Previously published version on `tag` or empty if no previous version on tag. |
143-
| `tag` | string | [Distribution tag][npm-tag] the package was published to. |
144-
| `access` | string | [Access level][npm-access] the package was published with, or `default` if scoped-package defaults were used. |
145-
| `registry` | string | Registry the package was published to. |
146-
| `dry-run` | boolean | Whether `npm publish` was run in "dry run" mode. |
136+
| Name | Type | Description |
137+
| ------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
138+
| `id` | string | Package identifier of the release: `${name}@${version}` or empty if no release. |
139+
| `type` | string | [Semver release type][], `initial` if first release, `different` if other change, `dry-run` if no release would happen in a dry run, or empty if no release. |
140+
| `name` | string | Name of the package. |
141+
| `version` | string | Version of the package. |
142+
| `old-version` | string | Previously published version on `tag` or empty if no previous version on tag. |
143+
| `tag` | string | [Distribution tag][npm-tag] the package was published to. |
144+
| `access` | string | [Access level][npm-access] the package was published with, or `default` if scoped-package defaults were used. |
145+
| `registry` | string | Registry the package was published to. |
146+
| `dry-run` | boolean | Whether `npm publish` was run in "dry run" mode. |
147147

148148
[semver release type]: https://github.com/npm/node-semver#release_types
149149

@@ -199,17 +199,17 @@ The `npmPublish()` function returns a promise of a `Results` object. In TypeScri
199199
import type { Results } from "@jsdevtools/npm-publish";
200200
```
201201

202-
| Name | Type | Description |
203-
| ------------ | --------------- | --------------------------------------------------------------------------------------------------------------- |
204-
| `id` | Optional string | Package identifier of the release: `${name}@${version}` or `undefined` if no release. |
205-
| `type` | Optional string | [Semver release type][], `initial` if first release, `different` if other change, or `undefined` if no release. |
206-
| `name` | string | Name of the package. |
207-
| `version` | string | Version of the package. |
208-
| `oldVersion` | Optional string | Previously published version on `tag` or `undefined` if no previous version. |
209-
| `tag` | string | [Distribution tag][npm-tag] that the package was published to. |
210-
| `access` | Optional string | [Access level][npm-access] the package was published with, or `undefined` if scoped-package defaults were used. |
211-
| `registry` | `URL` | Registry the package was published to. |
212-
| `dryRun` | boolean | Whether `npm publish` was run in "dry run" mode. |
202+
| Name | Type | Description |
203+
| ------------ | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
204+
| `id` | Optional string | Package identifier of the release: `${name}@${version}` or `undefined` if no release. |
205+
| `type` | Optional string | [Semver release type][], `initial` if first release, `different` if other change, `dry-run` if no release would happen in a dry run, or `undefined` if no release. |
206+
| `name` | string | Name of the package. |
207+
| `version` | string | Version of the package. |
208+
| `oldVersion` | Optional string | Previously published version on `tag` or `undefined` if no previous version. |
209+
| `tag` | string | [Distribution tag][npm-tag] that the package was published to. |
210+
| `access` | Optional string | [Access level][npm-access] the package was published with, or `undefined` if scoped-package defaults were used. |
211+
| `registry` | `URL` | Registry the package was published to. |
212+
| `dryRun` | boolean | Whether `npm publish` was run in "dry run" mode. |
213213

214214
## Command Line Interface
215215

dist/main.js

Lines changed: 21 additions & 10 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/main.js.map

Lines changed: 3 additions & 3 deletions
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

Lines changed: 22 additions & 5 deletions
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+
41+
files: [{ path: "cool-file-1", size: 1 }],
42+
type: "dry-run",
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-versions.test.ts

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ describe("compareVersions", () => {
88
const result = subject.compareVersions(
99
"0.0.0",
1010
{ versions: [], "dist-tags": {} },
11-
{ tag: { value: "next" } } as NormalizedOptions
11+
{ tag: { value: "next" }, dryRun: { value: false } } as NormalizedOptions
1212
);
1313

1414
expect(result).toEqual({
@@ -20,6 +20,7 @@ describe("compareVersions", () => {
2020
it("should handle no known versions or dist tags", () => {
2121
const result = subject.compareVersions("0.0.0", undefined, {
2222
tag: { value: "next" },
23+
dryRun: { value: false },
2324
} as NormalizedOptions);
2425

2526
expect(result).toEqual({
@@ -35,6 +36,7 @@ describe("compareVersions", () => {
3536
{
3637
strategy: { value: "all" },
3738
tag: { value: "next" },
39+
dryRun: { value: false },
3840
} as NormalizedOptions
3941
);
4042

@@ -51,6 +53,7 @@ describe("compareVersions", () => {
5153
{
5254
strategy: { value: "upgrade" },
5355
tag: { value: "next" },
56+
dryRun: { value: false },
5457
} as NormalizedOptions
5558
);
5659

@@ -67,6 +70,7 @@ describe("compareVersions", () => {
6770
{
6871
strategy: { value: "all" },
6972
tag: { value: "next" },
73+
dryRun: { value: false },
7074
} as NormalizedOptions
7175
);
7276

@@ -83,6 +87,7 @@ describe("compareVersions", () => {
8387
{
8488
strategy: { value: "upgrade" },
8589
tag: { value: "next" },
90+
dryRun: { value: false },
8691
} as NormalizedOptions
8792
);
8893

@@ -99,6 +104,7 @@ describe("compareVersions", () => {
99104
{
100105
strategy: { value: "upgrade" },
101106
tag: { value: "next" },
107+
dryRun: { value: false },
102108
} as NormalizedOptions
103109
);
104110

@@ -107,4 +113,21 @@ describe("compareVersions", () => {
107113
oldVersion: "1.2.3",
108114
});
109115
});
116+
117+
it('should "release" anyway if dry run', () => {
118+
const result = subject.compareVersions(
119+
"4.5.6",
120+
{ versions: ["1.2.3", "4.5.6"], "dist-tags": { next: "4.5.6" } },
121+
{
122+
strategy: { value: "all" },
123+
tag: { value: "next" },
124+
dryRun: { value: true },
125+
} as NormalizedOptions
126+
);
127+
128+
expect(result).toEqual({
129+
type: "dry-run",
130+
oldVersion: "4.5.6",
131+
});
132+
});
110133
});

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

Lines changed: 6 additions & 5 deletions
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, DRY_RUN, 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
*
@@ -29,7 +26,7 @@ export function compareVersions(
2926
options: NormalizedOptions
3027
): VersionComparison {
3128
const { versions, "dist-tags": tags } = publishedVersions ?? {};
32-
const { strategy, tag: publishTag } = options;
29+
const { strategy, dryRun, tag: publishTag } = options;
3330
const oldVersion = semverValid(tags?.[publishTag.value]) ?? undefined;
3431
const isUnique = !versions?.includes(currentVersion);
3532
let type: ReleaseType | undefined;
@@ -44,5 +41,9 @@ export function compareVersions(
4441
}
4542
}
4643

44+
if (type === undefined && dryRun.value) {
45+
type = DRY_RUN;
46+
}
47+
4748
return { type, oldVersion };
4849
}

src/format-publish-result.ts

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,14 @@ import os from "node:os";
22

33
import type { PublishResult } from "./compare-and-publish/index.js";
44
import type { PackageManifest } from "./read-manifest.js";
5+
import { DRY_RUN } from "./results.js";
56
import type { NormalizedOptions } from "./normalize-options.js";
67

8+
const DRY_RUN_BANNER =
9+
"=== DRY RUN === DRY RUN === DRY RUN === DRY RUN === DRY RUN ===";
10+
11+
const CONTENTS_BANNER = "=== Contents ===";
12+
713
/**
814
* Format publish results into a string.
915
*
@@ -17,15 +23,25 @@ export function formatPublishResult(
1723
options: NormalizedOptions,
1824
result: PublishResult
1925
): string {
20-
if (result.id === undefined) {
21-
return `🙅‍♀️ ${manifest.name}@${manifest.version} publish skipped.`;
26+
const lines = [];
27+
28+
lines.push(
29+
result.id === undefined || result.type === DRY_RUN
30+
? `🙅‍♀️ ${manifest.name}@${manifest.version} already published.`
31+
: `📦 ${result.id}`
32+
);
33+
34+
if (result.id) {
35+
lines.push(CONTENTS_BANNER);
36+
}
37+
38+
for (const { path, size } of result.files) {
39+
lines.push(`${formatSize(size)}\t${path}`);
2240
}
2341

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);
42+
return (
43+
options.dryRun.value ? [DRY_RUN_BANNER, ...lines, DRY_RUN_BANNER] : lines
44+
).join(os.EOL);
2945
}
3046

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

src/results.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,15 @@ import type { Access, Strategy } from "./options.js";
22
import type { ReleaseType as SemverReleaseType } from "semver";
33

44
/** Release type */
5-
export type ReleaseType = SemverReleaseType | typeof INITIAL | typeof DIFFERENT;
5+
export type ReleaseType =
6+
| SemverReleaseType
7+
| typeof INITIAL
8+
| typeof DIFFERENT
9+
| typeof DRY_RUN;
10+
611
export const INITIAL = "initial";
712
export const DIFFERENT = "different";
13+
export const DRY_RUN = "dry-run";
814

915
/** Results of the publish */
1016
export interface Results {

0 commit comments

Comments
 (0)