Skip to content

Commit 3ffded3

Browse files
authored
Merge pull request #1610 from appwrite/fix/cli-keyring-bun-compile-binding
Fix CLI crash in compiled binaries from @napi-rs/keyring native binding
2 parents c4882c7 + f670bea commit 3ffded3

3 files changed

Lines changed: 69 additions & 6 deletions

File tree

.github/workflows/validation.yml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,15 @@ jobs:
297297
working-directory: examples/cli
298298
run: python3 ../../scripts/verify-darwin-signatures.py
299299

300+
- name: Test compiled CLI binary
301+
if: matrix.sdk == 'cli'
302+
working-directory: examples/cli
303+
run: |
304+
BIN=build/appwrite-cli-linux-x64
305+
"./$BIN" --version
306+
"./$BIN" --help | grep -q "Usage:"
307+
SHELL=bash "./$BIN" completion bash >/dev/null
308+
300309
- name: Verify npm package entry points
301310
if: matrix.sdk == 'web' || matrix.sdk == 'node' || matrix.sdk == 'react-native' || matrix.sdk == 'cli'
302311
working-directory: examples/${{ matrix.sdk }}

templates/cli/lib/auth/refresh-token.ts

Lines changed: 57 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { Entry } from "@napi-rs/keyring";
21
import { globalConfig } from "../config.js";
32
import { EXECUTABLE_NAME } from "../constants.js";
43
import type { SessionData } from "../types.js";
@@ -11,10 +10,65 @@ interface KeyringEntry {
1110
deletePassword(): boolean;
1211
}
1312

13+
type KeyringEntryConstructor = new (
14+
service: string,
15+
account: string,
16+
) => KeyringEntry;
17+
1418
type KeyringEntryFactory = (service: string, account: string) => KeyringEntry;
1519

16-
let keyringEntryFactory: KeyringEntryFactory = (service, account) =>
17-
new Entry(service, account);
20+
// undefined = unresolved; null = resolved but no native binding (use fallback).
21+
let cachedEntry: KeyringEntryConstructor | null | undefined;
22+
23+
// Resolve @napi-rs/keyring lazily via a literal per-platform require: a top-level
24+
// import would crash the `bun --compile` binaries ("Cannot find native binding"),
25+
// whereas a literal specifier lets bun embed the matching .node per --target so
26+
// secure storage still works. Anything unresolved falls back to config storage.
27+
/* eslint-disable @typescript-eslint/no-require-imports */
28+
const loadEntry = (): KeyringEntryConstructor | null => {
29+
if (cachedEntry !== undefined) {
30+
return cachedEntry;
31+
}
32+
33+
cachedEntry = null;
34+
if (typeof require !== "function") {
35+
return cachedEntry;
36+
}
37+
38+
try {
39+
let mod: { Entry: KeyringEntryConstructor } | undefined;
40+
if (process.platform === "darwin" && process.arch === "arm64") {
41+
mod = require("@napi-rs/keyring-darwin-arm64");
42+
} else if (process.platform === "darwin" && process.arch === "x64") {
43+
mod = require("@napi-rs/keyring-darwin-x64");
44+
} else if (process.platform === "linux" && process.arch === "x64") {
45+
mod = require("@napi-rs/keyring-linux-x64-gnu");
46+
} else if (process.platform === "linux" && process.arch === "arm64") {
47+
mod = require("@napi-rs/keyring-linux-arm64-gnu");
48+
} else if (process.platform === "win32" && process.arch === "x64") {
49+
mod = require("@napi-rs/keyring-win32-x64-msvc");
50+
} else if (process.platform === "win32" && process.arch === "arm64") {
51+
mod = require("@napi-rs/keyring-win32-arm64-msvc");
52+
}
53+
54+
mod ??= require("@napi-rs/keyring");
55+
cachedEntry = mod.Entry;
56+
} catch {
57+
cachedEntry = null;
58+
}
59+
60+
return cachedEntry;
61+
};
62+
/* eslint-enable @typescript-eslint/no-require-imports */
63+
64+
let keyringEntryFactory: KeyringEntryFactory = (service, account) => {
65+
const Entry = loadEntry();
66+
if (!Entry) {
67+
throw new Error("Secure credential storage is unavailable in this build.");
68+
}
69+
70+
return new Entry(service, account);
71+
};
1872

1973
const getSessionData = (sessionId: string): SessionData | undefined =>
2074
globalConfig.get(sessionId) as SessionData | undefined;

templates/cli/package.json.twig

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,9 @@
3535
"build:types": "tsc -p tsconfig.json --emitDeclarationOnly",
3636
"build:runtime": "npm run build:lib:runtime && npm run build:cli",
3737
"build:lib:runtime": "npm run build:lib:esm && npm run build:lib:cjs",
38-
"build:lib:esm": "esbuild index.ts --bundle --platform=node --target=node18 --format=esm --loader:.hbs=text --external:@napi-rs/keyring --external:terminal-image --outfile=dist/index.js",
39-
"build:lib:cjs": "esbuild index.ts --bundle --platform=node --target=node18 --format=cjs --loader:.hbs=text --external:@napi-rs/keyring --external:terminal-image --outfile=dist/index.cjs",
40-
"build:cli": "esbuild cli.ts --bundle --platform=node --target=node18 --format=cjs --loader:.hbs=text --external:@napi-rs/keyring --external:fsevents --external:terminal-image --outfile=dist/cli.cjs",
38+
"build:lib:esm": "esbuild index.ts --bundle --platform=node --target=node18 --format=esm --loader:.hbs=text --external:@napi-rs/keyring --external:@napi-rs/keyring-* --external:terminal-image --outfile=dist/index.js",
39+
"build:lib:cjs": "esbuild index.ts --bundle --platform=node --target=node18 --format=cjs --loader:.hbs=text --external:@napi-rs/keyring --external:@napi-rs/keyring-* --external:terminal-image --outfile=dist/index.cjs",
40+
"build:cli": "esbuild cli.ts --bundle --platform=node --target=node18 --format=cjs --loader:.hbs=text --external:@napi-rs/keyring --external:@napi-rs/keyring-* --external:fsevents --external:terminal-image --outfile=dist/cli.cjs",
4141
"lint": "eslint .",
4242
"format": "prettier --write \"**/*.{js,ts,json,md}\"",
4343
"generate": "tsx scripts/generate-commands.ts",

0 commit comments

Comments
 (0)