Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: handle missing latest tag during version check #90

Merged
merged 8 commits into from
May 1, 2023
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Handle missing latest tag during version check
  • Loading branch information
mcous committed Apr 29, 2023
commit 836f3b8ea0ad07dfdb5151bc45755a0a6933ec67
52 changes: 33 additions & 19 deletions dist/main.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 4 additions & 4 deletions dist/main.js.map

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -31,7 +31,7 @@
"!__tests__"
],
"scripts": {
"all": "npm run clean && npm run build && npm run lint && npm run coverage",
"all": "npm run clean && npm run build && npm run format && npm run coverage",
"clean": "rimraf coverage lib dist e2e/fixture *.tgz",
"lint": "npm run _eslint && npm run _prettier -- --check",
"format": "npm run _eslint -- --fix && npm run _prettier -- --write",
11 changes: 11 additions & 0 deletions src/__tests__/compare-versions.test.ts
Original file line number Diff line number Diff line change
@@ -17,6 +17,17 @@ describe("compareVersions", () => {
});
});

it("should handle no known versions or dist tags", () => {
const result = subject.compareVersions("0.0.0", {}, {
tag: { value: "next" },
} as NormalizedOptions);

expect(result).toEqual({
type: "initial",
oldVersion: undefined,
});
});

it("should recognize an unpublished release", () => {
const result = subject.compareVersions(
"0.0.0",
14 changes: 6 additions & 8 deletions src/compare-versions.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import {
valid as semverValid,
gt as semverGreaterThan,
diff as semverDifference,
type ReleaseType as SemverReleaseType,
} from "semver";
import semverDifference from "semver/functions/diff.js";
import semverGreaterThan from "semver/functions/gt.js";
import semverValid from "semver/functions/valid.js";
import type { ReleaseType as SemverReleaseType } from "semver";

import { STRATEGY_ALL } from "./options.js";
import type { NormalizedOptions } from "./normalize-options.js";
@@ -34,8 +32,8 @@ export function compareVersions(
): VersionComparison {
const { versions: existingVersions, "dist-tags": tags } = publishedVersions;
const { strategy, tag: publishTag } = options;
const oldVersion = semverValid(tags[publishTag.value]) ?? undefined;
const isUnique = !existingVersions.includes(version);
const oldVersion = semverValid(tags?.[publishTag.value]) ?? undefined;
const isUnique = !existingVersions?.includes(version);
let type: ReleaseType | undefined;

if (isUnique) {
8 changes: 4 additions & 4 deletions src/normalize-options.ts
Original file line number Diff line number Diff line change
@@ -13,8 +13,8 @@ import {
type Logger,
} from "./options.js";

const DEFAULT_REGISTRY = "https://registry.npmjs.org/";
const DEFAULT_TAG = "latest";
const REGISTRY_NPM = "https://registry.npmjs.org/";
export const TAG_LATEST = "latest";

/** Normalized and sanitized auth, publish, and runtime configurations. */
export interface NormalizedOptions {
@@ -45,9 +45,9 @@ export function normalizeOptions(
options: Options,
manifest: PackageManifest
): NormalizedOptions {
const defaultTag = manifest.publishConfig?.tag ?? DEFAULT_TAG;
const defaultTag = manifest.publishConfig?.tag ?? TAG_LATEST;

const defaultRegistry = manifest.publishConfig?.registry ?? DEFAULT_REGISTRY;
const defaultRegistry = manifest.publishConfig?.registry ?? REGISTRY_NPM;

const defaultAccess =
manifest.publishConfig?.access ??
22 changes: 22 additions & 0 deletions src/npm/__tests__/call-npm-cli.test.ts
Original file line number Diff line number Diff line change
@@ -49,4 +49,26 @@ describe("callNpmCli", () => {

expect(result).toEqual(customPath);
});

it("should return empty object if stdout is empty", async () => {
const result = await subject.callNpmCli("config", ["get", "scope"], {
environment: {
npm_config_scope: "",
},
});

expect(result).toEqual({});
});

it("should retry a command with new arguments if empty", async () => {
const result = await subject.callNpmCli("config", ["get", "scope"], {
retryIfEmpty: ["get", "preid"],
environment: {
npm_config_scope: "",
npm_config_preid: "hello",
},
});

expect(result).toEqual("hello");
});
});
77 changes: 77 additions & 0 deletions src/npm/__tests__/get-arguments.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { describe, it, expect } from "vitest";

import * as subject from "../get-arguments.js";
import type { NormalizedOptions } from "../../normalize-options.js";

describe("get command arguments", () => {
describe("getViewArguments", () => {
it("should get dist-tags and versions", () => {
const result = subject.getViewArguments(
"cool-package",
{} as NormalizedOptions
);

expect(result).toEqual(["cool-package", "dist-tags", "versions"]);
});

it("should include a tag", () => {
const options = { tag: { value: "cool-tag" } } as NormalizedOptions;
const result = subject.getViewArguments("cool-package", options, true);

expect(result).toEqual([
"cool-package@cool-tag",
"dist-tags",
"versions",
]);
});

it("should return undefined if tag is latest", () => {
const options = { tag: { value: "latest" } } as NormalizedOptions;
const result = subject.getViewArguments("cool-package", options, true);

expect(result).toEqual(undefined);
});
});

describe("getPublishArguments", () => {
it("should add explicit arguments", () => {
const result = subject.getPublishArguments("./cool-package", {
tag: { value: "next", isDefault: false },
access: { value: "restricted", isDefault: false },
dryRun: { value: true, isDefault: false },
strategy: { value: "upgrade", isDefault: true },
} as NormalizedOptions);

expect(result).toEqual([
"./cool-package",
"--tag",
"next",
"--access",
"restricted",
"--dry-run",
]);
});

it("should omit default arguments", () => {
const result = subject.getPublishArguments("./cool-package", {
tag: { value: "next", isDefault: true },
access: { value: "restricted", isDefault: true },
dryRun: { value: false, isDefault: true },
strategy: { value: "upgrade", isDefault: true },
} as NormalizedOptions);

expect(result).toEqual(["./cool-package"]);
});

it("should omit undefined string arguments and false boolean arguments", () => {
const result = subject.getPublishArguments("./cool-package", {
tag: { value: "next", isDefault: false },
access: { value: undefined, isDefault: false },
dryRun: { value: false, isDefault: false },
strategy: { value: "upgrade", isDefault: true },
} as NormalizedOptions);

expect(result).toEqual(["./cool-package", "--tag", "next"]);
});
});
});
46 changes: 0 additions & 46 deletions src/npm/__tests__/get-publish-arguments.test.ts

This file was deleted.

27 changes: 22 additions & 5 deletions src/npm/__tests__/npm.test.ts
Original file line number Diff line number Diff line change
@@ -8,18 +8,20 @@ import type { NormalizedOptions } from "../../normalize-options.js";
import * as subject from "../index.js";
import { useNpmEnvironment, type NpmCliTask } from "../use-npm-environment.js";
import { callNpmCli } from "../call-npm-cli.js";
import { getPublishArguments } from "../get-publish-arguments.js";
import { getViewArguments, getPublishArguments } from "../get-arguments.js";

vi.mock("../use-npm-environment", () => imitateEsm("../use-npm-environment"));
vi.mock("../call-npm-cli", () => imitateEsm("../call-npm-cli"));
vi.mock("../get-publish-arguments", () =>
imitateEsm("../get-publish-arguments")
);
vi.mock("../get-arguments", () => imitateEsm("../get-arguments"));

describe("npm", () => {
const environment = { foo: "bar" };
const logger = { debug: (message: string) => void message } as Logger;
const options = { token: "abc123", logger } as NormalizedOptions;
const options = {
logger,
token: "abc123",
tag: { value: "cool-tag" },
} as NormalizedOptions;

beforeEach(() => {
td.when(useNpmEnvironment(options, td.matchers.isA(Function))).thenDo(
@@ -32,6 +34,16 @@ describe("npm", () => {
});

it("should get existing versions", async () => {
td.when(getViewArguments("@example/cool-package", options)).thenReturn([
"@example/cool-package",
"dist-tags",
"versions",
]);

td.when(
getViewArguments("@example/cool-package", options, true)
).thenReturn(["@example/cool-package@cool-tag", "dist-tags", "versions"]);

td.when(
callNpmCli<subject.PublishedVersions>(
"view",
@@ -40,6 +52,11 @@ describe("npm", () => {
logger,
environment,
ifError: { e404: { "dist-tags": {}, versions: [] } },
retryIfEmpty: [
"@example/cool-package@cool-tag",
"dist-tags",
"versions",
],
}
)
).thenResolve({
Loading
Oops, something went wrong.