@dbx-tools/projen 0.9.19 → 0.9.28
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/README.md +83 -229
- package/index.ts +11 -18
- package/package.json +9 -5
- package/src/_task-command.ts +75 -0
- package/src/barrels.ts +1 -74
- package/src/module-exports.ts +175 -1
- package/src/openapi.ts +5 -72
- package/src/packages.ts +45 -0
- package/src/project-js.ts +5 -48
- package/src/project-predicate.ts +1 -2
- package/src/project-py.ts +87 -199
- package/src/project.ts +2 -2
- package/src/python-node-bindings.ts +138 -0
- package/src/python-node-bundle.ts +92 -0
- package/src/release-catalog.ts +40 -208
- package/src/{release-dispatch.ts → release-context.ts} +0 -11
- package/src/release.ts +74 -778
- package/src/scaffold.ts +2 -6
- package/src/workspace-version.ts +9 -121
- package/tasks/bump.ts +17 -34
- package/tasks/databricks-cli-assets.ts +129 -0
- package/tasks/local-publish.ts +61 -138
- package/tasks/publish-npm.ts +71 -30
- package/tasks/publish-python.ts +61 -148
- package/tasks/publish.ts +32 -182
- package/tasks/python-node-bindings-watch.ts +47 -0
- package/tasks/python-node-bindings.ts +1017 -0
- package/tasks/python-registry.ts +2 -9
- package/tasks/python-release.ts +74 -0
- package/tasks/release-version.ts +23 -2
- package/tasks/release.ts +99 -0
- package/tasks/sync.ts +13 -18
- package/tasks/version-check.ts +13 -72
- package/src/_rust-project.ts +0 -429
- package/src/_rust-release-workflow.ts +0 -698
- package/src/project-rs.ts +0 -1127
- package/src/release-plan.ts +0 -269
- package/src/release-please.ts +0 -242
- package/src/uniffi.ts +0 -65
- package/tasks/publish-uniffi-local.ts +0 -314
- package/tasks/release-assets.ts +0 -476
- package/tasks/release-bootstrap.ts +0 -59
- package/tasks/release-candidate.ts +0 -383
- package/tasks/release-manifest.ts +0 -273
- package/tasks/release-plan.ts +0 -89
- package/tasks/release-please.ts +0 -162
- package/tasks/release-pr.ts +0 -563
- package/tasks/release-request.ts +0 -166
- package/tasks/release-summary-units.ts +0 -75
- package/tasks/release-summary.ts +0 -381
- package/tasks/rust.ts +0 -178
- package/tasks/stamp-python.ts +0 -13
- package/tasks/uniffi-python.d.ts +0 -61
- package/tasks/uniffi-python.js +0 -191
- package/tasks/uniffi-release.mjs +0 -573
- package/tasks/uniffi.ts +0 -271
package/src/scaffold.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Runs a projen re-synth, for the `sync` task and its watchers.
|
|
3
3
|
*/
|
|
4
4
|
import { join } from "node:path";
|
|
5
|
-
import
|
|
5
|
+
import { runTaskCommand } from "./_task-command.ts";
|
|
6
6
|
import { repoRoot } from "./packages.ts";
|
|
7
7
|
|
|
8
8
|
/**
|
|
@@ -34,9 +34,5 @@ export function runSynth(options: { post?: boolean } = {}): void {
|
|
|
34
34
|
else env.PROJEN_DISABLE_POST = "true";
|
|
35
35
|
const projenrc = join(repoRoot, ".projenrc.ts");
|
|
36
36
|
const args = process.versions.bun ? [projenrc] : ["--import", "tsx", projenrc];
|
|
37
|
-
|
|
38
|
-
cwd: repoRoot,
|
|
39
|
-
env,
|
|
40
|
-
check: true,
|
|
41
|
-
});
|
|
37
|
+
runTaskCommand(repoRoot, process.execPath, args, { env });
|
|
42
38
|
}
|
package/src/workspace-version.ts
CHANGED
|
@@ -8,22 +8,17 @@
|
|
|
8
8
|
* DEFAULT_VERSION} when it is absent on a fresh tree); it never rewrites it, so an
|
|
9
9
|
* ordinary `bunx projen` cannot move a package version up or down.
|
|
10
10
|
*
|
|
11
|
-
* Only
|
|
12
|
-
*
|
|
13
|
-
* `VERSION` yet. Both resolve the base from remote git tags first ({@link
|
|
14
|
-
* resolveRemoteVersion}) so a release cut elsewhere is respected, and fall back
|
|
15
|
-
* to the local file (or {@link DEFAULT_VERSION}) when the remote is unreachable
|
|
16
|
-
* or has no tags. The remote is consulted only on those paths.
|
|
11
|
+
* Only the pure `bump` task changes the number. It increments the checked-in
|
|
12
|
+
* file while release preparation owns the surrounding Git transaction.
|
|
17
13
|
*/
|
|
18
14
|
import { chmodSync, existsSync, readFileSync, statSync, writeFileSync } from "node:fs";
|
|
19
15
|
import { join } from "node:path";
|
|
20
16
|
import { parse as parseVersion } from "semver";
|
|
21
|
-
import { captureTaskCommand } from "./_task-command.ts";
|
|
22
17
|
|
|
23
18
|
/** Name of the repo-root file holding the workspace version. */
|
|
24
19
|
export const VERSION_FILE = "VERSION";
|
|
25
20
|
|
|
26
|
-
/** Version a fresh workspace
|
|
21
|
+
/** Version a fresh workspace reads before its first explicit VERSION write. */
|
|
27
22
|
export const DEFAULT_VERSION = "0.0.1";
|
|
28
23
|
|
|
29
24
|
const SEMVER = /^\d+\.\d+\.\d+$/;
|
|
@@ -115,127 +110,20 @@ export function syncWorkspaceManifestVersion(manifestPath: string, version: stri
|
|
|
115
110
|
return true;
|
|
116
111
|
}
|
|
117
112
|
|
|
118
|
-
/**
|
|
119
|
-
function
|
|
120
|
-
|
|
121
|
-
return captureTaskCommand(cwd, "git", args);
|
|
122
|
-
} catch {
|
|
123
|
-
return "";
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
/** Highest tag matching `<prefix><semver>` in the local tag list, or `undefined`. */
|
|
128
|
-
export function latestTagVersion(cwd: string, prefix: string): Semver | undefined {
|
|
129
|
-
const out = gitCapture(cwd, [
|
|
130
|
-
"-c",
|
|
131
|
-
"versionsort.suffix=-",
|
|
132
|
-
"tag",
|
|
133
|
-
"--sort=-version:refname",
|
|
134
|
-
"--list",
|
|
135
|
-
`${prefix}*`,
|
|
136
|
-
]);
|
|
137
|
-
for (const tag of out.split("\n")) {
|
|
138
|
-
if (!tag.startsWith(prefix)) continue;
|
|
139
|
-
const v = parseSemver(tag.slice(prefix.length));
|
|
140
|
-
if (v) return v;
|
|
141
|
-
}
|
|
142
|
-
return undefined;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
/** Highest historical `<component>-<prefix><semver>` tag, or `undefined`. */
|
|
146
|
-
export function latestComponentTagVersion(cwd: string, prefix: string): Semver | undefined {
|
|
147
|
-
const marker = `-${prefix}`;
|
|
148
|
-
const out = gitCapture(cwd, [
|
|
149
|
-
"-c",
|
|
150
|
-
"versionsort.suffix=-",
|
|
151
|
-
"tag",
|
|
152
|
-
"--sort=-version:refname",
|
|
153
|
-
"--list",
|
|
154
|
-
`*${marker}*`,
|
|
155
|
-
]);
|
|
156
|
-
let best: Semver | undefined;
|
|
157
|
-
for (const tag of out.split("\n")) {
|
|
158
|
-
const index = tag.lastIndexOf(marker);
|
|
159
|
-
const raw = index >= 0 ? tag.slice(index + marker.length) : "";
|
|
160
|
-
if (!/^\d+\.\d+\.\d+$/.test(raw)) continue;
|
|
161
|
-
const version = parseSemver(raw);
|
|
162
|
-
if (version && (!best || compareSemver(version, best) > 0)) best = version;
|
|
163
|
-
}
|
|
164
|
-
return best;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
/**
|
|
168
|
-
* Highest published version across repository and historical component tags,
|
|
169
|
-
* or `undefined` when the remote is unreachable or no matching tag exists.
|
|
170
|
-
* Fetches tags first (best effort) so a release made elsewhere is respected; a
|
|
171
|
-
* fetch failure just means the local tag list is used, and callers fall back to
|
|
172
|
-
* the `VERSION` file.
|
|
173
|
-
*/
|
|
174
|
-
export function resolveRemoteVersion(
|
|
175
|
-
cwd: string,
|
|
176
|
-
prefixes: readonly string[],
|
|
177
|
-
{
|
|
178
|
-
fetch = true,
|
|
179
|
-
includeComponentTags = true,
|
|
180
|
-
}: { fetch?: boolean; includeComponentTags?: boolean } = {},
|
|
181
|
-
): string | undefined {
|
|
182
|
-
if (fetch) gitCapture(cwd, ["fetch", "--tags", "--quiet"]);
|
|
183
|
-
let best: Semver | undefined;
|
|
184
|
-
for (const prefix of prefixes) {
|
|
185
|
-
const versions = [
|
|
186
|
-
latestTagVersion(cwd, prefix),
|
|
187
|
-
...(includeComponentTags ? [latestComponentTagVersion(cwd, prefix)] : []),
|
|
188
|
-
];
|
|
189
|
-
for (const version of versions) {
|
|
190
|
-
if (version && (!best || compareSemver(version, best) > 0)) best = version;
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
return best ? best.join(".") : undefined;
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
/**
|
|
197
|
-
* The base version a `bump` increments from: the highest remote tag if any exists
|
|
198
|
-
* (a local file that is ahead does NOT win), else the local `VERSION` file, else
|
|
199
|
-
* {@link DEFAULT_VERSION}.
|
|
200
|
-
*/
|
|
201
|
-
export function resolveBaseVersion(
|
|
202
|
-
root: string,
|
|
203
|
-
prefixes: readonly string[],
|
|
204
|
-
options: { fetch?: boolean; includeComponentTags?: boolean } = {},
|
|
205
|
-
): { version: string; source: "remote" | "local" } {
|
|
206
|
-
const remote = resolveRemoteVersion(root, prefixes, options);
|
|
207
|
-
if (remote) return { version: remote, source: "remote" };
|
|
208
|
-
return { version: readWorkspaceVersion(root), source: "local" };
|
|
113
|
+
/** The checked-in version a release increment starts from. */
|
|
114
|
+
export function resolveBaseVersion(root: string): string {
|
|
115
|
+
return readWorkspaceVersion(root);
|
|
209
116
|
}
|
|
210
117
|
|
|
211
118
|
/** Resolve the next release version without mutating the workspace. */
|
|
212
119
|
export function resolveNextVersion(
|
|
213
120
|
root: string,
|
|
214
|
-
prefixes: readonly string[],
|
|
215
121
|
level: VersionLevel,
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
const
|
|
219
|
-
const parsed = parseSemver(base.version) ?? [0, 0, 1];
|
|
122
|
+
): { base: string; version: string } {
|
|
123
|
+
const base = resolveBaseVersion(root);
|
|
124
|
+
const parsed = parseSemver(base) ?? [0, 0, 1];
|
|
220
125
|
return {
|
|
221
126
|
base: parsed.join("."),
|
|
222
127
|
version: incrementSemver(parsed, level).join("."),
|
|
223
|
-
source: base.source,
|
|
224
128
|
};
|
|
225
129
|
}
|
|
226
|
-
|
|
227
|
-
/**
|
|
228
|
-
* Create the `VERSION` file when it does not yet exist, seeding it from the remote
|
|
229
|
-
* tags (or {@link DEFAULT_VERSION} when the remote is unreachable / has no tag). An
|
|
230
|
-
* existing file is left untouched - only `bump` moves an established version, so
|
|
231
|
-
* bootstrap never upgrades or downgrades one. Returns the current version.
|
|
232
|
-
*/
|
|
233
|
-
export function ensureWorkspaceVersion(
|
|
234
|
-
root: string,
|
|
235
|
-
{ prefixes = ["v"], fetch = true }: { prefixes?: readonly string[]; fetch?: boolean } = {},
|
|
236
|
-
): string {
|
|
237
|
-
if (!existsSync(versionPath(root))) {
|
|
238
|
-
writeWorkspaceVersion(root, resolveRemoteVersion(root, prefixes, { fetch }) ?? DEFAULT_VERSION);
|
|
239
|
-
}
|
|
240
|
-
return readWorkspaceVersion(root);
|
|
241
|
-
}
|
package/tasks/bump.ts
CHANGED
|
@@ -4,7 +4,6 @@
|
|
|
4
4
|
* generated version surface. This task has no git, registry, or publication
|
|
5
5
|
* side effects; release preparation owns those operations.
|
|
6
6
|
*/
|
|
7
|
-
import * as exec from "@dbx-tools/core/exec";
|
|
8
7
|
import * as projectUtils from "@dbx-tools/core/project-utils";
|
|
9
8
|
import { log } from "@dbx-tools/shared-core";
|
|
10
9
|
import { Command } from "commander";
|
|
@@ -17,6 +16,7 @@ import {
|
|
|
17
16
|
type ReleaseOs,
|
|
18
17
|
type VersionLevel,
|
|
19
18
|
} from "../src/_release-platform.ts";
|
|
19
|
+
import { runTaskCommand } from "../src/_task-command.ts";
|
|
20
20
|
import { resolveNextVersion, writeWorkspaceVersion } from "../src/workspace-version.ts";
|
|
21
21
|
|
|
22
22
|
const logger = log.logger("projen:bump");
|
|
@@ -25,43 +25,26 @@ const program = new Command();
|
|
|
25
25
|
program
|
|
26
26
|
.description("Increment VERSION and synchronize generated workspace versions")
|
|
27
27
|
.addOption(releaseLevelOption())
|
|
28
|
-
.option("--prefix <prefix>", "git tag prefix used to resolve the published base", "v")
|
|
29
28
|
.addOption(releaseOperatingSystemOption())
|
|
30
29
|
.addOption(releaseArchitectureOption())
|
|
31
30
|
.option("--no-synth", "write VERSION without synchronizing generated files")
|
|
32
|
-
.action(
|
|
33
|
-
(
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
os: ReleaseOs[];
|
|
37
|
-
arch: ReleaseArch[];
|
|
38
|
-
synth: boolean;
|
|
39
|
-
}) => {
|
|
40
|
-
const root = projectUtils.root() ?? process.cwd();
|
|
41
|
-
const next = resolveNextVersion(root, [opts.prefix], opts.level);
|
|
42
|
-
const releasePlatforms = releasePlatformFilter(opts.os, opts.arch);
|
|
31
|
+
.action((opts: { level: VersionLevel; os: ReleaseOs[]; arch: ReleaseArch[]; synth: boolean }) => {
|
|
32
|
+
const root = projectUtils.root() ?? process.cwd();
|
|
33
|
+
const next = resolveNextVersion(root, opts.level);
|
|
34
|
+
const releasePlatforms = releasePlatformFilter(opts.os, opts.arch);
|
|
43
35
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
`${next.source === "remote" ? "" : " [no remote tag; used local VERSION]"}`,
|
|
47
|
-
);
|
|
48
|
-
writeWorkspaceVersion(root, next.version);
|
|
36
|
+
logger.info(`bump ${next.base} -> ${next.version} (${opts.level})`);
|
|
37
|
+
writeWorkspaceVersion(root, next.version);
|
|
49
38
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
},
|
|
61
|
-
});
|
|
62
|
-
}
|
|
63
|
-
logger.success(`workspace version synchronized at ${next.version}`);
|
|
64
|
-
},
|
|
65
|
-
);
|
|
39
|
+
if (opts.synth) {
|
|
40
|
+
runTaskCommand(root, process.execPath, [".projenrc.ts"], {
|
|
41
|
+
env: {
|
|
42
|
+
...process.env,
|
|
43
|
+
...(releasePlatforms ? { DBX_TOOLS_RELEASE_PLATFORMS: releasePlatforms } : {}),
|
|
44
|
+
},
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
logger.success(`workspace version synchronized at ${next.version}`);
|
|
48
|
+
});
|
|
66
49
|
|
|
67
50
|
await program.parseAsync();
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
#!/usr/bin/env -S bun
|
|
2
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { dirname, join, resolve } from "node:path";
|
|
4
|
+
import { parseArgs } from "node:util";
|
|
5
|
+
|
|
6
|
+
import * as projectUtils from "@dbx-tools/core/project-utils";
|
|
7
|
+
|
|
8
|
+
import { makeReadonly, makeWritable } from "../src/generated.ts";
|
|
9
|
+
|
|
10
|
+
const MINIMUM_VERSION = "0.296.0";
|
|
11
|
+
const REPOSITORY = "databricks/cli";
|
|
12
|
+
const PLATFORM_ASSETS = {
|
|
13
|
+
"darwin-arm64": "darwin_arm64",
|
|
14
|
+
"darwin-x64": "darwin_amd64",
|
|
15
|
+
"linux-arm64": "linux_arm64",
|
|
16
|
+
"linux-x64": "linux_amd64",
|
|
17
|
+
"win32-arm64": "windows_arm64",
|
|
18
|
+
"win32-x64": "windows_amd64",
|
|
19
|
+
} as const;
|
|
20
|
+
|
|
21
|
+
interface GitHubAsset {
|
|
22
|
+
readonly browser_download_url: string;
|
|
23
|
+
readonly digest?: string;
|
|
24
|
+
readonly name: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
interface GitHubRelease {
|
|
28
|
+
readonly assets: GitHubAsset[];
|
|
29
|
+
readonly published_at: string;
|
|
30
|
+
readonly tag_name: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
interface CliAsset {
|
|
34
|
+
readonly sha256: string;
|
|
35
|
+
readonly url: string;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
interface CliAssetManifest {
|
|
39
|
+
readonly schemaVersion: 1;
|
|
40
|
+
readonly dbxToolsVersion: string;
|
|
41
|
+
readonly minimumVersion: string;
|
|
42
|
+
readonly version: string;
|
|
43
|
+
readonly publishedAt: string;
|
|
44
|
+
readonly assets: Record<keyof typeof PLATFORM_ASSETS, CliAsset>;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function validManifest(value: unknown, dbxToolsVersion: string): value is CliAssetManifest {
|
|
48
|
+
if (!value || typeof value !== "object") return false;
|
|
49
|
+
const manifest = value as Partial<CliAssetManifest>;
|
|
50
|
+
return (
|
|
51
|
+
manifest.schemaVersion === 1 &&
|
|
52
|
+
manifest.dbxToolsVersion === dbxToolsVersion &&
|
|
53
|
+
manifest.minimumVersion === MINIMUM_VERSION &&
|
|
54
|
+
typeof manifest.version === "string" &&
|
|
55
|
+
typeof manifest.publishedAt === "string" &&
|
|
56
|
+
Boolean(manifest.assets) &&
|
|
57
|
+
Object.keys(PLATFORM_ASSETS).every((key) => {
|
|
58
|
+
const asset = manifest.assets?.[key as keyof typeof PLATFORM_ASSETS];
|
|
59
|
+
return (
|
|
60
|
+
typeof asset?.url === "string" &&
|
|
61
|
+
typeof asset.sha256 === "string" &&
|
|
62
|
+
/^[0-9a-f]{64}$/.test(asset.sha256)
|
|
63
|
+
);
|
|
64
|
+
})
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async function latestManifest(dbxToolsVersion: string): Promise<CliAssetManifest> {
|
|
69
|
+
const response = await fetch(`https://api.github.com/repos/${REPOSITORY}/releases/latest`, {
|
|
70
|
+
headers: {
|
|
71
|
+
accept: "application/vnd.github+json",
|
|
72
|
+
"user-agent": `dbx-tools/${dbxToolsVersion}`,
|
|
73
|
+
},
|
|
74
|
+
});
|
|
75
|
+
if (!response.ok) {
|
|
76
|
+
throw new Error(`Could not resolve the latest Databricks CLI release (${response.status})`);
|
|
77
|
+
}
|
|
78
|
+
const release = (await response.json()) as GitHubRelease;
|
|
79
|
+
const version = release.tag_name.replace(/^v/, "");
|
|
80
|
+
const assets = Object.fromEntries(
|
|
81
|
+
Object.entries(PLATFORM_ASSETS).map(([platform, suffix]) => {
|
|
82
|
+
const name = `databricks_cli_${version}_${suffix}.tar.gz`;
|
|
83
|
+
const asset = release.assets.find((candidate) => candidate.name === name);
|
|
84
|
+
const sha256 = asset?.digest?.match(/^sha256:([0-9a-f]{64})$/i)?.[1]?.toLowerCase();
|
|
85
|
+
if (!asset || !sha256)
|
|
86
|
+
throw new Error(`Release ${release.tag_name} has no digest for ${name}`);
|
|
87
|
+
return [platform, { url: asset.browser_download_url, sha256 }];
|
|
88
|
+
}),
|
|
89
|
+
) as CliAssetManifest["assets"];
|
|
90
|
+
return {
|
|
91
|
+
schemaVersion: 1,
|
|
92
|
+
dbxToolsVersion,
|
|
93
|
+
minimumVersion: MINIMUM_VERSION,
|
|
94
|
+
version,
|
|
95
|
+
publishedAt: release.published_at,
|
|
96
|
+
assets,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const { values } = parseArgs({
|
|
101
|
+
options: {
|
|
102
|
+
check: { type: "boolean", default: false },
|
|
103
|
+
root: { type: "string" },
|
|
104
|
+
},
|
|
105
|
+
strict: true,
|
|
106
|
+
});
|
|
107
|
+
const root = resolve(values.root ?? projectUtils.root() ?? process.cwd());
|
|
108
|
+
const output = join(root, "packages/js/node/auth/src/generated/databricks-cli-assets.json");
|
|
109
|
+
const dbxToolsVersion = readFileSync(join(root, "VERSION"), "utf8").trim();
|
|
110
|
+
let current: unknown;
|
|
111
|
+
if (existsSync(output)) {
|
|
112
|
+
try {
|
|
113
|
+
current = JSON.parse(readFileSync(output, "utf8"));
|
|
114
|
+
} catch {
|
|
115
|
+
current = undefined;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
if (validManifest(current, dbxToolsVersion)) {
|
|
119
|
+
console.log(`verified ${output.slice(root.length + 1)}`);
|
|
120
|
+
} else if (values.check) {
|
|
121
|
+
throw new Error("Databricks CLI asset manifest is missing or stale");
|
|
122
|
+
} else {
|
|
123
|
+
const manifest = await latestManifest(dbxToolsVersion);
|
|
124
|
+
mkdirSync(dirname(output), { recursive: true });
|
|
125
|
+
makeWritable(output);
|
|
126
|
+
writeFileSync(output, `${JSON.stringify(manifest, undefined, 2)}\n`);
|
|
127
|
+
makeReadonly(output);
|
|
128
|
+
console.log(`generated ${output.slice(root.length + 1)}`);
|
|
129
|
+
}
|
package/tasks/local-publish.ts
CHANGED
|
@@ -1,29 +1,24 @@
|
|
|
1
|
-
/**
|
|
2
|
-
import {
|
|
3
|
-
import { homedir } from "node:os";
|
|
4
|
-
import { join, resolve } from "node:path";
|
|
1
|
+
/** Build and publish the current workspace to configured loopback registries. */
|
|
2
|
+
import { resolve } from "node:path";
|
|
5
3
|
import { fileURLToPath } from "node:url";
|
|
6
|
-
import * as exec from "@dbx-tools/core/exec";
|
|
7
4
|
import * as projectUtils from "@dbx-tools/core/project-utils";
|
|
8
5
|
import { log, net } from "@dbx-tools/shared-core";
|
|
6
|
+
import { Command } from "commander";
|
|
9
7
|
import { activePythonIndexes, resolveLocalPypi } from "./python-registry.ts";
|
|
10
|
-
import {
|
|
8
|
+
import { runTaskCommandAsync } from "../src/_task-command.ts";
|
|
11
9
|
|
|
12
10
|
const logger = log.logger("projen:local-publish");
|
|
13
11
|
|
|
14
|
-
/** Options for publishing
|
|
12
|
+
/** Options for publishing the current workspace to local registries. */
|
|
15
13
|
export interface LocalPublishOptions {
|
|
14
|
+
readonly localPypi: string;
|
|
15
|
+
readonly localRegistry: string;
|
|
16
|
+
readonly pythonRoot: string;
|
|
16
17
|
readonly root: string;
|
|
17
18
|
readonly version: string;
|
|
18
|
-
readonly localRegistry: string;
|
|
19
|
-
readonly localPypi: string;
|
|
20
|
-
readonly pythonRoot?: string;
|
|
21
|
-
readonly localCargo: boolean;
|
|
22
|
-
/** Reuse the root TypeScript compile that immediately preceded local publication. */
|
|
23
|
-
readonly reuseValidatedNodeCompile?: boolean;
|
|
24
19
|
}
|
|
25
20
|
|
|
26
|
-
function resolveLocalRegistry(value: string): string | undefined {
|
|
21
|
+
export function resolveLocalRegistry(value: string): string | undefined {
|
|
27
22
|
const trimmed = value.trim();
|
|
28
23
|
if (!trimmed || trimmed.toLowerCase() === "false") return undefined;
|
|
29
24
|
if (trimmed.toLowerCase() === "auto") {
|
|
@@ -33,43 +28,13 @@ function resolveLocalRegistry(value: string): string | undefined {
|
|
|
33
28
|
return trimmed;
|
|
34
29
|
}
|
|
35
30
|
|
|
36
|
-
|
|
37
|
-
if (process.env.LOCAL_CARGO_REGISTRY) return process.env.LOCAL_CARGO_REGISTRY;
|
|
38
|
-
const config = join(homedir(), ".cargo", "config.toml");
|
|
39
|
-
if (!existsSync(config)) return undefined;
|
|
40
|
-
const source = readFileSync(config, "utf8");
|
|
41
|
-
const sections = [
|
|
42
|
-
...source.matchAll(/^\[registries\.([^\]]+)\]\s*\n([\s\S]*?)(?=^\[|(?![\s\S]))/gm),
|
|
43
|
-
];
|
|
44
|
-
for (const section of sections) {
|
|
45
|
-
const index = section[2]?.match(/^\s*index\s*=\s*["']([^"']+)["']/m)?.[1];
|
|
46
|
-
if (index && net.isLoopbackHost(index.replace(/^sparse\+/, ""))) return section[1];
|
|
47
|
-
}
|
|
48
|
-
return undefined;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/** Arguments for the workspace npm publisher used by local release preflight. */
|
|
52
|
-
export function localNodePublishArguments(
|
|
53
|
-
publishScript: string,
|
|
54
|
-
options: Pick<LocalPublishOptions, "version" | "reuseValidatedNodeCompile">,
|
|
55
|
-
registry: string,
|
|
56
|
-
): string[] {
|
|
57
|
-
return [
|
|
58
|
-
publishScript,
|
|
59
|
-
options.version,
|
|
60
|
-
"--registry",
|
|
61
|
-
registry,
|
|
62
|
-
...(options.reuseValidatedNodeCompile ? ["--skip-compile"] : []),
|
|
63
|
-
];
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/** Publish the exact release candidate to local npm, PyPI, and Cargo mirrors. */
|
|
31
|
+
/** Build and publish the current workspace directly to local registries. */
|
|
67
32
|
export async function publishLocalRelease(options: LocalPublishOptions): Promise<void> {
|
|
33
|
+
const root = resolve(options.root);
|
|
68
34
|
const localRegistry = resolveLocalRegistry(options.localRegistry);
|
|
69
35
|
const activeIndexes = activePythonIndexes();
|
|
70
36
|
const localPypi = resolveLocalPypi(options.localPypi, activeIndexes);
|
|
71
|
-
const
|
|
72
|
-
const localPublishes: Promise<unknown>[] = [];
|
|
37
|
+
const publishes: Promise<unknown>[] = [];
|
|
73
38
|
|
|
74
39
|
if (
|
|
75
40
|
options.localPypi.toLowerCase() === "auto" &&
|
|
@@ -82,103 +47,61 @@ export async function publishLocalRelease(options: LocalPublishOptions): Promise
|
|
|
82
47
|
}
|
|
83
48
|
|
|
84
49
|
if (localRegistry) {
|
|
85
|
-
const
|
|
86
|
-
logger.info(`publishing
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
check: true,
|
|
95
|
-
})
|
|
96
|
-
.then(() => logger.success(`published ${options.version} to ${localRegistry}`)),
|
|
50
|
+
const publishNpmScript = fileURLToPath(new URL("./publish.ts", import.meta.url));
|
|
51
|
+
logger.info(`building and publishing npm workspace to ${localRegistry}`);
|
|
52
|
+
publishes.push(
|
|
53
|
+
runTaskCommandAsync(root, process.execPath, [
|
|
54
|
+
publishNpmScript,
|
|
55
|
+
options.version,
|
|
56
|
+
"--registry",
|
|
57
|
+
localRegistry,
|
|
58
|
+
]),
|
|
97
59
|
);
|
|
98
60
|
}
|
|
99
61
|
|
|
100
|
-
if (localPypi
|
|
62
|
+
if (localPypi) {
|
|
101
63
|
const publishPythonScript = fileURLToPath(new URL("./publish-python.ts", import.meta.url));
|
|
102
|
-
logger.info(`publishing Python
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
process.execPath,
|
|
107
|
-
[
|
|
108
|
-
publishPythonScript,
|
|
109
|
-
options.version,
|
|
110
|
-
"--root",
|
|
111
|
-
pythonRoot,
|
|
112
|
-
"--index-url",
|
|
113
|
-
localPypi.indexUrl,
|
|
114
|
-
"--publish-url",
|
|
115
|
-
localPypi.publishUrl,
|
|
116
|
-
],
|
|
117
|
-
{
|
|
118
|
-
cwd: options.root,
|
|
119
|
-
stdout: "inherit",
|
|
120
|
-
stderr: "inherit",
|
|
121
|
-
stdin: "ignore",
|
|
122
|
-
check: true,
|
|
123
|
-
},
|
|
124
|
-
)
|
|
125
|
-
.then(() =>
|
|
126
|
-
logger.success(`published Python ${options.version} to ${localPypi.publishUrl}`),
|
|
127
|
-
),
|
|
128
|
-
);
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
await Promise.all(localPublishes);
|
|
132
|
-
if (localRegistry || localPypi) {
|
|
133
|
-
const publishUniFFIScript = fileURLToPath(
|
|
134
|
-
new URL("./publish-uniffi-local.ts", import.meta.url),
|
|
135
|
-
);
|
|
136
|
-
await exec.spawn(
|
|
137
|
-
process.execPath,
|
|
138
|
-
[
|
|
139
|
-
publishUniFFIScript,
|
|
140
|
-
"--version",
|
|
64
|
+
logger.info(`building and publishing Python workspace to ${localPypi.publishUrl}`);
|
|
65
|
+
publishes.push(
|
|
66
|
+
runTaskCommandAsync(root, process.execPath, [
|
|
67
|
+
publishPythonScript,
|
|
141
68
|
options.version,
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
stdin: "ignore",
|
|
150
|
-
check: true,
|
|
151
|
-
},
|
|
69
|
+
"--root",
|
|
70
|
+
options.pythonRoot,
|
|
71
|
+
"--index-url",
|
|
72
|
+
localPypi.indexUrl,
|
|
73
|
+
"--publish-url",
|
|
74
|
+
localPypi.publishUrl,
|
|
75
|
+
]),
|
|
152
76
|
);
|
|
153
|
-
logger.success(`published host-native bindings for ${options.version}`);
|
|
154
77
|
}
|
|
155
78
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
(
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
79
|
+
await Promise.all(publishes);
|
|
80
|
+
logger.success(`published local workspace ${options.version}`);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (import.meta.main) {
|
|
84
|
+
await new Command()
|
|
85
|
+
.requiredOption("--version <version>", "release version")
|
|
86
|
+
.option("--root <path>", "repository root")
|
|
87
|
+
.option("--python-root <path>", "Python package root", "packages/py")
|
|
88
|
+
.option("--local-registry <url>", "local npm registry", "auto")
|
|
89
|
+
.option("--local-pypi <url>", "local devpi index", "auto")
|
|
90
|
+
.action(
|
|
91
|
+
(options: {
|
|
92
|
+
version: string;
|
|
93
|
+
root?: string;
|
|
94
|
+
pythonRoot: string;
|
|
95
|
+
localRegistry: string;
|
|
96
|
+
localPypi: string;
|
|
97
|
+
}) =>
|
|
98
|
+
publishLocalRelease({
|
|
99
|
+
localPypi: options.localPypi,
|
|
100
|
+
localRegistry: options.localRegistry,
|
|
101
|
+
pythonRoot: options.pythonRoot,
|
|
102
|
+
root: options.root ?? projectUtils.root() ?? process.cwd(),
|
|
103
|
+
version: options.version,
|
|
104
|
+
}),
|
|
105
|
+
)
|
|
106
|
+
.parseAsync();
|
|
184
107
|
}
|