@dbx-tools/projen 0.9.161 → 0.9.165
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.ts +2 -2
- package/package.json +6 -6
- package/src/npm-release.ts +53 -21
- package/src/release-context.ts +6 -0
- package/src/release.ts +2 -0
- package/tasks/release.ts +11 -3
- package/tasks/verify-publications.ts +29 -9
package/index.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// Hand edits are overwritten on the next watch; this file is read-only.
|
|
4
4
|
|
|
5
5
|
export const PACKAGE_IDENTIFIER = "@dbx-tools/projen";
|
|
6
|
-
export const PACKAGE_VERSION = "0.9.
|
|
6
|
+
export const PACKAGE_VERSION = "0.9.165";
|
|
7
7
|
export * as appkitWorkspace from "./src/appkit-workspace.ts";
|
|
8
8
|
export * as barrels from "./src/barrels.ts";
|
|
9
9
|
export * as bunApp from "./src/bun-app.ts";
|
|
@@ -85,7 +85,7 @@ export { PYTHON_NODE_RUNTIME_DISTRIBUTION, PYTHON_NODE_RUNTIME_MODULE, PYTHON_NO
|
|
|
85
85
|
export type { PythonNodeRuntimeOptions } from "./src/python-node-runtime.ts";
|
|
86
86
|
export { pythonDistributionPaths, publishPythonProjects, buildPythonProjects } from "./src/python-packaging.ts";
|
|
87
87
|
export { pythonProjectInfo, preparePythonProjectForPublication } from "./src/python-release.ts";
|
|
88
|
-
export { RELEASE_VERSION, RELEASE_WORKFLOW_FILE, PUBLIC_NPM_REGISTRY_URL, PUBLIC_PYPI_REGISTRY_URL, PUBLICATION_WAIT_TIMEOUT_SECONDS, PUBLICATION_WAIT_INTERVAL_SECONDS } from "./src/release-context.ts";
|
|
88
|
+
export { RELEASE_VERSION, RELEASE_WORKFLOW_FILE, RELEASE_INSTALL_ENV, PUBLIC_NPM_REGISTRY_URL, PUBLIC_PYPI_REGISTRY_URL, PUBLICATION_WAIT_TIMEOUT_SECONDS, PUBLICATION_WAIT_INTERVAL_SECONDS, PUBLICATION_REQUEST_TIMEOUT_SECONDS } from "./src/release-context.ts";
|
|
89
89
|
export { RELEASE_PUBLISH_TARGETS, RELEASE_INSTALL_MODES, ReleaseStepSelectionSchema, releaseStepSelection, releaseTagAnnotation, parseReleaseTagAnnotation, releasePublishesLocally } from "./src/release-options.ts";
|
|
90
90
|
export type { ReleasePublishTarget, ReleaseInstallMode, ReleaseStepSelection, ReleaseSelectionOptions } from "./src/release-options.ts";
|
|
91
91
|
export { tryReleaseWorkflow, releaseTagPattern, releaseCondition, releaseBuildSteps, nodeReleaseSetupSteps, uvSetupStep, refreshReleaseDocsDependencies, refreshPublicationVerification, DBXToolsRelease } from "./src/release.ts";
|
package/package.json
CHANGED
|
@@ -30,11 +30,11 @@
|
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"@clack/prompts": "^1.7.0",
|
|
33
|
-
"@dbx-tools/auth": "^0.9.
|
|
34
|
-
"@dbx-tools/cli-args": "^0.9.
|
|
35
|
-
"@dbx-tools/core": "^0.9.
|
|
36
|
-
"@dbx-tools/path": "^0.9.
|
|
37
|
-
"@dbx-tools/shared-core": "^0.9.
|
|
33
|
+
"@dbx-tools/auth": "^0.9.165",
|
|
34
|
+
"@dbx-tools/cli-args": "^0.9.165",
|
|
35
|
+
"@dbx-tools/core": "^0.9.165",
|
|
36
|
+
"@dbx-tools/path": "^0.9.165",
|
|
37
|
+
"@dbx-tools/shared-core": "^0.9.165",
|
|
38
38
|
"commander": "^15.0.0",
|
|
39
39
|
"concurrently": "^10.0.3",
|
|
40
40
|
"constructs": "^10.6.0",
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
},
|
|
57
57
|
"main": "index.ts",
|
|
58
58
|
"license": "Apache-2.0",
|
|
59
|
-
"version": "0.9.
|
|
59
|
+
"version": "0.9.165",
|
|
60
60
|
"types": "index.ts",
|
|
61
61
|
"type": "module",
|
|
62
62
|
"exports": {
|
package/src/npm-release.ts
CHANGED
|
@@ -12,9 +12,10 @@ import {
|
|
|
12
12
|
import { tmpdir } from "node:os";
|
|
13
13
|
import { join, relative } from "node:path";
|
|
14
14
|
import { spawn, spawnSync } from "@dbx-tools/core";
|
|
15
|
-
import { log, poll, toError } from "@dbx-tools/shared-core";
|
|
15
|
+
import { combineAbortSignals, log, poll, toError } from "@dbx-tools/shared-core";
|
|
16
16
|
import {
|
|
17
17
|
PUBLIC_NPM_REGISTRY_URL,
|
|
18
|
+
PUBLICATION_REQUEST_TIMEOUT_SECONDS,
|
|
18
19
|
PUBLICATION_WAIT_INTERVAL_SECONDS,
|
|
19
20
|
PUBLICATION_WAIT_TIMEOUT_SECONDS,
|
|
20
21
|
} from "./release-context.ts";
|
|
@@ -79,11 +80,15 @@ export async function publishedNpmRelease(
|
|
|
79
80
|
version: string,
|
|
80
81
|
registry = process.env.NPM_CONFIG_REGISTRY ?? PUBLIC_NPM_REGISTRY_URL,
|
|
81
82
|
signal?: AbortSignal,
|
|
82
|
-
options: { readonly content?: boolean } = {},
|
|
83
|
+
options: { readonly content?: boolean; readonly timeoutMs?: number } = {},
|
|
83
84
|
): Promise<NpmReleaseIdentity | undefined> {
|
|
85
|
+
const requestSignal = combineAbortSignals(
|
|
86
|
+
signal,
|
|
87
|
+
AbortSignal.timeout(options.timeoutMs ?? PUBLICATION_REQUEST_TIMEOUT_SECONDS * 1_000),
|
|
88
|
+
);
|
|
84
89
|
const response = await fetch(registryUrl(registry, name, version), {
|
|
85
90
|
headers: { accept: "application/json" },
|
|
86
|
-
signal,
|
|
91
|
+
signal: requestSignal,
|
|
87
92
|
});
|
|
88
93
|
if (response.status === 404) return undefined;
|
|
89
94
|
if (!response.ok) {
|
|
@@ -100,7 +105,8 @@ export async function publishedNpmRelease(
|
|
|
100
105
|
}
|
|
101
106
|
let contentDigest: string | undefined;
|
|
102
107
|
if (options.content !== false && metadata.dist?.tarball) {
|
|
103
|
-
const archive = await fetch(metadata.dist.tarball, { signal });
|
|
108
|
+
const archive = await fetch(metadata.dist.tarball, { signal: requestSignal });
|
|
109
|
+
if (archive.status === 404) return undefined;
|
|
104
110
|
if (!archive.ok) {
|
|
105
111
|
throw new Error(`npm tarball lookup failed for ${name}@${version}: ${archive.status}`);
|
|
106
112
|
}
|
|
@@ -132,24 +138,47 @@ export function isNpmStagedVersionConflict(error: unknown): boolean {
|
|
|
132
138
|
export async function waitForMatchingNpmRelease(
|
|
133
139
|
local: NpmReleaseIdentity,
|
|
134
140
|
registry?: string,
|
|
135
|
-
options: {
|
|
141
|
+
options: {
|
|
142
|
+
readonly intervalMs?: number;
|
|
143
|
+
readonly requestTimeoutMs?: number;
|
|
144
|
+
readonly timeoutMs?: number;
|
|
145
|
+
} = {},
|
|
136
146
|
): Promise<void> {
|
|
137
147
|
const selectedRegistry = registry ?? process.env.NPM_CONFIG_REGISTRY ?? PUBLIC_NPM_REGISTRY_URL;
|
|
148
|
+
let lastError: Error | undefined;
|
|
138
149
|
try {
|
|
139
150
|
for await (const _available of poll(
|
|
140
151
|
async ({ signal }) => {
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
152
|
+
let published: NpmReleaseIdentity | undefined;
|
|
153
|
+
try {
|
|
154
|
+
published = await publishedNpmRelease(
|
|
155
|
+
local.name,
|
|
156
|
+
local.version,
|
|
157
|
+
selectedRegistry,
|
|
158
|
+
signal,
|
|
159
|
+
{ content: false, timeoutMs: options.requestTimeoutMs },
|
|
160
|
+
);
|
|
161
|
+
} catch (error) {
|
|
162
|
+
lastError = toError(error);
|
|
163
|
+
return false;
|
|
164
|
+
}
|
|
148
165
|
if (!published) return false;
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
await publishedNpmRelease(
|
|
152
|
-
|
|
166
|
+
let complete: NpmReleaseIdentity | undefined;
|
|
167
|
+
try {
|
|
168
|
+
complete = await publishedNpmRelease(
|
|
169
|
+
local.name,
|
|
170
|
+
local.version,
|
|
171
|
+
selectedRegistry,
|
|
172
|
+
signal,
|
|
173
|
+
{ timeoutMs: options.requestTimeoutMs },
|
|
174
|
+
);
|
|
175
|
+
} catch (error) {
|
|
176
|
+
lastError = toError(error);
|
|
177
|
+
return false;
|
|
178
|
+
}
|
|
179
|
+
if (!complete) return false;
|
|
180
|
+
lastError = undefined;
|
|
181
|
+
return npmReleaseMatches(local, complete);
|
|
153
182
|
},
|
|
154
183
|
{
|
|
155
184
|
intervalMs: options.intervalMs ?? PUBLICATION_WAIT_INTERVAL_SECONDS * 1_000,
|
|
@@ -160,10 +189,9 @@ export async function waitForMatchingNpmRelease(
|
|
|
160
189
|
void _available;
|
|
161
190
|
}
|
|
162
191
|
} catch (error) {
|
|
163
|
-
throw new Error(
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
);
|
|
192
|
+
throw new Error(`Staged npm version ${local.name}@${local.version} did not become available`, {
|
|
193
|
+
cause: lastError ?? error,
|
|
194
|
+
});
|
|
167
195
|
}
|
|
168
196
|
}
|
|
169
197
|
|
|
@@ -178,6 +206,7 @@ export async function publishNpmArchive(options: {
|
|
|
178
206
|
readonly dryRun?: boolean;
|
|
179
207
|
readonly env?: NodeJS.ProcessEnv;
|
|
180
208
|
readonly intervalMs?: number;
|
|
209
|
+
readonly requestTimeoutMs?: number;
|
|
181
210
|
readonly registry?: string;
|
|
182
211
|
readonly timeoutMs?: number;
|
|
183
212
|
}): Promise<NpmPublishOutcome> {
|
|
@@ -205,7 +234,9 @@ export async function publishNpmArchive(options: {
|
|
|
205
234
|
check: false,
|
|
206
235
|
},
|
|
207
236
|
);
|
|
208
|
-
if (result.stderr)
|
|
237
|
+
if (result.stderr) {
|
|
238
|
+
process.stderr.write(result.stderr.endsWith("\n") ? result.stderr : `${result.stderr}\n`);
|
|
239
|
+
}
|
|
209
240
|
if (result.exitCode === 0) return "published";
|
|
210
241
|
const error = new Error(
|
|
211
242
|
`npm publish failed (exit ${result.exitCode})${result.stderr ? `: ${result.stderr}` : ""}`,
|
|
@@ -217,6 +248,7 @@ export async function publishNpmArchive(options: {
|
|
|
217
248
|
logger.info(`waiting for staged ${identity.name}@${identity.version}`);
|
|
218
249
|
await waitForMatchingNpmRelease(identity, options.registry, {
|
|
219
250
|
intervalMs: options.intervalMs,
|
|
251
|
+
requestTimeoutMs: options.requestTimeoutMs,
|
|
220
252
|
timeoutMs: options.timeoutMs,
|
|
221
253
|
});
|
|
222
254
|
return "staged";
|
package/src/release-context.ts
CHANGED
|
@@ -3,6 +3,10 @@
|
|
|
3
3
|
export const RELEASE_VERSION = "${{ needs.build-release.outputs.release_version }}";
|
|
4
4
|
/** Generated GitHub Actions workflow triggered by release tags. */
|
|
5
5
|
export const RELEASE_WORKFLOW_FILE = "release.yml";
|
|
6
|
+
/** Environment applied to release dependency installation and synthesis. */
|
|
7
|
+
export const RELEASE_INSTALL_ENV = {
|
|
8
|
+
ONNXRUNTIME_NODE_INSTALL: "skip",
|
|
9
|
+
} as const;
|
|
6
10
|
/** Public npm registry queried after npm publication completes. */
|
|
7
11
|
export const PUBLIC_NPM_REGISTRY_URL = "https://registry.npmjs.org";
|
|
8
12
|
/** Public PyPI JSON API queried after Python publication completes. */
|
|
@@ -11,3 +15,5 @@ export const PUBLIC_PYPI_REGISTRY_URL = "https://pypi.org/pypi";
|
|
|
11
15
|
export const PUBLICATION_WAIT_TIMEOUT_SECONDS = 20 * 60;
|
|
12
16
|
/** Delay between public registry probes while a release is still invisible. */
|
|
13
17
|
export const PUBLICATION_WAIT_INTERVAL_SECONDS = 5;
|
|
18
|
+
/** Maximum duration of one public registry request before the next poll can proceed. */
|
|
19
|
+
export const PUBLICATION_REQUEST_TIMEOUT_SECONDS = 10;
|
package/src/release.ts
CHANGED
|
@@ -19,6 +19,7 @@ import { projectReleaseBranch, taskCommand, type DBXToolsJavaScriptProject } fro
|
|
|
19
19
|
import {
|
|
20
20
|
PUBLIC_NPM_REGISTRY_URL,
|
|
21
21
|
PUBLICATION_WAIT_TIMEOUT_SECONDS,
|
|
22
|
+
RELEASE_INSTALL_ENV,
|
|
22
23
|
RELEASE_VERSION,
|
|
23
24
|
RELEASE_WORKFLOW_FILE,
|
|
24
25
|
} from "./release-context.ts";
|
|
@@ -254,6 +255,7 @@ function releaseBuildJob(
|
|
|
254
255
|
timeoutMinutes: 60,
|
|
255
256
|
env: {
|
|
256
257
|
CI: "true",
|
|
258
|
+
...RELEASE_INSTALL_ENV,
|
|
257
259
|
...(options.docs
|
|
258
260
|
? { DOCS_SITE_URL: options.docs.siteUrl, DOCS_BASE: options.docs.base ?? "/" }
|
|
259
261
|
: {}),
|
package/tasks/release.ts
CHANGED
|
@@ -13,7 +13,7 @@ import { TaskDirectoriesOptionSchema, TaskRootOptionSchema } from "./options.ts"
|
|
|
13
13
|
import { writeReleaseNotes } from "./release-notes.ts";
|
|
14
14
|
import { assertReleaseVersion } from "./release-version.ts";
|
|
15
15
|
import { captureTaskCommand, runTaskCommand, taskCommandSucceeds } from "../src/_task-command.ts";
|
|
16
|
-
import { RELEASE_WORKFLOW_FILE } from "../src/release-context.ts";
|
|
16
|
+
import { RELEASE_INSTALL_ENV, RELEASE_WORKFLOW_FILE } from "../src/release-context.ts";
|
|
17
17
|
import {
|
|
18
18
|
RELEASE_INSTALL_MODES,
|
|
19
19
|
RELEASE_PUBLISH_TARGETS,
|
|
@@ -504,7 +504,11 @@ export async function runRelease(
|
|
|
504
504
|
throw new Error("--no-release-notes cannot be combined with supplied release notes");
|
|
505
505
|
}
|
|
506
506
|
|
|
507
|
-
if (install === "always")
|
|
507
|
+
if (install === "always") {
|
|
508
|
+
runTaskCommand(root, "bun", ["install"], {
|
|
509
|
+
env: { ...process.env, ...RELEASE_INSTALL_ENV },
|
|
510
|
+
});
|
|
511
|
+
}
|
|
508
512
|
if (selection.validation && hasPackageScript(root, "eslint:fix")) {
|
|
509
513
|
runTaskCommand(root, "bun", ["run", "eslint:fix"]);
|
|
510
514
|
}
|
|
@@ -514,7 +518,11 @@ export async function runRelease(
|
|
|
514
518
|
runTaskCommand(root, "git", ["fetch", remote, branch, "--tags"]);
|
|
515
519
|
if (options.bump ?? true) {
|
|
516
520
|
runTaskCommand(root, "bun", ["run", "bump"], {
|
|
517
|
-
env: {
|
|
521
|
+
env: {
|
|
522
|
+
...process.env,
|
|
523
|
+
...RELEASE_INSTALL_ENV,
|
|
524
|
+
DBX_TOOLS_RELEASE_INSTALL: install === "auto" ? "auto" : "never",
|
|
525
|
+
},
|
|
518
526
|
});
|
|
519
527
|
}
|
|
520
528
|
|
|
@@ -14,12 +14,14 @@ import {
|
|
|
14
14
|
import {
|
|
15
15
|
PUBLIC_NPM_REGISTRY_URL,
|
|
16
16
|
PUBLIC_PYPI_REGISTRY_URL,
|
|
17
|
+
PUBLICATION_REQUEST_TIMEOUT_SECONDS,
|
|
17
18
|
PUBLICATION_WAIT_INTERVAL_SECONDS,
|
|
18
19
|
PUBLICATION_WAIT_TIMEOUT_SECONDS,
|
|
19
20
|
} from "../src/release-context.ts";
|
|
20
21
|
|
|
21
22
|
const DEFAULT_CONCURRENCY = 8;
|
|
22
23
|
const DEFAULT_INTERVAL_SECONDS = PUBLICATION_WAIT_INTERVAL_SECONDS;
|
|
24
|
+
const DEFAULT_REQUEST_TIMEOUT_SECONDS = PUBLICATION_REQUEST_TIMEOUT_SECONDS;
|
|
23
25
|
const DEFAULT_TIMEOUT_SECONDS = PUBLICATION_WAIT_TIMEOUT_SECONDS;
|
|
24
26
|
const logger = log.logger("projen:verify-publications");
|
|
25
27
|
|
|
@@ -60,9 +62,16 @@ export interface PublicationProbe {
|
|
|
60
62
|
export interface PublicationWaitOptions {
|
|
61
63
|
readonly concurrency?: number;
|
|
62
64
|
readonly intervalMs?: number;
|
|
65
|
+
readonly requestTimeoutMs?: number;
|
|
63
66
|
readonly timeoutMs?: number;
|
|
64
67
|
}
|
|
65
68
|
|
|
69
|
+
/** Human-readable state for one sorted set of unresolved publications. */
|
|
70
|
+
export function publicationWaitMessage(probes: readonly PublicationProbe[]): string {
|
|
71
|
+
const labels = probes.map(({ label }) => label).sort((left, right) => left.localeCompare(right));
|
|
72
|
+
return `waiting for ${labels.length} public publication${labels.length === 1 ? "" : "s"} - ${labels.join(", ")}`;
|
|
73
|
+
}
|
|
74
|
+
|
|
66
75
|
/** Poll every unresolved publication concurrently and stop after one shared timeout. */
|
|
67
76
|
export async function waitForPublications(
|
|
68
77
|
probes: readonly PublicationProbe[],
|
|
@@ -70,9 +79,10 @@ export async function waitForPublications(
|
|
|
70
79
|
): Promise<void> {
|
|
71
80
|
if (probes.length === 0) throw new Error("No public publications were provided");
|
|
72
81
|
const concurrency = options.concurrency ?? DEFAULT_CONCURRENCY;
|
|
82
|
+
const requestTimeoutMs = options.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_SECONDS * 1_000;
|
|
73
83
|
const errors = new Map<string, Error>();
|
|
74
|
-
let pending = [...probes];
|
|
75
|
-
let
|
|
84
|
+
let pending = [...probes].sort((left, right) => left.label.localeCompare(right.label));
|
|
85
|
+
let previousPending = "";
|
|
76
86
|
try {
|
|
77
87
|
for await (const unavailable of poll<PublicationProbe[]>(
|
|
78
88
|
async ({ signal }) => {
|
|
@@ -80,7 +90,9 @@ export async function waitForPublications(
|
|
|
80
90
|
pending,
|
|
81
91
|
async (probe) => {
|
|
82
92
|
try {
|
|
83
|
-
const available = await probe.available(
|
|
93
|
+
const available = await probe.available(
|
|
94
|
+
AbortSignal.any([signal, AbortSignal.timeout(requestTimeoutMs)]),
|
|
95
|
+
);
|
|
84
96
|
if (available) errors.delete(probe.label);
|
|
85
97
|
return { available, probe };
|
|
86
98
|
} catch (error) {
|
|
@@ -90,7 +102,10 @@ export async function waitForPublications(
|
|
|
90
102
|
},
|
|
91
103
|
{ concurrency },
|
|
92
104
|
);
|
|
93
|
-
return results
|
|
105
|
+
return results
|
|
106
|
+
.filter(({ available }) => !available)
|
|
107
|
+
.map(({ probe }) => probe)
|
|
108
|
+
.sort((left, right) => left.label.localeCompare(right.label));
|
|
94
109
|
},
|
|
95
110
|
{
|
|
96
111
|
intervalMs: options.intervalMs ?? DEFAULT_INTERVAL_SECONDS * 1_000,
|
|
@@ -99,12 +114,11 @@ export async function waitForPublications(
|
|
|
99
114
|
},
|
|
100
115
|
)) {
|
|
101
116
|
pending = unavailable;
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
);
|
|
117
|
+
const pendingKey = pending.map(({ label }) => label).join("\0");
|
|
118
|
+
if (pending.length > 0 && pendingKey !== previousPending) {
|
|
119
|
+
logger.info(publicationWaitMessage(pending));
|
|
106
120
|
}
|
|
107
|
-
|
|
121
|
+
previousPending = pendingKey;
|
|
108
122
|
}
|
|
109
123
|
} catch (error) {
|
|
110
124
|
if (pending.length === 0) throw error;
|
|
@@ -243,6 +257,11 @@ export const VerifyPublicationsOptionsSchema = z
|
|
|
243
257
|
.positive()
|
|
244
258
|
.default(DEFAULT_TIMEOUT_SECONDS)
|
|
245
259
|
.describe("Maximum time to wait for the complete publication batch"),
|
|
260
|
+
requestTimeoutSeconds: z.coerce
|
|
261
|
+
.number()
|
|
262
|
+
.positive()
|
|
263
|
+
.default(DEFAULT_REQUEST_TIMEOUT_SECONDS)
|
|
264
|
+
.describe("Maximum duration of one public registry request"),
|
|
246
265
|
})
|
|
247
266
|
.refine((options) => options.npmDirectory || options.pypiDirectory, {
|
|
248
267
|
message: "At least one publication directory is required",
|
|
@@ -271,6 +290,7 @@ export async function main(args: readonly string[] = process.argv.slice(2)): Pro
|
|
|
271
290
|
await waitForPublications(probes, {
|
|
272
291
|
concurrency: options.concurrency,
|
|
273
292
|
intervalMs: options.intervalSeconds * 1_000,
|
|
293
|
+
requestTimeoutMs: options.requestTimeoutSeconds * 1_000,
|
|
274
294
|
timeoutMs: options.timeoutSeconds * 1_000,
|
|
275
295
|
});
|
|
276
296
|
}
|