Skip to content

Commit 8aee3d5

Browse files
Refactored and reorganized the code, in preparation for expsing it as a CLI and NPM package in addition to a GitHub Action
1 parent c4c8360 commit 8aee3d5

File tree

11 files changed

+303
-154
lines changed

11 files changed

+303
-154
lines changed

dist/index.js

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

dist/index.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/action/index.ts

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import { debug, getInput, setFailed, setOutput } from "@actions/core";
2+
import { npmPublish } from "../npm-publish";
3+
import { Options } from "../options";
4+
5+
/**
6+
* The main entry point of the GitHub Action
7+
* @internal
8+
*/
9+
async function main(): Promise<void> {
10+
try {
11+
// Setup global error handlers
12+
process.on("uncaughtException", errorHandler);
13+
process.on("unhandledRejection", errorHandler);
14+
15+
// Get the GitHub Actions input options
16+
const options: Options = {
17+
token: getInput("token", { required: true }),
18+
registry: getInput("registry", { required: true }),
19+
package: getInput("package", { required: true }),
20+
checkVersion: getInput("check-version", { required: true }).toLowerCase() === "true",
21+
debug,
22+
};
23+
24+
// Puglish to NPM
25+
let results = await npmPublish(options);
26+
27+
// tslint:disable: no-console
28+
if (results.type === "none") {
29+
console.log(`\n📦 ${results.package} v${results.version} is already published to NPM`);
30+
}
31+
else {
32+
console.log(`\n📦 Successfully published ${results.package} v${results.version} to NPM`);
33+
}
34+
35+
// Set the GitHub Actions output variables
36+
setOutput("type", results.type);
37+
setOutput("version", results.version);
38+
setOutput("old-version", results.oldVersion);
39+
}
40+
catch (error) {
41+
errorHandler(error as Error);
42+
}
43+
}
44+
45+
/**
46+
* Prints errors to the GitHub Actions console
47+
*/
48+
function errorHandler(error: Error): void {
49+
let message = error.stack || error.message || String(error);
50+
setFailed(message);
51+
process.exit();
52+
}
53+
54+
// tslint:disable-next-line: no-floating-promises
55+
main();

src/normalize-options.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { URL } from "url";
2+
import { Debug, Options } from "./options";
3+
4+
/**
5+
* Normalized and sanitized options
6+
* @internal
7+
*/
8+
export interface NormalizedOptions {
9+
token: string;
10+
registry: URL;
11+
package: string;
12+
checkVersion: boolean;
13+
quiet: boolean;
14+
debug: Debug;
15+
}
16+
17+
/**
18+
* Normalizes and sanitizes options, and fills-in any default values.
19+
* @internal
20+
*/
21+
export function normalizeOptions(options: Options): NormalizedOptions {
22+
let registryURL = typeof options.registry === "string" ? new URL(options.registry) : options.registry;
23+
24+
return {
25+
token: options.token || "",
26+
registry: registryURL || new URL("https://registry.npmjs.org/"),
27+
package: options.package || "./package.json",
28+
checkVersion: options.checkVersion === undefined ? true : Boolean(options.checkVersion),
29+
quiet: options.quiet || false,
30+
debug: options.debug || (() => undefined),
31+
};
32+
}

src/npm-config.ts

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,31 @@
1-
import { debug } from "@actions/core";
21
import * as ezSpawn from "ez-spawn";
32
import { promises as fs } from "fs";
43
import { ono } from "ono";
54
import { EOL } from "os";
65
import { dirname } from "path";
7-
import { URL } from "url";
8-
import { Options } from "./options";
6+
import { NormalizedOptions } from "./normalize-options";
97

