Skip to content

Commit 8ce850e

Browse files
committed
Publish older versions without touching latest tag
1 parent cfaa4c0 commit 8ce850e

19 files changed

+623
-205
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.5.5"
31+
"typescript": "^4.6.4"
3232
}
3333
}

packages/publisher/package.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,18 +19,23 @@
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
"oboe": "^2.1.3",
25+
"pacote": "^13.6.0",
2426
"semver": "^7.3.7",
2527
"source-map-support": "^0.4.0",
2628
"typescript": "^4.1.0",
2729
"yargs": "15.3.1"
2830
},
2931
"devDependencies": {
32+
"@npm/types": "^1.0.2",
3033
"@types/fs-extra": "^9.0.8",
3134
"@types/hh-mm-ss": "^1.2.1",
35+
"@types/libnpmpublish": "^4.0.3",
3236
"@types/mz": "^0.0.31",
3337
"@types/oboe": "^2.0.28",
38+
"@types/pacote": "^11.1.4",
3439
"@types/source-map-support": "^0.4.0",
3540
"@types/yargs": "^15.0.4"
3641
},

packages/publisher/src/calculate-versions.ts

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,7 @@ async function computeAndSaveChangedPackages(
4646
): Promise<ChangedPackages> {
4747
const cp = await computeChangedPackages(allPackages, log, client);
4848
const json: ChangedPackagesJson = {
49-
changedTypings: cp.changedTypings.map(
50-
({ pkg: { id }, version, latestVersion }): ChangedTypingJson => ({ id, version, latestVersion })
51-
),
49+
changedTypings: cp.changedTypings.map(({ pkg: { id }, version }): ChangedTypingJson => ({ id, version })),
5250
changedNotNeededPackages: cp.changedNotNeededPackages.map((p) => p.name),
5351
};
5452
await writeDataFile(versionsFilename, json);
@@ -71,10 +69,7 @@ async function computeChangedPackages(
7169
`'${pkg.name}' depends on '${name}' which does not exist on npm. All dependencies must exist.`
7270
);
7371
}
74-
const latestVersion = pkg.isLatest
75-
? undefined
76-
: (await fetchTypesPackageVersionInfo(allPackages.getLatest(pkg), client, /*publish*/ true)).version;
77-
return { pkg, version, latestVersion };
72+
return { pkg, version };
7873
}
7974
return undefined;
8075
});

