@dbx-tools/projen 0.9.21 → 0.9.29
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 +77 -207
- package/index.ts +10 -14
- package/package.json +4 -4
- 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 +3 -6
- package/src/project-predicate.ts +1 -2
- package/src/project-py.ts +82 -103
- 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 +3 -5
- package/src/{release-dispatch.ts → release-context.ts} +0 -11
- package/src/release.ts +70 -152
- package/tasks/bump.ts +12 -21
- package/tasks/databricks-cli-assets.ts +129 -0
- package/tasks/local-publish.ts +23 -107
- package/tasks/publish-npm.ts +10 -1
- package/tasks/publish-python.ts +26 -11
- package/tasks/publish.ts +12 -48
- package/tasks/python-node-bindings-watch.ts +47 -0
- package/tasks/python-node-bindings.ts +1017 -0
- package/tasks/python-release.ts +74 -0
- package/tasks/release.ts +99 -0
- package/tasks/sync.ts +13 -18
- package/tasks/version-check.ts +1 -28
- package/._LICENSE +0 -0
- package/._README.md +0 -0
- package/._index.ts +0 -0
- package/._package.json +0 -0
- package/src/.__lazy-require.ts +0 -0
- package/src/.__release-platform.ts +0 -0
- package/src/.__rust-project.ts +0 -0
- package/src/.__rust-release-workflow.ts +0 -0
- package/src/.__task-command.ts +0 -0
- package/src/._barrels.ts +0 -0
- package/src/._bun-app.ts +0 -0
- package/src/._bun-workflow.ts +0 -0
- package/src/._clean.ts +0 -0
- package/src/._codegen.ts +0 -0
- package/src/._dbx-tools-config.ts +0 -0
- package/src/._engine-root.ts +0 -0
- package/src/._generated.ts +0 -0
- package/src/._mixin.ts +0 -0
- package/src/._module-exports.ts +0 -0
- package/src/._openapi.ts +0 -0
- package/src/._packages.ts +0 -0
- package/src/._pnpm-workspace.ts +0 -0
- package/src/._project-js.ts +0 -0
- package/src/._project-predicate.ts +0 -0
- package/src/._project-py.ts +0 -0
- package/src/._project-rs.ts +0 -0
- package/src/._project.ts +0 -0
- package/src/._projen-version.ts +0 -0
- package/src/._publish.ts +0 -0
- package/src/._release-catalog.ts +0 -0
- package/src/._release-dispatch.ts +0 -0
- package/src/._release-github.ts +0 -0
- package/src/._release.ts +0 -0
- package/src/._scaffold.ts +0 -0
- package/src/._tags.ts +0 -0
- package/src/._tsconfig.ts +0 -0
- package/src/._uniffi.ts +0 -0
- package/src/._vscode.ts +0 -0
- package/src/._watch.ts +0 -0
- package/src/._workspace-lock.ts +0 -0
- package/src/._workspace-version.ts +0 -0
- package/src/_release-platform.ts +0 -78
- package/src/_rust-project.ts +0 -429
- package/src/_rust-release-workflow.ts +0 -311
- package/src/project-rs.ts +0 -1054
- package/src/release-github.ts +0 -109
- package/src/uniffi.ts +0 -65
- package/tasks/._barrels.ts +0 -0
- package/tasks/._bump.ts +0 -0
- package/tasks/._clean.ts +0 -0
- package/tasks/._compile-workspace.ts +0 -0
- package/tasks/._local-publish.ts +0 -0
- package/tasks/._openapi.ts +0 -0
- package/tasks/._projenrc.ts +0 -0
- package/tasks/._publish-npm.ts +0 -0
- package/tasks/._publish-python.ts +0 -0
- package/tasks/._publish-uniffi-local.ts +0 -0
- package/tasks/._publish.ts +0 -0
- package/tasks/._python-registry.ts +0 -0
- package/tasks/._release-assets.ts +0 -0
- package/tasks/._release-candidate.ts +0 -0
- package/tasks/._release-manifest.ts +0 -0
- package/tasks/._release-pr.ts +0 -0
- package/tasks/._release-summary.ts +0 -0
- package/tasks/._release-version.ts +0 -0
- package/tasks/._rust.ts +0 -0
- package/tasks/._sync.ts +0 -0
- package/tasks/._uniffi-python.d.ts +0 -0
- package/tasks/._uniffi-python.js +0 -0
- package/tasks/._uniffi-release.mjs +0 -0
- package/tasks/._uniffi.ts +0 -0
- package/tasks/._version-check.ts +0 -0
- package/tasks/publish-uniffi-local.ts +0 -359
- package/tasks/release-assets.ts +0 -467
- package/tasks/release-candidate.ts +0 -416
- package/tasks/release-manifest.ts +0 -273
- package/tasks/release-pr.ts +0 -589
- package/tasks/release-summary.ts +0 -379
- package/tasks/rust.ts +0 -173
- package/tasks/uniffi-python.d.ts +0 -60
- package/tasks/uniffi-python.js +0 -178
- package/tasks/uniffi-release.mjs +0 -579
- package/tasks/uniffi.ts +0 -262
package/src/release-github.ts
DELETED
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
import { json, object } from "@dbx-tools/shared-core";
|
|
2
|
-
|
|
3
|
-
/** GitHub repository identity used by release preparation. */
|
|
4
|
-
export interface GithubRepositoryIdentity {
|
|
5
|
-
hostname: string;
|
|
6
|
-
owner: string;
|
|
7
|
-
repository: string;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
/** Parse a GitHub repository URL without treating its owner as the authenticated actor. */
|
|
11
|
-
export function githubRepositoryIdentity(repositoryUrl: string): GithubRepositoryIdentity {
|
|
12
|
-
const url = new URL(repositoryUrl);
|
|
13
|
-
const [owner, name] = url.pathname.split("/").filter(Boolean);
|
|
14
|
-
if (!owner || !name) {
|
|
15
|
-
throw new Error(`Cannot determine GitHub repository identity from ${repositoryUrl}`);
|
|
16
|
-
}
|
|
17
|
-
return {
|
|
18
|
-
hostname: url.hostname,
|
|
19
|
-
owner,
|
|
20
|
-
repository: name.replace(/\.git$/, ""),
|
|
21
|
-
};
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
/** One authenticated GitHub CLI account available on a repository host. */
|
|
25
|
-
export interface GithubAuthenticatedAccount {
|
|
26
|
-
login: string;
|
|
27
|
-
active: boolean;
|
|
28
|
-
scopes: string[];
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/** Repository capabilities returned for one authenticated actor. */
|
|
32
|
-
export interface GithubRepositoryPermissions {
|
|
33
|
-
readonly push: boolean;
|
|
34
|
-
readonly maintain: boolean;
|
|
35
|
-
readonly admin: boolean;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/** Read normalized repository permissions from a GitHub repository response. */
|
|
39
|
-
export function githubRepositoryPermissions(value: unknown): GithubRepositoryPermissions {
|
|
40
|
-
const repository = object.isRecord(value) ? value : undefined;
|
|
41
|
-
const permissions = object.isRecord(repository?.permissions) ? repository.permissions : undefined;
|
|
42
|
-
return {
|
|
43
|
-
push: permissions?.push === true,
|
|
44
|
-
maintain: permissions?.maintain === true,
|
|
45
|
-
admin: permissions?.admin === true,
|
|
46
|
-
};
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/** Whether the actor can manage releases rather than only push source. */
|
|
50
|
-
export function githubRepositoryCanManage(permissions: GithubRepositoryPermissions): boolean {
|
|
51
|
-
return permissions.maintain || permissions.admin;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/** Parse successful GitHub CLI accounts with the active account first. */
|
|
55
|
-
export function githubAuthenticatedAccounts(
|
|
56
|
-
statusOutput: string,
|
|
57
|
-
hostname: string,
|
|
58
|
-
): GithubAuthenticatedAccount[] {
|
|
59
|
-
const status = json.parseRecord(statusOutput);
|
|
60
|
-
const hosts = object.isRecord(status?.hosts) ? status.hosts : undefined;
|
|
61
|
-
const entries = hosts?.[hostname];
|
|
62
|
-
if (!Array.isArray(entries)) return [];
|
|
63
|
-
return entries
|
|
64
|
-
.flatMap((entry) => {
|
|
65
|
-
if (!object.isRecord(entry) || entry.state !== "success" || typeof entry.login !== "string") {
|
|
66
|
-
return [];
|
|
67
|
-
}
|
|
68
|
-
const scopes =
|
|
69
|
-
typeof entry.scopes === "string"
|
|
70
|
-
? entry.scopes
|
|
71
|
-
.split(",")
|
|
72
|
-
.map((scope) => scope.trim())
|
|
73
|
-
.filter(Boolean)
|
|
74
|
-
: [];
|
|
75
|
-
return [{ login: entry.login, active: entry.active === true, scopes }];
|
|
76
|
-
})
|
|
77
|
-
.filter(
|
|
78
|
-
(account, index, accounts) =>
|
|
79
|
-
accounts.findIndex((candidate) => candidate.login === account.login) === index,
|
|
80
|
-
)
|
|
81
|
-
.sort((left, right) => Number(right.active) - Number(left.active));
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/** Whether an account can update workflow files when using a classic OAuth token. */
|
|
85
|
-
export function githubAccountSupportsWorkflowChanges(account: GithubAuthenticatedAccount): boolean {
|
|
86
|
-
return !account.scopes.includes("repo") || account.scopes.includes("workflow");
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/** Ask GitHub CLI for one account token on the repository host. */
|
|
90
|
-
export function githubTokenArguments(hostname: string, user?: string): string[] {
|
|
91
|
-
return ["auth", "token", "--hostname", hostname, ...(user ? ["--user", user] : [])];
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
/** GitHub CLI token variable for a public, data-residency, or enterprise host. */
|
|
95
|
-
export function githubTokenEnvironmentName(hostname: string): "GH_TOKEN" | "GH_ENTERPRISE_TOKEN" {
|
|
96
|
-
return hostname === "github.com" || hostname.endsWith(".ghe.com")
|
|
97
|
-
? "GH_TOKEN"
|
|
98
|
-
: "GH_ENTERPRISE_TOKEN";
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
/** Host-qualified repository selector consumed by GitHub CLI. */
|
|
102
|
-
export function githubRepositorySpecifier(identity: GithubRepositoryIdentity): string {
|
|
103
|
-
return `${identity.hostname}/${identity.owner}/${identity.repository}`;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
/** GitHub REST path for the detected repository. */
|
|
107
|
-
export function githubRepositoryApiPath(identity: GithubRepositoryIdentity): string {
|
|
108
|
-
return `repos/${identity.owner}/${identity.repository}`;
|
|
109
|
-
}
|
package/src/uniffi.ts
DELETED
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
const defaultedParameters = (source: string): Map<string, Set<string>> => {
|
|
2
|
-
const methods = new Map<string, Set<string>>();
|
|
3
|
-
for (const match of source.matchAll(/^\s*(?:async\s+)?(\w+)\(([^)]*)\)/gm)) {
|
|
4
|
-
const parameters = match[2]
|
|
5
|
-
.split(",")
|
|
6
|
-
.map((parameter) => parameter.trim())
|
|
7
|
-
.filter((parameter) => parameter.includes("="))
|
|
8
|
-
.map((parameter) => parameter.match(/^(\w+)\s*:/)?.[1])
|
|
9
|
-
.filter((parameter): parameter is string => parameter !== undefined);
|
|
10
|
-
if (parameters.length > 0) methods.set(match[1], new Set(parameters));
|
|
11
|
-
}
|
|
12
|
-
return methods;
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
export const makeDefaultedInterfaceParametersOptional = (source: string): string => {
|
|
16
|
-
const defaults = defaultedParameters(source);
|
|
17
|
-
return source.replace(/export interface \w+Like \{[\s\S]*?^\}/gm, (block) =>
|
|
18
|
-
block.replace(/^(\s*)(\w+)\(([^)]*)\)/gm, (signature, indent, method, parameters) => {
|
|
19
|
-
const defaulted = defaults.get(method);
|
|
20
|
-
if (!defaulted) return signature;
|
|
21
|
-
const repaired = parameters
|
|
22
|
-
.split(",")
|
|
23
|
-
.map((parameter: string) => {
|
|
24
|
-
const match = parameter.match(/^(\s*)(\w+)(\s*:\s*.*)$/);
|
|
25
|
-
if (!match || !defaulted.has(match[2])) return parameter;
|
|
26
|
-
return `${match[1]}${match[2]}?${match[3]}`;
|
|
27
|
-
})
|
|
28
|
-
.join(",");
|
|
29
|
-
return `${indent}${method}(${repaired})`;
|
|
30
|
-
}),
|
|
31
|
-
);
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
/** Remove UBRN's obsolete `*Interface` aliases once the `*Like` owner exists. */
|
|
35
|
-
export const removeObsoleteInterfaceAliases = (source: string): string =>
|
|
36
|
-
source.replace(
|
|
37
|
-
/\n\/\*\*\n \* @deprecated Use `\w+Like` instead\.\n \*\/\nexport type \w+Interface = \w+Like;\n/g,
|
|
38
|
-
"\n",
|
|
39
|
-
);
|
|
40
|
-
|
|
41
|
-
export interface TypeScriptBindingModule {
|
|
42
|
-
readonly specifier: string;
|
|
43
|
-
readonly source: string;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/** Add source extensions to UBRN's generated relative binding imports. */
|
|
47
|
-
export const addTypeScriptExtensionsToBindingImports = (source: string): string =>
|
|
48
|
-
source.replace(
|
|
49
|
-
/(["'])\.\/_bindings(-ffi)?\1/g,
|
|
50
|
-
(_specifier, quote: string, suffix: string | undefined) =>
|
|
51
|
-
`${quote}./_bindings${suffix ?? ""}.ts${quote}`,
|
|
52
|
-
);
|
|
53
|
-
|
|
54
|
-
/** Add explicit type exports for interfaces that TypeScript misses through UBRN's star exports. */
|
|
55
|
-
export const addExplicitInterfaceReexports = (
|
|
56
|
-
facade: string,
|
|
57
|
-
modules: readonly TypeScriptBindingModule[],
|
|
58
|
-
): string => {
|
|
59
|
-
const exports = modules.flatMap(({ specifier, source }) => {
|
|
60
|
-
const names = [...source.matchAll(/^export interface (\w+)/gm)].map((match) => match[1]!);
|
|
61
|
-
return names.length > 0 ? [`export type { ${names.join(", ")} } from '${specifier}';`] : [];
|
|
62
|
-
});
|
|
63
|
-
if (exports.length === 0) return facade;
|
|
64
|
-
return `${facade.trimEnd()}\n${exports.join("\n")}\n`;
|
|
65
|
-
};
|
package/tasks/._barrels.ts
DELETED
|
Binary file
|
package/tasks/._bump.ts
DELETED
|
Binary file
|
package/tasks/._clean.ts
DELETED
|
Binary file
|
|
Binary file
|
package/tasks/._local-publish.ts
DELETED
|
Binary file
|
package/tasks/._openapi.ts
DELETED
|
Binary file
|
package/tasks/._projenrc.ts
DELETED
|
Binary file
|
package/tasks/._publish-npm.ts
DELETED
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/tasks/._publish.ts
DELETED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/tasks/._release-pr.ts
DELETED
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/tasks/._rust.ts
DELETED
|
Binary file
|
package/tasks/._sync.ts
DELETED
|
Binary file
|
|
Binary file
|
package/tasks/._uniffi-python.js
DELETED
|
Binary file
|
|
Binary file
|
package/tasks/._uniffi.ts
DELETED
|
Binary file
|
package/tasks/._version-check.ts
DELETED
|
Binary file
|
|
@@ -1,359 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env -S bun
|
|
2
|
-
import {
|
|
3
|
-
cpSync,
|
|
4
|
-
chmodSync,
|
|
5
|
-
existsSync,
|
|
6
|
-
mkdirSync,
|
|
7
|
-
mkdtempSync,
|
|
8
|
-
readFileSync,
|
|
9
|
-
readdirSync,
|
|
10
|
-
rmSync,
|
|
11
|
-
writeFileSync,
|
|
12
|
-
} from "node:fs";
|
|
13
|
-
import { arch, platform, tmpdir } from "node:os";
|
|
14
|
-
import { dirname, join, relative, resolve } from "node:path";
|
|
15
|
-
import { fileURLToPath } from "node:url";
|
|
16
|
-
import { parseArgs } from "node:util";
|
|
17
|
-
import { json, log, object } from "@dbx-tools/shared-core";
|
|
18
|
-
import { parse, stringify } from "smol-toml";
|
|
19
|
-
import { captureTaskCommand, runTaskCommand, taskCommandSucceeds } from "../src/_task-command.ts";
|
|
20
|
-
import { readDbxToolsConfig, repoRoot } from "../src/packages.ts";
|
|
21
|
-
import type { RustBindingMapping, RustWorkspaceMapping } from "../src/project-rs.ts";
|
|
22
|
-
|
|
23
|
-
const logger = log.logger("projen:publish-uniffi-local");
|
|
24
|
-
|
|
25
|
-
function commandAvailable(command: string): boolean {
|
|
26
|
-
return taskCommandSucceeds(repoRoot, command, ["--version"]);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
function rustHost(): string {
|
|
30
|
-
const host = captureTaskCommand(repoRoot, "rustc", ["-vV"], { check: true }).match(
|
|
31
|
-
/^host:\s+(.+)$/m,
|
|
32
|
-
)?.[1];
|
|
33
|
-
if (!host) throw new Error("Unable to detect the local Rust target from rustc -vV");
|
|
34
|
-
return host;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
function pythonTag(): string {
|
|
38
|
-
const value = captureTaskCommand(
|
|
39
|
-
repoRoot,
|
|
40
|
-
"uv",
|
|
41
|
-
[
|
|
42
|
-
"run",
|
|
43
|
-
"--no-project",
|
|
44
|
-
"python",
|
|
45
|
-
"-c",
|
|
46
|
-
"import sysconfig; print(sysconfig.get_platform().replace('-', '_').replace('.', '_'))",
|
|
47
|
-
],
|
|
48
|
-
{ check: true },
|
|
49
|
-
);
|
|
50
|
-
if (!value) throw new Error("Unable to detect the local Python wheel platform tag");
|
|
51
|
-
return value;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
function nativeTarget(): {
|
|
55
|
-
cpu: "arm64" | "x64";
|
|
56
|
-
libc?: "glibc";
|
|
57
|
-
node: string;
|
|
58
|
-
os: "darwin" | "linux" | "win32";
|
|
59
|
-
} {
|
|
60
|
-
const os = platform();
|
|
61
|
-
const machine = arch();
|
|
62
|
-
const cpu = machine === "arm64" ? "arm64" : machine === "x64" ? "x64" : undefined;
|
|
63
|
-
if (!cpu) throw new Error(`Unsupported local architecture: ${machine}`);
|
|
64
|
-
if (os === "darwin") return { os, cpu, node: `darwin-${cpu}` };
|
|
65
|
-
if (os === "win32") return { os, cpu, node: `win32-${cpu}-msvc` };
|
|
66
|
-
if (os === "linux") {
|
|
67
|
-
const libc = rustHost().includes("musl") ? undefined : "glibc";
|
|
68
|
-
return { os, cpu, node: `linux-${cpu}-${libc ? "gnu" : "musl"}`, ...(libc ? { libc } : {}) };
|
|
69
|
-
}
|
|
70
|
-
throw new Error(`Unsupported local platform: ${os}`);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
function rustConfig(): RustWorkspaceMapping | undefined {
|
|
74
|
-
const value = readDbxToolsConfig(repoRoot)?.rust;
|
|
75
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) return undefined;
|
|
76
|
-
const config = value as Partial<RustWorkspaceMapping>;
|
|
77
|
-
return Array.isArray(config.bindings) ? (config as RustWorkspaceMapping) : undefined;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
function artifacts(directory: string, suffix: string): string[] {
|
|
81
|
-
if (!existsSync(directory)) return [];
|
|
82
|
-
return readdirSync(directory)
|
|
83
|
-
.filter((name) => name.endsWith(suffix))
|
|
84
|
-
.map((name) => join(directory, name));
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
function cargoVersionExists(crateName: string, version: string, registry: string): boolean {
|
|
88
|
-
return taskCommandSucceeds(repoRoot, "cargo", [
|
|
89
|
-
"info",
|
|
90
|
-
`${crateName}@${version}`,
|
|
91
|
-
"--registry",
|
|
92
|
-
registry,
|
|
93
|
-
]);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
interface CargoMetadataPackage {
|
|
97
|
-
dependencies: Array<{ name: string; source?: string | null }>;
|
|
98
|
-
manifest_path: string;
|
|
99
|
-
name: string;
|
|
100
|
-
version: string;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
function orderedCargoManifests(root: string, config: RustWorkspaceMapping): CargoMetadataPackage[] {
|
|
104
|
-
const configured = new Set(config.crates.map((crate) => resolve(root, crate, "Cargo.toml")));
|
|
105
|
-
const metadata = json.parseRecord(
|
|
106
|
-
captureTaskCommand(
|
|
107
|
-
root,
|
|
108
|
-
"cargo",
|
|
109
|
-
["metadata", "--format-version", "1", "--no-deps", "--locked"],
|
|
110
|
-
{ check: true },
|
|
111
|
-
),
|
|
112
|
-
);
|
|
113
|
-
if (!metadata || !Array.isArray(metadata.packages)) {
|
|
114
|
-
throw new Error("Cargo metadata did not return workspace packages");
|
|
115
|
-
}
|
|
116
|
-
const packages = metadata.packages.filter(object.isRecord).map((pkg) => {
|
|
117
|
-
if (
|
|
118
|
-
typeof pkg.name !== "string" ||
|
|
119
|
-
typeof pkg.version !== "string" ||
|
|
120
|
-
typeof pkg.manifest_path !== "string" ||
|
|
121
|
-
!Array.isArray(pkg.dependencies)
|
|
122
|
-
) {
|
|
123
|
-
throw new Error("Cargo metadata returned an invalid package");
|
|
124
|
-
}
|
|
125
|
-
return {
|
|
126
|
-
name: pkg.name,
|
|
127
|
-
version: pkg.version,
|
|
128
|
-
manifest_path: resolve(pkg.manifest_path),
|
|
129
|
-
dependencies: pkg.dependencies.filter(object.isRecord).map((dependency) => {
|
|
130
|
-
if (typeof dependency.name !== "string") {
|
|
131
|
-
throw new Error(`Cargo metadata returned an invalid dependency for ${pkg.name}`);
|
|
132
|
-
}
|
|
133
|
-
return {
|
|
134
|
-
name: dependency.name,
|
|
135
|
-
source: typeof dependency.source === "string" ? dependency.source : null,
|
|
136
|
-
};
|
|
137
|
-
}),
|
|
138
|
-
} satisfies CargoMetadataPackage;
|
|
139
|
-
});
|
|
140
|
-
const publishable = new Map(
|
|
141
|
-
packages
|
|
142
|
-
.filter((pkg) => configured.has(pkg.manifest_path))
|
|
143
|
-
.filter((pkg) => {
|
|
144
|
-
const manifest = parse(readFileSync(pkg.manifest_path, "utf8")) as {
|
|
145
|
-
package?: { publish?: boolean };
|
|
146
|
-
};
|
|
147
|
-
return manifest.package?.publish !== false;
|
|
148
|
-
})
|
|
149
|
-
.map((pkg) => [pkg.name, pkg]),
|
|
150
|
-
);
|
|
151
|
-
const ordered: CargoMetadataPackage[] = [];
|
|
152
|
-
const visiting = new Set<string>();
|
|
153
|
-
const completed = new Set<string>();
|
|
154
|
-
const visit = (pkg: CargoMetadataPackage): void => {
|
|
155
|
-
if (completed.has(pkg.name)) return;
|
|
156
|
-
if (visiting.has(pkg.name)) {
|
|
157
|
-
throw new Error(`Cyclic Cargo publication dependency: ${pkg.name}`);
|
|
158
|
-
}
|
|
159
|
-
visiting.add(pkg.name);
|
|
160
|
-
for (const dependency of pkg.dependencies) {
|
|
161
|
-
if (dependency.source) continue;
|
|
162
|
-
const workspaceDependency = publishable.get(dependency.name);
|
|
163
|
-
if (workspaceDependency) visit(workspaceDependency);
|
|
164
|
-
}
|
|
165
|
-
visiting.delete(pkg.name);
|
|
166
|
-
completed.add(pkg.name);
|
|
167
|
-
ordered.push(pkg);
|
|
168
|
-
};
|
|
169
|
-
for (const pkg of publishable.values()) visit(pkg);
|
|
170
|
-
return ordered;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
function copyCargoWorkspace(config: RustWorkspaceMapping): string {
|
|
174
|
-
const root = mkdtempSync(join(tmpdir(), "dbx-tools-local-cargo-"));
|
|
175
|
-
for (const path of ["Cargo.toml", "Cargo.lock", "LICENSE", "README.md", "rust-toolchain.toml"]) {
|
|
176
|
-
const source = join(repoRoot, path);
|
|
177
|
-
if (!existsSync(source)) continue;
|
|
178
|
-
cpSync(source, join(root, path));
|
|
179
|
-
}
|
|
180
|
-
const cargoConfig = join(repoRoot, ".cargo");
|
|
181
|
-
if (existsSync(cargoConfig)) cpSync(cargoConfig, join(root, ".cargo"), { recursive: true });
|
|
182
|
-
for (const crate of config.crates) {
|
|
183
|
-
const source = resolve(repoRoot, crate);
|
|
184
|
-
const destination = resolve(root, relative(repoRoot, source));
|
|
185
|
-
mkdirSync(dirname(destination), { recursive: true });
|
|
186
|
-
cpSync(source, destination, { recursive: true });
|
|
187
|
-
}
|
|
188
|
-
return root;
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
export function projectCargoRegistry(
|
|
192
|
-
document: Record<string, unknown>,
|
|
193
|
-
crateNames: ReadonlySet<string>,
|
|
194
|
-
registry: string,
|
|
195
|
-
): void {
|
|
196
|
-
const sections: unknown[] = [
|
|
197
|
-
document.dependencies,
|
|
198
|
-
document["dev-dependencies"],
|
|
199
|
-
document["build-dependencies"],
|
|
200
|
-
];
|
|
201
|
-
const workspace = document.workspace;
|
|
202
|
-
if (workspace && typeof workspace === "object" && !Array.isArray(workspace)) {
|
|
203
|
-
sections.push((workspace as Record<string, unknown>).dependencies);
|
|
204
|
-
}
|
|
205
|
-
for (const value of sections) {
|
|
206
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) continue;
|
|
207
|
-
const section = value as Record<string, unknown>;
|
|
208
|
-
for (const crateName of crateNames) {
|
|
209
|
-
const dependency = section[crateName];
|
|
210
|
-
if (!dependency || typeof dependency !== "object" || Array.isArray(dependency)) continue;
|
|
211
|
-
const record = dependency as Record<string, unknown>;
|
|
212
|
-
if (record.workspace === true) continue;
|
|
213
|
-
record.registry = registry;
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
function publishCargo(config: RustWorkspaceMapping, registry: string, version: string): void {
|
|
219
|
-
const root = copyCargoWorkspace(config);
|
|
220
|
-
try {
|
|
221
|
-
const packages = orderedCargoManifests(root, config);
|
|
222
|
-
const crateNames = new Set(packages.map((pkg) => pkg.name));
|
|
223
|
-
for (const pkg of packages) {
|
|
224
|
-
if (pkg.version !== version) {
|
|
225
|
-
throw new Error(`${pkg.name} version ${pkg.version} does not match release ${version}`);
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
const manifests = [join(root, "Cargo.toml"), ...packages.map((pkg) => pkg.manifest_path)];
|
|
229
|
-
for (const manifest of manifests) {
|
|
230
|
-
const document = parse(readFileSync(manifest, "utf8")) as Record<string, unknown>;
|
|
231
|
-
projectCargoRegistry(document, crateNames, registry);
|
|
232
|
-
chmodSync(manifest, 0o644);
|
|
233
|
-
writeFileSync(manifest, `${stringify(document).trimEnd()}\n`);
|
|
234
|
-
}
|
|
235
|
-
for (const pkg of packages) {
|
|
236
|
-
if (cargoVersionExists(pkg.name, version, registry)) {
|
|
237
|
-
logger.info(`skip published ${pkg.name} @ ${version}`);
|
|
238
|
-
continue;
|
|
239
|
-
}
|
|
240
|
-
runTaskCommand(root, "cargo", [
|
|
241
|
-
"publish",
|
|
242
|
-
"--manifest-path",
|
|
243
|
-
pkg.manifest_path,
|
|
244
|
-
"--registry",
|
|
245
|
-
registry,
|
|
246
|
-
"--locked",
|
|
247
|
-
]);
|
|
248
|
-
}
|
|
249
|
-
} finally {
|
|
250
|
-
rmSync(root, { recursive: true, force: true });
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
function buildAndPublish(
|
|
255
|
-
binding: RustBindingMapping,
|
|
256
|
-
version: string,
|
|
257
|
-
registry: string | undefined,
|
|
258
|
-
pypiPublishUrl: string | undefined,
|
|
259
|
-
): void {
|
|
260
|
-
const includeNode = Boolean(registry && binding.node && binding.nodePackage);
|
|
261
|
-
const includePython = Boolean(pypiPublishUrl && binding.python && binding.pythonPackage);
|
|
262
|
-
if (!includeNode && !includePython) return;
|
|
263
|
-
|
|
264
|
-
const target = nativeTarget();
|
|
265
|
-
const output = resolve(repoRoot, "dist/uniffi");
|
|
266
|
-
runTaskCommand(repoRoot, "node", [
|
|
267
|
-
resolve(dirname(fileURLToPath(import.meta.url)), "uniffi-release.mjs"),
|
|
268
|
-
"build",
|
|
269
|
-
"--crate",
|
|
270
|
-
binding.crate,
|
|
271
|
-
"--node",
|
|
272
|
-
includeNode ? binding.node! : "",
|
|
273
|
-
"--python",
|
|
274
|
-
includePython ? binding.python! : "",
|
|
275
|
-
"--node-package",
|
|
276
|
-
includeNode ? binding.nodePackage! : "",
|
|
277
|
-
"--python-package",
|
|
278
|
-
includePython ? binding.pythonPackage! : "",
|
|
279
|
-
"--python-module",
|
|
280
|
-
includePython ? binding.pythonModule! : "",
|
|
281
|
-
"--cargo-target",
|
|
282
|
-
rustHost(),
|
|
283
|
-
"--node-triple",
|
|
284
|
-
target.node,
|
|
285
|
-
"--python-tag",
|
|
286
|
-
pythonTag(),
|
|
287
|
-
"--os",
|
|
288
|
-
target.os,
|
|
289
|
-
"--cpu",
|
|
290
|
-
target.cpu,
|
|
291
|
-
"--libc",
|
|
292
|
-
target.libc ?? "",
|
|
293
|
-
"--facade",
|
|
294
|
-
"true",
|
|
295
|
-
"--version",
|
|
296
|
-
version,
|
|
297
|
-
"--output",
|
|
298
|
-
"dist/uniffi",
|
|
299
|
-
]);
|
|
300
|
-
|
|
301
|
-
if (includeNode) {
|
|
302
|
-
const publishNpmScript = resolve(dirname(fileURLToPath(import.meta.url)), "publish-npm.ts");
|
|
303
|
-
for (const directory of [join(output, "npm"), join(output, "npm-facade")]) {
|
|
304
|
-
if (artifacts(directory, ".tgz").length === 0) continue;
|
|
305
|
-
runTaskCommand(repoRoot, process.execPath, [
|
|
306
|
-
publishNpmScript,
|
|
307
|
-
"--directory",
|
|
308
|
-
directory,
|
|
309
|
-
"--version",
|
|
310
|
-
version,
|
|
311
|
-
"--registry",
|
|
312
|
-
registry!,
|
|
313
|
-
]);
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
if (includePython) {
|
|
317
|
-
const wheels = artifacts(join(output, "python"), ".whl");
|
|
318
|
-
if (wheels.length === 0) throw new Error(`No wheel produced for ${binding.crate}`);
|
|
319
|
-
runTaskCommand(repoRoot, "uvx", [
|
|
320
|
-
"--from",
|
|
321
|
-
"devpi-client",
|
|
322
|
-
"devpi",
|
|
323
|
-
"upload",
|
|
324
|
-
"--index",
|
|
325
|
-
pypiPublishUrl!,
|
|
326
|
-
"--from-dir",
|
|
327
|
-
join(output, "python"),
|
|
328
|
-
]);
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
function main(args: string[] = process.argv.slice(2)): void {
|
|
333
|
-
const parsed = parseArgs({
|
|
334
|
-
args,
|
|
335
|
-
options: {
|
|
336
|
-
version: { type: "string" },
|
|
337
|
-
registry: { type: "string" },
|
|
338
|
-
"pypi-publish-url": { type: "string" },
|
|
339
|
-
"cargo-registry": { type: "string" },
|
|
340
|
-
},
|
|
341
|
-
});
|
|
342
|
-
const version = parsed.values.version;
|
|
343
|
-
if (!version) throw new Error("Missing --version");
|
|
344
|
-
const config = rustConfig();
|
|
345
|
-
if (config?.bindings.length) {
|
|
346
|
-
if (!commandAvailable("cargo") || !commandAvailable("rustc")) {
|
|
347
|
-
throw new Error("Cargo and rustc are required because UniFFI Rust projects were detected");
|
|
348
|
-
}
|
|
349
|
-
for (const binding of config.bindings) {
|
|
350
|
-
buildAndPublish(binding, version, parsed.values.registry, parsed.values["pypi-publish-url"]);
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
|
-
const cargoRegistry = parsed.values["cargo-registry"];
|
|
354
|
-
if (cargoRegistry && config?.crates.length) {
|
|
355
|
-
publishCargo(config, cargoRegistry, version);
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
if (import.meta.main) main();
|