@dbx-tools/projen 0.6.151 → 0.6.152
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/package.json +4 -4
- package/src/_release-platform.ts +14 -0
- package/src/project-rs.ts +241 -150
- package/tasks/bump.ts +2 -6
- package/tasks/publish-uniffi-local.ts +6 -4
- package/tasks/uniffi-release.mjs +267 -0
- package/tasks/uniffi.ts +14 -8
- package/tasks/uniffi-release.ts +0 -168
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.152",
|
|
30
|
+
"@dbx-tools/path": "0.6.152",
|
|
31
|
+
"@dbx-tools/shared-core": "0.6.152",
|
|
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.152",
|
|
52
52
|
"types": "index.ts",
|
|
53
53
|
"type": "module",
|
|
54
54
|
"exports": {
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/** Cartesian-product release target filter shared by the bump task. */
|
|
2
|
+
export function releasePlatformFilter(
|
|
3
|
+
operatingSystems: readonly string[],
|
|
4
|
+
architectures: readonly string[],
|
|
5
|
+
): string {
|
|
6
|
+
if ((operatingSystems.length === 0) !== (architectures.length === 0)) {
|
|
7
|
+
throw new Error("--os and --arch must be used together");
|
|
8
|
+
}
|
|
9
|
+
return operatingSystems
|
|
10
|
+
.flatMap((operatingSystem) =>
|
|
11
|
+
architectures.map((architecture) => `${operatingSystem}:${architecture}`),
|
|
12
|
+
)
|
|
13
|
+
.join(",");
|
|
14
|
+
}
|
package/src/project-rs.ts
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
/** Filesystem-discovered Rust workspaces and UniFFI binding package wiring. */
|
|
2
2
|
import { existsSync, readFileSync, readdirSync } from "node:fs";
|
|
3
|
-
import { join, relative, resolve } from "node:path";
|
|
3
|
+
import { dirname, join, relative, resolve } from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
4
5
|
import { string } from "@dbx-tools/shared-core";
|
|
5
6
|
import { Project, TextFile, YamlFile, javascript } from "projen";
|
|
6
7
|
import type { DBXToolsProject } from "./project.ts";
|
|
7
8
|
import { DBXToolsTypeScriptProject } from "./project-js.ts";
|
|
8
9
|
import type { PythonPackageOptions } from "./project-py.ts";
|
|
9
10
|
import { readWorkspaceVersion } from "./workspace-version.ts";
|
|
10
|
-
import {
|
|
11
|
-
import { isDBXToolsJavaScriptProject, isDBXToolsProject } from "./project-predicate.ts";
|
|
11
|
+
import { isDBXToolsJavaScriptProject } from "./project-predicate.ts";
|
|
12
12
|
|
|
13
13
|
export interface CargoDependencyOptions {
|
|
14
14
|
readonly version?: string;
|
|
@@ -172,6 +172,17 @@ function releaseTargets(options: DBXToolsRustWorkspaceOptions): readonly UniFFIR
|
|
|
172
172
|
});
|
|
173
173
|
}
|
|
174
174
|
|
|
175
|
+
function uniffiReleaseTaskSource(): string {
|
|
176
|
+
const sourceDirectory = dirname(fileURLToPath(import.meta.url));
|
|
177
|
+
const candidates = [
|
|
178
|
+
resolve(sourceDirectory, "../tasks/uniffi-release.mjs"),
|
|
179
|
+
resolve(sourceDirectory, "../../tasks/uniffi-release.mjs"),
|
|
180
|
+
];
|
|
181
|
+
const source = candidates.find(existsSync);
|
|
182
|
+
if (!source) throw new Error("Could not locate tasks/uniffi-release.mjs");
|
|
183
|
+
return readFileSync(source, "utf8");
|
|
184
|
+
}
|
|
185
|
+
|
|
175
186
|
/** Persisted mapping consumed by the focused Rust source watcher. */
|
|
176
187
|
export interface RustBindingMapping {
|
|
177
188
|
readonly crate: string;
|
|
@@ -525,7 +536,10 @@ export class DBXToolsRustWorkspace {
|
|
|
525
536
|
});
|
|
526
537
|
if (
|
|
527
538
|
(options.release ?? true) &&
|
|
528
|
-
(this.bindingMappings.length > 0 ||
|
|
539
|
+
(this.bindingMappings.length > 0 ||
|
|
540
|
+
this.packages.some(
|
|
541
|
+
(pkg) => pkg.packageOptions.release || !pkg.packageOptions.private,
|
|
542
|
+
))
|
|
529
543
|
) {
|
|
530
544
|
this.addReleaseWorkflow(project, options, releaseTargets(options));
|
|
531
545
|
}
|
|
@@ -544,37 +558,131 @@ export class DBXToolsRustWorkspace {
|
|
|
544
558
|
nodePackage: binding.nodePackage ?? "",
|
|
545
559
|
pythonPackage: binding.pythonPackage ?? "",
|
|
546
560
|
}));
|
|
547
|
-
const matrix = bindings.flatMap((binding) =>
|
|
548
|
-
targets.map((target, index) => ({
|
|
549
|
-
binding,
|
|
550
|
-
target: { ...target, facade: index === 0 },
|
|
551
|
-
})),
|
|
552
|
-
);
|
|
553
561
|
const releaseBinaries = this.packages
|
|
554
562
|
.filter((pkg) => pkg.packageOptions.release)
|
|
555
563
|
.map((pkg) => ({
|
|
556
564
|
crate: pkg.crateName,
|
|
557
565
|
binary: pkg.packageOptions.binaryName ?? pkg.crateName,
|
|
558
566
|
}));
|
|
559
|
-
const
|
|
560
|
-
|
|
567
|
+
const publicCrates = this.packages
|
|
568
|
+
.filter((pkg) => !pkg.packageOptions.private)
|
|
569
|
+
.map((pkg) => pkg.crateName);
|
|
570
|
+
const targetMatrix = targets.map((target, index) => ({
|
|
571
|
+
target: { ...target, facade: index === 0 },
|
|
572
|
+
}));
|
|
573
|
+
const hasNodeBindings = bindings.some((binding) => binding.node);
|
|
574
|
+
const hasPythonBindings = bindings.some((binding) => binding.python);
|
|
575
|
+
const hasTargetOutputs = bindings.length > 0 || releaseBinaries.length > 0;
|
|
576
|
+
const releaseTask = ".projen/uniffi-release.mjs";
|
|
577
|
+
if (bindings.length) {
|
|
578
|
+
new TextFile(project, releaseTask, {
|
|
579
|
+
lines: uniffiReleaseTaskSource().trimEnd().split("\n"),
|
|
580
|
+
});
|
|
581
|
+
} else {
|
|
582
|
+
project.tryRemoveFile(releaseTask);
|
|
583
|
+
}
|
|
584
|
+
const bindingCommands = bindings.map((binding) =>
|
|
585
|
+
[
|
|
586
|
+
`node ${releaseTask} build`,
|
|
587
|
+
`--crate "${binding.crate}"`,
|
|
588
|
+
`--node "${binding.node}"`,
|
|
589
|
+
`--python "${binding.python}"`,
|
|
590
|
+
`--node-package "${binding.nodePackage}"`,
|
|
591
|
+
`--python-package "${binding.pythonPackage}"`,
|
|
592
|
+
...(binding.node
|
|
593
|
+
? ['--node-generator "node_modules/@dbx-tools/projen/tasks/uniffi.ts"']
|
|
594
|
+
: []),
|
|
595
|
+
'--cargo-target "${{ matrix.target.cargo }}"',
|
|
596
|
+
'--node-triple "${{ matrix.target.node }}"',
|
|
597
|
+
'--python-tag "${{ matrix.target.python }}"',
|
|
598
|
+
'--os "${{ matrix.target.os }}"',
|
|
599
|
+
'--cpu "${{ matrix.target.cpu }}"',
|
|
600
|
+
'--libc "${{ matrix.target.libc }}"',
|
|
601
|
+
'--facade "${{ matrix.target.facade }}"',
|
|
602
|
+
'--version "${VERSION#v}"',
|
|
603
|
+
`--output "dist/release/${binding.crate}/\${{ matrix.target.node }}"`,
|
|
604
|
+
"--skip-build",
|
|
605
|
+
].join(" \\\n "),
|
|
561
606
|
);
|
|
607
|
+
const binaryCommands = releaseBinaries.flatMap((pkg) => [
|
|
608
|
+
`mkdir -p "dist/release/${pkg.crate}/\${{ matrix.target.node }}/binary/stage"`,
|
|
609
|
+
`SOURCE="target/\${{ matrix.target.cargo }}/release/${pkg.binary}\${{ matrix.target.os == 'win32' && '.exe' || '' }}"`,
|
|
610
|
+
`DESTINATION="dist/release/${pkg.crate}/\${{ matrix.target.node }}/binary/stage/${pkg.binary}\${{ matrix.target.os == 'win32' && '.exe' || '' }}"`,
|
|
611
|
+
'cp "$SOURCE" "$DESTINATION"',
|
|
612
|
+
'if [ "${{ matrix.target.os }}" = "win32" ]; then',
|
|
613
|
+
` 7z a "dist/release/${pkg.crate}/\${{ matrix.target.node }}/binary/${pkg.binary}-\${{ matrix.target.node }}.zip" "$DESTINATION"`,
|
|
614
|
+
"else",
|
|
615
|
+
` tar -C "dist/release/${pkg.crate}/\${{ matrix.target.node }}/binary/stage" -czf "dist/release/${pkg.crate}/\${{ matrix.target.node }}/binary/${pkg.binary}-\${{ matrix.target.node }}.tar.gz" "${pkg.binary}"`,
|
|
616
|
+
"fi",
|
|
617
|
+
`rm -rf "dist/release/${pkg.crate}/\${{ matrix.target.node }}/binary/stage"`,
|
|
618
|
+
]);
|
|
619
|
+
const artifactSteps = [
|
|
620
|
+
...bindings.flatMap((binding) => [
|
|
621
|
+
...(binding.node
|
|
622
|
+
? [
|
|
623
|
+
{
|
|
624
|
+
name: `Upload ${binding.crate} native npm package`,
|
|
625
|
+
uses: "actions/upload-artifact@v7",
|
|
626
|
+
with: {
|
|
627
|
+
name: `${binding.crate}-\${{ matrix.target.node }}-npm`,
|
|
628
|
+
path: `dist/release/${binding.crate}/\${{ matrix.target.node }}/npm/*.tgz`,
|
|
629
|
+
},
|
|
630
|
+
},
|
|
631
|
+
{
|
|
632
|
+
name: `Upload ${binding.crate} npm facade`,
|
|
633
|
+
if: "${{ matrix.target.facade }}",
|
|
634
|
+
uses: "actions/upload-artifact@v7",
|
|
635
|
+
with: {
|
|
636
|
+
name: `${binding.crate}-npm-facade`,
|
|
637
|
+
path: `dist/release/${binding.crate}/\${{ matrix.target.node }}/npm-facade/*.tgz`,
|
|
638
|
+
},
|
|
639
|
+
},
|
|
640
|
+
]
|
|
641
|
+
: []),
|
|
642
|
+
...(binding.python
|
|
643
|
+
? [
|
|
644
|
+
{
|
|
645
|
+
name: `Upload ${binding.crate} Python wheel`,
|
|
646
|
+
uses: "actions/upload-artifact@v7",
|
|
647
|
+
with: {
|
|
648
|
+
name: `${binding.crate}-\${{ matrix.target.python }}-python-wheel`,
|
|
649
|
+
path: `dist/release/${binding.crate}/\${{ matrix.target.node }}/python/*.whl`,
|
|
650
|
+
},
|
|
651
|
+
},
|
|
652
|
+
]
|
|
653
|
+
: []),
|
|
654
|
+
]),
|
|
655
|
+
...releaseBinaries.map((pkg) => ({
|
|
656
|
+
name: `Upload ${pkg.crate} release binary`,
|
|
657
|
+
uses: "actions/upload-artifact@v7",
|
|
658
|
+
with: {
|
|
659
|
+
name: `${pkg.crate}-\${{ matrix.target.node }}-binary`,
|
|
660
|
+
path: `dist/release/${pkg.crate}/\${{ matrix.target.node }}/binary/*`,
|
|
661
|
+
},
|
|
662
|
+
})),
|
|
663
|
+
];
|
|
562
664
|
const buildJob = {
|
|
563
|
-
name: "${{ matrix.
|
|
665
|
+
name: "${{ matrix.target.node }}",
|
|
564
666
|
"runs-on": "${{ matrix.target.runner }}",
|
|
565
667
|
env: RUST_CACHE_ENV,
|
|
566
668
|
strategy: {
|
|
567
669
|
"fail-fast": false,
|
|
568
|
-
matrix: { include:
|
|
670
|
+
matrix: { include: targetMatrix },
|
|
569
671
|
},
|
|
570
672
|
steps: [
|
|
571
673
|
{ name: "Checkout", uses: "actions/checkout@v6" },
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
674
|
+
...(hasNodeBindings
|
|
675
|
+
? [
|
|
676
|
+
{
|
|
677
|
+
name: "Setup Bun",
|
|
678
|
+
uses: "oven-sh/setup-bun@v2",
|
|
679
|
+
with: { "bun-version": "1.3.14" },
|
|
680
|
+
},
|
|
681
|
+
]
|
|
682
|
+
: []),
|
|
683
|
+
...(hasPythonBindings
|
|
684
|
+
? [{ name: "Setup uv", uses: "astral-sh/setup-uv@v7" }]
|
|
685
|
+
: []),
|
|
578
686
|
{
|
|
579
687
|
name: "Setup Rust",
|
|
580
688
|
uses: "dtolnay/rust-toolchain@stable",
|
|
@@ -586,76 +694,108 @@ export class DBXToolsRustWorkspace {
|
|
|
586
694
|
if: "${{ matrix.target.os == 'linux' }}",
|
|
587
695
|
run: "sudo apt-get update && sudo apt-get install --yes libdbus-1-dev pkg-config",
|
|
588
696
|
},
|
|
589
|
-
{ name: "Install", run: "bun install" },
|
|
697
|
+
...(hasNodeBindings ? [{ name: "Install Node tooling", run: "bun install" }] : []),
|
|
590
698
|
{
|
|
591
|
-
name: "Build
|
|
592
|
-
|
|
593
|
-
env: {
|
|
594
|
-
VERSION: "${{ github.event_name == 'push' && github.ref_name || inputs.version }}",
|
|
595
|
-
},
|
|
596
|
-
run: 'bun node_modules/@dbx-tools/projen/tasks/uniffi-release.ts build --crate "${{ matrix.binding.crate }}" --rust "${{ matrix.binding.rust }}" --node "${{ matrix.binding.node }}" --python "${{ matrix.binding.python }}" --node-package "${{ matrix.binding.nodePackage }}" --python-package "${{ matrix.binding.pythonPackage }}" --cargo-target "${{ matrix.target.cargo }}" --node-triple "${{ matrix.target.node }}" --python-tag "${{ matrix.target.python }}" --os "${{ matrix.target.os }}" --cpu "${{ matrix.target.cpu }}" --libc "${{ matrix.target.libc }}" --facade "${{ matrix.target.facade }}" --version "${VERSION#v}"',
|
|
597
|
-
},
|
|
598
|
-
{
|
|
599
|
-
name: "Upload native packages",
|
|
600
|
-
uses: "actions/upload-artifact@v7",
|
|
601
|
-
with: {
|
|
602
|
-
name: "${{ matrix.binding.crate }}-${{ matrix.target.node }}",
|
|
603
|
-
path: [
|
|
604
|
-
"dist/uniffi/npm/*.tgz",
|
|
605
|
-
"dist/uniffi/npm-facade/*.tgz",
|
|
606
|
-
"dist/uniffi/python/*.whl",
|
|
607
|
-
].join("\n"),
|
|
608
|
-
},
|
|
699
|
+
name: "Build Rust outputs",
|
|
700
|
+
run: 'cargo build --release --workspace --target "${{ matrix.target.cargo }}"',
|
|
609
701
|
},
|
|
702
|
+
...(bindingCommands.length
|
|
703
|
+
? [
|
|
704
|
+
{
|
|
705
|
+
name: "Package UniFFI outputs",
|
|
706
|
+
shell: "bash",
|
|
707
|
+
env: {
|
|
708
|
+
VERSION: "${{ github.event_name == 'push' && github.ref_name || inputs.version }}",
|
|
709
|
+
},
|
|
710
|
+
run: bindingCommands.join("\n"),
|
|
711
|
+
},
|
|
712
|
+
]
|
|
713
|
+
: []),
|
|
714
|
+
...(binaryCommands.length
|
|
715
|
+
? [
|
|
716
|
+
{
|
|
717
|
+
name: "Package release binaries",
|
|
718
|
+
shell: "bash",
|
|
719
|
+
run: binaryCommands.join("\n"),
|
|
720
|
+
},
|
|
721
|
+
]
|
|
722
|
+
: []),
|
|
723
|
+
...artifactSteps,
|
|
610
724
|
],
|
|
611
725
|
};
|
|
612
|
-
const
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
env: RUST_CACHE_ENV,
|
|
616
|
-
strategy: { "fail-fast": false, matrix: { include: binaryMatrix } },
|
|
617
|
-
steps: [
|
|
618
|
-
{ name: "Checkout", uses: "actions/checkout@v6" },
|
|
619
|
-
{
|
|
620
|
-
name: "Setup Rust",
|
|
621
|
-
uses: "dtolnay/rust-toolchain@stable",
|
|
622
|
-
with: { targets: "${{ matrix.target.cargo }}" },
|
|
623
|
-
},
|
|
624
|
-
...rustCacheSteps("release-${{ matrix.target.cargo }}"),
|
|
726
|
+
const bindingPublishJobs = Object.fromEntries(
|
|
727
|
+
bindings.map((binding) => [
|
|
728
|
+
`publish-${binding.crate}`,
|
|
625
729
|
{
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
shell: "bash",
|
|
633
|
-
run: [
|
|
634
|
-
'cargo build --release --package "${{ matrix.package.crate }}" --bin "${{ matrix.package.binary }}" --target "${{ matrix.target.cargo }}"',
|
|
635
|
-
"mkdir -p dist/rust-release/stage",
|
|
636
|
-
"SOURCE=\"target/${{ matrix.target.cargo }}/release/${{ matrix.package.binary }}${{ matrix.target.os == 'win32' && '.exe' || '' }}\"",
|
|
637
|
-
"DESTINATION=\"dist/rust-release/stage/${{ matrix.package.binary }}${{ matrix.target.os == 'win32' && '.exe' || '' }}\"",
|
|
638
|
-
'cp "$SOURCE" "$DESTINATION"',
|
|
639
|
-
'if [ "${{ matrix.target.os }}" = "win32" ]; then',
|
|
640
|
-
' ARCHIVE="dist/rust-release/${{ matrix.package.binary }}-${{ matrix.target.node }}.zip"',
|
|
641
|
-
' 7z a "$ARCHIVE" "$DESTINATION"',
|
|
642
|
-
"else",
|
|
643
|
-
' ARCHIVE="dist/rust-release/${{ matrix.package.binary }}-${{ matrix.target.node }}.tar.gz"',
|
|
644
|
-
' tar -C dist/rust-release/stage -czf "$ARCHIVE" "${{ matrix.package.binary }}"',
|
|
645
|
-
"fi",
|
|
646
|
-
"rm -rf dist/rust-release/stage",
|
|
647
|
-
].join("\n"),
|
|
648
|
-
},
|
|
649
|
-
{
|
|
650
|
-
name: "Upload release binary",
|
|
651
|
-
uses: "actions/upload-artifact@v7",
|
|
652
|
-
with: {
|
|
653
|
-
name: "release-${{ matrix.package.crate }}-${{ matrix.target.node }}",
|
|
654
|
-
path: "dist/rust-release/*",
|
|
730
|
+
if: "${{ github.event_name == 'push' }}",
|
|
731
|
+
needs: ["build"],
|
|
732
|
+
"runs-on": "ubuntu-latest",
|
|
733
|
+
permissions: {
|
|
734
|
+
contents: "read",
|
|
735
|
+
...(binding.python ? { "id-token": "write" } : {}),
|
|
655
736
|
},
|
|
737
|
+
...(binding.python
|
|
738
|
+
? { environment: { name: `pypi-${binding.crate}` } }
|
|
739
|
+
: {}),
|
|
740
|
+
steps: [
|
|
741
|
+
...(binding.node
|
|
742
|
+
? [
|
|
743
|
+
{
|
|
744
|
+
name: "Setup Node.js",
|
|
745
|
+
uses: "actions/setup-node@v6",
|
|
746
|
+
with: { "registry-url": "https://registry.npmjs.org" },
|
|
747
|
+
},
|
|
748
|
+
{
|
|
749
|
+
name: "Download native npm packages",
|
|
750
|
+
uses: "actions/download-artifact@v8",
|
|
751
|
+
with: {
|
|
752
|
+
pattern: `${binding.crate}-*-npm`,
|
|
753
|
+
path: "dist/npm",
|
|
754
|
+
"merge-multiple": true,
|
|
755
|
+
},
|
|
756
|
+
},
|
|
757
|
+
{
|
|
758
|
+
name: "Download npm facade",
|
|
759
|
+
uses: "actions/download-artifact@v8",
|
|
760
|
+
with: {
|
|
761
|
+
name: `${binding.crate}-npm-facade`,
|
|
762
|
+
path: "dist/npm-facade",
|
|
763
|
+
},
|
|
764
|
+
},
|
|
765
|
+
{
|
|
766
|
+
name: "Publish native npm packages",
|
|
767
|
+
env: { NODE_AUTH_TOKEN: "${{ secrets.NPM_TOKEN }}" },
|
|
768
|
+
run: 'for package in dist/npm/*.tgz; do npm publish "$package" --access public; done',
|
|
769
|
+
},
|
|
770
|
+
{
|
|
771
|
+
name: "Publish npm facade",
|
|
772
|
+
env: { NODE_AUTH_TOKEN: "${{ secrets.NPM_TOKEN }}" },
|
|
773
|
+
run: 'for package in dist/npm-facade/*.tgz; do npm publish "$package" --access public; done',
|
|
774
|
+
},
|
|
775
|
+
]
|
|
776
|
+
: []),
|
|
777
|
+
...(binding.python
|
|
778
|
+
? [
|
|
779
|
+
{ name: "Setup uv", uses: "astral-sh/setup-uv@v7" },
|
|
780
|
+
{
|
|
781
|
+
name: "Download Python wheels",
|
|
782
|
+
uses: "actions/download-artifact@v8",
|
|
783
|
+
with: {
|
|
784
|
+
pattern: `${binding.crate}-*-python-wheel`,
|
|
785
|
+
path: "dist/python",
|
|
786
|
+
"merge-multiple": true,
|
|
787
|
+
},
|
|
788
|
+
},
|
|
789
|
+
{
|
|
790
|
+
name: "Publish Python wheels",
|
|
791
|
+
run: "uv publish --trusted-publishing always dist/python/*.whl",
|
|
792
|
+
},
|
|
793
|
+
]
|
|
794
|
+
: []),
|
|
795
|
+
],
|
|
656
796
|
},
|
|
657
|
-
],
|
|
658
|
-
|
|
797
|
+
]),
|
|
798
|
+
);
|
|
659
799
|
const workflowName = options.releaseWorkflowName ?? "rust-release";
|
|
660
800
|
new YamlFile(project, `.github/workflows/${workflowName}.yml`, {
|
|
661
801
|
obj: {
|
|
@@ -674,81 +814,32 @@ export class DBXToolsRustWorkspace {
|
|
|
674
814
|
},
|
|
675
815
|
permissions: { contents: "read" },
|
|
676
816
|
jobs: {
|
|
677
|
-
...(
|
|
678
|
-
...
|
|
679
|
-
...(
|
|
817
|
+
...(hasTargetOutputs && targetMatrix.length ? { build: buildJob } : {}),
|
|
818
|
+
...bindingPublishJobs,
|
|
819
|
+
...(publicCrates.length
|
|
680
820
|
? {
|
|
681
|
-
publish: {
|
|
821
|
+
"publish-cargo": {
|
|
682
822
|
if: "${{ github.event_name == 'push' }}",
|
|
683
|
-
needs: ["build"],
|
|
823
|
+
...(hasTargetOutputs ? { needs: ["build"] } : {}),
|
|
684
824
|
"runs-on": "ubuntu-latest",
|
|
685
|
-
|
|
686
|
-
permissions: { contents: "read", "id-token": "write" },
|
|
687
|
-
environment: { name: "pypi-${{ matrix.binding.crate }}" },
|
|
825
|
+
permissions: { contents: "read" },
|
|
688
826
|
steps: [
|
|
689
827
|
{ name: "Checkout", uses: "actions/checkout@v6" },
|
|
828
|
+
{ name: "Setup Rust", uses: "dtolnay/rust-toolchain@stable" },
|
|
690
829
|
{
|
|
691
|
-
name: "
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
},
|
|
700
|
-
{ name: "Setup uv", uses: "astral-sh/setup-uv@v7" },
|
|
701
|
-
{ name: "Install", run: "bun install" },
|
|
702
|
-
{
|
|
703
|
-
name: "Download packages",
|
|
704
|
-
uses: "actions/download-artifact@v8",
|
|
705
|
-
with: {
|
|
706
|
-
pattern: "${{ matrix.binding.crate }}-*",
|
|
707
|
-
path: "dist/uniffi",
|
|
708
|
-
"merge-multiple": true,
|
|
709
|
-
},
|
|
710
|
-
},
|
|
711
|
-
{
|
|
712
|
-
name: "Publish npm packages",
|
|
713
|
-
env: { NODE_AUTH_TOKEN: "${{ secrets.NPM_TOKEN }}" },
|
|
714
|
-
run: 'for package in dist/uniffi/npm/*.tgz; do npm publish "$package" --access public; done',
|
|
715
|
-
},
|
|
716
|
-
{
|
|
717
|
-
name: "Publish npm facades",
|
|
718
|
-
if: "${{ matrix.binding.node != '' }}",
|
|
719
|
-
env: { NODE_AUTH_TOKEN: "${{ secrets.NPM_TOKEN }}" },
|
|
720
|
-
run: 'for package in dist/uniffi/npm-facade/*.tgz; do npm publish "$package" --access public; done',
|
|
721
|
-
},
|
|
722
|
-
{
|
|
723
|
-
name: "Publish Python wheels",
|
|
724
|
-
if: "${{ matrix.binding.python != '' }}",
|
|
725
|
-
run: "uv publish --trusted-publishing always dist/uniffi/python/*.whl",
|
|
830
|
+
name: "Publish public crates",
|
|
831
|
+
env: { CARGO_REGISTRY_TOKEN: "${{ secrets.CARGO_REGISTRY_TOKEN }}" },
|
|
832
|
+
run: publicCrates
|
|
833
|
+
.map(
|
|
834
|
+
(crate) =>
|
|
835
|
+
`cargo publish --package "${crate}" --registry crates-io --no-verify`,
|
|
836
|
+
)
|
|
837
|
+
.join("\n"),
|
|
726
838
|
},
|
|
727
839
|
],
|
|
728
840
|
},
|
|
729
841
|
}
|
|
730
842
|
: {}),
|
|
731
|
-
"publish-cargo": {
|
|
732
|
-
if: "${{ github.event_name == 'push' }}",
|
|
733
|
-
needs: [matrix.length ? "build" : "build-binaries"],
|
|
734
|
-
"runs-on": "ubuntu-latest",
|
|
735
|
-
permissions: { contents: "read" },
|
|
736
|
-
env: RUST_CACHE_ENV,
|
|
737
|
-
steps: [
|
|
738
|
-
{ name: "Checkout", uses: "actions/checkout@v6" },
|
|
739
|
-
{ name: "Setup Rust", uses: "dtolnay/rust-toolchain@stable" },
|
|
740
|
-
...rustCacheSteps("cargo-publish"),
|
|
741
|
-
{
|
|
742
|
-
name: "Install Linux native dependencies",
|
|
743
|
-
run: "sudo apt-get update && sudo apt-get install --yes libdbus-1-dev pkg-config",
|
|
744
|
-
},
|
|
745
|
-
{
|
|
746
|
-
name: "Publish public crates",
|
|
747
|
-
env: { CARGO_REGISTRY_TOKEN: "${{ secrets.CARGO_REGISTRY_TOKEN }}" },
|
|
748
|
-
run: "cargo publish --workspace --registry crates-io",
|
|
749
|
-
},
|
|
750
|
-
],
|
|
751
|
-
},
|
|
752
843
|
"publish-local": {
|
|
753
844
|
if: "${{ github.event_name == 'push' && vars.LOCAL_REPOSITORIES == 'true' }}",
|
|
754
845
|
"runs-on": ["self-hosted"],
|
|
@@ -780,11 +871,11 @@ export class DBXToolsRustWorkspace {
|
|
|
780
871
|
},
|
|
781
872
|
],
|
|
782
873
|
},
|
|
783
|
-
...(
|
|
874
|
+
...(releaseBinaries.length
|
|
784
875
|
? {
|
|
785
876
|
"publish-github-release": {
|
|
786
877
|
if: "${{ github.event_name == 'push' }}",
|
|
787
|
-
needs: ["build
|
|
878
|
+
needs: ["build"],
|
|
788
879
|
"runs-on": "ubuntu-latest",
|
|
789
880
|
permissions: { contents: "write" },
|
|
790
881
|
steps: [
|
|
@@ -792,7 +883,7 @@ export class DBXToolsRustWorkspace {
|
|
|
792
883
|
name: "Download release binaries",
|
|
793
884
|
uses: "actions/download-artifact@v8",
|
|
794
885
|
with: {
|
|
795
|
-
pattern: "
|
|
886
|
+
pattern: "*-binary",
|
|
796
887
|
path: "dist/rust-release",
|
|
797
888
|
"merge-multiple": true,
|
|
798
889
|
},
|
package/tasks/bump.ts
CHANGED
|
@@ -56,6 +56,7 @@ import { log, net } from "@dbx-tools/shared-core";
|
|
|
56
56
|
import { Command, Option } from "commander";
|
|
57
57
|
import { activePythonIndexes, resolveLocalPypi } from "./python-registry.ts";
|
|
58
58
|
import { readDbxToolsConfig, repoRoot } from "../src/packages.ts";
|
|
59
|
+
import { releasePlatformFilter } from "../src/_release-platform.ts";
|
|
59
60
|
import {
|
|
60
61
|
type Semver,
|
|
61
62
|
compareSemver,
|
|
@@ -234,9 +235,7 @@ program
|
|
|
234
235
|
}) => {
|
|
235
236
|
const pkgPath = resolve(process.cwd(), "package.json");
|
|
236
237
|
if (!existsSync(pkgPath)) throw new Error(`no package.json in ${process.cwd()}`);
|
|
237
|
-
|
|
238
|
-
throw new Error("--os and --arch must be used together");
|
|
239
|
-
}
|
|
238
|
+
const releasePlatforms = releasePlatformFilter(opts.os, opts.arch);
|
|
240
239
|
|
|
241
240
|
const siblings = opts.sibling.map((s) => ({ ...s, pkgPath: resolve(s.dir, "package.json") }));
|
|
242
241
|
for (const s of siblings) {
|
|
@@ -285,9 +284,6 @@ program
|
|
|
285
284
|
|
|
286
285
|
if (opts.synth) {
|
|
287
286
|
logger.info("synthesizing (projen)");
|
|
288
|
-
const releasePlatforms = opts.os
|
|
289
|
-
.flatMap((os) => opts.arch.map((arch) => `${os}:${arch}`))
|
|
290
|
-
.join(",");
|
|
291
287
|
exec.spawnSync("bun", [".projenrc.ts"], {
|
|
292
288
|
cwd: process.cwd(),
|
|
293
289
|
stdout: "inherit",
|
|
@@ -144,19 +144,19 @@ function buildAndPublish(binding: RustBindingMapping, version: string): void {
|
|
|
144
144
|
|
|
145
145
|
const target = nativeTarget();
|
|
146
146
|
const output = resolve(repoRoot, "dist/uniffi");
|
|
147
|
-
run("
|
|
148
|
-
resolve(dirname(fileURLToPath(import.meta.url)), "uniffi-release.
|
|
147
|
+
run("node", [
|
|
148
|
+
resolve(dirname(fileURLToPath(import.meta.url)), "uniffi-release.mjs"),
|
|
149
149
|
"build",
|
|
150
150
|
"--crate",
|
|
151
151
|
binding.crate,
|
|
152
|
-
"--rust",
|
|
153
|
-
binding.rust,
|
|
154
152
|
"--node",
|
|
155
153
|
includeNode ? binding.node! : "",
|
|
156
154
|
"--python",
|
|
157
155
|
includePython ? binding.python! : "",
|
|
158
156
|
"--node-package",
|
|
159
157
|
includeNode ? binding.nodePackage! : "",
|
|
158
|
+
"--node-generator",
|
|
159
|
+
"projen/tasks/uniffi.ts",
|
|
160
160
|
"--python-package",
|
|
161
161
|
includePython ? binding.pythonPackage! : "",
|
|
162
162
|
"--cargo-target",
|
|
@@ -175,6 +175,8 @@ function buildAndPublish(binding: RustBindingMapping, version: string): void {
|
|
|
175
175
|
"true",
|
|
176
176
|
"--version",
|
|
177
177
|
version,
|
|
178
|
+
"--output",
|
|
179
|
+
"dist/uniffi",
|
|
178
180
|
]);
|
|
179
181
|
|
|
180
182
|
if (includeNode) {
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
chmodSync,
|
|
4
|
+
cpSync,
|
|
5
|
+
existsSync,
|
|
6
|
+
mkdirSync,
|
|
7
|
+
mkdtempSync,
|
|
8
|
+
readFileSync,
|
|
9
|
+
readdirSync,
|
|
10
|
+
rmSync,
|
|
11
|
+
statSync,
|
|
12
|
+
writeFileSync,
|
|
13
|
+
} from "node:fs";
|
|
14
|
+
import { basename, dirname, join, resolve } from "node:path";
|
|
15
|
+
import { tmpdir } from "node:os";
|
|
16
|
+
import { fileURLToPath } from "node:url";
|
|
17
|
+
import { parseArgs } from "node:util";
|
|
18
|
+
import { spawnSync } from "node:child_process";
|
|
19
|
+
|
|
20
|
+
const parsed = parseArgs({
|
|
21
|
+
allowPositionals: true,
|
|
22
|
+
options: {
|
|
23
|
+
root: { type: "string" },
|
|
24
|
+
crate: { type: "string" },
|
|
25
|
+
node: { type: "string" },
|
|
26
|
+
python: { type: "string" },
|
|
27
|
+
"node-package": { type: "string" },
|
|
28
|
+
"node-generator": { type: "string" },
|
|
29
|
+
"python-package": { type: "string" },
|
|
30
|
+
"cargo-target": { type: "string" },
|
|
31
|
+
"node-triple": { type: "string" },
|
|
32
|
+
"python-tag": { type: "string" },
|
|
33
|
+
os: { type: "string" },
|
|
34
|
+
cpu: { type: "string" },
|
|
35
|
+
libc: { type: "string" },
|
|
36
|
+
facade: { type: "string" },
|
|
37
|
+
version: { type: "string" },
|
|
38
|
+
output: { type: "string" },
|
|
39
|
+
"skip-build": { type: "boolean" },
|
|
40
|
+
},
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
const required = (name) => {
|
|
44
|
+
const value = parsed.values[name];
|
|
45
|
+
if (!value) throw new Error(`Missing --${name}`);
|
|
46
|
+
return value;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const root = resolve(parsed.values.root ?? process.cwd());
|
|
50
|
+
const run = (command, args, cwd = root) => {
|
|
51
|
+
const result = spawnSync(command, args, { cwd, stdio: "inherit" });
|
|
52
|
+
if (result.status !== 0) throw new Error(`${command} exited with ${result.status}`);
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const replaceVersion = (source, version) =>
|
|
56
|
+
source.replace(/^version = "[^"]+"$/m, `version = "${version}"`);
|
|
57
|
+
|
|
58
|
+
const writable = (path) => {
|
|
59
|
+
if (existsSync(path)) chmodSync(path, statSync(path).mode | 0o200);
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const libraryPath = (crate, cargoTarget, os) => {
|
|
63
|
+
const name = crate.replaceAll("-", "_");
|
|
64
|
+
const extension = os === "darwin" ? "dylib" : os === "win32" ? "dll" : "so";
|
|
65
|
+
const prefix = os === "win32" ? "" : "lib";
|
|
66
|
+
return resolve(root, "target", cargoTarget, "release", `${prefix}${name}.${extension}`);
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
const packageNode = ({
|
|
70
|
+
crate,
|
|
71
|
+
library,
|
|
72
|
+
output,
|
|
73
|
+
nodeDirectory,
|
|
74
|
+
nodePackage,
|
|
75
|
+
nodeTriple,
|
|
76
|
+
os,
|
|
77
|
+
cpu,
|
|
78
|
+
version,
|
|
79
|
+
facade,
|
|
80
|
+
nodeGenerator,
|
|
81
|
+
}) => {
|
|
82
|
+
const libraryFile = basename(library);
|
|
83
|
+
const nativePackage = resolve(output, "native-node");
|
|
84
|
+
mkdirSync(nativePackage, { recursive: true });
|
|
85
|
+
cpSync(library, join(nativePackage, libraryFile));
|
|
86
|
+
writeFileSync(
|
|
87
|
+
join(nativePackage, "package.json"),
|
|
88
|
+
`${JSON.stringify(
|
|
89
|
+
{
|
|
90
|
+
name: `${nodePackage}-${nodeTriple}`,
|
|
91
|
+
version,
|
|
92
|
+
description: `Native ${nodeTriple} library for ${nodePackage}`,
|
|
93
|
+
license: "Apache-2.0",
|
|
94
|
+
os: [os],
|
|
95
|
+
cpu: [cpu],
|
|
96
|
+
...(parsed.values.libc ? { libc: [parsed.values.libc] } : {}),
|
|
97
|
+
files: [libraryFile],
|
|
98
|
+
},
|
|
99
|
+
null,
|
|
100
|
+
2,
|
|
101
|
+
)}\n`,
|
|
102
|
+
);
|
|
103
|
+
mkdirSync(resolve(output, "npm"), { recursive: true });
|
|
104
|
+
run("npm", ["pack", "--pack-destination", resolve(output, "npm")], nativePackage);
|
|
105
|
+
|
|
106
|
+
if (!facade) return;
|
|
107
|
+
const facadeDirectory = resolve(output, "facade-node");
|
|
108
|
+
cpSync(resolve(root, nodeDirectory), facadeDirectory, { recursive: true });
|
|
109
|
+
rmSync(join(facadeDirectory, "src", libraryFile), { force: true });
|
|
110
|
+
const manifestPath = join(facadeDirectory, "package.json");
|
|
111
|
+
writable(manifestPath);
|
|
112
|
+
const manifest = JSON.parse(readFileSync(manifestPath, "utf8"));
|
|
113
|
+
manifest.version = version;
|
|
114
|
+
manifest.private = false;
|
|
115
|
+
manifest.license = manifest.license === "UNLICENSED" ? "Apache-2.0" : manifest.license;
|
|
116
|
+
manifest.optionalDependencies = Object.fromEntries(
|
|
117
|
+
Object.keys(manifest.optionalDependencies ?? {}).map((name) => [name, version]),
|
|
118
|
+
);
|
|
119
|
+
manifest.dependencies = Object.fromEntries(
|
|
120
|
+
Object.entries(manifest.dependencies ?? {}).map(([name, dependency]) => [
|
|
121
|
+
name,
|
|
122
|
+
typeof dependency === "string" && dependency.startsWith("workspace:")
|
|
123
|
+
? version
|
|
124
|
+
: dependency,
|
|
125
|
+
]),
|
|
126
|
+
);
|
|
127
|
+
delete manifest.scripts;
|
|
128
|
+
delete manifest.devDependencies;
|
|
129
|
+
writeFileSync(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`);
|
|
130
|
+
run("bun", [
|
|
131
|
+
resolve(root, nodeGenerator),
|
|
132
|
+
"--root",
|
|
133
|
+
root,
|
|
134
|
+
"--crate",
|
|
135
|
+
crate,
|
|
136
|
+
"--node",
|
|
137
|
+
facadeDirectory,
|
|
138
|
+
"--cargo-target",
|
|
139
|
+
required("cargo-target"),
|
|
140
|
+
"--node-package-base",
|
|
141
|
+
`${nodePackage}-`,
|
|
142
|
+
"--skip-build",
|
|
143
|
+
]);
|
|
144
|
+
mkdirSync(resolve(output, "npm-facade"), { recursive: true });
|
|
145
|
+
run(
|
|
146
|
+
"npm",
|
|
147
|
+
["pack", "--pack-destination", resolve(output, "npm-facade")],
|
|
148
|
+
facadeDirectory,
|
|
149
|
+
);
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
const packagePython = ({
|
|
153
|
+
crate,
|
|
154
|
+
library,
|
|
155
|
+
output,
|
|
156
|
+
pythonDirectory,
|
|
157
|
+
pythonTag,
|
|
158
|
+
version,
|
|
159
|
+
}) => {
|
|
160
|
+
const pythonRoot = resolve(output, "python-root");
|
|
161
|
+
cpSync(resolve(root, pythonDirectory), pythonRoot, { recursive: true });
|
|
162
|
+
const pyproject = join(pythonRoot, "pyproject.toml");
|
|
163
|
+
writable(pyproject);
|
|
164
|
+
writeFileSync(pyproject, replaceVersion(readFileSync(pyproject, "utf8"), version));
|
|
165
|
+
|
|
166
|
+
const packageName = crate.replace(/^dbx-tools-/, "").replaceAll("-", "_");
|
|
167
|
+
const packageDirectory = resolve(pythonRoot, "src", "dbx_tools", packageName);
|
|
168
|
+
const generatedDirectory = mkdtempSync(join(tmpdir(), `${packageName}-python-`));
|
|
169
|
+
run("cargo", [
|
|
170
|
+
"run",
|
|
171
|
+
"--release",
|
|
172
|
+
"--package",
|
|
173
|
+
crate,
|
|
174
|
+
"--bin",
|
|
175
|
+
"uniffi-bindgen",
|
|
176
|
+
"--",
|
|
177
|
+
"generate",
|
|
178
|
+
"--language",
|
|
179
|
+
"python",
|
|
180
|
+
"--out-dir",
|
|
181
|
+
generatedDirectory,
|
|
182
|
+
library,
|
|
183
|
+
]);
|
|
184
|
+
const bindings = join(packageDirectory, "bindings.py");
|
|
185
|
+
writable(bindings);
|
|
186
|
+
const body = readFileSync(join(generatedDirectory, `${crate.replaceAll("-", "_")}.py`), "utf8");
|
|
187
|
+
writeFileSync(
|
|
188
|
+
bindings,
|
|
189
|
+
[
|
|
190
|
+
"# GENERATED by UniFFI binding generation - DO NOT EDIT.",
|
|
191
|
+
`# Regenerated from the ${crate} Rust exports.`,
|
|
192
|
+
"# Hand edits are overwritten on the next watch; this file is read-only.",
|
|
193
|
+
"",
|
|
194
|
+
body,
|
|
195
|
+
].join("\n"),
|
|
196
|
+
);
|
|
197
|
+
cpSync(library, join(packageDirectory, basename(library)));
|
|
198
|
+
rmSync(generatedDirectory, { recursive: true, force: true });
|
|
199
|
+
|
|
200
|
+
const wheelDirectory = resolve(output, "python");
|
|
201
|
+
mkdirSync(wheelDirectory, { recursive: true });
|
|
202
|
+
run("uv", ["build", "--wheel", "--out-dir", wheelDirectory], pythonRoot);
|
|
203
|
+
const wheels = readdirSync(wheelDirectory).filter((file) => file.endsWith(".whl"));
|
|
204
|
+
if (wheels.length !== 1) throw new Error(`Expected one Python wheel, found ${wheels.length}`);
|
|
205
|
+
run("uvx", [
|
|
206
|
+
"--from",
|
|
207
|
+
"wheel",
|
|
208
|
+
"wheel",
|
|
209
|
+
"tags",
|
|
210
|
+
"--remove",
|
|
211
|
+
"--platform-tag",
|
|
212
|
+
pythonTag,
|
|
213
|
+
join(wheelDirectory, wheels[0]),
|
|
214
|
+
]);
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
const build = () => {
|
|
218
|
+
const crate = required("crate");
|
|
219
|
+
const cargoTarget = required("cargo-target");
|
|
220
|
+
const nodeTriple = required("node-triple");
|
|
221
|
+
const pythonTag = required("python-tag");
|
|
222
|
+
const version = required("version");
|
|
223
|
+
const os = required("os");
|
|
224
|
+
const cpu = required("cpu");
|
|
225
|
+
const output = resolve(root, required("output"));
|
|
226
|
+
const library = libraryPath(crate, cargoTarget, os);
|
|
227
|
+
|
|
228
|
+
rmSync(output, { recursive: true, force: true });
|
|
229
|
+
mkdirSync(output, { recursive: true });
|
|
230
|
+
if (!parsed.values["skip-build"]) {
|
|
231
|
+
run("cargo", ["build", "--release", "--package", crate, "--target", cargoTarget]);
|
|
232
|
+
}
|
|
233
|
+
if (!existsSync(library)) throw new Error(`Missing native library ${library}`);
|
|
234
|
+
|
|
235
|
+
const nodeDirectory = parsed.values.node;
|
|
236
|
+
const nodePackage = parsed.values["node-package"];
|
|
237
|
+
if (nodeDirectory && nodePackage) {
|
|
238
|
+
packageNode({
|
|
239
|
+
crate,
|
|
240
|
+
library,
|
|
241
|
+
output,
|
|
242
|
+
nodeDirectory,
|
|
243
|
+
nodePackage,
|
|
244
|
+
nodeTriple,
|
|
245
|
+
os,
|
|
246
|
+
cpu,
|
|
247
|
+
version,
|
|
248
|
+
facade: parsed.values.facade === "true",
|
|
249
|
+
nodeGenerator: required("node-generator"),
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
const pythonDirectory = parsed.values.python;
|
|
254
|
+
if (pythonDirectory) {
|
|
255
|
+
packagePython({
|
|
256
|
+
crate,
|
|
257
|
+
library,
|
|
258
|
+
output,
|
|
259
|
+
pythonDirectory,
|
|
260
|
+
pythonTag,
|
|
261
|
+
version,
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
};
|
|
265
|
+
|
|
266
|
+
if (parsed.positionals[0] !== "build") throw new Error("Expected build command");
|
|
267
|
+
build();
|
package/tasks/uniffi.ts
CHANGED
|
@@ -23,18 +23,22 @@ import {
|
|
|
23
23
|
|
|
24
24
|
const { values } = parseArgs({
|
|
25
25
|
options: {
|
|
26
|
+
root: { type: "string" },
|
|
26
27
|
crate: { type: "string" },
|
|
27
28
|
node: { type: "string" },
|
|
28
29
|
python: { type: "string" },
|
|
29
30
|
"cargo-target": { type: "string" },
|
|
30
31
|
"node-package-base": { type: "string" },
|
|
32
|
+
"skip-build": { type: "boolean" },
|
|
31
33
|
},
|
|
32
34
|
});
|
|
33
35
|
if (!values.crate || (!values.node && !values.python)) {
|
|
34
36
|
throw new Error("Expected --crate and at least one of --node or --python");
|
|
35
37
|
}
|
|
36
38
|
|
|
37
|
-
const root =
|
|
39
|
+
const root = values.root
|
|
40
|
+
? resolve(values.root)
|
|
41
|
+
: resolve(dirname(fileURLToPath(import.meta.url)), "../..");
|
|
38
42
|
const crate = values.crate;
|
|
39
43
|
const libraryName = crate.replaceAll("-", "_");
|
|
40
44
|
const extension =
|
|
@@ -90,13 +94,15 @@ const stampGeneratedPython = (file: string): void => {
|
|
|
90
94
|
makeReadonly(file);
|
|
91
95
|
};
|
|
92
96
|
|
|
93
|
-
|
|
94
|
-
"
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
])
|
|
97
|
+
if (!values["skip-build"]) {
|
|
98
|
+
run("cargo", [
|
|
99
|
+
"build",
|
|
100
|
+
"--release",
|
|
101
|
+
"--package",
|
|
102
|
+
crate,
|
|
103
|
+
...(values["cargo-target"] ? ["--target", values["cargo-target"]] : []),
|
|
104
|
+
]);
|
|
105
|
+
}
|
|
100
106
|
if (!existsSync(library)) throw new Error(`Missing compiled UniFFI library: ${library}`);
|
|
101
107
|
|
|
102
108
|
if (values.node) {
|
package/tasks/uniffi-release.ts
DELETED
|
@@ -1,168 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env -S bun
|
|
2
|
-
import {
|
|
3
|
-
chmodSync,
|
|
4
|
-
cpSync,
|
|
5
|
-
existsSync,
|
|
6
|
-
mkdirSync,
|
|
7
|
-
readFileSync,
|
|
8
|
-
rmSync,
|
|
9
|
-
statSync,
|
|
10
|
-
writeFileSync,
|
|
11
|
-
} from "node:fs";
|
|
12
|
-
import { dirname, join, resolve } from "node:path";
|
|
13
|
-
import { fileURLToPath } from "node:url";
|
|
14
|
-
import { parseArgs } from "node:util";
|
|
15
|
-
import { spawnSync } from "node:child_process";
|
|
16
|
-
|
|
17
|
-
const root = resolve(dirname(fileURLToPath(import.meta.url)), "../..");
|
|
18
|
-
const parsed = parseArgs({
|
|
19
|
-
allowPositionals: true,
|
|
20
|
-
options: {
|
|
21
|
-
crate: { type: "string" },
|
|
22
|
-
rust: { type: "string" },
|
|
23
|
-
node: { type: "string" },
|
|
24
|
-
python: { type: "string" },
|
|
25
|
-
"node-package": { type: "string" },
|
|
26
|
-
"python-package": { type: "string" },
|
|
27
|
-
"cargo-target": { type: "string" },
|
|
28
|
-
"node-triple": { type: "string" },
|
|
29
|
-
"python-tag": { type: "string" },
|
|
30
|
-
os: { type: "string" },
|
|
31
|
-
cpu: { type: "string" },
|
|
32
|
-
libc: { type: "string" },
|
|
33
|
-
facade: { type: "string" },
|
|
34
|
-
version: { type: "string" },
|
|
35
|
-
},
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
const required = (name: keyof typeof parsed.values): string => {
|
|
39
|
-
const value = parsed.values[name];
|
|
40
|
-
if (!value) throw new Error(`Missing --${name}`);
|
|
41
|
-
return value;
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
const run = (command: string, args: string[], cwd = root): void => {
|
|
45
|
-
const result = spawnSync(command, args, { cwd, stdio: "inherit" });
|
|
46
|
-
if (result.status !== 0) throw new Error(`${command} exited with ${result.status}`);
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
const replaceVersion = (source: string, version: string): string =>
|
|
50
|
-
source.replace(/^version = "[^"]+"$/m, `version = "${version}"`);
|
|
51
|
-
|
|
52
|
-
function build(): void {
|
|
53
|
-
const crate = required("crate");
|
|
54
|
-
required("rust");
|
|
55
|
-
const cargoTarget = required("cargo-target");
|
|
56
|
-
const nodeTriple = required("node-triple");
|
|
57
|
-
const pythonTag = required("python-tag");
|
|
58
|
-
const version = required("version");
|
|
59
|
-
const os = required("os");
|
|
60
|
-
const cpu = required("cpu");
|
|
61
|
-
const nodeDirectory = parsed.values.node;
|
|
62
|
-
const pythonDirectory = parsed.values.python;
|
|
63
|
-
const nodePackage = parsed.values["node-package"];
|
|
64
|
-
const pythonPackage = parsed.values["python-package"];
|
|
65
|
-
const libraryName = crate.replaceAll("-", "_");
|
|
66
|
-
const extension = os === "darwin" ? "dylib" : os === "win32" ? "dll" : "so";
|
|
67
|
-
const prefix = os === "win32" ? "" : "lib";
|
|
68
|
-
const libraryFile = `${prefix}${libraryName}.${extension}`;
|
|
69
|
-
const library = resolve(root, "target", cargoTarget, "release", libraryFile);
|
|
70
|
-
const output = resolve(root, "dist/uniffi");
|
|
71
|
-
|
|
72
|
-
rmSync(output, { recursive: true, force: true });
|
|
73
|
-
mkdirSync(join(output, "npm"), { recursive: true });
|
|
74
|
-
mkdirSync(join(output, "npm-facade"), { recursive: true });
|
|
75
|
-
mkdirSync(join(output, "python"), { recursive: true });
|
|
76
|
-
run("cargo", ["build", "--release", "--package", crate, "--target", cargoTarget]);
|
|
77
|
-
if (!existsSync(library)) throw new Error(`Missing native library ${library}`);
|
|
78
|
-
|
|
79
|
-
if (nodeDirectory && nodePackage) {
|
|
80
|
-
const nativePackage = resolve(output, "native-node");
|
|
81
|
-
mkdirSync(nativePackage, { recursive: true });
|
|
82
|
-
cpSync(library, join(nativePackage, libraryFile));
|
|
83
|
-
writeFileSync(
|
|
84
|
-
join(nativePackage, "package.json"),
|
|
85
|
-
`${JSON.stringify(
|
|
86
|
-
{
|
|
87
|
-
name: `${nodePackage}-${nodeTriple}`,
|
|
88
|
-
version,
|
|
89
|
-
description: `Native ${nodeTriple} library for ${nodePackage}`,
|
|
90
|
-
license: "Apache-2.0",
|
|
91
|
-
os: [os],
|
|
92
|
-
cpu: [cpu],
|
|
93
|
-
...(parsed.values.libc ? { libc: [parsed.values.libc] } : {}),
|
|
94
|
-
files: [libraryFile],
|
|
95
|
-
},
|
|
96
|
-
null,
|
|
97
|
-
2,
|
|
98
|
-
)}\n`,
|
|
99
|
-
);
|
|
100
|
-
run("npm", ["pack", "--pack-destination", resolve(output, "npm")], nativePackage);
|
|
101
|
-
|
|
102
|
-
if (parsed.values.facade === "true") {
|
|
103
|
-
const facade = resolve(output, "facade-node");
|
|
104
|
-
cpSync(resolve(root, nodeDirectory), facade, { recursive: true });
|
|
105
|
-
rmSync(join(facade, "src", libraryFile), { force: true });
|
|
106
|
-
const manifestPath = join(facade, "package.json");
|
|
107
|
-
const manifestMode = statSync(manifestPath).mode;
|
|
108
|
-
chmodSync(manifestPath, manifestMode | 0o200);
|
|
109
|
-
const manifest = JSON.parse(readFileSync(manifestPath, "utf8"));
|
|
110
|
-
manifest.version = version;
|
|
111
|
-
manifest.private = false;
|
|
112
|
-
manifest.license = manifest.license === "UNLICENSED" ? "Apache-2.0" : manifest.license;
|
|
113
|
-
manifest.optionalDependencies = Object.fromEntries(
|
|
114
|
-
Object.keys(manifest.optionalDependencies ?? {}).map((name) => [name, version]),
|
|
115
|
-
);
|
|
116
|
-
manifest.dependencies = Object.fromEntries(
|
|
117
|
-
Object.entries(manifest.dependencies ?? {}).map(([name, dependency]) => [
|
|
118
|
-
name,
|
|
119
|
-
typeof dependency === "string" && dependency.startsWith("workspace:")
|
|
120
|
-
? version
|
|
121
|
-
: dependency,
|
|
122
|
-
]),
|
|
123
|
-
);
|
|
124
|
-
delete manifest.scripts;
|
|
125
|
-
delete manifest.devDependencies;
|
|
126
|
-
writeFileSync(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`);
|
|
127
|
-
const generator = resolve(root, "node_modules/@dbx-tools/projen/tasks/uniffi.ts");
|
|
128
|
-
run("bun", [
|
|
129
|
-
generator,
|
|
130
|
-
"--crate",
|
|
131
|
-
crate,
|
|
132
|
-
"--node",
|
|
133
|
-
facade,
|
|
134
|
-
"--cargo-target",
|
|
135
|
-
cargoTarget,
|
|
136
|
-
"--node-package-base",
|
|
137
|
-
`${nodePackage}-`,
|
|
138
|
-
]);
|
|
139
|
-
run("npm", ["pack", "--pack-destination", resolve(output, "npm-facade")], facade);
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
if (pythonDirectory && pythonPackage) {
|
|
144
|
-
const pythonRoot = resolve(output, "python-root");
|
|
145
|
-
cpSync(resolve(root, pythonDirectory), pythonRoot, { recursive: true });
|
|
146
|
-
const pyproject = join(pythonRoot, "pyproject.toml");
|
|
147
|
-
const mode = statSync(pyproject).mode;
|
|
148
|
-
chmodSync(pyproject, mode | 0o200);
|
|
149
|
-
writeFileSync(pyproject, replaceVersion(readFileSync(pyproject, "utf8"), version));
|
|
150
|
-
const generator = resolve(root, "node_modules/@dbx-tools/projen/tasks/uniffi.ts");
|
|
151
|
-
run("bun", [
|
|
152
|
-
generator,
|
|
153
|
-
"--crate",
|
|
154
|
-
crate,
|
|
155
|
-
"--python",
|
|
156
|
-
pythonRoot,
|
|
157
|
-
"--cargo-target",
|
|
158
|
-
cargoTarget,
|
|
159
|
-
]);
|
|
160
|
-
run("uv", ["build", "--wheel", "--out-dir", resolve(output, "python")], pythonRoot);
|
|
161
|
-
const wheels = [...new Bun.Glob("*.whl").scanSync(join(output, "python"))];
|
|
162
|
-
if (wheels.length !== 1) throw new Error(`Expected one Python wheel, found ${wheels.length}`);
|
|
163
|
-
run("uvx", ["--from", "wheel", "wheel", "tags", "--remove", "--platform-tag", pythonTag, join(output, "python", wheels[0]!)]);
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
if (parsed.positionals[0] !== "build") throw new Error("Expected build command");
|
|
168
|
-
build();
|