packages/publisher/src/generate-packages.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ import {
1717
writeLog,
1818
writeFile,
1919
Logger,
20-
writeTgz,
2120
withNpmCache,
2221
UncachedNpmInfoClient,
2322
CachedNpmInfoClient,
@@ -34,6 +33,7 @@ import {
3433
License,
3534
formatTypingVersion,
3635
} from "@definitelytyped/definitions-parser";
36+
import * as pacote from "pacote";
3737
import { readChangedPackages, ChangedPackages } from "./lib/versions";
3838
import { outputDirectory } from "./util/util";
3939
import { skipBadPublishes } from "./lib/npm";
@@ -65,7 +65,7 @@ export default async function generatePackages(
6565
for (const { pkg, version } of changedPackages.changedTypings) {
6666
await generateTypingPackage(pkg, allPackages, version, dt);
6767
if (tgz) {
68-
await writeTgz(outputDirectory(pkg), `${outputDirectory(pkg)}.tgz`);
68+
await pacote.tarball.file(outputDirectory(pkg), `${outputDirectory(pkg)}.tgz`);
6969
}
7070
log(` * ${pkg.desc}`);
7171
}
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 older 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
@@ -45,12 +45,13 @@ export default async function publishPackages(
4545
log("=== Publishing packages ===");
4646
}
4747

48-
const client = await NpmPublishClient.create(await getSecret(Secret.NPM_TOKEN), undefined);
48+
const token = await getSecret(Secret.NPM_TOKEN);
49+
const client = await NpmPublishClient.create(token, undefined);
4950

5051
for (const cp of changedPackages.changedTypings) {
5152
log(`Publishing ${cp.pkg.desc}...`);
5253

53-
await publishTypingsPackage(client, cp, dry, log);
54+
await publishTypingsPackage(client, cp, token, dry, log);
5455

5556
const commits = (await queryGithub(
5657
`repos/DefinitelyTyped/DefinitelyTyped/commits?path=types%2f${cp.pkg.subDirectoryPath}`,
@@ -134,7 +135,7 @@ export default async function publishPackages(
134135
async (infoClient) => {
135136
for (const n of changedPackages.changedNotNeededPackages) {
136137
const target = skipBadPublishes(n, infoClient, log);
137-
await publishNotNeededPackage(client, target, dry, log);
138+
await publishNotNeededPackage(target, token, dry, log);
138139
}
139140
},
140141
cacheDirPath

packages/publisher/src/publish-registry.ts

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ import {
3434
isObject,
3535
max,
3636
} from "@definitelytyped/utils";
37+
import { PackageJson } from "@npm/types";
38+
import * as libpub from "libnpmpublish";
39+
import * as pacote from "pacote";
3740
import * as semver from "semver";
3841
import { getSecret, Secret } from "./lib/secrets";
3942
// @ts-ignore
@@ -88,7 +91,7 @@ export default async function publishRegistry(
8891

8992
const token = await getSecret(Secret.NPM_TOKEN);
9093

91-
const publishClient = () => NpmPublishClient.create(token, { defaultTag: "next" });
94+
const publishClient = () => NpmPublishClient.create(token);
9295
if (!semver.eq(highestSemverVersion, npmVersion)) {
9396
// There was an error in the last publish and types-registry wasn't validated.
9497
// 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.
@@ -98,7 +101,7 @@ export default async function publishRegistry(
98101
await (await publishClient()).tag(typesRegistry, String(highestSemverVersion), "latest", dry, log);
99102
} else if (npmContentHash !== newContentHash && isTimeForNewVersion) {
100103
log("New packages have been added, so publishing a new registry.");
101-
await publish(await publishClient(), typesRegistry, packageJson, newVersion, dry, log);
104+
await publish(await publishClient(), packageJson, token, dry, log);
102105
} else {
103106
const reason =
104107
npmContentHash === newContentHash ? "No new packages published" : "Was modified less than a week ago";
@@ -137,13 +140,13 @@ async function generate(registry: string, packageJson: {}): Promise<void> {
137140

138141
async function publish(
139142
client: NpmPublishClient,
140-
packageName: string,
141-
packageJson: {},
142-
version: string,
143+
packageJson: PackageJson,
144+
token: string,
143145
dry: boolean,
144146
log: Logger
145147
): Promise<void> {
146-
await client.publish(registryOutputPath, packageJson, dry, log);
148+
const tarData = await pacote.tarball(registryOutputPath);
149+
if (!dry) await libpub.publish(packageJson, tarData, { defaultTag: "next", access: "public", token });
147150
// Sleep for 60 seconds to let NPM update.
148151
if (dry) {
149152
log("(dry) Skipping 60 second sleep...");
@@ -153,7 +156,7 @@ async function publish(
153156
}
154157
// Don't set it as "latest" until *after* it's been validated.
155158
await validate(log);
156-
await client.tag(packageName, version, "latest", dry, log);
159+
await client.tag(packageJson.name, packageJson.version, "latest", dry, log);
157160
}
158161

159162
async function installForValidate(log: Logger): Promise<void> {
@@ -228,7 +231,7 @@ function assertJsonNewer(newer: { [s: string]: any }, older: { [s: string]: any
228231
}
229232
}
230233

231-
function generatePackageJson(name: string, version: string, typesPublisherContentHash: string): object {
234+
function generatePackageJson(name: string, version: string, typesPublisherContentHash: string): PackageJson {
232235
const json = {
233236
name,
234237
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 olderVersion = {
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 older versions doesn't change the latest tag", async () => {
32+
await publishTypingsPackage(client as never, olderVersion, /*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 types 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/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)