Skip to content

Commit 1ad4b5f

Browse files
committed
Publish old versions without touching latest tag
1 parent 39e0d00 commit 1ad4b5f

File tree

18 files changed

+120
-199
lines changed

18 files changed

+120
-199
lines changed

packages/publisher/package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
"fs-extra": "^9.1.0",
2020
"fstream": "^1.0.12",
2121
"hh-mm-ss": "^1.2.0",
22+
"libnpmpublish": "^6.0.4",
2223
"longjohn": "^0.2.11",
2324
"pacote": "^13.6.1",
2425
"semver": "^7.3.7",
@@ -27,8 +28,10 @@
2728
"yargs": "15.3.1"
2829
},
2930
"devDependencies": {
31+
"@npm/types": "^1.0.2",
3032
"@types/fs-extra": "^9.0.8",
3133
"@types/hh-mm-ss": "^1.2.1",
34+
"@types/libnpmpublish": "^4.0.3",
3235
"@types/mz": "^0.0.31",
3336
"@types/pacote": "^11.1.5",
3437
"@types/source-map-support": "^0.4.0",

packages/publisher/src/calculate-versions.ts

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,7 @@ async function computeAndSaveChangedPackages(
3636
): Promise<ChangedPackages> {
3737
const cp = await computeChangedPackages(allPackages, log);
3838
const json: ChangedPackagesJson = {
39-
changedTypings: cp.changedTypings.map(
40-
({ pkg: { id }, version, latestVersion }): ChangedTypingJson => ({ id, version, latestVersion })
41-
),
39+
changedTypings: cp.changedTypings.map(({ pkg: { id }, version }): ChangedTypingJson => ({ id, version })),
4240
changedNotNeededPackages: cp.changedNotNeededPackages.map((p) => p.name),
4341
};
4442
await writeDataFile(versionsFilename, json);
@@ -63,10 +61,7 @@ async function computeChangedPackages(allPackages: AllPackages, log: LoggerWithE
6361
: reason;
6462
});
6563
}
66-
const latestVersion = pkg.isLatest
67-
? undefined
68-
: (await fetchTypesPackageVersionInfo(allPackages.getLatest(pkg), /*publish*/ true)).version;
69-
return { pkg, version, latestVersion };
64+
return { pkg, version };
7065
}
7166
return undefined;
7267
});

