@deadbolt/cli 0.0.0-dev-143 → 0.0.0-dev-145

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/bin/deadbolt CHANGED
@@ -1,8 +1,9 @@
1
1
  #!/bin/sh
2
2
  # @deadbolt/cli's postinstall replaces this with a link to this platform's
3
3
  # deadbolt (postinstall.mjs). When the package manager skipped install
4
- # scripts (pnpm, bun, npm with scripts disallowed), the first run links it
5
- # here, then runs it.
4
+ # scripts (pnpm, bun, npm with scripts disallowed), or the postinstall
5
+ # couldn't link (npm didn't serve this platform's package yet), the first run
6
+ # links it here, then runs it. It never deletes anything.
6
7
  self=$0
7
8
  while [ -L "$self" ]; do
8
9
  link=$(readlink "$self")
@@ -12,10 +13,15 @@ while [ -L "$self" ]; do
12
13
  esac
13
14
  done
14
15
  package=$(cd "$(dirname "$self")/.." && pwd)
15
- if command -v node > /dev/null 2>&1 && node "$package/postinstall.mjs" >&2 && [ -L "$package/bin/deadbolt" ]; then
16
+ if ! command -v node > /dev/null 2>&1; then
17
+ echo "Error: @deadbolt/cli isn't fully installed, and finishing that needs node on PATH." >&2
18
+ echo "Run \`node $package/postinstall.mjs\`, or reinstall @deadbolt/cli." >&2
19
+ exit 1
20
+ fi
21
+ node "$package/postinstall.mjs" >&2
22
+ if [ -L "$package/bin/deadbolt" ]; then
16
23
  exec "$package/bin/deadbolt" "$@"
17
24
  fi
18
- echo "Error: @deadbolt/cli's postinstall script was not run, and running it failed." >&2
19
- echo "" >&2
20
- echo "Run \`node $package/postinstall.mjs\`, or reinstall with install scripts enabled." >&2
25
+ echo "Error: @deadbolt/cli isn't fully installed: this platform's package couldn't be installed (above)." >&2
26
+ echo "If @deadbolt/cli was just released, npm may not serve it yet; run deadbolt again in a few minutes." >&2
21
27
  exit 1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deadbolt/cli",
3
- "version": "0.0.0-dev-143",
3
+ "version": "0.0.0-dev-145",
4
4
  "description": "Deadbolt, a local-first password manager: the CLI, with the desktop app built in.",
5
5
  "license": "UNLICENSED",
6
6
  "homepage": "https://deadbolt.pw",
@@ -26,11 +26,11 @@
26
26
  "x64"
27
27
  ],
28
28
  "optionalDependencies": {
29
- "@deadbolt/cli-linux-x64": "0.0.0-dev-143",
30
- "@deadbolt/cli-linux-arm64": "0.0.0-dev-143",
31
- "@deadbolt/cli-linux-x64-musl": "0.0.0-dev-143",
32
- "@deadbolt/cli-linux-arm64-musl": "0.0.0-dev-143",
33
- "@deadbolt/cli-darwin-arm64": "0.0.0-dev-143",
34
- "@deadbolt/cli-darwin-x64": "0.0.0-dev-143"
29
+ "@deadbolt/cli-linux-x64": "0.0.0-dev-145",
30
+ "@deadbolt/cli-linux-arm64": "0.0.0-dev-145",
31
+ "@deadbolt/cli-linux-x64-musl": "0.0.0-dev-145",
32
+ "@deadbolt/cli-linux-arm64-musl": "0.0.0-dev-145",
33
+ "@deadbolt/cli-darwin-arm64": "0.0.0-dev-145",
34
+ "@deadbolt/cli-darwin-x64": "0.0.0-dev-145"
35
35
  }
36
36
  }
package/postinstall.mjs CHANGED
@@ -6,6 +6,11 @@
6
6
  // are one install, and `deadbolt app` opens that Deadbolt.app. Links, never
7
7
  // copies: Deadbolt knows its own files by real path (the Touch ID helper
8
8
  // beside it, the app around it).
9
+ //
10
+ // It never fails the install: a package manager that sees a failed script
11
+ // removes the package, and with it the user's `deadbolt`. When it can't link
12
+ // (npm doesn't serve this platform's package yet, say), it says so and leaves
13
+ // the placeholder bin/deadbolt, which runs this again on first use.
9
14
 
10
15
  import childProcess from "node:child_process"
11
16
  import fs from "node:fs"
@@ -21,26 +26,32 @@ const target = path.join(directory, "bin", "deadbolt")
21
26
  const dependencies = packageJson.optionalDependencies ?? {}
22
27
  const platform = { darwin: "darwin", linux: "linux" }[os.platform()]
23
28
  const arch = { x64: "x64", arm64: "arm64" }[os.arch()]
24
- if (!platform || !arch) throw new Error(`Deadbolt has no build for ${os.platform()}-${os.arch()}`)
25
29
  const base = `@deadbolt/cli-${platform}-${arch}`
26
30
  const npm = process.env.npm_execpath?.endsWith("npm-cli.js") ? [process.execPath, process.env.npm_execpath] : ["npm"]
27
31
 
