Skip to content

Commit c7187a7

Browse files
committed
Move skipBadPublishes() -> calculate-versions
1 parent b7dd694 commit c7187a7

File tree

9 files changed

+1495
-1576
lines changed

9 files changed

+1495
-1576
lines changed

packages/publisher/src/calculate-versions.ts

Lines changed: 36 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
} from "@definitelytyped/utils";
1212
import { fetchTypesPackageVersionInfo } from "@definitelytyped/retag";
1313
import * as pacote from "pacote";
14+
import * as semver from "semver";
1415

1516
if (!module.parent) {
1617
const log = loggerWithErrors()[0];
@@ -33,7 +34,7 @@ async function computeAndSaveChangedPackages(
3334
changedTypings: cp.changedTypings.map(
3435
({ pkg: { id }, version, latestVersion }): ChangedTypingJson => ({ id, version, latestVersion })
3536
),
36-
changedNotNeededPackages: cp.changedNotNeededPackages.map((p) => p.name),
37+
changedNotNeededPackages: cp.changedNotNeededPackages.map(({ pkg: { name }, version }) => ({ name, version })),
3738
};
3839
await writeDataFile(versionsFilename, json);
3940
return cp;
@@ -64,32 +65,50 @@ async function computeChangedPackages(allPackages: AllPackages, log: LoggerWithE
6465
});
6566
log.info("# Computing deprecated packages...");
6667
const changedNotNeededPackages = await mapDefinedAsync(allPackages.allNotNeeded(), async (pkg) => {
67-
if (!(await isAlreadyDeprecated(pkg, log))) {
68+
const incipientVersion = await fetchIncipientStubVersion(pkg, log);
69+
if (incipientVersion) {
6870
await pacote.manifest(pkg.libraryName, { cache: defaultCacheDir }).catch((cause) => {
6971
throw cause.code === "E404"
7072
? new Error(`To deprecate '@types/${pkg.name}', '${pkg.libraryName}' must exist on npm.`, { cause })
7173
: cause;
7274
});
7375
log.info(`To be deprecated: ${pkg.name}`);
74-
return pkg;
76+
return { pkg, version: incipientVersion };
7577
}
7678
return undefined;
7779
});
7880
return { changedTypings, changedNotNeededPackages };
7981
}
8082

