Skip to content

Commit 0e603aa

Browse files
committed
Move skipBadPublishes() -> calculate-versions
1 parent ed74841 commit 0e603aa

File tree

10 files changed

+1679
-1799
lines changed

10 files changed

+1679
-1799
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,6 @@
2828
"ts-jest": "^25.2.1",
2929
"tslint": "^6.1.2",
3030
"tslint-microsoft-contrib": "^6.2.0",
31-
"typescript": "^4.7.4"
31+
"typescript": "^4.6.4"
3232
}
3333
}

packages/publisher/src/calculate-versions.ts

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
} from "@definitelytyped/utils";
1313
import { fetchTypesPackageVersionInfo } from "@definitelytyped/retag";
1414
import * as pacote from "pacote";
15+
import * as semver from "semver";
1516

1617
if (!module.parent) {
1718
const log = loggerWithErrors()[0];
@@ -37,9 +38,9 @@ async function computeAndSaveChangedPackages(
3738
const cp = await computeChangedPackages(allPackages, log);
3839
const json: ChangedPackagesJson = {
3940
changedTypings: cp.changedTypings.map(
40-
({ pkg: { id }, version, latestVersion }): ChangedTypingJson => ({ id, version, latestVersion })
41+
({ pkg: { name }, version, latestVersion }): ChangedTypingJson => ({ name, version, latestVersion })
4142
),
42-
changedNotNeededPackages: cp.changedNotNeededPackages.map((p) => p.name),
43+
changedNotNeededPackages: cp.changedNotNeededPackages.map(({ pkg: { name }, version }) => ({ name, version })),
4344
};
4445
await writeDataFile(versionsFilename, json);
4546
return cp;
@@ -72,7 +73,8 @@ async function computeChangedPackages(allPackages: AllPackages, log: LoggerWithE
7273
});
7374
log.info("# Computing deprecated packages...");
7475
const changedNotNeededPackages = await mapDefinedAsync(allPackages.allNotNeeded(), async (pkg) => {
75-
if (!(await isAlreadyDeprecated(pkg, log))) {
76+
const incipientVersion = await fetchIncipientStubVersion(pkg, log);
77+
if (incipientVersion) {
7678
// Assert that dependencies (i.e. the replacement package) exist on npm.
7779
// Also checked in checkNotNeededPackage().
7880
await pacote.manifest(pkg.libraryName, { cache: cacheDir }).catch((reason) => {
@@ -81,20 +83,30 @@ async function computeChangedPackages(allPackages: AllPackages, log: LoggerWithE
8183
: reason;
8284
});
8385
log.info(`To be deprecated: ${pkg.name}`);
84-
return pkg;
86+
return { pkg, version: incipientVersion };
8587
}
8688
return undefined;
8789
});
8890
return { changedTypings, changedNotNeededPackages };
8991
}
9092

91-
async function isAlreadyDeprecated(pkg: NotNeededPackage, log: LoggerWithErrors): Promise<unknown> {
92-
const offline = await pacote.manifest(pkg.fullNpmName, { cache: cacheDir, offline: true }).catch((reason) => {
93-
if (reason.code !== "ENOTCACHED") throw reason;
94-
return undefined;
95-
});
96-
if (offline?.deprecated) return offline.deprecated;
97-
log.info(`Version info not cached for deprecated package ${pkg.desc}`);
98-
const online = await pacote.manifest(pkg.fullNpmName, { cache: cacheDir, preferOnline: true });
99-
return online.deprecated;
93+
/**
94+
* Return the version of the stub @types package we're about to publish and deprecate, if we haven't already deprecated that package.
95+
* It's the max of the not-needed version and the max published version + 1, in case we already published a not-needed-versioned stub but failed to deprecate it, for whatever reason.
96+
*/
97+
export async function fetchIncipientStubVersion(pkg: NotNeededPackage, log: LoggerWithErrors) {
98+
const packument = await revalidate();
99+
if (packument.versions[packument["dist-tags"].latest].deprecated) return;
100+
const maxVersion = semver.maxSatisfying(Object.keys(packument.versions), "*")!;
101+
return String(semver.maxSatisfying([pkg.version, semver.inc(maxVersion, "patch")!], "*"));
102+
103+
async function revalidate() {
104+
const offline = await pacote.packument(pkg.fullNpmName, { cache: cacheDir, offline: true }).catch((reason) => {
105+
if (reason.code !== "ENOTCACHED") throw reason;
106+
return undefined;
107+
});
108+
if (offline?.versions[offline["dist-tags"].latest].deprecated) return offline;
109+
log.info(`Version info not cached for deprecated package ${pkg.desc}`);
110+
return pacote.packument(pkg.fullNpmName, { cache: cacheDir, preferOnline: true });
111+
}
100112
}

packages/publisher/src/generate-packages.ts

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ import {
1515
logger,
1616
writeLog,
1717
writeFile,
18-
Logger,
1918
cacheDir,
2019
writeTgz,
2120
} from "@definitelytyped/utils";
@@ -34,7 +33,6 @@ import {
3433
import * as pacote from "pacote";
3534
import { readChangedPackages, ChangedPackages } from "./lib/versions";
3635
import { outputDirectory } from "./util/util";
37-
import { skipBadPublishes } from "./lib/npm";
3836

3937
const mitLicense = readFileSync(joinPaths(__dirname, "..", "LICENSE"), "utf-8");
4038

@@ -68,9 +66,9 @@ export default async function generatePackages(
6866
log(` * ${pkg.desc}`);
6967
}
7068
log("## Generating deprecated packages");
71-
for (const pkg of changedPackages.changedNotNeededPackages) {
69+
for (const { pkg, version } of changedPackages.changedNotNeededPackages) {
7270
log(` * ${pkg.libraryName}`);
73-
await generateNotNeededPackage(pkg, log);
71+
await generateNotNeededPackage(pkg, version);
7472
}
7573
await writeLog("package-generator.md", logResult());
7674
}
@@ -92,12 +90,11 @@ async function generateTypingPackage(
9290
);
9391
}
9492

95-
async function generateNotNeededPackage(pkg: NotNeededPackage, log: Logger): Promise<void> {
96-
pkg = await skipBadPublishes(pkg, log);
93+
async function generateNotNeededPackage(pkg: NotNeededPackage, version: string): Promise<void> {
9794
const info = await pacote.manifest(pkg.libraryName, { cache: cacheDir, fullMetadata: true });
9895
const readme = `This is a stub types definition for ${getFullNpmName(pkg.name)} (${info.homepage}).\n
9996
${pkg.libraryName} provides its own type definitions, so you don't need ${getFullNpmName(pkg.name)} installed!`;
100-
await writeCommonOutputs(pkg, createNotNeededPackageJSON(pkg), readme);
97+
await writeCommonOutputs(pkg, createNotNeededPackageJSON(pkg, version), readme);
10198
}
10299

103100
async function writeCommonOutputs(pkg: AnyPackage, packageJson: string, readme: string): Promise<void> {
@@ -197,10 +194,10 @@ function dependencySemver(dependency: DependencyVersion): string {
197194
return dependency === "*" ? dependency : "^" + formatTypingVersion(dependency);
198195
}
199196

200-
export function createNotNeededPackageJSON(pkg: NotNeededPackage): string {
197+
export function createNotNeededPackageJSON(pkg: NotNeededPackage, version: string): string {
201198
const out = {
202199
name: pkg.fullNpmName,
203-
version: String(pkg.version),
200+
version,
204201
description: `Stub TypeScript definitions entry for ${pkg.libraryName}, which provides its own types definitions`,
205202
main: "",
206203
scripts: {},

packages/publisher/src/lib/npm.ts

Lines changed: 0 additions & 21 deletions
This file was deleted.

packages/publisher/src/lib/package-publisher.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
import assert = require("assert");
22
import { Logger, joinPaths, readFileAndWarn, NpmPublishClient } from "@definitelytyped/utils";
3-
import { NotNeededPackage, AnyPackage } from "@definitelytyped/definitions-parser";
3+
import { NotNeededPackage, AnyPackage, TypingsData } from "@definitelytyped/definitions-parser";
44
import { updateTypeScriptVersionTags, updateLatestTag } from "@definitelytyped/retag";
55
import { ChangedTyping } from "./versions";
66
import { outputDirectory } from "../util/util";
77

88
export async function publishTypingsPackage(
99
client: NpmPublishClient,
10-
changedTyping: ChangedTyping,
10+
changedTyping: ChangedTyping<TypingsData>,
1111
dry: boolean,
1212
log: Logger
1313
): Promise<void> {
Lines changed: 15 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,46 @@
11
import { assertDefined } from "@definitelytyped/utils";
2-
import {
3-
AllPackages,
4-
NotNeededPackage,
5-
PackageId,
6-
TypingsData,
7-
readDataFile,
8-
} from "@definitelytyped/definitions-parser";
2+
import { AllPackages, NotNeededPackage, TypingsData, readDataFile } from "@definitelytyped/definitions-parser";
3+
import * as semver from "semver";
94

105
export const versionsFilename = "versions.json";
116

12-
export interface ChangedTyping {
13-
readonly pkg: TypingsData;
7+
export interface ChangedTyping<T> {
8+
readonly pkg: T;
149
/** This is the version to be published, meaning it's the version that doesn't exist yet. */
1510
readonly version: string;
1611
/** For a non-latest version, this is the latest version; publishing an old version updates the 'latest' tag and we want to change it back. */
17-
readonly latestVersion: string | undefined;
12+
readonly latestVersion?: string;
1813
}
1914

2015
export interface ChangedPackagesJson {
2116
readonly changedTypings: readonly ChangedTypingJson[];
22-
readonly changedNotNeededPackages: readonly string[];
17+
readonly changedNotNeededPackages: readonly ChangedTypingJson[];
2318
}
2419

2520
export interface ChangedTypingJson {
26-
readonly id: PackageId;
21+
readonly name: string;
2722
readonly version: string;
2823
readonly latestVersion?: string;
2924
}
3025

3126
export interface ChangedPackages {
32-
readonly changedTypings: readonly ChangedTyping[];
33-
readonly changedNotNeededPackages: readonly NotNeededPackage[];
27+
readonly changedTypings: readonly ChangedTyping<TypingsData>[];
28+
readonly changedNotNeededPackages: readonly ChangedTyping<NotNeededPackage>[];
3429
}
3530

3631
export async function readChangedPackages(allPackages: AllPackages): Promise<ChangedPackages> {
3732
const json = (await readDataFile("calculate-versions", versionsFilename)) as ChangedPackagesJson;
3833
return {
3934
changedTypings: json.changedTypings.map(
40-
({ id, version, latestVersion }): ChangedTyping => ({
41-
pkg: allPackages.getTypingsData(id),
35+
({ name, version, latestVersion }): ChangedTyping<TypingsData> => ({
36+
pkg: allPackages.getTypingsData({ name, version: new semver.SemVer(version) }),
4237
version,
4338
latestVersion,
4439
})
4540
),
46-
changedNotNeededPackages: json.changedNotNeededPackages.map((id) =>
47-
assertDefined(allPackages.getNotNeededPackage(id))
48-
),
41+
changedNotNeededPackages: json.changedNotNeededPackages.map(({ name, version }) => ({
42+
pkg: assertDefined(allPackages.getNotNeededPackage(name)),
43+
version,
44+
})),
4945
};
5046
}

packages/publisher/src/publish-packages.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ import {
1313
NpmPublishClient,
1414
} from "@definitelytyped/utils";
1515
import { readChangedPackages, ChangedPackages } from "./lib/versions";
16-
import { skipBadPublishes } from "./lib/npm";
1716
import { getSecret, Secret } from "./lib/secrets";
1817

1918
if (!module.parent) {
@@ -126,9 +125,8 @@ export default async function publishPackages(
126125
}
127126
}
128127

129-
for (const n of changedPackages.changedNotNeededPackages) {
130-
const target = await skipBadPublishes(n, log);
131-
await publishNotNeededPackage(client, target, dry, log);
128+
for (const { pkg } of changedPackages.changedNotNeededPackages) {
129+
await publishNotNeededPackage(client, pkg, dry, log);
132130
}
133131

134132
await writeLog("publishing.md", logResult());
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import * as util from "util";
2+
import { NotNeededPackage } from "@definitelytyped/definitions-parser";
3+
import { fetchIncipientStubVersion } from "../src/calculate-versions";
4+
5+
jest.mock("pacote", () => ({
6+
async packument(spec: string) {
7+
switch (spec) {
8+
case "@types/not-needed": // A not-needed @types package about to be deprecated.
9+
return {
10+
"dist-tags": { latest: "1.0.1" },
11+
versions: { "1.0.0": {}, "1.0.1": {} },
12+
};
13+
case "@types/bad-publish": // A not-needed package after a bad publish.
14+
return {
15+
"dist-tags": { latest: "1.2.3" },
16+
versions: { "1.0.0": {}, "1.0.1": {}, "1.2.3": {} },
17+
};
18+
case "@types/already-deprecated": // A successfully deprecated not-needed stub package.
19+
return {
20+
"dist-tags": { latest: "1.2.3" },
21+
versions: { "1.0.0": {}, "1.0.1": {}, "1.2.3": { deprecated: "Contains built-in declarations." } },
22+
};
23+
}
24+
throw new Error(`Unexpected npm registry fetch: ${util.inspect(spec)}`);
25+
},
26+
}));
27+
28+
const notNeeded = new NotNeededPackage("not-needed", "not-needed", "1.2.3");
29+
const badPublish = new NotNeededPackage("bad-publish", "bad-publish", "1.2.3");
30+
const alreadyDeprecated = new NotNeededPackage("already-deprecated", "already-deprecated", "1.2.3");
31+
32+
const log = { info() {}, error() {} };
33+
34+
test("Returns the not-needed version", () => {
35+
return expect(fetchIncipientStubVersion(notNeeded, log)).resolves.toBe("1.2.3");
36+
});
37+
38+
test("Skips bad publishes", () => {
39+
return expect(fetchIncipientStubVersion(badPublish, log)).resolves.toBe("1.2.4");
40+
});
41+
42+
test("Ignores already-deprecated packages", () => {
43+
return expect(fetchIncipientStubVersion(alreadyDeprecated, log)).resolves.toBeUndefined();
44+
});

packages/publisher/test/generate-packages.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ testo({
137137
}`);
138138
},
139139
basicNotNeededPackageJson() {
140-
const s = createNotNeededPackageJSON(createUnneededPackage());
140+
const s = createNotNeededPackageJSON(createUnneededPackage(), "1.1.1");
141141
expect(s).toEqual(`{
142142
"name": "@types/absalom",
143143
"version": "1.1.1",
@@ -153,7 +153,7 @@ testo({
153153
},
154154
scopedNotNeededPackageJson() {
155155
const scopedUnneeded = new NotNeededPackage("google-cloud__pubsub", "@google-cloud/chubdub", "0.26.0");
156-
const s = createNotNeededPackageJSON(scopedUnneeded);
156+
const s = createNotNeededPackageJSON(scopedUnneeded, "0.26.0");
157157
expect(s).toEqual(`{
158158
"name": "@types/google-cloud__pubsub",
159159
"version": "0.26.0",

0 commit comments

Comments
 (0)