28
- function isMusl() {
29
- if (platform !== "linux") return false
32
+ // The system's libc, as the install script tells it: musl (Alpine,
33
+ // /lib/ld-musl-*, or `ldd --version` says so), glibc older than 2.28 (too old
34
+ // for the build with the app), else glibc.
35
+ function libc() {
36
+ if (fs.existsSync("/etc/alpine-release")) return "musl"
30
37
  try {
31
- if (fs.existsSync("/etc/alpine-release")) return true
32
- if (fs.readdirSync("/lib").some((name) => name.startsWith("ld-musl-"))) return true
33
- const result = childProcess.spawnSync("ldd", ["--version"], { encoding: "utf8" })
34
- return `${result.stdout || ""}${result.stderr || ""}`.toLowerCase().includes("musl")
35
- } catch {
36
- return false
37
- }
38
+ if (fs.readdirSync("/lib").some((name) => name.startsWith("ld-musl-"))) return "musl"
39
+ } catch {}
40
+ const result = childProcess.spawnSync("ldd", ["--version"], { encoding: "utf8" })
41
+ const text = `${result.stdout || ""}${result.stderr || ""}`
42
+ if (text.toLowerCase().includes("musl")) return "musl"
43
+ const version = text.split("\n")[0].trim().match(/(\d+)\.(\d+)$/)
44
+ if (version && (Number(version[1]) < 2 || (Number(version[1]) === 2 && Number(version[2]) < 28))) return "old-glibc"
45
+ return "glibc"
38
46
  }
39
47
 
40
- // The build with the app first; on musl, the static build first. Either runs
41
- // a glibc system's CLI, so each falls back to the other.
48
+ // On glibc only the build with the app: never the static build quietly in
49
+ // its place. On musl the static build, else the glibc one (which runs where
50
+ // glibc compatibility is installed); on a glibc older than 2.28 only the
51
+ // static build.
42
52
  function packageNames() {
43
- const names = platform === "linux" ? (isMusl() ? [`${base}-musl`, base] : [base, `${base}-musl`]) : [base]
53
+ if (platform !== "linux") return [base]
54
+ const names = { musl: [`${base}-musl`, base], "old-glibc": [`${base}-musl`], glibc: [base] }[libc()]
44
55
  return names.filter((name) => dependencies[name])
45
56
  }
46
57
 
@@ -51,11 +62,19 @@ function binaryIn(packageDir) {
51
62
  return fs.existsSync(helper) ? helper : path.join(packageDir, "bin", "deadbolt")
52
63
  }
53
64
 
65
+ // Links bin/deadbolt to `source` once a link beside it runs, so a build that
66
+ // doesn't run here never replaces the placeholder.
54
67
  function link(source) {
55
68
  if (!fs.existsSync(source)) throw new Error(`Binary not found at ${source}`)
69
+ const staged = `${target}.link`
56
70
  fs.mkdirSync(path.dirname(target), { recursive: true })
57
- fs.rmSync(target, { force: true })
58
- fs.symlinkSync(path.relative(path.dirname(target), source), target)
71
+ fs.rmSync(staged, { force: true })
72
+ fs.symlinkSync(path.relative(path.dirname(target), source), staged)
73
+ if (childProcess.spawnSync(staged, ["--version"], { stdio: "ignore" }).status !== 0) {
74
+ fs.rmSync(staged, { force: true })
75
+ throw new Error(`${source} doesn't run here`)
76
+ }
77
+ fs.renameSync(staged, target)
59
78
  }
60
79
 
61
80
  function resolvePackage(name) {
@@ -94,40 +113,28 @@ function installPackage(name) {
94
113
  return true
95
114
  }
96
115
 
97
- function verify() {
98
- return childProcess.spawnSync(target, ["--version"], { stdio: "ignore" }).status === 0
99
- }
100
-
101
- // Says so when this system's first choice wasn't installable (a registry
102
- // that doesn't serve the new version yet, say) and another build stands in:
103
- // on glibc that's the static build, which has no desktop app.
104
- function fellBack(name, names) {
105
- if (name === names[0]) return
106
- console.error(
107
- `deadbolt: installed ${name}, as ${names[0]}@${dependencies[names[0]]} couldn't be installed` +
108
- (name.endsWith("-musl") ? " (this build has no desktop app)" : "") +
109
- `. Reinstall @deadbolt/cli later to get ${names[0]}.`,
110
- )
111
- }
112
-
113
116
  function main() {
117
+ if (!platform || !arch) return `Deadbolt has no build for ${os.platform()}-${os.arch()}`
114
118
  const names = packageNames()
115
119
  for (const name of names) {
116
120
  try {
117
- link(binaryIn(resolvePackage(name)))
118
- if (verify()) return fellBack(name, names)
121
+ return link(binaryIn(resolvePackage(name)))
119
122
  } catch {
120
- if (installPackage(name) && verify()) return fellBack(name, names)
123
+ try {
124
+ if (installPackage(name)) return
125
+ } catch {}
121
126
  }
122
127
  }
123
- throw new Error(
124
- `Failed to install Deadbolt. Try manually installing ${names.map((name) => JSON.stringify(name)).join(" or ")}.`,
128
+ const wanted = names.map((name) => `${name}@${dependencies[name]}`).join(" or ") || `a package for ${base}`
129
+ return (
130
+ `couldn't install ${wanted}; if it was just released, npm may not serve it yet. ` +
131
+ "`deadbolt` finishes this when it next runs, or reinstall @deadbolt/cli."
125
132
  )
126
133
  }
127
134
 
128
135
  try {
129
- main()
136
+ const problem = main()
137
+ if (problem) console.error(`deadbolt: ${problem}`)
130
138
  } catch (error) {
131
- console.error(error instanceof Error ? error.message : String(error))
132
- process.exit(1)
139
+ console.error(`deadbolt: ${error instanceof Error ? error.message : String(error)}`)
133
140
  }