81-
async function isAlreadyDeprecated(pkg: NotNeededPackage, log: LoggerWithErrors): Promise<unknown> {
82-
const offline = await pacote.manifest(pkg.fullNpmName, {
83-
cache: defaultCacheDir,
84-
fullMetadata: true,
85-
preferOffline: true,
86-
});
87-
if (!offline._cached || offline.deprecated) return offline.deprecated;
88-
log.info(`Version info not cached for deprecated package ${pkg.desc}`);
89-
const online = await pacote.manifest(pkg.fullNpmName, {
90-
cache: defaultCacheDir,
91-
fullMetadata: true,
92-
preferOnline: true,
93-
});
94-
return online.deprecated;
83+
/**
84+
* Return the version of the stub @types package we're about to publish and deprecate, if we haven't already deprecated that package.
85+
* 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.
86+
*/
87+
export async function fetchIncipientStubVersion(pkg: NotNeededPackage, log: LoggerWithErrors): Promise<string | false> {
88+
const packument = await revalidate();
89+
return (
90+
!packument.deprecated &&
91+
String(
92+
semver.maxSatisfying(
93+
[pkg.version, semver.inc(semver.maxSatisfying(Object.keys(packument.versions), "*")!, "patch")!],
94+
"*"
95+
)
96+
)
97+
);
98+
99+
async function revalidate() {
100+
const offline = await pacote.packument(pkg.fullNpmName, {
101+
cache: defaultCacheDir,
102+
fullMetadata: true,
103+
preferOffline: true,
104+
});
105+
if (!offline._cached || offline.deprecated) return offline;
106+
log.info(`Version info not cached for deprecated package ${pkg.desc}`);
107+
const online = await pacote.packument(pkg.fullNpmName, {
108+
cache: defaultCacheDir,
109+
fullMetadata: true,
110+
preferOnline: true,
111+
});
112+
return online;
113+
}
95114
}

packages/publisher/src/generate-packages.ts

Lines changed: 9 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
defaultCacheDir,
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: defaultCacheDir, 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,13 @@ function dependencySemver(dependency: DependencyVersion): string {
197194
return dependency === "*" ? dependency : "^" + formatTypingVersion(dependency);
198195
}
199196

200-
export function createNotNeededPackageJSON({ libraryName, license, fullNpmName, version }: NotNeededPackage): string {
197+
export function createNotNeededPackageJSON(
198+
{ libraryName, license, fullNpmName }: NotNeededPackage,
199+
version: string
200+
): string {
201201
const out = {
202202
name: fullNpmName,
203-
version: String(version),
203+
version,
204204
typings: null, // tslint:disable-line no-null-keyword
205205
description: `Stub TypeScript definitions entry for ${libraryName}, which provides its own types definitions`,
206206
main: "",

packages/publisher/src/lib/npm.ts

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

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

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,14 @@ export async function publishTypingsPackage(
2828
export async function publishNotNeededPackage(
2929
client: NpmPublishClient,
3030
pkg: NotNeededPackage,
31+
version: string,
3132
dry: boolean,
3233
log: Logger
3334
): Promise<void> {
3435
log(`Deprecating ${pkg.name}`);
3536
await common(client, pkg, log, dry);
3637
// Don't use a newline in the deprecation message because it will be displayed as "\n" and not as a newline.
37-
await deprecateNotNeededPackage(client, pkg, dry, log);
38+
await deprecateNotNeededPackage(client, pkg, version, dry, log);
3839
}
3940

4041
async function common(client: NpmPublishClient, pkg: AnyPackage, log: Logger, dry: boolean): Promise<void> {
@@ -46,14 +47,15 @@ async function common(client: NpmPublishClient, pkg: AnyPackage, log: Logger, dr
4647
export async function deprecateNotNeededPackage(
4748
client: NpmPublishClient,
4849
pkg: NotNeededPackage,
50+
version: string,
4951
dry = false,
5052
log: Logger
5153
): Promise<void> {
5254
const name = pkg.fullNpmName;
5355
if (dry) {
54-
log("(dry) Skip deprecate not needed package " + name + " at " + pkg.version);
56+
log("(dry) Skip deprecate not needed package " + name + " at " + version);
5557
} else {
56-
log(`Deprecating ${name} at ${pkg.version} with message: ${pkg.deprecatedMessage()}.`);
57-
await client.deprecate(name, String(pkg.version), pkg.deprecatedMessage());
58+
log(`Deprecating ${name} at ${version} with message: ${pkg.deprecatedMessage()}.`);
59+
await client.deprecate(name, version, pkg.deprecatedMessage());
5860
}
5961
}

packages/publisher/src/lib/versions.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ export interface ChangedTyping {
1919

2020
export interface ChangedPackagesJson {
2121
readonly changedTypings: readonly ChangedTypingJson[];
22-
readonly changedNotNeededPackages: readonly string[];
22+
readonly changedNotNeededPackages: readonly { readonly name: string; readonly version: string }[];
2323
}
2424

2525
export interface ChangedTypingJson {
@@ -30,7 +30,7 @@ export interface ChangedTypingJson {
3030

3131
export interface ChangedPackages {
3232
readonly changedTypings: readonly ChangedTyping[];
33-
readonly changedNotNeededPackages: readonly NotNeededPackage[];
33+
readonly changedNotNeededPackages: readonly { readonly pkg: NotNeededPackage; readonly version: string }[];
3434
}
3535

3636
export async function readChangedPackages(allPackages: AllPackages): Promise<ChangedPackages> {
@@ -43,8 +43,9 @@ export async function readChangedPackages(allPackages: AllPackages): Promise<Cha
4343
latestVersion,
4444
})
4545
),
46-
changedNotNeededPackages: json.changedNotNeededPackages.map((id) =>
47-
assertDefined(allPackages.getNotNeededPackage(id))
48-
),
46+
changedNotNeededPackages: json.changedNotNeededPackages.map(({ name, version }) => ({
47+
pkg: assertDefined(allPackages.getNotNeededPackage(name)),
48+
version,
49+
})),
4950
};
5051
}

packages/publisher/src/publish-packages.ts

Lines changed: 5 additions & 6 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) {
@@ -24,11 +23,12 @@ if (!module.parent) {
2423
if (deprecateName !== undefined) {
2524
// A '--deprecate' command is available in case types-publisher got stuck *while* trying to deprecate a package.
2625
// Normally this should not be needed.
27-
26+
const pkg = AllPackages.readSingleNotNeeded(deprecateName, dt);
2827
const log = logger()[0];
2928
await deprecateNotNeededPackage(
3029
await NpmPublishClient.create(await getSecret(Secret.NPM_TOKEN), undefined),
31-
AllPackages.readSingleNotNeeded(deprecateName, dt),
30+
pkg,
31+
String(pkg.version),
3232
/*dry*/ false,
3333
log
3434
);
@@ -140,9 +140,8 @@ export default async function publishPackages(
140140
}
141141
}
142142

143-
for (const n of changedPackages.changedNotNeededPackages) {
144-
const target = await skipBadPublishes(n, log);
145-
await publishNotNeededPackage(client, target, dry, log);
143+
for (const { pkg, version } of changedPackages.changedNotNeededPackages) {
144+
await publishNotNeededPackage(client, pkg, version, dry, log);
146145
}
147146

148147
await writeLog("publishing.md", logResult());
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
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 { versions: { "1.0.0": undefined, "1.0.1": undefined } };
10+
case "@types/bad-publish": // A not-needed package after a bad publish.
11+
return { versions: { "1.0.0": undefined, "1.0.1": undefined, "1.2.3": undefined } };
12+
case "@types/already-deprecated": // A successfully deprecated not-needed stub package.
13+
return { deprecated: "Contains built-in declarations." };
14+
}
15+
throw new Error(`Unexpected npm registry fetch: ${util.inspect(spec)}`);
16+
},
17+
}));
18+
19+
const notNeeded = new NotNeededPackage("not-needed", "not-needed", "1.2.3");
20+
const badPublish = new NotNeededPackage("bad-publish", "bad-publish", "1.2.3");
21+
const alreadyDeprecated = new NotNeededPackage("already-deprecated", "already-deprecated", "1.2.3");
22+
23+
const log = { info() {}, error() {} };
24+
25+
test("Returns the not-needed version", () => {
26+
return expect(fetchIncipientStubVersion(notNeeded, log)).resolves.toBe("1.2.3");
27+
});
28+
29+
test("Skips bad publishes", () => {
30+
return expect(fetchIncipientStubVersion(badPublish, log)).resolves.toBe("1.2.4");
31+
});
32+
33+
test("Ignores already-deprecated packages", () => {
34+
return expect(fetchIncipientStubVersion(alreadyDeprecated, log)).resolves.toBe(false);
35+
});

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",
@@ -154,7 +154,7 @@ testo({
154154
},
155155
scopedNotNeededPackageJson() {
156156
const scopedUnneeded = new NotNeededPackage("google-cloud__pubsub", "@google-cloud/chubdub", "0.26.0");
157-
const s = createNotNeededPackageJSON(scopedUnneeded);
157+
const s = createNotNeededPackageJSON(scopedUnneeded, "0.26.0");
158158
expect(s).toEqual(`{
159159
"name": "@types/google-cloud__pubsub",
160160
"version": "0.26.0",

0 commit comments

Comments
 (0)