packages/publisher/src/generate-packages.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ import {
1717
writeFile,
1818
Logger,
1919
cacheDir,
20-
writeTgz,
2120
} from "@definitelytyped/utils";
2221
import {
2322
getDefinitelyTyped,
@@ -63,7 +62,7 @@ export default async function generatePackages(
6362
for (const { pkg, version } of changedPackages.changedTypings) {
6463
await generateTypingPackage(pkg, allPackages, version, dt);
6564
if (tgz) {
66-
await writeTgz(outputDirectory(pkg), `${outputDirectory(pkg)}.tgz`);
65+
await pacote.tarball.file(outputDirectory(pkg), `${outputDirectory(pkg)}.tgz`);
6766
}
6867
log(` * ${pkg.desc}`);
6968
}
Lines changed: 26 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,51 @@
1-
import assert = require("assert");
2-
import { Logger, joinPaths, readFileAndWarn, NpmPublishClient } from "@definitelytyped/utils";
1+
import { Logger, NpmPublishClient } from "@definitelytyped/utils";
32
import { NotNeededPackage, AnyPackage } from "@definitelytyped/definitions-parser";
4-
import { updateTypeScriptVersionTags, updateLatestTag } from "@definitelytyped/retag";
3+
import { updateTypeScriptVersionTags } from "@definitelytyped/retag";
4+
import * as libpub from "libnpmpublish";
5+
import * as pacote from "pacote";
56
import { ChangedTyping } from "./versions";
67
import { outputDirectory } from "../util/util";
78

9+
// https://github.com/npm/types/pull/18
10+
declare module "libnpmpublish" {
11+
function publish(
12+
manifest: Omit<import("@npm/types").PackageJson, "bundledDependencies">,
13+
tarData: Buffer,
14+
opts?: import("npm-registry-fetch").Options
15+
): Promise<Response>;
16+
}
17+
818
export async function publishTypingsPackage(
919
client: NpmPublishClient,
1020
changedTyping: ChangedTyping,
21+
token: string,
1122
dry: boolean,
1223
log: Logger
1324
): Promise<void> {
14-
const { pkg, version, latestVersion } = changedTyping;
15-
await common(client, pkg, log, dry);
25+
const { pkg, version } = changedTyping;
26+
await common(pkg, token, dry);
1627
if (pkg.isLatest) {
1728
await updateTypeScriptVersionTags(pkg, version, client, log, dry);
1829
}
19-
assert((latestVersion === undefined) === pkg.isLatest);
20-
if (latestVersion !== undefined) {
21-
// If this is an older version of the package, we still update tags for the *latest*.
22-
// NPM will update "latest" even if we are publishing an older version of a package (https://github.com/npm/npm/issues/6778),
23-
// so we must undo that by re-tagging latest.
24-
await updateLatestTag(pkg.fullNpmName, latestVersion, client, log, dry);
25-
}
2630
}
2731

2832
export async function publishNotNeededPackage(
29-
client: NpmPublishClient,
3033
pkg: NotNeededPackage,
34+
token: string,
3135
dry: boolean,
3236
log: Logger
3337
): Promise<void> {
3438
log(`Deprecating ${pkg.name}`);
35-
await common(client, pkg, log, dry);
39+
await common(pkg, token, dry);
3640
}
3741

38-
async function common(client: NpmPublishClient, pkg: AnyPackage, log: Logger, dry: boolean): Promise<void> {
42+
async function common(pkg: AnyPackage, token: string, dry: boolean): Promise<void> {
3943
const packageDir = outputDirectory(pkg);
40-
const packageJson = await readFileAndWarn("generate", joinPaths(packageDir, "package.json"));
41-
await client.publish(packageDir, packageJson, dry, log);
44+
const manifest = await pacote.manifest(packageDir).catch((reason) => {
45+
throw reason.code === "ENOENT" ? new Error("Run generate first!", { cause: reason }) : reason;
46+
});
47+
const tarData = await pacote.tarball(packageDir);
48+
// Make sure we never assign the latest tag to an old version.
49+
if (!dry)
50+
await libpub.publish(manifest, tarData, { defaultTag: pkg.isLatest ? "latest" : "", access: "public", token });
4251
}

packages/publisher/src/lib/versions.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,6 @@ export interface ChangedTyping {
1313
readonly pkg: TypingsData;
1414
/** This is the version to be published, meaning it's the version that doesn't exist yet. */
1515
readonly version: string;
16-
/** 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;
1816
}
1917

2018
export interface ChangedPackagesJson {
@@ -25,7 +23,6 @@ export interface ChangedPackagesJson {
2523
export interface ChangedTypingJson {
2624
readonly id: PackageId;
2725
readonly version: string;
28-
readonly latestVersion?: string;
2926
}
3027

3128
export interface ChangedPackages {
@@ -37,10 +34,9 @@ export async function readChangedPackages(allPackages: AllPackages): Promise<Cha
3734
const json = (await readDataFile("calculate-versions", versionsFilename)) as ChangedPackagesJson;
3835
return {
3936
changedTypings: json.changedTypings.map(
40-
({ id, version, latestVersion }): ChangedTyping => ({
37+
({ id, version }): ChangedTyping => ({
4138
pkg: allPackages.getTypingsData(id),
4239
version,
43-
latestVersion,
4440
})
4541
),
4642
changedNotNeededPackages: json.changedNotNeededPackages.map((id) =>

packages/publisher/src/publish-packages.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,12 +42,13 @@ export default async function publishPackages(
4242
log("=== Publishing packages ===");
4343
}
4444

45-
const client = await NpmPublishClient.create(await getSecret(Secret.NPM_TOKEN), undefined);
45+
const token = await getSecret(Secret.NPM_TOKEN);
46+
const client = await NpmPublishClient.create(token, undefined);
4647

4748
for (const cp of changedPackages.changedTypings) {
4849
log(`Publishing ${cp.pkg.desc}...`);
4950

50-
await publishTypingsPackage(client, cp, dry, log);
51+
await publishTypingsPackage(client, cp, token, dry, log);
5152

5253
const commits = (await queryGithub(
5354
`repos/DefinitelyTyped/DefinitelyTyped/commits?path=types%2f${cp.pkg.subDirectoryPath}`,
@@ -128,7 +129,7 @@ export default async function publishPackages(
128129

129130
for (const n of changedPackages.changedNotNeededPackages) {
130131
const target = await skipBadPublishes(n, log);
131-
await publishNotNeededPackage(client, target, dry, log);
132+
await publishNotNeededPackage(target, token, dry, log);
132133
}
133134

134135
await writeLog("publishing.md", logResult());

packages/publisher/src/publish-registry.ts

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ import {
3131
cacheDir,
3232
isObject,
3333
} from "@definitelytyped/utils";
34+
import { PackageJson } from "@npm/types";
35+
import * as libpub from "libnpmpublish";
3436
import * as pacote from "pacote";
3537
import * as semver from "semver";
3638
// @ts-ignore
@@ -74,7 +76,7 @@ export default async function publishRegistry(dt: FS, allPackages: AllPackages,
7476

7577
const token = process.env.NPM_TOKEN!;
7678

77-
const publishClient = () => NpmPublishClient.create(token, { defaultTag: "next" });
79+
const publishClient = () => NpmPublishClient.create(token);
7880
if (maxVersion !== latestVersion) {
7981
// There was an error in the last publish and types-registry wasn't validated.
8082
// This may have just been due to a timeout, so test if types-registry@next is a subset of the one we're about to publish.
@@ -84,7 +86,7 @@ export default async function publishRegistry(dt: FS, allPackages: AllPackages,
8486
await (await publishClient()).tag(typesRegistry, maxVersion, "latest", dry, log);
8587
} else if (latestContentHash !== newContentHash) {
8688
log("New packages have been added, so publishing a new registry.");
87-
await publish(await publishClient(), typesRegistry, packageJson, newVersion, dry, log);
89+
await publish(await publishClient(), packageJson, token, dry, log);
8890
} else {
8991
log("No new packages published, so no need to publish new registry.");
9092
// Just making sure...
@@ -114,13 +116,13 @@ async function generate(registry: string, packageJson: {}): Promise<void> {
114116

115117
async function publish(
116118
client: NpmPublishClient,
117-
packageName: string,
118-
packageJson: {},
119-
version: string,
119+
packageJson: PackageJson,
120+
token: string,
120121
dry: boolean,
121122
log: Logger
122123
): Promise<void> {
123-
await client.publish(registryOutputPath, packageJson, dry, log);
124+
const tarData = await pacote.tarball(registryOutputPath);
125+
if (!dry) await libpub.publish(packageJson, tarData, { defaultTag: "next", access: "public", token });
124126
// Sleep for 60 seconds to let NPM update.
125127
if (dry) {
126128
log("(dry) Skipping 60 second sleep...");
@@ -130,7 +132,7 @@ async function publish(
130132
}
131133
// Don't set it as "latest" until *after* it's been validated.
132134
await validate(log);
133-
await client.tag(packageName, version, "latest", dry, log);
135+
await client.tag(packageJson.name, packageJson.version, "latest", dry, log);
134136
}
135137

136138
async function installForValidate(log: Logger): Promise<void> {
@@ -205,7 +207,7 @@ function assertJsonNewer(newer: { [s: string]: any }, older: { [s: string]: any
205207
}
206208
}
207209

208-
function generatePackageJson(name: string, version: string, typesPublisherContentHash: string): object {
210+
function generatePackageJson(name: string, version: string, typesPublisherContentHash: string): PackageJson {
209211
const json = {
210212
name,
211213
version,
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { NotNeededPackage, TypingsData } from "@definitelytyped/definitions-parser";
2+
import * as libpub from "libnpmpublish";
3+
import { publishNotNeededPackage, publishTypingsPackage } from "../src/lib/package-publisher";
4+
5+
jest.mock("libnpmpublish");
6+
jest.mock("pacote", () => ({ async manifest() {}, async tarball() {} }));
7+
8+
const client = { async tag() {} };
9+
10+
const latestVersion = {
11+
pkg: new TypingsData({ typingsPackageName: "some-package" } as never, /*isLatest*/ true),
12+
version: "1.2.3",
13+
};
14+
const oldVersion = {
15+
pkg: new TypingsData({ typingsPackageName: "some-package" } as never, /*isLatest*/ false),
16+
version: "1.2.3",
17+
};
18+
const notNeeded = new NotNeededPackage("not-needed", "not-needed", "1.2.3");
19+
20+
function log() {}
21+
22+
test("Latest version gets latest tag", async () => {
23+
await publishTypingsPackage(client as never, latestVersion, /*token*/ "", /*dry*/ false, log);
24+
expect(libpub.publish).toHaveBeenLastCalledWith(/*manifest*/ undefined, /*tarData*/ undefined, {
25+
defaultTag: "latest",
26+
access: "public",
27+
token: "",
28+
});
29+
});
30+
31+
test("Publishing old versions doesn't change the latest tag", async () => {
32+
await publishTypingsPackage(client as never, oldVersion, /*token*/ "", /*dry*/ false, log);
33+
expect(libpub.publish).toHaveBeenLastCalledWith(/*manifest*/ undefined, /*tarData*/ undefined, {
34+
defaultTag: "",
35+
access: "public",
36+
token: "",
37+
});
38+
});
39+
40+
test("Not-needed package gets latest tag", async () => {
41+
await publishNotNeededPackage(notNeeded, /*token*/ "", /*dry*/ false, log);
42+
expect(libpub.publish).toHaveBeenLastCalledWith(/*manifest*/ undefined, /*tarData*/ undefined, {
43+
defaultTag: "latest",
44+
access: "public",
45+
token: "",
46+
});
47+
});

packages/retag/src/index.ts

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,6 @@ async function tag(dry: boolean, nProcesses: number, name?: string) {
6262
const pkg = await AllPackages.readSingle(name);
6363
const version = await getLatestTypingVersion(pkg);
6464
await updateTypeScriptVersionTags(pkg, version, publishClient, consoleLogger.info, dry);
65-
await updateLatestTag(pkg.fullNpmName, version, publishClient, consoleLogger.info, dry);
6665
} else {
6766
await Promise.all(
6867
(
@@ -71,7 +70,6 @@ async function tag(dry: boolean, nProcesses: number, name?: string) {
7170
// Only update tags for the latest version of the package.
7271
const version = await getLatestTypingVersion(pkg);
7372
await updateTypeScriptVersionTags(pkg, version, publishClient, consoleLogger.info, dry);
74-
await updateLatestTag(pkg.fullNpmName, version, publishClient, consoleLogger.info, dry);
7573
})
7674
);
7775
}
@@ -97,21 +95,6 @@ export async function updateTypeScriptVersionTags(
9795
}
9896
}
9997

100-
export async function updateLatestTag(
101-
fullName: string,
102-
version: string,
103-
client: NpmPublishClient,
104-
log: Logger,
105-
dry: boolean
106-
): Promise<void> {
107-
log(` but tag ${fullName}@${version} as "latest"`);
108-
if (dry) {
109-
log(' (dry) Skip move "latest" back to newest version');
110-
} else {
111-
await client.tag(fullName, version, "latest", dry, log);
112-
}
113-
}
114-
11598
export async function getLatestTypingVersion(pkg: TypingsData): Promise<string> {
11699
return (await fetchTypesPackageVersionInfo(pkg, /*publish*/ false)).version;
117100
}

packages/utils/.gitignore

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1 @@
11
logs
2-
test/data/pack.tgz

0 commit comments

Comments
 (0)