@dbx-tools/projen 0.6.160 → 0.6.163
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 +17 -7
- package/index.ts +3 -0
- package/package.json +4 -4
- package/src/bun-workflow.ts +145 -0
- package/src/project-js.ts +21 -0
- package/src/project-py.ts +5 -1
- package/src/project-rs.ts +50 -20
- package/src/release.ts +41 -13
- package/tasks/uniffi-release.mjs +2 -0
- package/tasks/uniffi.ts +10 -6
package/README.md
CHANGED
|
@@ -115,18 +115,28 @@ Cargo/sccache once,
|
|
|
115
115
|
builds the Rust workspace once, then packages every discovered output from that
|
|
116
116
|
shared build. Bun and the workspace install are present only when a Node binding
|
|
117
117
|
needs TypeScript generation; uv is present only when a Python wheel is needed.
|
|
118
|
+
A new dispatch cancels queued and in-progress release and docs workflows before
|
|
119
|
+
starting, and each workflow's concurrency group also cancels its previous run.
|
|
118
120
|
A binary-only workspace therefore installs neither. `rustVersion` remains the
|
|
119
121
|
MSRV recorded in package manifests, while `releaseRustVersion` independently
|
|
120
122
|
defaults release compilation to `stable`. UBRN uses that same release toolchain
|
|
121
123
|
unless `ubrnRustVersion` explicitly selects another one.
|
|
122
124
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
125
|
+
Generated build and release workflows share the Bun cache helpers from
|
|
126
|
+
`bun-workflow.ts`. One `BUN_VERSION` value drives setup and cache keys, while a
|
|
127
|
+
generated dependency-only fingerprint keeps release version bumps from
|
|
128
|
+
invalidating Bun's global package cache. `node_modules` remains uncached.
|
|
129
|
+
|
|
130
|
+
The final host UBRN executable is cached by pinned UBRN version, Rust toolchain,
|
|
131
|
+
runner OS, and runner architecture. A hit skips both the workspace `bun install`
|
|
132
|
+
and the UBRN Cargo build. Release packaging passes the cached executable directly
|
|
133
|
+
to the Node binding generator and keeps the package barrel copied from source,
|
|
134
|
+
so it does not need the installed projen dependency graph. A miss saves the
|
|
135
|
+
validated executable immediately, before workspace build and packaging can fail.
|
|
136
|
+
Bun runtime setup still runs because the binding generator is TypeScript. Cargo registry caches
|
|
137
|
+
and the `SCCACHE_GHA_VERSION` namespace stay stable per target/toolchain across
|
|
138
|
+
version tags. Cache keys, restore results, sccache statistics, and phase timings
|
|
139
|
+
are written to each build log. Python generation executes the already-built
|
|
130
140
|
`target/<triple>/release/uniffi-bindgen` directly. Artifact packaging therefore
|
|
131
141
|
does no Rust compilation after the main workspace build.
|
|
132
142
|
|
package/index.ts
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
export const PACKAGE_IDENTIFIER = "@dbx-tools/projen";
|
|
6
6
|
export * as barrels from "./src/barrels.ts";
|
|
7
7
|
export * as bunApp from "./src/bun-app.ts";
|
|
8
|
+
export * as bunWorkflow from "./src/bun-workflow.ts";
|
|
8
9
|
export * as clean from "./src/clean.ts";
|
|
9
10
|
export * as codegen from "./src/codegen.ts";
|
|
10
11
|
export * as dbxToolsConfig from "./src/dbx-tools-config.ts";
|
|
@@ -31,6 +32,8 @@ export * as vscode from "./src/vscode.ts";
|
|
|
31
32
|
export * as watch from "./src/watch.ts";
|
|
32
33
|
export * as workspaceVersion from "./src/workspace-version.ts";
|
|
33
34
|
export { BUN_DEV_OVERRIDE, BUN_BUILD_OVERRIDE, BUN_APP_OVERRIDES, RootBunfigFile, BunfigFile, BunDevServerFile, BunBuildFile } from "./src/bun-app.ts";
|
|
35
|
+
export { BUN_VERSION } from "./src/bun-workflow.ts";
|
|
36
|
+
export type { BunWorkflowCacheOptions } from "./src/bun-workflow.ts";
|
|
34
37
|
export { DBXToolsConfig } from "./src/dbx-tools-config.ts";
|
|
35
38
|
export type { DBXToolsConfigOptions } from "./src/dbx-tools-config.ts";
|
|
36
39
|
export { resolvePkgRoot } from "./src/engine-root.ts";
|
package/package.json
CHANGED
|
@@ -26,9 +26,9 @@
|
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
28
|
"@clack/prompts": "^1.7.0",
|
|
29
|
-
"@dbx-tools/core": "0.6.
|
|
30
|
-
"@dbx-tools/path": "0.6.
|
|
31
|
-
"@dbx-tools/shared-core": "0.6.
|
|
29
|
+
"@dbx-tools/core": "0.6.163",
|
|
30
|
+
"@dbx-tools/path": "0.6.163",
|
|
31
|
+
"@dbx-tools/shared-core": "0.6.163",
|
|
32
32
|
"commander": "^15.0.0",
|
|
33
33
|
"concurrently": "^10.0.3",
|
|
34
34
|
"constructs": "^10.6.0",
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
},
|
|
49
49
|
"main": "index.ts",
|
|
50
50
|
"license": "Apache-2.0",
|
|
51
|
-
"version": "0.6.
|
|
51
|
+
"version": "0.6.163",
|
|
52
52
|
"types": "index.ts",
|
|
53
53
|
"type": "module",
|
|
54
54
|
"exports": {
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/** Shared Bun setup and package-cache steps for generated workflows. */
|
|
2
|
+
import { TextFile, javascript } from "projen";
|
|
3
|
+
|
|
4
|
+
export const BUN_VERSION = "1.3.14";
|
|
5
|
+
|
|
6
|
+
const cacheKeyScript = `#!/usr/bin/env node
|
|
7
|
+
import { createHash } from "node:crypto";
|
|
8
|
+
import { readdirSync, readFileSync } from "node:fs";
|
|
9
|
+
import { join } from "node:path";
|
|
10
|
+
|
|
11
|
+
const root = process.cwd();
|
|
12
|
+
const ignored = new Set([
|
|
13
|
+
".git",
|
|
14
|
+
".docs-build",
|
|
15
|
+
".venv",
|
|
16
|
+
".worktrees",
|
|
17
|
+
"coverage",
|
|
18
|
+
"dist",
|
|
19
|
+
"lib",
|
|
20
|
+
"node_modules",
|
|
21
|
+
"target",
|
|
22
|
+
]);
|
|
23
|
+
const manifests = [];
|
|
24
|
+
const walk = (directory) => {
|
|
25
|
+
for (const entry of readdirSync(directory, { withFileTypes: true })) {
|
|
26
|
+
if (ignored.has(entry.name)) continue;
|
|
27
|
+
const path = join(directory, entry.name);
|
|
28
|
+
if (entry.isDirectory()) walk(path);
|
|
29
|
+
else if (entry.name === "package.json") manifests.push(path);
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
walk(root);
|
|
33
|
+
|
|
34
|
+
const dependencyFields = [
|
|
35
|
+
"catalog",
|
|
36
|
+
"dependencies",
|
|
37
|
+
"devDependencies",
|
|
38
|
+
"optionalDependencies",
|
|
39
|
+
"overrides",
|
|
40
|
+
"peerDependencies",
|
|
41
|
+
"peerDependenciesMeta",
|
|
42
|
+
"resolutions",
|
|
43
|
+
"trustedDependencies",
|
|
44
|
+
];
|
|
45
|
+
const canonical = (value) => {
|
|
46
|
+
if (Array.isArray(value)) return value.map(canonical);
|
|
47
|
+
if (!value || typeof value !== "object") return value;
|
|
48
|
+
return Object.fromEntries(
|
|
49
|
+
Object.entries(value)
|
|
50
|
+
.sort(([left], [right]) => left.localeCompare(right))
|
|
51
|
+
.map(([key, child]) => [key, canonical(child)]),
|
|
52
|
+
);
|
|
53
|
+
};
|
|
54
|
+
const dependencies = manifests
|
|
55
|
+
.sort()
|
|
56
|
+
.map((path) => {
|
|
57
|
+
const manifest = JSON.parse(readFileSync(path, "utf8"));
|
|
58
|
+
return [
|
|
59
|
+
path.slice(root.length + 1),
|
|
60
|
+
Object.fromEntries(
|
|
61
|
+
dependencyFields
|
|
62
|
+
.filter((field) => manifest[field] !== undefined)
|
|
63
|
+
.map((field) => [field, canonical(manifest[field])]),
|
|
64
|
+
),
|
|
65
|
+
];
|
|
66
|
+
});
|
|
67
|
+
process.stdout.write(createHash("sha256").update(JSON.stringify(dependencies)).digest("hex"));
|
|
68
|
+
`;
|
|
69
|
+
|
|
70
|
+
const configured = new WeakSet<javascript.NodeProject>();
|
|
71
|
+
|
|
72
|
+
function ensureCacheKeyScript(project: javascript.NodeProject): void {
|
|
73
|
+
if (configured.has(project)) return;
|
|
74
|
+
new TextFile(project.root, ".projen/bun-cache-key.mjs", {
|
|
75
|
+
lines: cacheKeyScript.trimEnd().split("\n"),
|
|
76
|
+
});
|
|
77
|
+
configured.add(project);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function stepCondition(condition: string | undefined, cacheMiss = false): string | undefined {
|
|
81
|
+
const expressions = [
|
|
82
|
+
condition,
|
|
83
|
+
...(cacheMiss ? ["steps.bun_cache.outputs.cache-hit != 'true'"] : []),
|
|
84
|
+
].filter(Boolean);
|
|
85
|
+
return expressions.length ? `\${{ ${expressions.join(" && ")} }}` : undefined;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export interface BunWorkflowCacheOptions {
|
|
89
|
+
readonly setupCondition?: string;
|
|
90
|
+
readonly condition?: string;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** Set up Bun and restore its global package cache. */
|
|
94
|
+
export function bunCacheRestoreSteps(
|
|
95
|
+
project: javascript.NodeProject,
|
|
96
|
+
options: BunWorkflowCacheOptions = {},
|
|
97
|
+
): readonly Record<string, unknown>[] {
|
|
98
|
+
ensureCacheKeyScript(project);
|
|
99
|
+
const condition = stepCondition(options.condition);
|
|
100
|
+
const setupCondition = stepCondition(options.setupCondition);
|
|
101
|
+
return [
|
|
102
|
+
{
|
|
103
|
+
name: "Setup Bun",
|
|
104
|
+
...(setupCondition ? { if: setupCondition } : {}),
|
|
105
|
+
uses: "oven-sh/setup-bun@v2",
|
|
106
|
+
with: { "bun-version": "${{ env.BUN_VERSION }}" },
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
name: "Resolve Bun cache",
|
|
110
|
+
id: "bun_cache_metadata",
|
|
111
|
+
...(condition ? { if: condition } : {}),
|
|
112
|
+
shell: "bash",
|
|
113
|
+
run: [
|
|
114
|
+
'echo "path=$(bun pm cache)" >> "$GITHUB_OUTPUT"',
|
|
115
|
+
'echo "dependency_hash=$(node .projen/bun-cache-key.mjs)" >> "$GITHUB_OUTPUT"',
|
|
116
|
+
].join("\n"),
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
name: "Restore Bun cache",
|
|
120
|
+
id: "bun_cache",
|
|
121
|
+
...(condition ? { if: condition } : {}),
|
|
122
|
+
uses: "actions/cache/restore@v5",
|
|
123
|
+
with: {
|
|
124
|
+
path: "${{ steps.bun_cache_metadata.outputs.path }}",
|
|
125
|
+
key: `bun-\${{ runner.os }}-\${{ runner.arch }}-\${{ env.BUN_VERSION }}-\${{ steps.bun_cache_metadata.outputs.dependency_hash }}`,
|
|
126
|
+
"restore-keys": `bun-\${{ runner.os }}-\${{ runner.arch }}-\${{ env.BUN_VERSION }}-`,
|
|
127
|
+
},
|
|
128
|
+
},
|
|
129
|
+
];
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/** Save Bun's global package cache immediately after installation. */
|
|
133
|
+
export function bunCacheSaveStep(
|
|
134
|
+
options: BunWorkflowCacheOptions = {},
|
|
135
|
+
): Readonly<Record<string, unknown>> {
|
|
136
|
+
return {
|
|
137
|
+
name: "Save Bun cache",
|
|
138
|
+
if: stepCondition(options.condition, true),
|
|
139
|
+
uses: "actions/cache/save@v5",
|
|
140
|
+
with: {
|
|
141
|
+
path: "${{ steps.bun_cache_metadata.outputs.path }}",
|
|
142
|
+
key: "${{ steps.bun_cache.outputs.cache-primary-key }}",
|
|
143
|
+
},
|
|
144
|
+
};
|
|
145
|
+
}
|
package/src/project-js.ts
CHANGED
|
@@ -13,6 +13,7 @@ import { ignore, match } from "@dbx-tools/path";
|
|
|
13
13
|
import { object, string, type OneOrMany } from "@dbx-tools/shared-core";
|
|
14
14
|
import { type IConstruct } from "constructs";
|
|
15
15
|
import { Component, IgnoreFile, Project, type TaskOptions, javascript, typescript } from "projen";
|
|
16
|
+
import type { JobStep } from "projen/lib/github/workflows-model";
|
|
16
17
|
import { ReleaseTrigger } from "projen/lib/release";
|
|
17
18
|
import { mixin } from "..";
|
|
18
19
|
import { generateBarrels } from "./barrels.ts";
|
|
@@ -23,6 +24,7 @@ import {
|
|
|
23
24
|
BunfigFile,
|
|
24
25
|
RootBunfigFile,
|
|
25
26
|
} from "./bun-app.ts";
|
|
27
|
+
import { BUN_VERSION, bunCacheRestoreSteps, bunCacheSaveStep } from "./bun-workflow.ts";
|
|
26
28
|
import { codegenModulePaths, generateCodegen } from "./codegen.ts";
|
|
27
29
|
import { DBXToolsConfig, type DBXToolsConfigOptions } from "./dbx-tools-config.ts";
|
|
28
30
|
import { resolvePkgRoot } from "./engine-root.ts";
|
|
@@ -642,6 +644,24 @@ export class DBXToolsNodeProject
|
|
|
642
644
|
initProject(this, options);
|
|
643
645
|
}
|
|
644
646
|
|
|
647
|
+
public override renderWorkflowSetup(options?: javascript.RenderWorkflowSetupOptions): JobStep[] {
|
|
648
|
+
const steps = super.renderWorkflowSetup(options);
|
|
649
|
+
if (this.parent) return steps;
|
|
650
|
+
return steps.flatMap((step) => {
|
|
651
|
+
if (step.uses === "oven-sh/setup-bun@v2") return [...bunCacheRestoreSteps(this)];
|
|
652
|
+
if (step.run === "bun install") {
|
|
653
|
+
return [
|
|
654
|
+
step,
|
|
655
|
+
bunCacheSaveStep({
|
|
656
|
+
condition:
|
|
657
|
+
"github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository",
|
|
658
|
+
}),
|
|
659
|
+
];
|
|
660
|
+
}
|
|
661
|
+
return [step];
|
|
662
|
+
}) as JobStep[];
|
|
663
|
+
}
|
|
664
|
+
|
|
645
665
|
public override preSynthesize(): void {
|
|
646
666
|
if (this.rootInstallOnly) this.with(ROOT_INSTALL_ONLY_MIXIN);
|
|
647
667
|
super.preSynthesize();
|
|
@@ -1066,6 +1086,7 @@ function initProject(
|
|
|
1066
1086
|
// a plain `bun` exec rather than any wrapper: the default task is spawned by
|
|
1067
1087
|
// nested installs/synths, and a wrapper that exported `npm_config_*` broke them.
|
|
1068
1088
|
project.defaultTask?.reset("bun .projenrc.ts");
|
|
1089
|
+
project.buildWorkflow?.workflow.file?.addOverride("jobs.build.env.BUN_VERSION", BUN_VERSION);
|
|
1069
1090
|
|
|
1070
1091
|
// Pin bun's hoisted linker workspace-wide (see RootBunfigFile) so a peer dep
|
|
1071
1092
|
// resolves to one copy and singletons/types stay coherent.
|
package/src/project-py.ts
CHANGED
|
@@ -429,7 +429,11 @@ export class DBXToolsPythonWorkspace extends Component {
|
|
|
429
429
|
if (!project.github) return;
|
|
430
430
|
const publications = this.publications(options);
|
|
431
431
|
if (publications.length === 0) return;
|
|
432
|
-
const
|
|
432
|
+
const workflowName = options.workflowName ?? "python-release";
|
|
433
|
+
const workflow = new GithubWorkflow(project.github, workflowName, {
|
|
434
|
+
limitConcurrency: true,
|
|
435
|
+
concurrencyOptions: { group: workflowName, cancelInProgress: true },
|
|
436
|
+
});
|
|
433
437
|
workflow.file?.addOverride("permissions", {
|
|
434
438
|
contents: "read",
|
|
435
439
|
...(options.upstreamWorkflow ? { actions: "read" } : {}),
|
package/src/project-rs.ts
CHANGED
|
@@ -5,6 +5,7 @@ import { fileURLToPath } from "node:url";
|
|
|
5
5
|
import { project as coreProject } from "@dbx-tools/core";
|
|
6
6
|
import { string } from "@dbx-tools/shared-core";
|
|
7
7
|
import { Project, TextFile, YamlFile, javascript } from "projen";
|
|
8
|
+
import { BUN_VERSION, bunCacheRestoreSteps, bunCacheSaveStep } from "./bun-workflow.ts";
|
|
8
9
|
import type { DBXToolsProject } from "./project.ts";
|
|
9
10
|
import {
|
|
10
11
|
DBXToolsTypeScriptProject,
|
|
@@ -641,6 +642,10 @@ export class DBXToolsRustWorkspace {
|
|
|
641
642
|
ubrnRustVersion === releaseRustVersion
|
|
642
643
|
? ""
|
|
643
644
|
: `rustup toolchain install ${ubrnRustVersion} --profile minimal\n`;
|
|
645
|
+
const ubrnExecutable =
|
|
646
|
+
"${{ github.workspace }}/.cache/ubrn/uniffi-bindgen-react-native${{ matrix.target.os == 'win32' && '.exe' || '' }}";
|
|
647
|
+
const builtUbrnExecutable =
|
|
648
|
+
"target/ubrn/debug/uniffi-bindgen-react-native${{ matrix.target.os == 'win32' && '.exe' || '' }}";
|
|
644
649
|
const releaseBranch = projectReleaseBranch(project);
|
|
645
650
|
const bindings = this.bindingMappings.map((binding) => ({
|
|
646
651
|
...binding,
|
|
@@ -685,7 +690,7 @@ export class DBXToolsRustWorkspace {
|
|
|
685
690
|
`--node-package "${binding.nodePackage}"`,
|
|
686
691
|
`--python-package "${binding.pythonPackage}"`,
|
|
687
692
|
...(binding.node
|
|
688
|
-
? ['--node-generator "
|
|
693
|
+
? ['--node-generator "projen/tasks/uniffi.ts"', '--ubrn "$UBRN_EXECUTABLE"']
|
|
689
694
|
: []),
|
|
690
695
|
'--cargo-target "${{ matrix.target.cargo }}"',
|
|
691
696
|
'--node-triple "${{ matrix.target.node }}"',
|
|
@@ -762,6 +767,7 @@ export class DBXToolsRustWorkspace {
|
|
|
762
767
|
"runs-on": "${{ matrix.target.runner }}",
|
|
763
768
|
env: {
|
|
764
769
|
...RUST_CACHE_ENV,
|
|
770
|
+
BUN_VERSION,
|
|
765
771
|
SCCACHE_GHA_VERSION: `release-\${{ matrix.target.cargo }}-rust-${releaseRustVersion}`,
|
|
766
772
|
},
|
|
767
773
|
strategy: {
|
|
@@ -770,15 +776,6 @@ export class DBXToolsRustWorkspace {
|
|
|
770
776
|
},
|
|
771
777
|
steps: [
|
|
772
778
|
...releaseSourceSteps(),
|
|
773
|
-
...(hasNodeBindings
|
|
774
|
-
? [
|
|
775
|
-
{
|
|
776
|
-
name: "Setup Bun",
|
|
777
|
-
uses: "oven-sh/setup-bun@v2",
|
|
778
|
-
with: { "bun-version": "1.3.14" },
|
|
779
|
-
},
|
|
780
|
-
]
|
|
781
|
-
: []),
|
|
782
779
|
...(hasPythonBindings ? [{ name: "Setup uv", uses: "astral-sh/setup-uv@v7" }] : []),
|
|
783
780
|
{
|
|
784
781
|
name: "Setup Rust",
|
|
@@ -789,17 +786,21 @@ export class DBXToolsRustWorkspace {
|
|
|
789
786
|
...(hasNodeBindings
|
|
790
787
|
? [
|
|
791
788
|
{
|
|
792
|
-
name: "
|
|
789
|
+
name: "Restore UBRN executable",
|
|
793
790
|
id: "ubrn_cache",
|
|
794
|
-
|
|
795
|
-
uses: "actions/cache@v5",
|
|
791
|
+
uses: "actions/cache/restore@v5",
|
|
796
792
|
with: {
|
|
797
|
-
path: "
|
|
798
|
-
key: `ubrn-\${{ runner.os }}-\${{ runner.arch }}-rust-${ubrnRustVersion}-${UBRN_VERSION}`,
|
|
793
|
+
path: ".cache/ubrn",
|
|
794
|
+
key: `ubrn-executable-\${{ runner.os }}-\${{ runner.arch }}-rust-${ubrnRustVersion}-${UBRN_VERSION}`,
|
|
799
795
|
},
|
|
800
796
|
},
|
|
801
797
|
]
|
|
802
798
|
: []),
|
|
799
|
+
...(hasNodeBindings
|
|
800
|
+
? bunCacheRestoreSteps(project, {
|
|
801
|
+
condition: "steps.ubrn_cache.outputs.cache-hit != 'true'",
|
|
802
|
+
})
|
|
803
|
+
: []),
|
|
803
804
|
{
|
|
804
805
|
name: "Log cache configuration",
|
|
805
806
|
shell: "bash",
|
|
@@ -811,9 +812,8 @@ export class DBXToolsRustWorkspace {
|
|
|
811
812
|
`echo "ubrn_rust_toolchain=${ubrnRustVersion}"`,
|
|
812
813
|
...(hasNodeBindings
|
|
813
814
|
? [
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
'echo "ubrn_target_cache_hit=${{ steps.ubrn_cache.outputs.cache-hit }}"',
|
|
815
|
+
`echo "ubrn_executable_cache_key=ubrn-executable-\${{ runner.os }}-\${{ runner.arch }}-rust-${ubrnRustVersion}-${UBRN_VERSION}"`,
|
|
816
|
+
'echo "ubrn_executable_cache_hit=${{ steps.ubrn_cache.outputs.cache-hit }}"',
|
|
817
817
|
]
|
|
818
818
|
: []),
|
|
819
819
|
].join("\n"),
|
|
@@ -827,24 +827,50 @@ export class DBXToolsRustWorkspace {
|
|
|
827
827
|
? [
|
|
828
828
|
{
|
|
829
829
|
name: "Install Node tooling",
|
|
830
|
+
if: "${{ steps.ubrn_cache.outputs.cache-hit != 'true' }}",
|
|
830
831
|
shell: "bash",
|
|
831
832
|
run: timedBash("node_tooling", "bun install"),
|
|
832
833
|
},
|
|
834
|
+
bunCacheSaveStep({
|
|
835
|
+
condition: "steps.ubrn_cache.outputs.cache-hit != 'true'",
|
|
836
|
+
}),
|
|
833
837
|
]
|
|
834
838
|
: []),
|
|
835
839
|
...(hasNodeBindings
|
|
836
840
|
? [
|
|
837
841
|
{
|
|
838
842
|
name: "Prepare UBRN generator",
|
|
843
|
+
if: "${{ steps.ubrn_cache.outputs.cache-hit != 'true' }}",
|
|
839
844
|
env: {
|
|
840
845
|
CARGO_TARGET_DIR: "${{ github.workspace }}/target/ubrn",
|
|
846
|
+
UBRN_EXECUTABLE: ubrnExecutable,
|
|
841
847
|
},
|
|
842
848
|
shell: "bash",
|
|
843
849
|
run: timedBash(
|
|
844
850
|
"ubrn_generator",
|
|
845
|
-
|
|
851
|
+
[
|
|
852
|
+
`${ubrnToolchainInstall}cargo +${ubrnRustVersion} build --manifest-path node_modules/uniffi-bindgen-react-native/crates/ubrn_cli/Cargo.toml`,
|
|
853
|
+
'mkdir -p "$(dirname "$UBRN_EXECUTABLE")"',
|
|
854
|
+
`cp "${builtUbrnExecutable}" "$UBRN_EXECUTABLE"`,
|
|
855
|
+
'chmod +x "$UBRN_EXECUTABLE"',
|
|
856
|
+
].join("\n"),
|
|
846
857
|
),
|
|
847
858
|
},
|
|
859
|
+
{
|
|
860
|
+
name: "Verify UBRN executable",
|
|
861
|
+
env: { UBRN_EXECUTABLE: ubrnExecutable },
|
|
862
|
+
shell: "bash",
|
|
863
|
+
run: 'test -f "$UBRN_EXECUTABLE"',
|
|
864
|
+
},
|
|
865
|
+
{
|
|
866
|
+
name: "Save UBRN executable",
|
|
867
|
+
if: "${{ steps.ubrn_cache.outputs.cache-hit != 'true' }}",
|
|
868
|
+
uses: "actions/cache/save@v5",
|
|
869
|
+
with: {
|
|
870
|
+
path: ".cache/ubrn",
|
|
871
|
+
key: "${{ steps.ubrn_cache.outputs.cache-primary-key }}",
|
|
872
|
+
},
|
|
873
|
+
},
|
|
848
874
|
]
|
|
849
875
|
: []),
|
|
850
876
|
{
|
|
@@ -864,7 +890,7 @@ export class DBXToolsRustWorkspace {
|
|
|
864
890
|
VERSION: RELEASE_TAG,
|
|
865
891
|
...(hasNodeBindings
|
|
866
892
|
? {
|
|
867
|
-
|
|
893
|
+
UBRN_EXECUTABLE: ubrnExecutable,
|
|
868
894
|
}
|
|
869
895
|
: {}),
|
|
870
896
|
},
|
|
@@ -990,6 +1016,10 @@ export class DBXToolsRustWorkspace {
|
|
|
990
1016
|
},
|
|
991
1017
|
},
|
|
992
1018
|
},
|
|
1019
|
+
concurrency: {
|
|
1020
|
+
group: workflowName,
|
|
1021
|
+
"cancel-in-progress": true,
|
|
1022
|
+
},
|
|
993
1023
|
permissions: { contents: "read" },
|
|
994
1024
|
jobs: {
|
|
995
1025
|
"verify-context": {
|
package/src/release.ts
CHANGED
|
@@ -4,12 +4,13 @@
|
|
|
4
4
|
* the project has a GitHub component - authors the tag-driven npm publish
|
|
5
5
|
* workflow that the pushed tag triggers.
|
|
6
6
|
*/
|
|
7
|
-
import { rmSync } from "node:fs";
|
|
7
|
+
import { existsSync, rmSync } from "node:fs";
|
|
8
8
|
import { resolve } from "node:path";
|
|
9
9
|
import { Component, YamlFile } from "projen";
|
|
10
10
|
import { GithubWorkflow } from "projen/lib/github";
|
|
11
11
|
import { JobPermission, type JobStep } from "projen/lib/github/workflows-model";
|
|
12
12
|
import { object } from "@dbx-tools/shared-core";
|
|
13
|
+
import { BUN_VERSION, bunCacheRestoreSteps, bunCacheSaveStep } from "./bun-workflow.ts";
|
|
13
14
|
import { applyTasks, taskScript, type DBXToolsNodeProject } from "./project.ts";
|
|
14
15
|
import {
|
|
15
16
|
DOWNSTREAM_RELEASE_EVENT,
|
|
@@ -21,7 +22,6 @@ import {
|
|
|
21
22
|
|
|
22
23
|
const NODE_VERSION = "lts/*";
|
|
23
24
|
const NPM_REGISTRY_URL = "https://registry.npmjs.org";
|
|
24
|
-
const BUN_VERSION = "1.3.14";
|
|
25
25
|
|
|
26
26
|
/**
|
|
27
27
|
* The `release` workflow's version-stamp + publish step, as a shell script.
|
|
@@ -73,10 +73,10 @@ interface PublishWorkflow {
|
|
|
73
73
|
}
|
|
74
74
|
|
|
75
75
|
/** Shared checkout and toolchain setup for every npm publish workflow. */
|
|
76
|
-
function publishSetupSteps(): JobStep[] {
|
|
76
|
+
function publishSetupSteps(project: DBXToolsNodeProject): JobStep[] {
|
|
77
77
|
return [
|
|
78
78
|
{ name: "Checkout", uses: "actions/checkout@v6", with: { "fetch-depth": 0 } },
|
|
79
|
-
|
|
79
|
+
...bunCacheRestoreSteps(project),
|
|
80
80
|
{
|
|
81
81
|
name: "Setup Node.js",
|
|
82
82
|
uses: "actions/setup-node@v6",
|
|
@@ -88,6 +88,7 @@ function publishSetupSteps(): JobStep[] {
|
|
|
88
88
|
},
|
|
89
89
|
// Bun's install; the lockfile may be absent or stale in CI so it is not frozen.
|
|
90
90
|
{ name: "Install", run: "bun install" },
|
|
91
|
+
bunCacheSaveStep(),
|
|
91
92
|
];
|
|
92
93
|
}
|
|
93
94
|
|
|
@@ -196,9 +197,23 @@ export class DBXToolsRelease extends Component {
|
|
|
196
197
|
object.isRecord(rust) && typeof rust.releaseWorkflow === "string"
|
|
197
198
|
? RUST_RELEASE_EVENT
|
|
198
199
|
: DOWNSTREAM_RELEASE_EVENT;
|
|
199
|
-
project
|
|
200
|
-
|
|
201
|
-
|
|
200
|
+
const dispatcher = project.tryFindObjectFile(".github/workflows/release-dispatch.yml");
|
|
201
|
+
dispatcher?.addOverride("jobs.dispatch.steps.1.env.RELEASE_EVENT", releaseEvent);
|
|
202
|
+
const releaseWorkflows = [
|
|
203
|
+
...(object.isRecord(rust) && typeof rust.releaseWorkflow === "string"
|
|
204
|
+
? [rust.releaseWorkflow]
|
|
205
|
+
: []),
|
|
206
|
+
...(typeof project.dbxToolsConfig.pythonReleaseWorkflow === "string"
|
|
207
|
+
? [project.dbxToolsConfig.pythonReleaseWorkflow]
|
|
208
|
+
: []),
|
|
209
|
+
...(this.workflowName ? [this.workflowName] : []),
|
|
210
|
+
...this.standaloneReleases.map(({ name }) => name),
|
|
211
|
+
...(existsSync(resolve(project.outdir, ".github/workflows/docs.yml")) ? ["docs"] : []),
|
|
212
|
+
];
|
|
213
|
+
dispatcher?.addOverride(
|
|
214
|
+
"jobs.dispatch.steps.1.env.RELEASE_WORKFLOWS",
|
|
215
|
+
[...new Set(releaseWorkflows)].join(","),
|
|
216
|
+
);
|
|
202
217
|
// Release the standalone projects in the SAME run, at the same version. They
|
|
203
218
|
// are not workspace members, so nothing else would ever bring them along.
|
|
204
219
|
const siblingArgs = this.standaloneReleases
|
|
@@ -233,7 +248,11 @@ export class DBXToolsRelease extends Component {
|
|
|
233
248
|
obj: {
|
|
234
249
|
name: "release-dispatch",
|
|
235
250
|
on: { push: { tags: [`${this.tagPrefix}*`] } },
|
|
236
|
-
|
|
251
|
+
concurrency: {
|
|
252
|
+
group: "release-dispatch",
|
|
253
|
+
"cancel-in-progress": true,
|
|
254
|
+
},
|
|
255
|
+
permissions: { actions: "write", contents: "write" },
|
|
237
256
|
jobs: {
|
|
238
257
|
dispatch: {
|
|
239
258
|
"runs-on": "ubuntu-latest",
|
|
@@ -250,10 +269,19 @@ export class DBXToolsRelease extends Component {
|
|
|
250
269
|
GH_TOKEN: "${{ github.token }}",
|
|
251
270
|
RELEASE_TAG: "${{ github.ref_name }}",
|
|
252
271
|
RELEASE_EVENT: DOWNSTREAM_RELEASE_EVENT,
|
|
272
|
+
RELEASE_WORKFLOWS: "",
|
|
253
273
|
},
|
|
254
274
|
run: [
|
|
255
275
|
`case "$RELEASE_TAG" in ${this.tagPrefix}*) ;; *) exit 1 ;; esac`,
|
|
256
276
|
'EXPECTED_SHA="$(git rev-parse "$RELEASE_TAG^{commit}")"',
|
|
277
|
+
'IFS="," read -r -a workflows <<< "$RELEASE_WORKFLOWS"',
|
|
278
|
+
'for workflow in "${workflows[@]}"; do',
|
|
279
|
+
" for status in in_progress queued requested waiting pending action_required; do",
|
|
280
|
+
" while IFS= read -r run_id; do",
|
|
281
|
+
' if [ -n "$run_id" ]; then gh run cancel "$run_id"; fi',
|
|
282
|
+
' done < <(gh run list --workflow "$workflow.yml" --status "$status" --limit 100 --json databaseId --jq \'.[].databaseId\')',
|
|
283
|
+
" done",
|
|
284
|
+
"done",
|
|
257
285
|
[
|
|
258
286
|
'gh api --method POST "repos/$GITHUB_REPOSITORY/dispatches"',
|
|
259
287
|
'--raw-field event_type="$RELEASE_EVENT"',
|
|
@@ -274,14 +302,13 @@ export class DBXToolsRelease extends Component {
|
|
|
274
302
|
project: DBXToolsNodeProject,
|
|
275
303
|
{ name, tagPrefix, steps, workingDirectory, upstreamWorkflow }: PublishWorkflow,
|
|
276
304
|
): void {
|
|
277
|
-
const setupSteps = publishSetupSteps();
|
|
305
|
+
const setupSteps = publishSetupSteps(project);
|
|
278
306
|
const branchDispatch = upstreamWorkflow === undefined;
|
|
279
307
|
const workflow = new GithubWorkflow(project.github!, name, {
|
|
280
|
-
//
|
|
281
|
-
//
|
|
282
|
-
// release is worse than a queued one.
|
|
308
|
+
// A newer release supersedes an older run even when publication has
|
|
309
|
+
// started; the dispatcher also cancels active ecosystem runs immediately.
|
|
283
310
|
limitConcurrency: true,
|
|
284
|
-
concurrencyOptions: { group: name, cancelInProgress:
|
|
311
|
+
concurrencyOptions: { group: name, cancelInProgress: true },
|
|
285
312
|
});
|
|
286
313
|
// Read-only floor for any job that does not declare its own permissions;
|
|
287
314
|
// the publish job below overrides it with the `id-token` it needs.
|
|
@@ -343,6 +370,7 @@ export class DBXToolsRelease extends Component {
|
|
|
343
370
|
// `DRY_RUN_INPUT` is `--dry-run` when the dispatch input is true, else empty;
|
|
344
371
|
// the publish script also FORCES it on any `workflow_dispatch` run.
|
|
345
372
|
env: {
|
|
373
|
+
BUN_VERSION,
|
|
346
374
|
CI: "true",
|
|
347
375
|
DRY_RUN_INPUT: "${{ github.event.inputs.dry_run == 'true' && '--dry-run' || '' }}",
|
|
348
376
|
},
|
package/tasks/uniffi-release.mjs
CHANGED
|
@@ -26,6 +26,7 @@ const parsed = parseArgs({
|
|
|
26
26
|
python: { type: "string" },
|
|
27
27
|
"node-package": { type: "string" },
|
|
28
28
|
"node-generator": { type: "string" },
|
|
29
|
+
ubrn: { type: "string" },
|
|
29
30
|
"python-package": { type: "string" },
|
|
30
31
|
"cargo-target": { type: "string" },
|
|
31
32
|
"node-triple": { type: "string" },
|
|
@@ -149,6 +150,7 @@ const packageNode = ({
|
|
|
149
150
|
required("cargo-target"),
|
|
150
151
|
"--node-package-base",
|
|
151
152
|
`${nodePackage}-`,
|
|
153
|
+
...(parsed.values.ubrn ? ["--ubrn", parsed.values.ubrn, "--skip-barrels"] : []),
|
|
152
154
|
"--skip-build",
|
|
153
155
|
]);
|
|
154
156
|
mkdirSync(resolve(output, "npm-facade"), { recursive: true });
|
package/tasks/uniffi.ts
CHANGED
|
@@ -29,7 +29,9 @@ const { values } = parseArgs({
|
|
|
29
29
|
python: { type: "string" },
|
|
30
30
|
"cargo-target": { type: "string" },
|
|
31
31
|
"node-package-base": { type: "string" },
|
|
32
|
+
ubrn: { type: "string" },
|
|
32
33
|
"skip-build": { type: "boolean" },
|
|
34
|
+
"skip-barrels": { type: "boolean" },
|
|
33
35
|
},
|
|
34
36
|
});
|
|
35
37
|
if (!values.crate || (!values.node && !values.python)) {
|
|
@@ -109,7 +111,7 @@ if (!existsSync(library)) throw new Error(`Missing compiled UniFFI library: ${li
|
|
|
109
111
|
if (values.node) {
|
|
110
112
|
const nodeSource = resolve(root, values.node, "src");
|
|
111
113
|
const nodeOutput = mkdtempSync(join(tmpdir(), `${libraryName}-node-`));
|
|
112
|
-
run(join(root, "node_modules/.bin/ubrn"), [
|
|
114
|
+
run(values.ubrn ? resolve(values.ubrn) : join(root, "node_modules/.bin/ubrn"), [
|
|
113
115
|
"generate",
|
|
114
116
|
"napi",
|
|
115
117
|
"bindings",
|
|
@@ -187,11 +189,13 @@ if (values.node) {
|
|
|
187
189
|
}
|
|
188
190
|
rmSync(resolve(root, values.node, "src/generated"), { recursive: true, force: true });
|
|
189
191
|
rmSync(nodeOutput, { recursive: true, force: true });
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
192
|
+
if (!values["skip-barrels"]) {
|
|
193
|
+
run(process.execPath, [
|
|
194
|
+
resolve(dirname(fileURLToPath(import.meta.url)), "barrels.ts"),
|
|
195
|
+
"--dir",
|
|
196
|
+
resolve(root, values.node),
|
|
197
|
+
]);
|
|
198
|
+
}
|
|
195
199
|
}
|
|
196
200
|
|
|
197
201
|
if (values.python) {
|