@dbx-tools/projen 0.6.158 → 0.6.161
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 +12 -7
- package/package.json +4 -4
- package/src/project-py.ts +5 -1
- package/src/project-rs.ts +40 -11
- package/src/release.ts +35 -9
- package/tasks/uniffi-release.mjs +18 -2
- package/tasks/uniffi.ts +10 -6
package/README.md
CHANGED
|
@@ -115,18 +115,23 @@ 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
|
-
The
|
|
124
|
-
|
|
125
|
-
the
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
125
|
+
The final host UBRN executable is cached by pinned UBRN version, Rust toolchain,
|
|
126
|
+
runner OS, and runner architecture. A hit skips both the workspace `bun install`
|
|
127
|
+
and the UBRN Cargo build. Release packaging passes the cached executable directly
|
|
128
|
+
to the Node binding generator and keeps the package barrel copied from source,
|
|
129
|
+
so it does not need the installed projen dependency graph. A miss saves the
|
|
130
|
+
validated executable immediately, before workspace build and packaging can fail.
|
|
131
|
+
Cargo registry caches
|
|
132
|
+
and the `SCCACHE_GHA_VERSION` namespace stay stable per target/toolchain across
|
|
133
|
+
version tags. Cache keys, restore results, sccache statistics, and phase timings
|
|
134
|
+
are written to each build log. Python generation executes the already-built
|
|
130
135
|
`target/<triple>/release/uniffi-bindgen` directly. Artifact packaging therefore
|
|
131
136
|
does no Rust compilation after the main workspace build.
|
|
132
137
|
|
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.161",
|
|
30
|
+
"@dbx-tools/path": "0.6.161",
|
|
31
|
+
"@dbx-tools/shared-core": "0.6.161",
|
|
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.161",
|
|
52
52
|
"types": "index.ts",
|
|
53
53
|
"type": "module",
|
|
54
54
|
"exports": {
|
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
|
@@ -641,6 +641,10 @@ export class DBXToolsRustWorkspace {
|
|
|
641
641
|
ubrnRustVersion === releaseRustVersion
|
|
642
642
|
? ""
|
|
643
643
|
: `rustup toolchain install ${ubrnRustVersion} --profile minimal\n`;
|
|
644
|
+
const ubrnExecutable =
|
|
645
|
+
"${{ github.workspace }}/.cache/ubrn/uniffi-bindgen-react-native${{ matrix.target.os == 'win32' && '.exe' || '' }}";
|
|
646
|
+
const builtUbrnExecutable =
|
|
647
|
+
"target/ubrn/debug/uniffi-bindgen-react-native${{ matrix.target.os == 'win32' && '.exe' || '' }}";
|
|
644
648
|
const releaseBranch = projectReleaseBranch(project);
|
|
645
649
|
const bindings = this.bindingMappings.map((binding) => ({
|
|
646
650
|
...binding,
|
|
@@ -685,7 +689,7 @@ export class DBXToolsRustWorkspace {
|
|
|
685
689
|
`--node-package "${binding.nodePackage}"`,
|
|
686
690
|
`--python-package "${binding.pythonPackage}"`,
|
|
687
691
|
...(binding.node
|
|
688
|
-
? ['--node-generator "
|
|
692
|
+
? ['--node-generator "projen/tasks/uniffi.ts"', '--ubrn "$UBRN_EXECUTABLE"']
|
|
689
693
|
: []),
|
|
690
694
|
'--cargo-target "${{ matrix.target.cargo }}"',
|
|
691
695
|
'--node-triple "${{ matrix.target.node }}"',
|
|
@@ -789,13 +793,12 @@ export class DBXToolsRustWorkspace {
|
|
|
789
793
|
...(hasNodeBindings
|
|
790
794
|
? [
|
|
791
795
|
{
|
|
792
|
-
name: "
|
|
796
|
+
name: "Restore UBRN executable",
|
|
793
797
|
id: "ubrn_cache",
|
|
794
|
-
|
|
795
|
-
uses: "actions/cache@v5",
|
|
798
|
+
uses: "actions/cache/restore@v5",
|
|
796
799
|
with: {
|
|
797
|
-
path: "
|
|
798
|
-
key: `ubrn-\${{ runner.os }}-\${{ runner.arch }}-rust-${ubrnRustVersion}-${UBRN_VERSION}`,
|
|
800
|
+
path: ".cache/ubrn",
|
|
801
|
+
key: `ubrn-executable-\${{ runner.os }}-\${{ runner.arch }}-rust-${ubrnRustVersion}-${UBRN_VERSION}`,
|
|
799
802
|
},
|
|
800
803
|
},
|
|
801
804
|
]
|
|
@@ -811,9 +814,8 @@ export class DBXToolsRustWorkspace {
|
|
|
811
814
|
`echo "ubrn_rust_toolchain=${ubrnRustVersion}"`,
|
|
812
815
|
...(hasNodeBindings
|
|
813
816
|
? [
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
'echo "ubrn_target_cache_hit=${{ steps.ubrn_cache.outputs.cache-hit }}"',
|
|
817
|
+
`echo "ubrn_executable_cache_key=ubrn-executable-\${{ runner.os }}-\${{ runner.arch }}-rust-${ubrnRustVersion}-${UBRN_VERSION}"`,
|
|
818
|
+
'echo "ubrn_executable_cache_hit=${{ steps.ubrn_cache.outputs.cache-hit }}"',
|
|
817
819
|
]
|
|
818
820
|
: []),
|
|
819
821
|
].join("\n"),
|
|
@@ -827,6 +829,7 @@ export class DBXToolsRustWorkspace {
|
|
|
827
829
|
? [
|
|
828
830
|
{
|
|
829
831
|
name: "Install Node tooling",
|
|
832
|
+
if: "${{ steps.ubrn_cache.outputs.cache-hit != 'true' }}",
|
|
830
833
|
shell: "bash",
|
|
831
834
|
run: timedBash("node_tooling", "bun install"),
|
|
832
835
|
},
|
|
@@ -836,15 +839,37 @@ export class DBXToolsRustWorkspace {
|
|
|
836
839
|
? [
|
|
837
840
|
{
|
|
838
841
|
name: "Prepare UBRN generator",
|
|
842
|
+
if: "${{ steps.ubrn_cache.outputs.cache-hit != 'true' }}",
|
|
839
843
|
env: {
|
|
840
844
|
CARGO_TARGET_DIR: "${{ github.workspace }}/target/ubrn",
|
|
845
|
+
UBRN_EXECUTABLE: ubrnExecutable,
|
|
841
846
|
},
|
|
842
847
|
shell: "bash",
|
|
843
848
|
run: timedBash(
|
|
844
849
|
"ubrn_generator",
|
|
845
|
-
|
|
850
|
+
[
|
|
851
|
+
`${ubrnToolchainInstall}cargo +${ubrnRustVersion} build --manifest-path node_modules/uniffi-bindgen-react-native/crates/ubrn_cli/Cargo.toml`,
|
|
852
|
+
'mkdir -p "$(dirname "$UBRN_EXECUTABLE")"',
|
|
853
|
+
`cp "${builtUbrnExecutable}" "$UBRN_EXECUTABLE"`,
|
|
854
|
+
'chmod +x "$UBRN_EXECUTABLE"',
|
|
855
|
+
].join("\n"),
|
|
846
856
|
),
|
|
847
857
|
},
|
|
858
|
+
{
|
|
859
|
+
name: "Verify UBRN executable",
|
|
860
|
+
env: { UBRN_EXECUTABLE: ubrnExecutable },
|
|
861
|
+
shell: "bash",
|
|
862
|
+
run: 'test -f "$UBRN_EXECUTABLE"',
|
|
863
|
+
},
|
|
864
|
+
{
|
|
865
|
+
name: "Save UBRN executable",
|
|
866
|
+
if: "${{ steps.ubrn_cache.outputs.cache-hit != 'true' }}",
|
|
867
|
+
uses: "actions/cache/save@v5",
|
|
868
|
+
with: {
|
|
869
|
+
path: ".cache/ubrn",
|
|
870
|
+
key: "${{ steps.ubrn_cache.outputs.cache-primary-key }}",
|
|
871
|
+
},
|
|
872
|
+
},
|
|
848
873
|
]
|
|
849
874
|
: []),
|
|
850
875
|
{
|
|
@@ -864,7 +889,7 @@ export class DBXToolsRustWorkspace {
|
|
|
864
889
|
VERSION: RELEASE_TAG,
|
|
865
890
|
...(hasNodeBindings
|
|
866
891
|
? {
|
|
867
|
-
|
|
892
|
+
UBRN_EXECUTABLE: ubrnExecutable,
|
|
868
893
|
}
|
|
869
894
|
: {}),
|
|
870
895
|
},
|
|
@@ -990,6 +1015,10 @@ export class DBXToolsRustWorkspace {
|
|
|
990
1015
|
},
|
|
991
1016
|
},
|
|
992
1017
|
},
|
|
1018
|
+
concurrency: {
|
|
1019
|
+
group: workflowName,
|
|
1020
|
+
"cancel-in-progress": true,
|
|
1021
|
+
},
|
|
993
1022
|
permissions: { contents: "read" },
|
|
994
1023
|
jobs: {
|
|
995
1024
|
"verify-context": {
|
package/src/release.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
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";
|
|
@@ -196,9 +196,23 @@ export class DBXToolsRelease extends Component {
|
|
|
196
196
|
object.isRecord(rust) && typeof rust.releaseWorkflow === "string"
|
|
197
197
|
? RUST_RELEASE_EVENT
|
|
198
198
|
: DOWNSTREAM_RELEASE_EVENT;
|
|
199
|
-
project
|
|
200
|
-
|
|
201
|
-
|
|
199
|
+
const dispatcher = project.tryFindObjectFile(".github/workflows/release-dispatch.yml");
|
|
200
|
+
dispatcher?.addOverride("jobs.dispatch.steps.1.env.RELEASE_EVENT", releaseEvent);
|
|
201
|
+
const releaseWorkflows = [
|
|
202
|
+
...(object.isRecord(rust) && typeof rust.releaseWorkflow === "string"
|
|
203
|
+
? [rust.releaseWorkflow]
|
|
204
|
+
: []),
|
|
205
|
+
...(typeof project.dbxToolsConfig.pythonReleaseWorkflow === "string"
|
|
206
|
+
? [project.dbxToolsConfig.pythonReleaseWorkflow]
|
|
207
|
+
: []),
|
|
208
|
+
...(this.workflowName ? [this.workflowName] : []),
|
|
209
|
+
...this.standaloneReleases.map(({ name }) => name),
|
|
210
|
+
...(existsSync(resolve(project.outdir, ".github/workflows/docs.yml")) ? ["docs"] : []),
|
|
211
|
+
];
|
|
212
|
+
dispatcher?.addOverride(
|
|
213
|
+
"jobs.dispatch.steps.1.env.RELEASE_WORKFLOWS",
|
|
214
|
+
[...new Set(releaseWorkflows)].join(","),
|
|
215
|
+
);
|
|
202
216
|
// Release the standalone projects in the SAME run, at the same version. They
|
|
203
217
|
// are not workspace members, so nothing else would ever bring them along.
|
|
204
218
|
const siblingArgs = this.standaloneReleases
|
|
@@ -233,7 +247,11 @@ export class DBXToolsRelease extends Component {
|
|
|
233
247
|
obj: {
|
|
234
248
|
name: "release-dispatch",
|
|
235
249
|
on: { push: { tags: [`${this.tagPrefix}*`] } },
|
|
236
|
-
|
|
250
|
+
concurrency: {
|
|
251
|
+
group: "release-dispatch",
|
|
252
|
+
"cancel-in-progress": true,
|
|
253
|
+
},
|
|
254
|
+
permissions: { actions: "write", contents: "write" },
|
|
237
255
|
jobs: {
|
|
238
256
|
dispatch: {
|
|
239
257
|
"runs-on": "ubuntu-latest",
|
|
@@ -250,10 +268,19 @@ export class DBXToolsRelease extends Component {
|
|
|
250
268
|
GH_TOKEN: "${{ github.token }}",
|
|
251
269
|
RELEASE_TAG: "${{ github.ref_name }}",
|
|
252
270
|
RELEASE_EVENT: DOWNSTREAM_RELEASE_EVENT,
|
|
271
|
+
RELEASE_WORKFLOWS: "",
|
|
253
272
|
},
|
|
254
273
|
run: [
|
|
255
274
|
`case "$RELEASE_TAG" in ${this.tagPrefix}*) ;; *) exit 1 ;; esac`,
|
|
256
275
|
'EXPECTED_SHA="$(git rev-parse "$RELEASE_TAG^{commit}")"',
|
|
276
|
+
'IFS="," read -r -a workflows <<< "$RELEASE_WORKFLOWS"',
|
|
277
|
+
'for workflow in "${workflows[@]}"; do',
|
|
278
|
+
" for status in in_progress queued requested waiting pending action_required; do",
|
|
279
|
+
" while IFS= read -r run_id; do",
|
|
280
|
+
' if [ -n "$run_id" ]; then gh run cancel "$run_id"; fi',
|
|
281
|
+
' done < <(gh run list --workflow "$workflow.yml" --status "$status" --limit 100 --json databaseId --jq \'.[].databaseId\')',
|
|
282
|
+
" done",
|
|
283
|
+
"done",
|
|
257
284
|
[
|
|
258
285
|
'gh api --method POST "repos/$GITHUB_REPOSITORY/dispatches"',
|
|
259
286
|
'--raw-field event_type="$RELEASE_EVENT"',
|
|
@@ -277,11 +304,10 @@ export class DBXToolsRelease extends Component {
|
|
|
277
304
|
const setupSteps = publishSetupSteps();
|
|
278
305
|
const branchDispatch = upstreamWorkflow === undefined;
|
|
279
306
|
const workflow = new GithubWorkflow(project.github!, name, {
|
|
280
|
-
//
|
|
281
|
-
//
|
|
282
|
-
// release is worse than a queued one.
|
|
307
|
+
// A newer release supersedes an older run even when publication has
|
|
308
|
+
// started; the dispatcher also cancels active ecosystem runs immediately.
|
|
283
309
|
limitConcurrency: true,
|
|
284
|
-
concurrencyOptions: { group: name, cancelInProgress:
|
|
310
|
+
concurrencyOptions: { group: name, cancelInProgress: true },
|
|
285
311
|
});
|
|
286
312
|
// Read-only floor for any job that does not declare its own permissions;
|
|
287
313
|
// the publish job below overrides it with the `id-token` it needs.
|
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" },
|
|
@@ -47,9 +48,21 @@ const required = (name) => {
|
|
|
47
48
|
};
|
|
48
49
|
|
|
49
50
|
const root = resolve(parsed.values.root ?? process.cwd());
|
|
51
|
+
const commandInvocation = (command, args) => {
|
|
52
|
+
if (process.platform !== "win32" || command !== "npm") return { command, args };
|
|
53
|
+
const npmCli = resolve(dirname(process.execPath), "node_modules", "npm", "bin", "npm-cli.js");
|
|
54
|
+
if (!existsSync(npmCli)) throw new Error(`Missing npm CLI ${npmCli}`);
|
|
55
|
+
return { command: process.execPath, args: [npmCli, ...args] };
|
|
56
|
+
};
|
|
50
57
|
const run = (command, args, cwd = root) => {
|
|
51
|
-
const
|
|
52
|
-
|
|
58
|
+
const invocation = commandInvocation(command, args);
|
|
59
|
+
const result = spawnSync(invocation.command, invocation.args, { cwd, stdio: "inherit" });
|
|
60
|
+
if (result.error) {
|
|
61
|
+
throw new Error(`${invocation.command} failed: ${result.error.message}`, {
|
|
62
|
+
cause: result.error,
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
if (result.status !== 0) throw new Error(`${invocation.command} exited with ${result.status}`);
|
|
53
66
|
};
|
|
54
67
|
|
|
55
68
|
const replaceVersion = (source, version) =>
|
|
@@ -137,7 +150,10 @@ const packageNode = ({
|
|
|
137
150
|
required("cargo-target"),
|
|
138
151
|
"--node-package-base",
|
|
139
152
|
`${nodePackage}-`,
|
|
153
|
+
"--ubrn",
|
|
154
|
+
required("ubrn"),
|
|
140
155
|
"--skip-build",
|
|
156
|
+
"--skip-barrels",
|
|
141
157
|
]);
|
|
142
158
|
mkdirSync(resolve(output, "npm-facade"), { recursive: true });
|
|
143
159
|
run("npm", ["pack", "--pack-destination", resolve(output, "npm-facade")], facadeDirectory);
|
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) {
|