108
/**
119
* Sets/updates the NPM config based on the options.
10+
* @internal
1211
*/
13-
export async function setNpmConfig(options: Options): Promise<void> {
12+
export async function setNpmConfig(options: NormalizedOptions): Promise<void> {
1413
// Read the current NPM config
15-
let configPath = await getNpmConfigPath();
16-
let config = await readNpmConfig(configPath);
14+
let configPath = await getNpmConfigPath(options);
15+
let config = await readNpmConfig(configPath, options);
1716

1817
// Update the config
1918
config = updateConfig(config, options);
2019

2120
// Save the new config
22-
await writeNpmConfig(configPath, config);
21+
await writeNpmConfig(configPath, config, options);
2322
}
2423

2524

2625
/**
2726
* Updates the given NPM config with the specified options.
2827
*/
29-
function updateConfig(config: string, options: Options): string {
30-
let registry = new URL(options.registry);
28+
function updateConfig(config: string, { registry, debug }: NormalizedOptions): string {
3129
let authDomain = registry.origin.slice(registry.protocol.length);
3230

3331
let lines = config.split(/\r?\n/);
@@ -51,7 +49,7 @@ function updateConfig(config: string, options: Options): string {
5149
/**
5250
* Gets the path of the NPM config file.
5351
*/
54-
async function getNpmConfigPath(): Promise<string> {
52+
async function getNpmConfigPath({ debug }: NormalizedOptions): Promise<string> {
5553
try {
5654
debug(`Running command: npm config get userconfig`);
5755

@@ -67,7 +65,7 @@ async function getNpmConfigPath(): Promise<string> {
6765
/**
6866
* Reads the NPM config file.
6967
*/
70-
async function readNpmConfig(configPath: string): Promise<string> {
68+
async function readNpmConfig(configPath: string, { debug }: NormalizedOptions): Promise<string> {
7169
try {
7270
debug(`Reading NPM config from ${configPath}`);
7371

@@ -90,7 +88,7 @@ async function readNpmConfig(configPath: string): Promise<string> {
9088
/**
9189
* Writes the NPM config file.
9290
*/
93-
async function writeNpmConfig(configPath: string, config: string): Promise<void> {
91+
async function writeNpmConfig(configPath: string, config: string, { debug }: NormalizedOptions): Promise<void> {
9492
try {
9593
debug(`Writing new NPM config to ${configPath}`);
9694

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,35 @@
1-
// tslint:disable: no-console
2-
import { debug } from "@actions/core";
31
import * as semver from "semver";
2+
import { normalizeOptions } from "./normalize-options";
43
import { npm } from "./npm";
54
import { Options } from "./options";
65
import { readManifest } from "./read-manifest";
76
import { Results } from "./results";
87

98
/**
10-
* Publishes an package to NPM, if its version has changed
9+
* Publishes a package to NPM, if its version has changed
1110
*/
12-
export async function publishToNPM(options: Options): Promise<Results> {
11+
export async function npmPublish(opts: Options = {}): Promise<Results> {
12+
let options = normalizeOptions(opts);
13+
1314
// Get the old and new version numbers
14-
let manifest = await readManifest(options.package);
15-
let publishedVersion = await npm.getLatestVersion(manifest.name);
15+
let manifest = await readManifest(options.package, options.debug);
16+
let publishedVersion = await npm.getLatestVersion(manifest.name, options);
1617

1718
// Determine if/how the version has changed
1819
let diff = semver.diff(manifest.version, publishedVersion);
1920

2021
if (diff || !options.checkVersion) {
2122
// Publish the new version to NPM
2223
await npm.publish(manifest, options);
23-
24-
console.log(`\n📦 Successfully published ${manifest.name} v${manifest.version} to NPM`);
25-
}
26-
else {
27-
console.log(`\n📦 ${manifest.name} v${publishedVersion} is already published to NPM`);
2824
}
2925

3026
let results: Results = {
27+
package: manifest.name,
3128
type: diff || "none",
3229
version: manifest.version.raw,
3330
oldVersion: publishedVersion.raw,
3431
};
3532

36-
debug(`OUTPUT: \n${JSON.stringify(results, undefined, 2)}`);
33+
options.debug(`OUTPUT: \n${JSON.stringify(results, undefined, 2)}`);
3734
return results;
3835
}

src/npm.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,20 @@
1-
import { debug } from "@actions/core";
21
import * as ezSpawn from "ez-spawn";
32
import { ono } from "ono";
43
import { dirname, resolve } from "path";
54
import { SemVer } from "semver";
5+
import { NormalizedOptions } from "./normalize-options";
66
import { setNpmConfig } from "./npm-config";
7-
import { Options } from "./options";
87
import { Manifest } from "./read-manifest";
98

109
/**
1110
* Runs NPM commands.
11+
* @internal
1212
*/
1313
export const npm = {
1414
/**
1515
* Gets the latest published version of the specified package.
1616
*/
17-
async getLatestVersion(name: string): Promise<SemVer> {
17+
async getLatestVersion(name: string, { debug }: NormalizedOptions): Promise<SemVer> {
1818
try {
1919
debug(`Running command: npm view ${name} version`);
2020

@@ -37,12 +37,12 @@ export const npm = {
3737
/**
3838
* Publishes the specified package to NPM
3939
*/
40-
async publish({ name, version }: Manifest, options: Options): Promise<void> {
40+
async publish({ name, version }: Manifest, options: NormalizedOptions): Promise<void> {
4141
// Update the NPM config with the specified registry and token
4242
await setNpmConfig(options);
4343

4444
try {
45-
debug(`Running command: npm publish`);
45+
options.debug(`Running command: npm publish`);
4646

4747
// Run NPM to publish the package
4848
await ezSpawn.async("npm", ["publish"], {

src/options.ts

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,52 @@
1+
import { URL } from "url";
2+
13
/**
24
* Options that determine how/whether the package is published.
35
*/
46
export interface Options {
57
/**
68
* The NPM access token to use when publishing
79
*/
8-
token: string;
10+
token?: string;
911

1012
/**
1113
* The NPM registry URL to use.
14+
*
15+
* Defaults to "https://registry.npmjs.org/"
1216
*/
13-
registry: string;
17+
registry?: string | URL;
1418

1519
/**
1620
* The absolute or relative path of your package.json file.
21+
*
22+
* Defaults to "./package.json"
1723
*/
18-
package: string;
24+
package?: string;
1925

2026
/**
2127
* If set to `true`, then the package will only be published if the version number
2228
* in package.json differs from the latest on NPM.
29+
*
30+
* Defaults to `true`
31+
*/
32+
checkVersion?: boolean;
33+
34+
/**
35+
* Suppress console output from NPM and npm-publish
36+
*
37+
* Defaults to `false`
38+
*/
39+
quiet?: boolean;
40+
41+
/**
42+
* A function to call to log debug messages.
43+
*
44+
* Defaults to a no-op function
2345
*/
24-
checkVersion: boolean;
46+
debug?: Debug;
2547
}
48+
49+
/**
50+
* A function that receives debug messages
51+
*/
52+
export type Debug = (message: string) => void;

src/read-manifest.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1-
import { debug } from "@actions/core";
21
import { promises as fs } from "fs";
32
import { ono } from "ono";
43
import { resolve } from "path";
54
import { SemVer } from "semver";
5+
import { Debug } from "./options";
66

77
/**
88
* A package manifest (package.json)
9+
* @internal
910
*/
1011
export interface Manifest {
1112
name: string;
@@ -14,9 +15,10 @@ export interface Manifest {
1415

1516
/**
1617
* Reads the package manifest (package.json) and returns its parsed contents
18+
* @internal
1719
*/
18-
export async function readManifest(path: string): Promise<Manifest> {
19-
debug(`Reading package manifest from ${resolve(path)}`);
20+
export async function readManifest(path: string, debug?: Debug): Promise<Manifest> {
21+
debug && debug(`Reading package manifest from ${resolve(path)}`);
2022
let json: string;
2123

2224
try {
@@ -38,7 +40,7 @@ export async function readManifest(path: string): Promise<Manifest> {
3840
version: new SemVer(version as string),
3941
};
4042

41-
debug(`MANIFEST: \n${JSON.stringify(manifest, undefined, 2)}`);
43+
debug && debug(`MANIFEST: \n${JSON.stringify(manifest, undefined, 2)}`);
4244
return manifest;
4345
}
4446
catch (error) {

src/results.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import { ReleaseType } from "semver";
22

3+
export { ReleaseType };
4+
35
/**
46
* Results of the `publish
57
*/
@@ -9,6 +11,11 @@ export interface Results {
911
*/
1012
type: ReleaseType | "none";
1113

14+
/**
15+
* The name of the NPM package that was published
16+
*/
17+
package: string;
18+
1219
/**
1320
* The version that was published
1421
*/

0 commit comments

Comments
 (0)