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

Add fetchTypesPackageVersionInfo() tests #464

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
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
14 changes: 9 additions & 5 deletions packages/publisher/src/calculate-versions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,9 @@ async function computeAndSaveChangedPackages(
async function computeChangedPackages(allPackages: AllPackages, log: LoggerWithErrors): Promise<ChangedPackages> {
log.info("# Computing changed packages...");
const changedTypings = await mapDefinedAsync(allPackages.allTypings(), async (pkg) => {
const { version, needsPublish } = await fetchTypesPackageVersionInfo(pkg, /*publish*/ true, log);
if (needsPublish) {
log.info(`Need to publish: ${pkg.desc}@${version}`);
const { incipientVersion } = await fetchTypesPackageVersionInfo(pkg, log);
if (incipientVersion) {
log.info(`Need to publish: ${pkg.desc}@${incipientVersion}`);
for (const { name } of pkg.packageJsonDependencies) {
// Assert that dependencies exist on npm.
// Also checked when we install the dependencies, in dtslint-runner.
Expand All @@ -65,8 +65,12 @@ async function computeChangedPackages(allPackages: AllPackages, log: LoggerWithE
}
const latestVersion = pkg.isLatest
? undefined
: (await fetchTypesPackageVersionInfo(allPackages.getLatest(pkg), /*publish*/ true)).version;
return { pkg, version, latestVersion };
: await fetchTypesPackageVersionInfo(allPackages.getLatest(pkg), log);
return {
pkg,
version: incipientVersion,
latestVersion: latestVersion?.incipientVersion || latestVersion?.maxVersion,
};
}
return undefined;
});
Expand Down
39 changes: 24 additions & 15 deletions packages/retag/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,18 +60,22 @@ async function tag(dry: boolean, nProcesses: number, name?: string) {
const publishClient = await NpmPublishClient.create(token, {});
if (name) {
const pkg = await AllPackages.readSingle(name);
const version = await getLatestTypingVersion(pkg);
await updateTypeScriptVersionTags(pkg, version, publishClient, consoleLogger.info, dry);
await updateLatestTag(pkg.fullNpmName, version, publishClient, consoleLogger.info, dry);
const { maxVersion } = await fetchTypesPackageVersionInfo(pkg);
if (maxVersion) {
await updateTypeScriptVersionTags(pkg, maxVersion, publishClient, consoleLogger.info, dry);
await updateLatestTag(pkg.fullNpmName, maxVersion, publishClient, consoleLogger.info, dry);
}
} else {
await Promise.all(
(
await AllPackages.readLatestTypings()
).map(async (pkg) => {
// Only update tags for the latest version of the package.
const version = await getLatestTypingVersion(pkg);
await updateTypeScriptVersionTags(pkg, version, publishClient, consoleLogger.info, dry);
await updateLatestTag(pkg.fullNpmName, version, publishClient, consoleLogger.info, dry);
const { maxVersion } = await fetchTypesPackageVersionInfo(pkg);
if (maxVersion) {
await updateTypeScriptVersionTags(pkg, maxVersion, publishClient, consoleLogger.info, dry);
await updateLatestTag(pkg.fullNpmName, maxVersion, publishClient, consoleLogger.info, dry);
}
})
);
}
Expand Down Expand Up @@ -112,15 +116,16 @@ export async function updateLatestTag(
}
}

export async function getLatestTypingVersion(pkg: TypingsData): Promise<string> {
return (await fetchTypesPackageVersionInfo(pkg, /*publish*/ false)).version;
}

/**
* Used for two purposes: to determine whether a @types package has changed since it was last published, and to get a package's version in the npm registry.
* We ignore whether the cached metadata is fresh or stale: We always revalidate if the content hashes differ (fresh or not) and never revalidate if they match (stale or not).
* Because the decider is the content hash, this isn't applicable to other npm packages.
* Target JS packages and not-needed stubs don't have content hashes.
*/
export async function fetchTypesPackageVersionInfo(
pkg: TypingsData,
canPublish: boolean,
log?: LoggerWithErrors
): Promise<{ version: string; needsPublish: boolean }> {
): Promise<{ maxVersion?: string; incipientVersion?: string }> {
const spec = `${pkg.fullNpmName}@~${pkg.major}.${pkg.minor}`;
let info = await pacote.manifest(spec, { cache: cacheDir, fullMetadata: true, offline: true }).catch((reason) => {
if (reason.code !== "ENOTCACHED" && reason.code !== "ETARGET") throw reason;
Expand All @@ -135,7 +140,7 @@ export async function fetchTypesPackageVersionInfo(
return undefined;
});
if (!info) {
return { version: `${pkg.major}.${pkg.minor}.0`, needsPublish: true };
return { incipientVersion: `${pkg.major}.${pkg.minor}.0` };
}
}

Expand All @@ -146,6 +151,10 @@ export async function fetchTypesPackageVersionInfo(
`Package ${pkg.name} has been deprecated, so we shouldn't have parsed it. Was it re-added?`
);
}
const needsPublish = canPublish && pkg.contentHash !== info.typesPublisherContentHash;
return { version: needsPublish ? semver.inc(info.version, "patch")! : info.version, needsPublish };
return {
maxVersion: info.version,
...(((pkg.contentHash === info.typesPublisherContentHash) as {}) || {
incipientVersion: semver.inc(info.version, "patch")!,
}),
};
}
64 changes: 64 additions & 0 deletions packages/retag/test/index.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import * as util from "util";
import { TypingsData } from "@definitelytyped/definitions-parser";
import * as pacote from "pacote";
import { fetchTypesPackageVersionInfo } from "../src/";

jest.mock("pacote", () => ({
async manifest(spec: string, opts: pacote.Options) {
switch (spec) {
case "@types/already-published@~1.2": // An already-published @types package.
return { version: "1.2.3", typesPublisherContentHash: "already-published-content-hash" };
case "@types/first-publish@~1.2": // A new, not-yet-published @types package.
// eslint-disable-next-line no-throw-literal
throw { code: opts.offline ? "ENOTCACHED" : "E404" };
}
throw new Error(`Unexpected npm registry fetch: ${util.inspect(spec)}`);
},
}));

const unchanged = new TypingsData(
{
typingsPackageName: "already-published",
libraryMajorVersion: 1,
libraryMinorVersion: 2,
contentHash: "already-published-content-hash",
} as never,
/*isLatest*/ true
);
const changed = new TypingsData(
{
typingsPackageName: "already-published",
libraryMajorVersion: 1,
libraryMinorVersion: 2,
contentHash: "changed-content-hash",
} as never,
/*isLatest*/ true
);
const firstPublish = new TypingsData(
{
typingsPackageName: "first-publish",
libraryMajorVersion: 1,
libraryMinorVersion: 2,
contentHash: "first-publish-content-hash",
} as never,
/*isLatest*/ true
);

test("Increments already-published patch version", () => {
return expect(fetchTypesPackageVersionInfo(changed)).resolves.toEqual({
maxVersion: "1.2.3",
incipientVersion: "1.2.4",
});
});

test("Doesn't increment unchanged @types package version", () => {
return expect(fetchTypesPackageVersionInfo(unchanged)).resolves.toEqual({
maxVersion: "1.2.3",
});
});

test("First-publish version", () => {
return expect(fetchTypesPackageVersionInfo(firstPublish)).resolves.toEqual({
incipientVersion: "1.2.0",
});
});