par3-cli 0.1.51 → 0.1.52-nightly.20261104.974

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.
Files changed (2) hide show
  1. package/bin/t3.mjs +55 -3
  2. package/package.json +4 -1
package/bin/t3.mjs CHANGED
@@ -90,6 +90,24 @@ async function install(dir, key) {
90
90
  });
91
91
  if (extract.status !== 0) throw new Error(`could not extract ${archive}`);
92
92
  fs.rmSync(file);
93
+ const validate = spawnSync(
94
+ path.join(staging, process.platform === "win32" ? "t3.exe" : "t3"),
95
+ ["--version"],
96
+ {
97
+ stdio: ["ignore", "ignore", "pipe"],
98
+ encoding: "utf8",
99
+ timeout: 30_000,
100
+ killSignal: "SIGKILL",
101
+ },
102
+ );
103
+ if (validate.status !== 0) {
104
+ throw executableError(
105
+ key,
106
+ validate.error?.message ||
107
+ validate.stderr?.trim() ||
108
+ `--version exited with ${validate.signal || validate.status}`,
109
+ );
110
+ }
93
111
  fs.writeFileSync(path.join(staging, ".install-complete"), `${version}\n`);
94
112
  publish(staging, dir);
95
113
  } finally {
@@ -109,11 +127,36 @@ const isComplete = (dir) => {
109
127
  function publish(staging, dir) {
110
128
  try {
111
129
  fs.renameSync(staging, dir);
130
+ return;
112
131
  } catch (error) {
113
132
  if (isComplete(dir)) return;
114
133
  if (!fs.existsSync(dir)) throw error;
115
- fs.rmSync(dir, { recursive: true, force: true });
116
- fs.renameSync(staging, dir);
134
+ }
135
+ const lock = `${dir}.lock`;
136
+ try {
137
+ fs.mkdirSync(lock);
138
+ } catch (error) {
139
+ if (error.code !== "EEXIST") throw error;
140
+ throw new Error(
141
+ `another t3 is installing ${version}. If none is running, delete ${lock} and try again.`,
142
+ { cause: error },
143
+ );
144
+ }
145
+ const stale = `${staging}.stale`;
146
+ try {
147
+ // A peer may have published between our marker check and taking the lock.
148
+ if (isComplete(dir)) return;
149
+ // Move it aside rather than deleting in place: once the folder is empty, a
150
+ // peer's first rename can land in it.
151
+ fs.renameSync(dir, stale);
152
+ try {
153
+ fs.renameSync(staging, dir);
154
+ } catch (error) {
155
+ if (!isComplete(dir)) throw error;
156
+ }
157
+ } finally {
158
+ fs.rmSync(lock, { recursive: true, force: true });
159
+ fs.rmSync(stale, { recursive: true, force: true });
117
160
  }
118
161
  }
119
162
 
@@ -147,12 +190,21 @@ async function main() {
147
190
  for (const signal of ["SIGINT", "SIGTERM", "SIGHUP"]) {
148
191
  process.on(signal, () => (ipc || signal !== "SIGINT") && child.kill(signal));
149
192
  }
150
- child.on("error", (error) => fail(error));
193
+ child.on("error", (error) => fail(executableError(key, error.message)));
151
194
  child.on("exit", (code, signal) =>
152
195
  process.exit(code ?? 128 + (os.constants.signals[signal] || 1)),
153
196
  );
154
197
  }
155
198
 
199
+ function executableError(key, detail) {
200
+ return new Error(
201
+ `could not run Par3 ${version} for ${key}: ${detail}` +
202
+ (process.platform === "linux"
203
+ ? "\nThe Linux release requires glibc; musl-based systems such as Alpine are not supported. Use a glibc-based Linux environment."
204
+ : ""),
205
+ );
206
+ }
207
+
156
208
  function fail(error) {
157
209
  process.stderr.write(`par3: ${error.message}\n`);
158
210
  process.exit(1);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "par3-cli",
3
- "version": "0.1.51",
3
+ "version": "0.1.52-nightly.20261104.974",
4
4
  "description": "Par3 CLI. Downloads the self-contained executable for this platform from the GitHub Release.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -18,5 +18,8 @@
18
18
  ],
19
19
  "engines": {
20
20
  "node": ">=20.11"
21
+ },
22
+ "t3code": {
23
+ "databaseMigration": 81
21
24
  }
22
25
  }