craft-native 0.0.436 → 0.0.443
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/LICENSE.md +30 -0
- package/README.md +35 -0
- package/dist/android/src/index.d.ts +44 -0
- package/dist/android/src/index.js +82 -3
- package/dist/android/templates/AndroidManifest.xml.template +2 -1
- package/dist/android/templates/CraftHealthServices.kt.template +2 -0
- package/dist/android/templates/CraftNativeCapabilities.kt.template +42 -1
- package/dist/android/templates/CraftNativeMotion.kt.template +269 -0
- package/dist/android/templates/CraftNativeServices.kt.template +6 -1
- package/dist/android/templates/CraftPassiveSteps.kt.template +159 -0
- package/dist/android/templates/CraftRecordingPressure.kt.template +156 -0
- package/dist/android/templates/LocationRecordingService.kt.template +77 -9
- package/dist/android/templates/MainActivity.kt.template +3 -0
- package/dist/android/templates/MainActivityNative.kt.template +132 -1
- package/dist/cli.js +289 -108
- package/dist/components/index.d.ts +25 -0
- package/dist/index.cjs +207 -27
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +321 -140
- package/dist/installer-background.d.ts +2 -0
- package/dist/installer-layout.d.ts +1 -0
- package/dist/ios/src/index.d.ts +23 -1
- package/dist/ios/src/index.js +49 -9
- package/dist/ios/templates/CraftApp.swift +14 -0
- package/dist/ios/templates/CraftHealth.swift +2 -1
- package/dist/ios/templates/CraftHealthBackground.swift +308 -0
- package/dist/ios/templates/CraftNativeActions.swift +153 -0
- package/dist/ios/templates/CraftNativePedometer.swift +181 -0
- package/dist/ios/templates/CraftNativeScreen.swift +76 -0
- package/dist/macos/src/index.js +1 -1
- package/dist/macos/src/toolchain.d.ts +1 -1
- package/dist/macos/templates/CraftMacApplication.swift +124 -39
- package/dist/macos/templates/CraftMacRenderer.swift +111 -33
- package/dist/macos/templates/CraftMacRuntime.swift +270 -81
- package/dist/package.d.ts +40 -0
- package/dist/types.d.ts +26 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -77,8 +77,8 @@ __export(exports_updater, {
|
|
|
77
77
|
|
|
78
78
|
// src/index.ts
|
|
79
79
|
import { execFile as execFile2, spawn as spawn4 } from "child_process";
|
|
80
|
-
import { writeFileSync as
|
|
81
|
-
import { join as
|
|
80
|
+
import { writeFileSync as writeFileSync5 } from "fs";
|
|
81
|
+
import { join as join6 } from "path";
|
|
82
82
|
import { promisify as promisify2 } from "util";
|
|
83
83
|
|
|
84
84
|
// src/binary-resolver.ts
|
|
@@ -117,7 +117,7 @@ function craftBinaryNotFoundMessage(triedPath) {
|
|
|
117
117
|
`);
|
|
118
118
|
}
|
|
119
119
|
// package.json
|
|
120
|
-
var version = "0.0.
|
|
120
|
+
var version = "0.0.443";
|
|
121
121
|
|
|
122
122
|
// src/sdk-version.ts
|
|
123
123
|
var SDK_VERSION = version;
|
|
@@ -236,6 +236,134 @@ function removeTempDir(dir) {
|
|
|
236
236
|
unhook();
|
|
237
237
|
}
|
|
238
238
|
|
|
239
|
+
// src/installer-layout.ts
|
|
240
|
+
import { execFileSync } from "child_process";
|
|
241
|
+
import { statSync as statSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
242
|
+
import { join as join2 } from "path";
|
|
243
|
+
/*!
|
|
244
|
+
* Format implementation adapted from MIT-licensed references:
|
|
245
|
+
* Copyright (c) 2020 Kenty Gwend
|
|
246
|
+
* Copyright (c) 2026 John Campion Jr
|
|
247
|
+
* Copyright (c) 2014 Alastair Houghton
|
|
248
|
+
* Copyright (c) 2022 Russell Keith-Magee
|
|
249
|
+
*
|
|
250
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
251
|
+
* of this software and associated documentation files (the "Software"), to deal
|
|
252
|
+
* in the Software without restriction, including without limitation the rights
|
|
253
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
254
|
+
* copies of the Software, and to permit persons to whom the Software is
|
|
255
|
+
* furnished to do so, subject to the following conditions:
|
|
256
|
+
* The above copyright notice and this permission notice shall be included in
|
|
257
|
+
* all copies or substantial portions of the Software.
|
|
258
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
259
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
260
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
261
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
262
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
263
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
264
|
+
* SOFTWARE.
|
|
265
|
+
*/
|
|
266
|
+
var number = (value, bytes = 4) => {
|
|
267
|
+
const out = Buffer.alloc(bytes);
|
|
268
|
+
out.writeUIntBE(value, 0, bytes);
|
|
269
|
+
return out;
|
|
270
|
+
};
|
|
271
|
+
var unicode = (value) => Buffer.from(value, "utf16le").swap16();
|
|
272
|
+
var pascal = (value, size) => {
|
|
273
|
+
const out = Buffer.alloc(size);
|
|
274
|
+
const bytes = Buffer.from(value).subarray(0, size - 1);
|
|
275
|
+
out[0] = bytes.length;
|
|
276
|
+
bytes.copy(out, 1);
|
|
277
|
+
return out;
|
|
278
|
+
};
|
|
279
|
+
var tag = (id, data) => Buffer.concat([number(id, 2), number(data.length, 2), data, Buffer.alloc(data.length % 2)]);
|
|
280
|
+
function backgroundAlias(mount, volume) {
|
|
281
|
+
const folder = ".background";
|
|
282
|
+
const filename = "installer.png";
|
|
283
|
+
const directory = statSync2(join2(mount, folder));
|
|
284
|
+
const file = statSync2(join2(mount, folder, filename));
|
|
285
|
+
const date = (ms) => Math.floor(ms / 1000) + 2082844800;
|
|
286
|
+
const volumeDate = date(statSync2(mount).birthtimeMs);
|
|
287
|
+
const fileDate = date(file.birthtimeMs);
|
|
288
|
+
const preciseDate = (value) => {
|
|
289
|
+
const out = Buffer.alloc(8);
|
|
290
|
+
out.writeBigUInt64BE(BigInt(value) * 65536n);
|
|
291
|
+
return out;
|
|
292
|
+
};
|
|
293
|
+
const body = Buffer.concat([
|
|
294
|
+
number(0, 2),
|
|
295
|
+
pascal(volume, 28),
|
|
296
|
+
number(volumeDate),
|
|
297
|
+
Buffer.from("H+"),
|
|
298
|
+
number(0, 2),
|
|
299
|
+
number(directory.ino),
|
|
300
|
+
pascal(filename, 64),
|
|
301
|
+
number(file.ino),
|
|
302
|
+
number(fileDate),
|
|
303
|
+
Buffer.alloc(8),
|
|
304
|
+
number(65535, 2),
|
|
305
|
+
number(65535, 2),
|
|
306
|
+
Buffer.alloc(16),
|
|
307
|
+
tag(0, Buffer.from(folder)),
|
|
308
|
+
tag(16, preciseDate(volumeDate)),
|
|
309
|
+
tag(17, preciseDate(fileDate)),
|
|
310
|
+
tag(1, number(directory.ino)),
|
|
311
|
+
tag(2, Buffer.from(`${volume}:${folder}:\x00${filename}`)),
|
|
312
|
+
tag(14, Buffer.concat([number(filename.length, 2), unicode(filename)])),
|
|
313
|
+
tag(15, Buffer.concat([number(volume.length, 2), unicode(volume)])),
|
|
314
|
+
tag(18, Buffer.from(`/${folder}/${filename}`)),
|
|
315
|
+
tag(19, Buffer.from(`/Volumes/${volume}`)),
|
|
316
|
+
number(65535, 2),
|
|
317
|
+
number(0, 2)
|
|
318
|
+
]);
|
|
319
|
+
return Buffer.concat([Buffer.alloc(4), number(body.length + 8, 2), number(2, 2), body]);
|
|
320
|
+
}
|
|
321
|
+
function plist(values) {
|
|
322
|
+
const escape = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">");
|
|
323
|
+
const entries = Object.entries(values).map(([key, value]) => {
|
|
324
|
+
const data = Buffer.isBuffer(value) ? `<data>${value.toString("base64")}</data>` : typeof value === "boolean" ? `<${value}/>` : typeof value === "number" ? ["viewOptionsVersion", "backgroundType"].includes(key) ? `<integer>${value}</integer>` : `<real>${value}</real>` : `<string>${escape(value)}</string>`;
|
|
325
|
+
return `<key>${key}</key>${data}`;
|
|
326
|
+
}).join("");
|
|
327
|
+
return execFileSync("plutil", ["-convert", "binary1", "-o", "-", "-"], { input: `<?xml version="1.0"?><plist version="1.0"><dict>${entries}</dict></plist>` });
|
|
328
|
+
}
|
|
329
|
+
function writeInstallerLayout(mount, volume, appName) {
|
|
330
|
+
const records = [];
|
|
331
|
+
const blob = (name, code, data) => records.push({ name, code, type: "blob", data: Buffer.concat([number(data.length), data]) });
|
|
332
|
+
blob(".", "bwsp", plist({ ShowStatusBar: false, ShowToolbar: false, ShowTabView: false, ShowSidebar: false, ContainerShowSidebar: false, ShowPathbar: false, SidebarWidth: 0, PreviewPaneVisibility: false, WindowBounds: "{{100, 100}, {660, 460}}" }));
|
|
333
|
+
blob(".", "icvp", plist({ viewOptionsVersion: 1, backgroundType: 2, backgroundColorRed: 1, backgroundColorGreen: 1, backgroundColorBlue: 1, backgroundImageAlias: backgroundAlias(mount, volume), iconSize: 96, arrangeBy: "none", gridSpacing: 100, gridOffsetX: 0, gridOffsetY: 0, scrollPositionX: 0, scrollPositionY: 0, textSize: 13, labelOnBottom: true, showIconPreview: true, showItemInfo: false }));
|
|
334
|
+
records.push({ name: ".", code: "vSrn", type: "long", data: number(1) });
|
|
335
|
+
records.push({ name: ".", code: "icvl", type: "type", data: Buffer.from("icnv") });
|
|
336
|
+
records.push({ name: ".", code: "vstl", type: "type", data: Buffer.from("icnv") });
|
|
337
|
+
for (const [name, x, y] of [[appName, 160, 190], ["Applications", 500, 190], [".background", 500, 600]])
|
|
338
|
+
blob(name, "Iloc", Buffer.concat([number(x), number(y), Buffer.from("ffffffffffff0000", "hex")]));
|
|
339
|
+
records.sort((a, b) => {
|
|
340
|
+
const left = a.name.normalize("NFD").toLowerCase();
|
|
341
|
+
const right = b.name.normalize("NFD").toLowerCase();
|
|
342
|
+
return left < right ? -1 : left > right ? 1 : a.code < b.code ? -1 : 1;
|
|
343
|
+
});
|
|
344
|
+
const leaf = Buffer.concat([number(0), number(records.length), ...records.map((record) => {
|
|
345
|
+
const name = record.name.normalize("NFD");
|
|
346
|
+
return Buffer.concat([number(name.length), unicode(name), Buffer.from(record.code + record.type), record.data]);
|
|
347
|
+
})]);
|
|
348
|
+
if (leaf.length > 4096)
|
|
349
|
+
throw new Error("Installer layout exceeds the Finder store leaf capacity");
|
|
350
|
+
const offsets = Buffer.alloc(1024);
|
|
351
|
+
offsets.writeUInt32BE(2048 | 11, 0);
|
|
352
|
+
offsets.writeUInt32BE(32 | 5, 4);
|
|
353
|
+
offsets.writeUInt32BE(4096 | 12, 8);
|
|
354
|
+
const free = Array.from({ length: 32 }, (_, power) => power >= 6 && power <= 10 || power >= 13 && power <= 30 ? Buffer.concat([number(1), number(2 ** power)]) : number(0));
|
|
355
|
+
const root = Buffer.concat([number(3), number(0), offsets, number(1), Buffer.from([4]), Buffer.from("DSDB"), number(1), ...free]);
|
|
356
|
+
const out = Buffer.alloc(8196);
|
|
357
|
+
Buffer.concat([number(1), Buffer.from("Bud1"), number(2048), number(root.length), number(2048), Buffer.alloc(16)]).copy(out);
|
|
358
|
+
Buffer.concat([number(2), number(0), number(records.length), number(1), number(4096)]).copy(out, 36);
|
|
359
|
+
root.copy(out, 2052);
|
|
360
|
+
leaf.copy(out, 4100);
|
|
361
|
+
writeFileSync2(join2(mount, ".DS_Store"), out);
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// src/installer-background.ts
|
|
365
|
+
var installerBackground = Buffer.from("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vpDh4twyyyyT7S6//PKDdsOXbt/xkT2miF0Kvq200kqZDVsFuI4P6667buZw9913p5deeilnMWGz3nrr5euIqtEURBYCiPFjn20ASyyxRO4TW1wPYQkXGLz44ovZD67hB8v0d955Z3rllVeyYAs/iEnZD2wg5LDx+OOPp9dffz33AcsNN9wwLb744pnpaHwn0/3GG29k34kpMV5yySXTWmutlV+won/en3zyyfTCCy/ksWIbP5577rnclnHgcxT8wxbbF2DL+JZaaqlsk72v2IQZBdvcp9ShEKsVV1xx8F58880309JLL53jsvLKK4/5/ZI79R8JSGDMCSgoxxypBiUwPgT+4z/+I++vZLI+7LDD8uT9v//7v1l4cA6BxaR/zDHH5Ad5YkJHXCFK/u///i/NmjUrixrEAAVxggA44ogj0he+8IUsAv7xH/8x70ukTgjRc889N51//vlp2sADNFynHYIDMYHda6+9dj677INEeH7xi19MhxxyyKAA7ZYUfSJQ/vu//zuPhTEeeOCB6fd///fT7Nmz01/+5V/m6wjaP/7jP07w4fx7772XRSSiZ9ddd00nnHBCFjVwG64gnB544IFBO4gzxCNMEUGbb755OvbYY9P06dOzEKL+jTfemLcLYBcRiG//8z//k+65557sB+IMUfe5z30u/eqv/moW9REreLN/8Qc/+EG68MILs4B9//33Mz9ELAL2q1/9am6L77AYqmALGz/60Y/ylwj2qVLof4UVVsgcyPQi8Fjiht2jjz6a+6ct5aSTTsovxsfyN+Om/R133JHjzpgQ0PiCeMUu2xF+5Vd+JY8L/+j3r//6r7Mwhd3v/u7v5rrf/e53s4BlfGRF+YL05S9/Od8v9D3c2LhukYAEektAQdlb/vYugY4JkBlj4mdSv+KKK7JIJLvDZ15MwGSCeEocIcPeQiZwMkN/9Vd/la666qo8kSNeED7UZzJHRCAWOEaoIprIpCF6EHAURAHXEWq0Q3CQJeQp7VtvvTXbw274gRglE8o+TAQHomssCwIx9vlF5os+OUfhOEQMPuEv74zjvPPOy6LpL/7iL/L4hhIuCKRbbrkl/emf/mkW7Qip4hgZF0L63nvvzXUQqvBCEOIHx/CHPSzCD3zBjx//+MeZ5R/90R9ln6P+3//932chSH36o198ZMxkW//sz/4sx+jQQw/NfeXG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6gzXR1zrxA7jHS7u2B5NnfCh3XsnfrWz4zkJSKA3BOZf/+mND/YqAQlIQAINIsCXCwQqr5HEdoOwOFQJ1JqAGcpah0/nJSABCdSHAFlM9nEeddRR+WeMEJM8BER20iIBCdSbgHso6x0/vZeABCRQOwI8xBN7QclWKihrF0IdlsCnCJih/BQST0hAAhKQQJUE4in4KvvQtgQkML4E3EM5vrztTQISkIAEJCABCUw4AgrKCRdSByQBCUhAAhKQgATGl4CCcnx525sEJCABCUhAAhKYcAQUlBMupA5IAhKQgAQkIAEJjC8BBeX48rY3CUhAAhKQgAQkMOEIKCgnXEgdkAQkIAEJSEACEhhfAgrK8eVtbxKQgAQkIAEJSGDCEVBQTriQOiAJSEACEpCABCQwvgQUlOPL294kIAEJSEACEpDAhCOgoJxwIXVAEpCABCQgAQlIYHwJKCjHl7e9SUACEpCABCQggQlHQEE54ULqgCTQGwKTJk1KkydPTrxbekOgyH+hhRbK8ajSk/Hoo0r/tS0BCYwdAQXl2LHUkgQqIYBAY+IuvhAOU6ZM6al4w58Qj7zPmTMnPfvss+mDDz6ohMN4Gy2Obyz6jviNha12Nj788MPM/7333stxeemll/LndnU7ORf3YbTl84svvjimfYRt3yUggfoRmFI/l/VYAs0hgHB86KGH0u23355Fwrx583LWaamllkprr712Wn/99bOw/Pjjj8cVCv29/vrrabHFFkuLLrpo9umJJ55IF1xwQTriiCOybx999NG4+jSWneE741t88cXTIosskuDeTYHXFVdckebOnZv22muvHLNu7JXbIlZfffXV9MMf/jDtu+++afPNN0/nnXdeeuONN9Lxxx+fx9HNGBCPCNX3338/ce9xXzKWc889N73zzjvpG9/4Rr4XuumjPCY/S0AC9SJghrJe8dLbhhFAKLz88svprrvuSm+99Vae0N9888105513ptNPPz395Cc/Se++++58mUImf0o5w8Z5hADn25XydT6HrWJ9bCBeTjnllHTvvfemqVOn5suIJrKTvIetdu2pHNeH8qXY33D1sRM2hrI5mjrF/hjfK6+8kk4++eT0wAMPDI4v6hT74Xikgr3nn38+3XzzzemWW25JzzzzzKcEJXbCFuPhFZ/D/kjjRMzBHzFMn5tttlmaMWNGWnjhhQcFMTaH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SEACEpCABCRQHQEFZXVstSwBCUhAAhKQgAQaQUBB2YgwO0gJSEACEpCABCRQHQEFZXVstSwBCUhAAhKQgAQaQUBB2YgwO0gJSEACEpCABCRQHQEFZXVstSwBCUhAAhKQgAQaQUBB2YgwO0gJSEACEpCABCRQHQEFZXVstSwBCUhAAhKQgAQaQUBB2YgwO0gJSEACEpCABCRQHQEFZXVstSwBCUhAAhKQgAQaQUBB2YgwO0gJSEACEpCABCRQHQEFZXVstSwBCUhAAhKQgAQaQUBB2YgwO0gJSEACEpCABCRQHQEFZXVstSwBCUhAAhKQgAQaQUBB2YgwO0gJSEACEpCABCRQHQEFZXVstSwBCUhAAhKQgAQaQUBB2YgwO0gJSEACEpCABCRQHQEFZXVstSwBCUhAAhKQgAQaQUBB2YgwO0gJSEACEpCABCRQHQEFZXVstSwBCUhAAhKQgAQaQUBB2YgwO0gJSEACEpCABCRQHQEFZXVstSwBCUhAAhKQgAQaQUBB2YgwO0gJSEACEpCABCRQHQEFZXVstSwBCUhAAhKQgAQaQUBB2YgwO0gJSEACEpCABCRQHQEFZXVstSwBCUhAAhKQgAQaQUBB2YgwO0gJSEACEpCABCRQHQEFZXVstSwBCUhAAhKQgAQaQUBB2YgwO0gJSEACEpCABCRQHQEFZXVstSwBCUhAAhKQgAQaQUBB2YgwO0gJSEACEpCABCRQHQEFZXVstSwBCUhAAhKQgAQaQUBB2YgwO0gJSEACEpCABCRQHQEFZXVstSwBCUhAAhKQgAQaQUBB2YgwO0gJSEACEpCABCRQHQEFZXVstSwBCUhAAhKQgAQaQUBB2YgwO0gJSEACEpCABCRQHQEFZXVstSwBCUhAAhKQgAQaQUBB2YgwO0gJSEACEpCABCRQHQEFZXVstSwBCUhAAhKQgAQaQUBB2YgwO0gJSEACEpCABCRQHQEFZXVstSwBCUhAAhKQgAQaQUBB2YgwO0gJSEACEpCABCRQHQEFZXVstSwBCUhAAhKQgAQaQUBB2YgwO0gJSEACEpCABCRQHQEFZXVstSwBCUhAAhKQgAQaQUBB2YgwO0gJSEACEpCABCRQHQEFZXVstSwBCUhAAhKQgAQaQUBB2YgwO0gJSEACEpCABCRQHQEFZXVstSwBCUhAAhKQgAQaQUBB2YgwO0gJSEACEpCABCRQHQEFZXVstSwBCUhAAhKQgAQaQUBB2YgwO0gJSEACEpCABCRQHQEFZXVstSwBCUhAAhKQgAQaQUBB2YgwO0gJSEACEpCABCRQHQEFZXVstSwBCUhAAhKQgAQaQUBB2YgwO0gJSEACEpCABCRQHQEFZXVstSwBCUhAAhKQgAQaQUBB2YgwO0gJSEACEpCABCRQHQEFZXVstSwBCUhAAhKQgAQaQUBB2YgwO0gJSEACEpCABCRQHQEFZXVstSwBCUhAAhKQgAQaQUBB2YgwO0gJSEACEpCABCRQHQEFZXVstSwBCUhAAhKQgAQaQUBB2YgwO0gJSEACEpCABCRQHQEFZXVstSwBCUhAAhKQgAQaQUBB2YgwO0gJSEACEpCABCRQHQEFZXVstSwBCUhAAhKQgAQaQUBB2YgwO8iqCUyaNCnxakpZaKGFFni8nbRpCs9Ox1m85+BbRanK7ki+jvZ+Gem/vZGuj+SH1yUggdERqOb/QKPr21oSqAUBJrapU6e2fU2ZMiWP4b333ktz586txXjGwsm33347ffjhh9nUSBN2XH/rrbcSnJpWGD/3z1gLs48++ijznDdvXkYK348//rhrvGU/x8rugjgGs3feeSe9++67IzZ7//33E6+hykjXh2rneQlIYMEITP7mN7/5rQVrYm0JNIfA5MmT01NPPZWuueaa9Mgjj+TXo48+mh588MH8evnll9NKK62UzjrrrCwaVltttRQT/ESkhNh45ZVX0g9+8IO08MILp7XXXjtP+h988EEef3nMCAOE9mWXXZYuvfTSzGb99dcfE+FT7qsfP8PrzTffTD//+c/TIosskpZddtkxuT+4L7kPf/rTn6ZNN900vfTSS+mUU05Jyy23XFp11VU75otIRcTxRYnX008/ne2uuOKKaZVVVunY7oLG5uqrr04XX3xxZrfhhhsO2Rwfr7rqqnT//fenjTba6FP14HTFFVekJ554Im2wwQafuu4JCUhg7Ah8kl4ZO3taksCEIoAgIsOBcCTL9Oqrr+ZJbq211srjRDAgIDlP9o0JjjZkisrZIiY3rlGfayE8Oc8x14ZqW4RKn9Fv2OI6bTmPbd7b2Yo67a6Nxg/6W2yxxdJmm22WVl555dzPDTfckF577bV06KGHZjeL44bHk08+me644460xx57ZPGDaCn2RSPOUYZixHiixBjpJ+pzXOy3WJexhn3Oh62oHzaCJe/t6gU72lGnOIZomxsW/qHOvffem6688spEVneNNdYoXP1kvNgbKl60b3cdX+bMmZPFPWNbaqmlckwQlDEubPIK32JcMbbyNWL14osvpnPPPTfHki9H2N18882zEC7bxQ7n4nyRD7bjerHfoVjnygP/cJ17iXtqxx13TFtvvXVcymMp+0yfcEW0Rwk/eOdFhrXoQ/E65+PeiPOc45j3GFvY9l0CEhiagIJyaDZekUBe1iWjRnaDCZdM02233Za+9KUv5YwTkw7ZOSYgXs8++2zOFpHNWX755fOkxHkK18juLb300mn11VfPkyeT2XPPPZfPMSm+8cYbWaiREWo3mTHhYuP555/P7bHDpI8fCFom4xVWWCH3heDgOlkxTfXbxQAAE0BJREFUbOEHy9RknVhOJLOKn7TlPH4ss8wy6fXXX88TNIKx7Ad1F1100bTFFlvkui+88ELihe+PP/54tsd16tEf4yGThhgPsYMvsIADQhwG6667bm7zzDPP5HNLLLFEFl9kQanPeMnwwZo2ZOGwR338ZSy8ysz4IsDYGEsUztEn5/ATHnBbcsklMy/65DycsQdPxkLfZALpB2ETccMen+NLRvQTbR544IFslywZPGCMXfyCA/cJfcEQ2+EXmd3yda5Rh4L9EFjw2nLLLQczoJxHHOIv1+BFXOiXdvjMdb4cIBy5xj2B+IcnvnKvEaOwS1vsMgbGDqOIA8cIO17cj1ynrLnmmrmP6Jc40jd26Zc+aEvBL3x46KGH8nniG9eK4+E+QJjje7TjOoV34kRM+SJI//RB/xT6IPasOsCb2DIGCu3CN+5pmNBP+JAr+Y8EJDAkAQXlkGi8IIFPCDCh8GJSismF45ikqMWkdfvtt6ebb745CwUmsy984Qs5a0Sb6667LovRxRdfPC8pIsj222+/nGX68Y9/nCdHJjREBELjwAMPTBtvvPFg9iT6QJxccMEFeeJkYmRSJTPIxPnwww+nn/zkJ3mCRCBhBxF82GGHZbGDwDz//PNzPdrxea+99krbbbddzuKceeaZWVThO35g/+CDD85immMKEzJLoqeeemraZ599smBBhHCeJUrGtM4662ROMMGnW2+9Nfty0UUXpe233z7NmDEjnXHGGdl3BASCnTZsK7j++uuzAEIcc+6ggw7KYhTb1MUm70z26623XhYfxAF21N1kk00GmTF2eN14443p+OOPz3apx/YEbO+///45c8h1RBfjwibssY8/+HHEEUdk3oiN0047LX31q1/N108//fREPPGHjG1ZUCJuEDa0415g2R8eMKAgaOCAcCKLBuOIPfYQmUNdJ2sYhX6w9cMf/jDfCywRw5z+EMfEki8VhxxySBajjIsXvjM+RCr3CH7CgvuV6/g0ffr0bJfrfKniSwP3GH5SiDvcWW6GNZlYvoTAEi6IRr58cb9hM1hjO+IbAj54cQ/AgqVsOHCf3XLLLXnpmrhwf9IH9z2CMAq+MFb++8AXviDwRQZbfLHiOseXXHJJuvvuuwdjt/vuu6ddd901i2Dua9rh+zbbbJP/u4r/5qMf3yUggfYEFJTtuXhWAgtEgImKzMlXvvKV/I5IZFJnQow9mHvuuWdewmMvJpPytGnTsphicqbt4YcfnoULogVxWtwTxmRI9ofJkHZMskye2GFv4tFHHz2YnWHSPfbYY/Okz/XLL788XyeziiA48sgj80R/7bXXpp/97GfZB4Ra0Q8mUQQTbRB85YI/FJaxmXzJGuJ/ZBS5hj3EI3URGl/72tdyZgoxwTUmboQKYge/EN3YQ8SQmYMhAoQJP/r74he/OMiIfayIPcTgj370o3TPPfdkQUnfFMYAQ8aJfewSCzJsW221Vd4Py9IqoposHOLv7LPPTjfddFP63Oc+l/uMfj+x+ImAimP4k11k3PhQLrRluZvx8eUAf/ERJvDmOjbwk5hwDyC6iTFii+sIYK4fddRRmS3Xife6Axnddr4hyogFMWdMu+22Wx7vOeeck2bPnp1Z8mVj5syZWTCRiWPvJb4hdA844IDMAPGJD4hMSviCsEcAI5AZw4UXXpj3cbKXNuog6rFFZpoYku1EqMOa8/DmvwE4R1aWMfLfEPFCBHLfEmu+KOEj+yC33XbbtPPOO+cMKl9+2GeJn1H4AsH9ipj8pV/6pYSwhjf7TLHDdbZeEBNsw5D/RmfNmpWPGQ+8yUrCDRFc/NIY/fguAQm0J9DamNT+umclIIFREGBCZCIl28NyHtmcEE6IIyZb6tx11105e8OkyUTLeV5kLMm2ID6YfBFpiA2uURAKLFGSUdpll11yHyx97rTTTvk8YpPCpMmky6SPL+xDY9kTX8gkstyKoGCiZQIl24N/2KcvRAgiifZMuPiB+As/cieFf7ARbRGT5XpcwycK16lP4TzCigwWQgoxR5+IBgQxYoCHTfAZDogNhC2ZJjiRoULE8JkxIRjIilEvCsfU4xoPbcCf5VTGh2iAP8dkougTMUMMOT/cmMM+Y2GPH5wZW7HAAX/oD2GGUCGDyZIv8aAt/lB22GGHnFUm9sSOmCDkgivX2ZoQ12PvJNfLhXOwpGALoQsjBCm+cn3ffffN7O677768ZYB7kTjjM8cU3iNWfOYa4o97h3sKrvjDvcg48Zc6ZD3pBx7TBr748KWBe5N7gDhx37FthOtkGLmHi6Kt7AN+MB7ac69jn7hz7xAn7o0o8OR+Id7890RMqQf3uC9og1DEJ/5bpBBrluijb/znHivHNPrxXQISaE/ADGV7Lp6VwAIRYDJiAmPi4jgmeybLEDqIiygIKcRMCBdEVUysxYk86mMTIUEpZk7ok3YIFgr9MuniB8dRl7b4wYsMDW2wifBl0mYypn7RjxhDNjyKf7CBzXIJ4RTvXKce/eIn5/ELX+kz6iFGELshBkKEBadiXey165s2iAsysWTuWHJGqNI3IgpWtKNP3umTjFj02c4m/kf94hg4H4V+Hx/IipINRbjRL34jFrkPED0U6kWMuI4/FL44IAaHu97ON87BknYRS3xFrIddtj0gvDjH/RP3INeDfbznRr/4B58ocZ9TB3955xp90y8vzvGKGOELWUGylGQRWc7mCwvbDhgndaPEcbwTJwRljAdOtEFMFn0nZtznXKNQj/7xlxLXaUdMsI/PfIGLGIT/tI3rtA1fOLZIQALtCSgo23PxrAQWmEB50uEzExoTN9kOluEQkUxWZKkic0dH5bblzmlDRohCNiX2jnGMbSbR2DeJCCOjFXW5TtaU9vTJ8ioTJyKUzBIPUUSmZyQ/stE2/zBZY5MyWhvU4wUjxsMkj3iAF/5E1rKbTBF+IVxYHmVJHcGBoKSQZUPcIYbInsEj+kQoMh6u4R/cyNBhr1iGGivxIhtHHNjqQMEGe2w5T7YNMRPZMTKj9EfsaAsDbA91nVgifMuFtmRMEa5ktBFL+HznnXfm+wSWLAmTHSQbTeb6xBNPnC9mMSbGHYVz9InP9ItdCg9FUfCXPZ9DFfzBN/bYUsgYs5eVWJBFjPuv3J5+GQ9x4F4l28g5+uW+RSyGv/DFD5baiSX/TRAz2pGl5TpxRuSzVQIWsIET/32U/Wes2CEuRRZlH/0sAQl8QkBB6Z0ggQUgwORVFhU051xMbHyOepxH0JBdYX8eS7pMXDx4wNIjQqLclomXV7HwmeVBlvvYx4YQYLJjfyBLdJEpos2sgT1hZKmYpLnO3kGu8yAHPpx33nl5+ZhJncmXvY3FrFP0286PuBY+M+liGyGEYGNvIhN5OxbRlvdoH8csZ5O9Yq8fAoMMGvsd2SNHH9QvMin7Vv4cfeEHwgP77MdkTyviFaFGdpZz7NeDEcuhjIM+ERFkkdnLysMk2GB8tItSHEOc4x3xwZPS7BNk/ydL6dTFJu88PMP48INz+ICfZMnoixjzxYMvCFxnryGFGLFcHNf5MoG9KBzz4joijIdTEK4IKOxyvyEEscmeSQpCE1+j0AdjZKyMo5iBJa4IQO4v6mAHu7BFyBGDoj/Y5DPxQ8iffPLJ2TfuV0Qcgg6BV4wrbeK/nXhHRGKf+5bldgQiopy9mPgYseed+LJXlLrsW+VLSghKfOE6S93cZ4yF+58HdNgSgOCkDv3iMyKWh6LgucfA3t6yn/hqkYAEWgT8YfMWC48kMCwBJi+WTSPLxYRKYQJCIDDxMZFTqMeExKSGGCFDwhIo2SEmcCZG9u4xMSOcpg3sN0NEUBCcnKctfUahP+pxnYmS/XiIxJkzZ+aJn4kTEcNT20ySZNsQM0yG2EHA4AttWYLF77333jtPmJERRHAweVPIolIHG0ywlBB3+IxQxibLxIgfBBl7E2lPu6hPlogx4yuTNmPDN/oi68VEjQ0YMcEjdhDEjIssGpM89ckowpj6iCna4AN94StjRCSWC6KIOogQ9hXiIzZpz0/GPPbYY/P1iZ9cJx6IdnjRH/s66YO4YBOfOAfTGC99U4f7gXjQH18msIffiDLihnjEPoIuxCxjxx+WgRHp7PMjjghs7h2uI3LjOkIR7vhL//jD+IkJIgif437jYRjuC/qnbxhHf4g6+uWFr9iiP2LN0jzZQMZJFg+7ZJHZNsF5+POAGO245/GZLQbcq4yZ+wTetKMf+qQtX4h48AW7RXYh5BCcfNnCV4Qn7emPL0GMmX2lvKgPT+IR9y7xiPGRIYZZ3Cvcb/CBC9lpMqc8gIUf/PeKLfqlPv9NUIc29G+RgASGJzBpYHJsbV4Zvq5XJdB4AkxglOIkyOfy+fJnRAZtmMCY/BBWCAzOUbdor9wW+1G4xgs7vDORY4cJnEmSzMsJJ5yQJ2ImxBAIYZ96CLq4hl9M/BTsRb34zHvxHJ8pxboc88IO7+1KsX65fdRvx6joG/XCl+hnqM9hk3dYkdmDz3HHHZfZRDv6hB/CMeJS7JPrXKM/rkfMsFseE+eKpexjXMMmBcF50kkn5afEeXCHLyqIJwptEcnf//73214P/4s+FI+jD8bOvVa837gHiD9jKd4fYTOY8BmbRbsccx27FPylXtTlXNjhmPoUztFvsKYddoJ1rlT4p9gnp6mLDWLBMe2jbbEP6sb4uM74KLQNv4r/DWCHz0VbUY92Zducs0hAAu0JuOTdnotnJdCWQHGyKVYony9/ZiKlxAQeExjnynXLn6kThWu8mAgpRTtMtNinr5h0o99oT30mybHwI2yGTzH5xvnie3lM5c/UDV878a2dPfxBgPDzR2R12ctIhq7IbKQ+qYsYw36xHf6265PzUYa6HvGJOER9YhptuDbc9WgT9flcPI5xhc2i7xxzfxSFVNjjPdrSP6Vol2PaI64pUZfjYj0+U4rnwod2Pn1Su/VvsR1no5929325bnF80a5lubUM38l9VrTjsQQkMD8BM5Tz8/CTBGpJgMmfrBHLyyylIhgsnwgR9kWyhMySeTuB0StOCB+Wb1kKDpFV9GWk68W6HktAAhLoNQEFZa8jYP8SGCMCkdXqJ9E0RkPrygyZOLJY/cgF4Y9v5SxbDHik61HPdwlIQAK9JqCg7HUE7F8CEpCABCQgAQnUnIDrYjUPoO5LQAISkIAEJCCBXhNQUPY6AvYvAQlIQAISkIAEak5AQVnzAOq+BCQgAQlIQAIS6DUBBWWvI2D/EpCABCQgAQlIoOYEFJQ1D6DuS0ACEpCABCQggV4TUFD2OgL2LwEJSEACEpCABGpOQEFZ8wDqvgQkIAEJSEACEug1AQVlryNg/xKQgAQkIAEJSKDmBBSUNQ+g7ktAAhKQgAQkIIFeE1BQ9joC9i8BCUhAAhKQgARqTkBBWfMA6r4EJCABCUhAAhLoNQEFZa8jYP8SkIAEJCABCUig5gQUlDUPoO5LQAISkIAEJCCBXhNQUPY6AvYvAQlIQAISkIAEak5AQVnzAOq+BCQgAQlIQAIS6DUBBWWvI2D/EpCABCQgAQlIoOYEFJQ1D6DuS0ACEpCABCQggV4TUFD2OgL2LwEJSEACEpCABGpOQEFZ8wDqvgQkIAEJSEACEug1AQVlryNg/xKQgAQkIAEJSKDmBBSUNQ+g7ktAAhKQgAQkIIFeE1BQ9joC9i8BCUhAAhKQgARqTkBBWfMA6r4EJCABCUhAAhLoNQEFZa8jYP8SkIAEJCABCUig5gQUlDUPoO5LQAISkIAEJCCBXhNQUPY6AvYvAQlIQAISkIAEak5AQVnzAOq+BCQgAQlIQAIS6DUBBWWvI2D/EpCABCQgAQlIoOYEFJQ1D6DuS0ACEpCABCQggV4TUFD2OgL2LwEJSEACEpCABGpOQEFZ8wDqvgQkIAEJSEACEug1AQVlryNg/xKQgAQkIAEJSKDmBBSUNQ+g7ktAAhKQgAQkIIFeE1BQ9joC9i8BCUhAAhKQgARqTkBBWfMA6r4EJCABCUhAAhLoNQEFZa8jYP8SkIAEJCABCUig5gQUlDUPoO5LQAISkIAEJCCBXhNQUPY6AvYvAQlIQAISkIAEak5AQVnzAOq+BCQgAQlIQAIS6DUBBWWvI2D/EpCABCQgAQlIoOYEFJQ1D6DuS0ACEpCABCQggV4TUFD2OgL2LwEJSEACEpCABGpOQEFZ8wDqvgQkIAEJSEACEug1AQVlryNg/xKQgAQkIAEJSKDmBBSUNQ+g7ktAAhKQgAQkIIFeE1BQ9joC9i8BCUhAAhKQgARqTkBBWfMA6r4EJCABCUhAAhLoNQEFZa8jYP8SkIAEJCABCUig5gQUlDUPoO5LQAISkIAEJCCBXhNQUPY6AvYvAQlIQAISkIAEak5AQVnzAOq+BCQgAQlIQAIS6DUBBWWvI2D/EpCABCQgAQlIoOYEFJQ1D6DuS0ACEpCABCQggV4TUFD2OgL2LwEJSEACEpCABGpOQEFZ8wDqvgQkIAEJSEACEug1AQVlryNg/xKQgAQkIAEJSKDmBBSUNQ+g7ktAAhKQgAQkIIFeE1BQ9joC9i8BCUhAAhKQgARqTuD/AXhQsJAZ8nP+AAAAAElFTkSuQmCC", "base64");
|
|
366
|
+
|
|
239
367
|
// src/package.ts
|
|
240
368
|
import { spawn, spawnSync } from "child_process";
|
|
241
369
|
import { createHash } from "crypto";
|
|
@@ -252,10 +380,11 @@ import {
|
|
|
252
380
|
readSync,
|
|
253
381
|
readdirSync as readdirSync2,
|
|
254
382
|
rmSync as rmSync2,
|
|
255
|
-
|
|
383
|
+
symlinkSync,
|
|
384
|
+
writeFileSync as writeFileSync3
|
|
256
385
|
} from "fs";
|
|
257
386
|
import { homedir } from "os";
|
|
258
|
-
import { basename, dirname, join as
|
|
387
|
+
import { basename, dirname, join as join3, resolve } from "path";
|
|
259
388
|
import { deflateRawSync } from "zlib";
|
|
260
389
|
var DEFAULT_DEB_DEPENDENCIES = ["libgtk-3-0", "libwebkit2gtk-4.1-37", "libnotify-bin"];
|
|
261
390
|
var CRC32_TABLE = (() => {
|
|
@@ -344,7 +473,7 @@ async function packageApp(config) {
|
|
|
344
473
|
throw new Error(`Binary not found: ${config.binaryPath}`);
|
|
345
474
|
}
|
|
346
475
|
const platforms = config.platforms || [detectPlatform()];
|
|
347
|
-
const outDir = config.outDir ||
|
|
476
|
+
const outDir = config.outDir || join3(process.cwd(), "dist");
|
|
348
477
|
if (!existsSync3(outDir)) {
|
|
349
478
|
mkdirSync(outDir, { recursive: true });
|
|
350
479
|
}
|
|
@@ -366,7 +495,7 @@ async function packageApp(config) {
|
|
|
366
495
|
function macOSAppStoreRuntimePath(providerPath) {
|
|
367
496
|
const providerName = basename(providerPath);
|
|
368
497
|
const storeName = providerName.startsWith("craft-webview") ? "craft-webview-app-store" : "craft-app-store";
|
|
369
|
-
const runtime = providerName === storeName ? providerPath :
|
|
498
|
+
const runtime = providerName === storeName ? providerPath : join3(dirname(providerPath), storeName);
|
|
370
499
|
if (!existsSync3(runtime))
|
|
371
500
|
throw new Error("App Store packaging requires Pantry\u2019s craft-app-store runtime. Upgrade Craft with Pantry before building.");
|
|
372
501
|
const bytes = readFileSync(runtime);
|
|
@@ -393,7 +522,7 @@ async function packageMacOS(config, outDir) {
|
|
|
393
522
|
return [{ success: false, platform: "macos", format: "app", error: error.message }];
|
|
394
523
|
}
|
|
395
524
|
}
|
|
396
|
-
const appBundlePath =
|
|
525
|
+
const appBundlePath = join3(outDir, `${name}.app`);
|
|
397
526
|
const appBundle = opts.appBundlePath ? copyMacOSAppBundle(opts.appBundlePath, appBundlePath) : createMacOSAppBundle({
|
|
398
527
|
name,
|
|
399
528
|
version,
|
|
@@ -422,25 +551,25 @@ async function packageMacOS(config, outDir) {
|
|
|
422
551
|
return results;
|
|
423
552
|
}
|
|
424
553
|
try {
|
|
425
|
-
const contents =
|
|
426
|
-
if (opts.appStore && opts.appBundlePath && existsSync3(
|
|
554
|
+
const contents = join3(appBundlePath, "Contents");
|
|
555
|
+
if (opts.appStore && opts.appBundlePath && existsSync3(join3(contents, "Resources", "craft.json"))) {
|
|
427
556
|
if (!nativeRuntimePath)
|
|
428
557
|
throw new Error("App Store packaging of a prebuilt Craft bundle requires macos.nativeRuntimePath from Pantry.");
|
|
429
|
-
const plist =
|
|
558
|
+
const plist = join3(contents, "Info.plist");
|
|
430
559
|
const executable = spawnSync("plutil", ["-extract", "CFBundleExecutable", "raw", "-o", "-", plist], { encoding: "utf8" });
|
|
431
560
|
const name = executable.stdout.trim();
|
|
432
561
|
if (executable.status !== 0 || !name || basename(name) !== name || name === "." || name === "..")
|
|
433
562
|
throw new Error("Prebuilt Craft bundle has an invalid CFBundleExecutable");
|
|
434
|
-
copyFileSync(nativeRuntimePath,
|
|
435
|
-
chmodSync(
|
|
563
|
+
copyFileSync(nativeRuntimePath, join3(contents, "MacOS", name));
|
|
564
|
+
chmodSync(join3(contents, "MacOS", name), 493);
|
|
436
565
|
}
|
|
437
566
|
if (opts.provisioningProfile) {
|
|
438
|
-
const profile =
|
|
567
|
+
const profile = join3(contents, "embedded.provisionprofile");
|
|
439
568
|
copyFileSync(opts.provisioningProfile, profile);
|
|
440
569
|
chmodSync(profile, 420);
|
|
441
570
|
}
|
|
442
571
|
if (opts.privacyManifest)
|
|
443
|
-
copyFileSync(opts.privacyManifest,
|
|
572
|
+
copyFileSync(opts.privacyManifest, join3(contents, "Resources", "PrivacyInfo.xcprivacy"));
|
|
444
573
|
if (opts.appBundlePath) {
|
|
445
574
|
const metadata = {
|
|
446
575
|
CFBundleVersion: opts.buildNumber,
|
|
@@ -450,13 +579,13 @@ async function packageMacOS(config, outDir) {
|
|
|
450
579
|
};
|
|
451
580
|
if (config.iconPath) {
|
|
452
581
|
const icon = basename(config.iconPath);
|
|
453
|
-
copyFileSync(config.iconPath,
|
|
582
|
+
copyFileSync(config.iconPath, join3(contents, "Resources", icon));
|
|
454
583
|
metadata.CFBundleIconFile = icon;
|
|
455
584
|
}
|
|
456
585
|
for (const [key, value] of Object.entries(metadata)) {
|
|
457
586
|
if (value === undefined)
|
|
458
587
|
continue;
|
|
459
|
-
const result = await runTool("plutil", ["-replace", key, typeof value === "boolean" ? "-bool" : "-string", String(value),
|
|
588
|
+
const result = await runTool("plutil", ["-replace", key, typeof value === "boolean" ? "-bool" : "-string", String(value), join3(contents, "Info.plist")]);
|
|
460
589
|
if (!result.success)
|
|
461
590
|
throw new Error(result.error);
|
|
462
591
|
}
|
|
@@ -477,8 +606,8 @@ async function packageMacOS(config, outDir) {
|
|
|
477
606
|
}
|
|
478
607
|
results.push({ success: true, platform: "macos", format: "app", outputPath: appBundlePath });
|
|
479
608
|
if (opts.dmg !== false) {
|
|
480
|
-
const outputPath =
|
|
481
|
-
let dmgResult = await
|
|
609
|
+
const outputPath = join3(outDir, `${name}-${version}.dmg`);
|
|
610
|
+
let dmgResult = await createDiskImage({ appBundlePath, outputPath, volumeName: name });
|
|
482
611
|
if (dmgResult.success && opts.signIdentity) {
|
|
483
612
|
const signed = await runTool("codesign", codesignArguments({
|
|
484
613
|
path: outputPath,
|
|
@@ -502,7 +631,7 @@ async function packageMacOS(config, outDir) {
|
|
|
502
631
|
results.push(...await notarizeIfRequested(opts, outputPath, "dmg"));
|
|
503
632
|
}
|
|
504
633
|
if (opts.pkg || opts.appStore) {
|
|
505
|
-
const outputPath =
|
|
634
|
+
const outputPath = join3(outDir, `${name}-${version}.pkg`);
|
|
506
635
|
const pkgResult = await createPKG({
|
|
507
636
|
appBundlePath,
|
|
508
637
|
outputPath,
|
|
@@ -537,7 +666,7 @@ async function notarizeIfRequested(opts, artifactPath, format) {
|
|
|
537
666
|
}
|
|
538
667
|
const scratch = format === "app" ? createTempDir("craft-notarize-") : undefined;
|
|
539
668
|
try {
|
|
540
|
-
const submission = scratch ?
|
|
669
|
+
const submission = scratch ? join3(scratch, "app.zip") : artifactPath;
|
|
541
670
|
if (scratch) {
|
|
542
671
|
const zipped = await runTool("ditto", ["-c", "-k", "--keepParent", artifactPath, submission]);
|
|
543
672
|
if (!zipped.success)
|
|
@@ -575,7 +704,7 @@ async function packageWindows(config, outDir) {
|
|
|
575
704
|
name,
|
|
576
705
|
version,
|
|
577
706
|
binaryPath: config.binaryPath,
|
|
578
|
-
outputPath:
|
|
707
|
+
outputPath: join3(outDir, `${name}-${version}.msi`),
|
|
579
708
|
manufacturer: config.author || "Unknown",
|
|
580
709
|
architecture: opts.architecture || windowsArchitecture(process.arch),
|
|
581
710
|
additionalFiles: opts.additionalFiles,
|
|
@@ -599,7 +728,7 @@ async function packageWindows(config, outDir) {
|
|
|
599
728
|
manufacturer: config.author || "Unknown",
|
|
600
729
|
binaryPath: config.binaryPath,
|
|
601
730
|
additionalFiles: opts.additionalFiles,
|
|
602
|
-
outputPath:
|
|
731
|
+
outputPath: join3(outDir, `${name}-${version}-windows.zip`)
|
|
603
732
|
});
|
|
604
733
|
results.push({
|
|
605
734
|
success: zipResult.success,
|
|
@@ -620,7 +749,7 @@ async function packageLinux(config, outDir) {
|
|
|
620
749
|
name,
|
|
621
750
|
version,
|
|
622
751
|
binaryPath: config.binaryPath,
|
|
623
|
-
outputPath:
|
|
752
|
+
outputPath: join3(outDir, `${name}_${version}_amd64.deb`),
|
|
624
753
|
description: config.description || "",
|
|
625
754
|
maintainer: config.author || "Unknown",
|
|
626
755
|
dependencies: opts.debDependencies || [...DEFAULT_DEB_DEPENDENCIES],
|
|
@@ -640,7 +769,7 @@ async function packageLinux(config, outDir) {
|
|
|
640
769
|
name,
|
|
641
770
|
version,
|
|
642
771
|
binaryPath: config.binaryPath,
|
|
643
|
-
outputPath:
|
|
772
|
+
outputPath: join3(outDir, `${name}-${version}-1.x86_64.rpm`),
|
|
644
773
|
description: config.description || "",
|
|
645
774
|
requires: opts.rpmDependencies || ["gtk3", "webkit2gtk3"]
|
|
646
775
|
});
|
|
@@ -657,7 +786,7 @@ async function packageLinux(config, outDir) {
|
|
|
657
786
|
name,
|
|
658
787
|
version,
|
|
659
788
|
binaryPath: config.binaryPath,
|
|
660
|
-
outputPath:
|
|
789
|
+
outputPath: join3(outDir, `${name}-${version}-x86_64.AppImage`),
|
|
661
790
|
iconPath: config.iconPath
|
|
662
791
|
});
|
|
663
792
|
results.push({
|
|
@@ -768,13 +897,13 @@ async function signMacOSAppBundle(app, bundleId, opts, hardenedRuntime) {
|
|
|
768
897
|
try {
|
|
769
898
|
let helperEntitlements = opts.helperEntitlements;
|
|
770
899
|
if (opts.appStore && !helperEntitlements) {
|
|
771
|
-
helperEntitlements =
|
|
772
|
-
|
|
900
|
+
helperEntitlements = join3(scratch, "inherit.plist");
|
|
901
|
+
writeFileSync3(helperEntitlements, '<?xml version="1.0"?><plist version="1.0"><dict><key>com.apple.security.app-sandbox</key><true/><key>com.apple.security.inherit</key><true/></dict></plist>');
|
|
773
902
|
}
|
|
774
|
-
const extracted = await runTool("plutil", ["-extract", "CFBundleExecutable", "raw", "-o", "-",
|
|
903
|
+
const extracted = await runTool("plutil", ["-extract", "CFBundleExecutable", "raw", "-o", "-", join3(app, "Contents", "Info.plist")]);
|
|
775
904
|
if (!extracted.success)
|
|
776
905
|
throw new Error(extracted.error);
|
|
777
|
-
const main =
|
|
906
|
+
const main = join3(app, "Contents", "MacOS", extracted.stdout.trim());
|
|
778
907
|
const sign = async (path, entitlements, identifier) => {
|
|
779
908
|
const signed = await runTool("codesign", codesignArguments({ path, identity: opts.signIdentity, entitlements, hardenedRuntime, identifier, keychain: opts.signingKeychain, deep: false }));
|
|
780
909
|
if (!signed.success)
|
|
@@ -782,7 +911,7 @@ async function signMacOSAppBundle(app, bundleId, opts, hardenedRuntime) {
|
|
|
782
911
|
};
|
|
783
912
|
const walk = async (directory) => {
|
|
784
913
|
for (const entry of readdirSync2(directory)) {
|
|
785
|
-
const path =
|
|
914
|
+
const path = join3(directory, entry);
|
|
786
915
|
const stat = lstatSync(path);
|
|
787
916
|
if (stat.isSymbolicLink())
|
|
788
917
|
continue;
|
|
@@ -796,7 +925,7 @@ async function signMacOSAppBundle(app, bundleId, opts, hardenedRuntime) {
|
|
|
796
925
|
}
|
|
797
926
|
}
|
|
798
927
|
};
|
|
799
|
-
await walk(
|
|
928
|
+
await walk(join3(app, "Contents"));
|
|
800
929
|
await sign(app, opts.entitlements);
|
|
801
930
|
const verified = await runTool("codesign", ["--verify", "--deep", "--strict", app]);
|
|
802
931
|
if (!verified.success)
|
|
@@ -864,8 +993,8 @@ function notarytoolArguments(opts) {
|
|
|
864
993
|
}
|
|
865
994
|
function copyMacOSAppBundle(source, destination) {
|
|
866
995
|
try {
|
|
867
|
-
const info =
|
|
868
|
-
if (!existsSync3(info) || !existsSync3(
|
|
996
|
+
const info = join3(source, "Contents", "Info.plist");
|
|
997
|
+
if (!existsSync3(info) || !existsSync3(join3(source, "Contents", "MacOS")))
|
|
869
998
|
throw new Error(`Invalid prebuilt macOS application: ${source}`);
|
|
870
999
|
if (resolve(source) !== resolve(destination)) {
|
|
871
1000
|
rmSync2(destination, { recursive: true, force: true });
|
|
@@ -877,29 +1006,29 @@ function copyMacOSAppBundle(source, destination) {
|
|
|
877
1006
|
}
|
|
878
1007
|
}
|
|
879
1008
|
function macOSRuntimeExecutablePath(appBundlePath) {
|
|
880
|
-
return
|
|
1009
|
+
return join3(appBundlePath, "Contents", "Helpers", "CraftRuntime.app", "Contents", "MacOS", "craft");
|
|
881
1010
|
}
|
|
882
1011
|
function createMacOSAppBundle(opts) {
|
|
883
1012
|
try {
|
|
884
1013
|
const { name, binaryPath, iconPath, provisioningProfile, additionalExecutables, outputPath } = opts;
|
|
885
|
-
const contents =
|
|
886
|
-
const macOS =
|
|
1014
|
+
const contents = join3(outputPath, "Contents");
|
|
1015
|
+
const macOS = join3(contents, "MacOS");
|
|
887
1016
|
mkdirSync(macOS, { recursive: true });
|
|
888
|
-
mkdirSync(
|
|
889
|
-
copyFileSync(opts.nativeHost ? opts.nativeRuntimePath : binaryPath,
|
|
890
|
-
chmodSync(
|
|
1017
|
+
mkdirSync(join3(contents, "Resources"), { recursive: true });
|
|
1018
|
+
copyFileSync(opts.nativeHost ? opts.nativeRuntimePath : binaryPath, join3(macOS, name));
|
|
1019
|
+
chmodSync(join3(macOS, name), 493);
|
|
891
1020
|
for (const executable of additionalExecutables || []) {
|
|
892
1021
|
if (!existsSync3(executable))
|
|
893
1022
|
return { success: false, error: `Additional executable not found: ${executable}` };
|
|
894
|
-
const destination =
|
|
1023
|
+
const destination = join3(macOS, basename(executable));
|
|
895
1024
|
copyFileSync(executable, destination);
|
|
896
1025
|
chmodSync(destination, 493);
|
|
897
1026
|
}
|
|
898
1027
|
if (opts.nativeHost) {
|
|
899
|
-
const backend =
|
|
1028
|
+
const backend = join3(macOS, "craft-backend");
|
|
900
1029
|
copyFileSync(binaryPath, backend);
|
|
901
1030
|
chmodSync(backend, 493);
|
|
902
|
-
|
|
1031
|
+
writeFileSync3(join3(contents, "Resources", "craft.json"), `${JSON.stringify({
|
|
903
1032
|
title: name,
|
|
904
1033
|
appName: name,
|
|
905
1034
|
...opts.nativeWindow,
|
|
@@ -909,14 +1038,14 @@ function createMacOSAppBundle(opts) {
|
|
|
909
1038
|
} else if (opts.nativeRuntimePath) {
|
|
910
1039
|
const helper = macOSRuntimeExecutablePath(outputPath);
|
|
911
1040
|
const helperContents = resolve(helper, "..", "..");
|
|
912
|
-
mkdirSync(
|
|
1041
|
+
mkdirSync(join3(helperContents, "MacOS"), { recursive: true });
|
|
913
1042
|
copyFileSync(opts.nativeRuntimePath, helper);
|
|
914
1043
|
chmodSync(helper, 493);
|
|
915
1044
|
if (iconPath) {
|
|
916
|
-
mkdirSync(
|
|
917
|
-
copyFileSync(iconPath,
|
|
1045
|
+
mkdirSync(join3(helperContents, "Resources"), { recursive: true });
|
|
1046
|
+
copyFileSync(iconPath, join3(helperContents, "Resources", "icon.icns"));
|
|
918
1047
|
}
|
|
919
|
-
|
|
1048
|
+
writeFileSync3(join3(helperContents, "Info.plist"), macOSInfoPlist({
|
|
920
1049
|
name,
|
|
921
1050
|
executableName: "craft",
|
|
922
1051
|
version: opts.version,
|
|
@@ -932,14 +1061,14 @@ function createMacOSAppBundle(opts) {
|
|
|
932
1061
|
if (!existsSync3(iconPath))
|
|
933
1062
|
return { success: false, error: `Icon not found: ${iconPath}` };
|
|
934
1063
|
iconName = basename(iconPath).replace(/\.icns$/i, "");
|
|
935
|
-
copyFileSync(iconPath,
|
|
1064
|
+
copyFileSync(iconPath, join3(contents, "Resources", `${iconName}.icns`));
|
|
936
1065
|
}
|
|
937
1066
|
if (provisioningProfile) {
|
|
938
1067
|
if (!existsSync3(provisioningProfile))
|
|
939
1068
|
return { success: false, error: `Provisioning profile not found: ${provisioningProfile}` };
|
|
940
|
-
copyFileSync(provisioningProfile,
|
|
1069
|
+
copyFileSync(provisioningProfile, join3(contents, "embedded.provisionprofile"));
|
|
941
1070
|
}
|
|
942
|
-
|
|
1071
|
+
writeFileSync3(join3(contents, "Info.plist"), macOSInfoPlist({ ...opts, iconName }));
|
|
943
1072
|
return { success: true };
|
|
944
1073
|
} catch (error) {
|
|
945
1074
|
return { success: false, error: error.message };
|
|
@@ -950,7 +1079,7 @@ function pathContentBytes(path) {
|
|
|
950
1079
|
const stat = lstatSync(path);
|
|
951
1080
|
if (!stat.isDirectory())
|
|
952
1081
|
return stat.size;
|
|
953
|
-
return readdirSync2(path).reduce((total, entry) => total + pathContentBytes(
|
|
1082
|
+
return readdirSync2(path).reduce((total, entry) => total + pathContentBytes(join3(path, entry)), 0);
|
|
954
1083
|
}
|
|
955
1084
|
function dmgCapacityMegabytes(contentBytes) {
|
|
956
1085
|
if (!Number.isSafeInteger(contentBytes) || contentBytes < 0)
|
|
@@ -965,6 +1094,8 @@ function dmgCreateArguments(opts, contentBytes) {
|
|
|
965
1094
|
opts.volumeName,
|
|
966
1095
|
"-srcfolder",
|
|
967
1096
|
opts.appBundlePath,
|
|
1097
|
+
"-fs",
|
|
1098
|
+
"HFS+",
|
|
968
1099
|
"-size",
|
|
969
1100
|
`${dmgCapacityMegabytes(contentBytes)}m`,
|
|
970
1101
|
"-ov",
|
|
@@ -1000,24 +1131,70 @@ function runTool(tool, args) {
|
|
|
1000
1131
|
});
|
|
1001
1132
|
}
|
|
1002
1133
|
var runHdiutil = (args) => runTool("hdiutil", args);
|
|
1003
|
-
async function
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
for (let attempt = 1
|
|
1009
|
-
const
|
|
1134
|
+
async function detachDiskImage(mount, options = {}) {
|
|
1135
|
+
const run = options.run ?? runHdiutil;
|
|
1136
|
+
const wait = options.wait ?? ((ms) => new Promise((resolve) => {
|
|
1137
|
+
setTimeout(resolve, ms);
|
|
1138
|
+
}));
|
|
1139
|
+
for (let attempt = 1;; attempt++) {
|
|
1140
|
+
const forced = attempt >= HDIUTIL_MAX_ATTEMPTS;
|
|
1141
|
+
const result = await run(forced ? ["detach", mount, "-force"] : ["detach", mount]);
|
|
1010
1142
|
if (result.success)
|
|
1011
|
-
return { success: true
|
|
1012
|
-
failures.push(`attempt ${attempt}: ${result.error}`);
|
|
1143
|
+
return { success: true };
|
|
1013
1144
|
if (!shouldRetryHdiutil(result.error, attempt))
|
|
1014
|
-
return { success: false, error:
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1145
|
+
return { success: false, error: result.error };
|
|
1146
|
+
await wait(attempt * 2000);
|
|
1147
|
+
}
|
|
1148
|
+
}
|
|
1149
|
+
async function createDiskImage(opts) {
|
|
1150
|
+
if (!/^[^/:\n]{1,27}$/.test(opts.volumeName))
|
|
1151
|
+
return { success: false, error: `Invalid DMG volume name "${opts.volumeName}"; must be 1..27 chars without /, :, or newline` };
|
|
1152
|
+
const scratch = createTempDir("craft-installer-");
|
|
1153
|
+
const payload = join3(scratch, "payload");
|
|
1154
|
+
const mount = join3(scratch, "mount");
|
|
1155
|
+
const writable = join3(scratch, "installer.dmg");
|
|
1156
|
+
let mounted = false;
|
|
1157
|
+
const run = async (tool, args) => {
|
|
1158
|
+
const result = await runTool(tool, args);
|
|
1159
|
+
if (!result.success)
|
|
1160
|
+
throw new Error(result.error);
|
|
1161
|
+
return result;
|
|
1162
|
+
};
|
|
1163
|
+
try {
|
|
1164
|
+
mkdirSync(payload);
|
|
1165
|
+
mkdirSync(mount);
|
|
1166
|
+
const bundleName = basename(opts.appBundlePath);
|
|
1167
|
+
await run("ditto", [opts.appBundlePath, join3(payload, bundleName)]);
|
|
1168
|
+
symlinkSync("/Applications", join3(payload, "Applications"));
|
|
1169
|
+
mkdirSync(join3(payload, ".background"));
|
|
1170
|
+
writeFileSync3(join3(payload, ".background", "installer.png"), installerBackground);
|
|
1171
|
+
const args = dmgCreateArguments({ ...opts, appBundlePath: payload, outputPath: writable }, pathContentBytes(payload));
|
|
1172
|
+
args[args.indexOf("ULMO")] = "UDRW";
|
|
1173
|
+
for (let attempt = 1;; attempt++) {
|
|
1174
|
+
const result = await runHdiutil(args);
|
|
1175
|
+
if (result.success)
|
|
1176
|
+
break;
|
|
1177
|
+
if (!shouldRetryHdiutil(result.error, attempt))
|
|
1178
|
+
throw new Error(result.error);
|
|
1179
|
+
rmSync2(writable, { force: true });
|
|
1180
|
+
await new Promise((resolve) => setTimeout(resolve, attempt * 2000));
|
|
1181
|
+
}
|
|
1182
|
+
await run("hdiutil", ["attach", writable, "-nobrowse", "-mountpoint", mount]);
|
|
1183
|
+
mounted = true;
|
|
1184
|
+
writeInstallerLayout(mount, opts.volumeName, bundleName);
|
|
1185
|
+
const detached = await detachDiskImage(mount);
|
|
1186
|
+
if (!detached.success)
|
|
1187
|
+
throw new Error(detached.error);
|
|
1188
|
+
mounted = false;
|
|
1189
|
+
await run("hdiutil", ["convert", writable, "-format", "ULMO", "-ov", "-o", opts.outputPath]);
|
|
1190
|
+
return { success: true, outputPath: opts.outputPath };
|
|
1191
|
+
} catch (error) {
|
|
1192
|
+
return { success: false, error: error instanceof Error ? error.message : String(error) };
|
|
1193
|
+
} finally {
|
|
1194
|
+
if (mounted)
|
|
1195
|
+
await runTool("hdiutil", ["detach", mount, "-force"]);
|
|
1196
|
+
removeTempDir(scratch);
|
|
1018
1197
|
}
|
|
1019
|
-
return { success: false, error: failures.join(`
|
|
1020
|
-
`) };
|
|
1021
1198
|
}
|
|
1022
1199
|
function pkgbuildComponentPlist(rootRelativeBundlePath) {
|
|
1023
1200
|
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
@@ -1074,14 +1251,14 @@ async function createPKG(opts) {
|
|
|
1074
1251
|
}
|
|
1075
1252
|
const tempDir = createTempDir("craft-pkg-");
|
|
1076
1253
|
try {
|
|
1077
|
-
const root =
|
|
1078
|
-
const appsDir =
|
|
1254
|
+
const root = join3(tempDir, "root");
|
|
1255
|
+
const appsDir = join3(root, "Applications");
|
|
1079
1256
|
mkdirSync(appsDir, { recursive: true, mode: 493 });
|
|
1080
1257
|
chmodSync(root, 493);
|
|
1081
1258
|
const bundleName = basename(opts.appBundlePath);
|
|
1082
|
-
cpSync(opts.appBundlePath,
|
|
1083
|
-
const componentPlistPath =
|
|
1084
|
-
|
|
1259
|
+
cpSync(opts.appBundlePath, join3(appsDir, bundleName), { recursive: true });
|
|
1260
|
+
const componentPlistPath = join3(tempDir, "component.plist");
|
|
1261
|
+
writeFileSync3(componentPlistPath, pkgbuildComponentPlist(`Applications/${bundleName}`));
|
|
1085
1262
|
const built = await runTool("pkgbuild", pkgbuildArguments({
|
|
1086
1263
|
root,
|
|
1087
1264
|
componentPlistPath,
|
|
@@ -1216,14 +1393,14 @@ async function createMSI(opts) {
|
|
|
1216
1393
|
try {
|
|
1217
1394
|
const binaryName = windowsExecutableName(opts.name);
|
|
1218
1395
|
const additionalFiles = windowsCompanionFiles(opts.additionalFiles, binaryName);
|
|
1219
|
-
const sourcePath =
|
|
1220
|
-
const wxsPath =
|
|
1221
|
-
const wixobjPath =
|
|
1396
|
+
const sourcePath = join3(tempDir, binaryName);
|
|
1397
|
+
const wxsPath = join3(tempDir, "installer.wxs");
|
|
1398
|
+
const wixobjPath = join3(tempDir, "installer.wixobj");
|
|
1222
1399
|
copyFileSync(opts.binaryPath, sourcePath);
|
|
1223
|
-
|
|
1400
|
+
writeFileSync3(join3(tempDir, WINDOWS_NOTIFICATION_ID_FILE), windowsNotificationAppId(opts.name, opts.manufacturer));
|
|
1224
1401
|
for (const file of additionalFiles)
|
|
1225
|
-
copyFileSync(file.path,
|
|
1226
|
-
|
|
1402
|
+
copyFileSync(file.path, join3(tempDir, file.name));
|
|
1403
|
+
writeFileSync3(wxsPath, renderWixSource(opts, binaryName, additionalFiles.map((file) => file.name)));
|
|
1227
1404
|
await runCommand("candle.exe", candleArguments(opts.architecture, wixobjPath, wxsPath), tempDir);
|
|
1228
1405
|
await runCommand("light.exe", ["-nologo", "-sval", "-out", opts.outputPath, wixobjPath], tempDir);
|
|
1229
1406
|
if (opts.certificatePath) {
|
|
@@ -1252,7 +1429,7 @@ async function createZIP(opts) {
|
|
|
1252
1429
|
for (const file of additionalFiles)
|
|
1253
1430
|
entries.push({ name: file.name, data: new Uint8Array(readFileSync(file.path)) });
|
|
1254
1431
|
const zip = buildZip(entries);
|
|
1255
|
-
|
|
1432
|
+
writeFileSync3(opts.outputPath, zip);
|
|
1256
1433
|
return { success: true, outputPath: opts.outputPath };
|
|
1257
1434
|
} catch (err) {
|
|
1258
1435
|
return { success: false, error: `Failed to write ZIP: ${err.message}` };
|
|
@@ -1285,15 +1462,15 @@ async function createDEB(opts) {
|
|
|
1285
1462
|
const desktopContent = linuxDesktopEntry(opts.name, sanitizedName, opts.urlSchemes, opts.launchUrl);
|
|
1286
1463
|
const tempDir = createTempDir("craft-deb-");
|
|
1287
1464
|
staging = tempDir;
|
|
1288
|
-
const debianDir =
|
|
1289
|
-
const binDir =
|
|
1290
|
-
const applicationsDir =
|
|
1465
|
+
const debianDir = join3(tempDir, "DEBIAN");
|
|
1466
|
+
const binDir = join3(tempDir, "usr", "bin");
|
|
1467
|
+
const applicationsDir = join3(tempDir, "usr", "share", "applications");
|
|
1291
1468
|
mkdirSync(debianDir, { recursive: true });
|
|
1292
1469
|
mkdirSync(binDir, { recursive: true });
|
|
1293
1470
|
mkdirSync(applicationsDir, { recursive: true });
|
|
1294
1471
|
const binaryName = sanitizedName;
|
|
1295
|
-
copyFileSync(opts.binaryPath,
|
|
1296
|
-
chmodSync(
|
|
1472
|
+
copyFileSync(opts.binaryPath, join3(binDir, binaryName));
|
|
1473
|
+
chmodSync(join3(binDir, binaryName), 493);
|
|
1297
1474
|
const controlContent = `Package: ${sanitizedName}
|
|
1298
1475
|
Version: ${opts.version}
|
|
1299
1476
|
Section: utils
|
|
@@ -1303,8 +1480,8 @@ Depends: ${opts.dependencies.join(", ")}
|
|
|
1303
1480
|
Maintainer: ${maintainer}
|
|
1304
1481
|
Description: ${description || opts.name}
|
|
1305
1482
|
`;
|
|
1306
|
-
|
|
1307
|
-
|
|
1483
|
+
writeFileSync3(join3(debianDir, "control"), controlContent);
|
|
1484
|
+
writeFileSync3(join3(applicationsDir, `${binaryName}.desktop`), desktopContent);
|
|
1308
1485
|
const proc = spawn("dpkg-deb", ["--build", tempDir, opts.outputPath]);
|
|
1309
1486
|
proc.on("close", (code) => {
|
|
1310
1487
|
removeTempDir(tempDir);
|
|
@@ -1328,11 +1505,11 @@ Description: ${description || opts.name}
|
|
|
1328
1505
|
async function createRPM(opts) {
|
|
1329
1506
|
return new Promise((resolve) => {
|
|
1330
1507
|
try {
|
|
1331
|
-
const buildRoot =
|
|
1332
|
-
const specDir =
|
|
1333
|
-
const sourcesDir =
|
|
1334
|
-
const buildDir =
|
|
1335
|
-
const rpmsDir =
|
|
1508
|
+
const buildRoot = join3(homedir(), "rpmbuild");
|
|
1509
|
+
const specDir = join3(buildRoot, "SPECS");
|
|
1510
|
+
const sourcesDir = join3(buildRoot, "SOURCES");
|
|
1511
|
+
const buildDir = join3(buildRoot, "BUILD");
|
|
1512
|
+
const rpmsDir = join3(buildRoot, "RPMS");
|
|
1336
1513
|
mkdirSync(specDir, { recursive: true });
|
|
1337
1514
|
mkdirSync(sourcesDir, { recursive: true });
|
|
1338
1515
|
mkdirSync(buildDir, { recursive: true });
|
|
@@ -1356,7 +1533,7 @@ async function createRPM(opts) {
|
|
|
1356
1533
|
}
|
|
1357
1534
|
return r;
|
|
1358
1535
|
}).join(", ");
|
|
1359
|
-
copyFileSync(opts.binaryPath,
|
|
1536
|
+
copyFileSync(opts.binaryPath, join3(sourcesDir, sanitizedName));
|
|
1360
1537
|
const specContent = `Name: ${sanitizedName}
|
|
1361
1538
|
Version: ${opts.version}
|
|
1362
1539
|
Release: 1%{?dist}
|
|
@@ -1374,13 +1551,13 @@ install -m 755 %{SOURCE0} %{buildroot}/usr/bin/${sanitizedName}
|
|
|
1374
1551
|
%files
|
|
1375
1552
|
/usr/bin/${sanitizedName}
|
|
1376
1553
|
`;
|
|
1377
|
-
const specPath =
|
|
1378
|
-
|
|
1554
|
+
const specPath = join3(specDir, `${opts.name.toLowerCase()}.spec`);
|
|
1555
|
+
writeFileSync3(specPath, specContent);
|
|
1379
1556
|
const proc = spawn("rpmbuild", ["-bb", specPath]);
|
|
1380
1557
|
proc.on("close", (code) => {
|
|
1381
1558
|
if (code === 0) {
|
|
1382
1559
|
const rpmName = `${opts.name.toLowerCase()}-${opts.version}-1.x86_64.rpm`;
|
|
1383
|
-
const builtRpmPath =
|
|
1560
|
+
const builtRpmPath = join3(rpmsDir, "x86_64", rpmName);
|
|
1384
1561
|
try {
|
|
1385
1562
|
copyFileSync(builtRpmPath, opts.outputPath);
|
|
1386
1563
|
resolve({ success: true, outputPath: opts.outputPath });
|
|
@@ -1405,22 +1582,22 @@ async function createAppImage(opts) {
|
|
|
1405
1582
|
try {
|
|
1406
1583
|
const appDir = createTempDir("craft-appimage-");
|
|
1407
1584
|
staging = appDir;
|
|
1408
|
-
const appDirPath =
|
|
1409
|
-
const binDir =
|
|
1410
|
-
const shareDir =
|
|
1585
|
+
const appDirPath = join3(appDir, `${opts.name}.AppDir`);
|
|
1586
|
+
const binDir = join3(appDirPath, "usr", "bin");
|
|
1587
|
+
const shareDir = join3(appDirPath, "usr", "share");
|
|
1411
1588
|
mkdirSync(binDir, { recursive: true });
|
|
1412
1589
|
mkdirSync(shareDir, { recursive: true });
|
|
1413
1590
|
const binaryName = opts.name.toLowerCase();
|
|
1414
|
-
copyFileSync(opts.binaryPath,
|
|
1415
|
-
chmodSync(
|
|
1591
|
+
copyFileSync(opts.binaryPath, join3(binDir, binaryName));
|
|
1592
|
+
chmodSync(join3(binDir, binaryName), 493);
|
|
1416
1593
|
const appRunContent = `#!/bin/bash
|
|
1417
1594
|
SELF=$(readlink -f "$0")
|
|
1418
1595
|
HERE=\${SELF%/*}
|
|
1419
1596
|
export PATH="\${HERE}/usr/bin/:\${PATH}"
|
|
1420
1597
|
exec "\${HERE}/usr/bin/${binaryName}" "$@"
|
|
1421
1598
|
`;
|
|
1422
|
-
|
|
1423
|
-
chmodSync(
|
|
1599
|
+
writeFileSync3(join3(appDirPath, "AppRun"), appRunContent);
|
|
1600
|
+
chmodSync(join3(appDirPath, "AppRun"), 493);
|
|
1424
1601
|
const desktopContent = `[Desktop Entry]
|
|
1425
1602
|
Type=Application
|
|
1426
1603
|
Name=${opts.name}
|
|
@@ -1429,9 +1606,9 @@ Terminal=false
|
|
|
1429
1606
|
Categories=Utility;
|
|
1430
1607
|
Icon=${binaryName}
|
|
1431
1608
|
`;
|
|
1432
|
-
|
|
1609
|
+
writeFileSync3(join3(appDirPath, `${binaryName}.desktop`), desktopContent);
|
|
1433
1610
|
if (opts.iconPath && existsSync3(opts.iconPath)) {
|
|
1434
|
-
copyFileSync(opts.iconPath,
|
|
1611
|
+
copyFileSync(opts.iconPath, join3(appDirPath, `${binaryName}.png`));
|
|
1435
1612
|
} else {
|
|
1436
1613
|
const placeholderPng = Buffer.from([
|
|
1437
1614
|
137,
|
|
@@ -1504,7 +1681,7 @@ Icon=${binaryName}
|
|
|
1504
1681
|
96,
|
|
1505
1682
|
130
|
|
1506
1683
|
]);
|
|
1507
|
-
|
|
1684
|
+
writeFileSync3(join3(appDirPath, `${binaryName}.png`), placeholderPng);
|
|
1508
1685
|
}
|
|
1509
1686
|
const proc = spawn("appimagetool", [appDirPath, opts.outputPath], {
|
|
1510
1687
|
env: { ...process.env, ARCH: "x86_64" }
|
|
@@ -10492,17 +10669,17 @@ var commonOptimizations = {
|
|
|
10492
10669
|
}
|
|
10493
10670
|
};
|
|
10494
10671
|
// src/updater/index.ts
|
|
10495
|
-
import { createReadStream, createWriteStream, existsSync as existsSync5, mkdirSync as mkdirSync2, readFileSync as readFileSync2, rmdirSync, statSync as
|
|
10496
|
-
import { join as
|
|
10672
|
+
import { createReadStream, createWriteStream, existsSync as existsSync5, mkdirSync as mkdirSync2, readFileSync as readFileSync2, rmdirSync, statSync as statSync4, unlinkSync, writeFileSync as writeFileSync4 } from "fs";
|
|
10673
|
+
import { join as join5, basename as basename3, dirname as dirname3 } from "path";
|
|
10497
10674
|
import { tmpdir as tmpdir2 } from "os";
|
|
10498
|
-
import { execFileSync as
|
|
10675
|
+
import { execFileSync as execFileSync3, spawn as spawn3 } from "child_process";
|
|
10499
10676
|
import { createHash as createHash2 } from "crypto";
|
|
10500
10677
|
import { EventEmitter as EventEmitter3 } from "events";
|
|
10501
10678
|
|
|
10502
10679
|
// src/updater/macos-bundle.ts
|
|
10503
|
-
import { execFile, execFileSync } from "child_process";
|
|
10504
|
-
import { accessSync, constants, existsSync as existsSync4, renameSync, rmSync as rmSync3, statSync as
|
|
10505
|
-
import { basename as basename2, dirname as dirname2, join as
|
|
10680
|
+
import { execFile, execFileSync as execFileSync2 } from "child_process";
|
|
10681
|
+
import { accessSync, constants, existsSync as existsSync4, renameSync, readdirSync as readdirSync3, rmSync as rmSync3, statSync as statSync3 } from "fs";
|
|
10682
|
+
import { basename as basename2, dirname as dirname2, join as join4 } from "path";
|
|
10506
10683
|
import { promisify } from "util";
|
|
10507
10684
|
var execFileAsync = promisify(execFile);
|
|
10508
10685
|
function isMacOS() {
|
|
@@ -10608,7 +10785,7 @@ async function dittoBundle(source, destination) {
|
|
|
10608
10785
|
await execFileAsync("ditto", [source, destination]);
|
|
10609
10786
|
}
|
|
10610
10787
|
function findTopLevelApp(root) {
|
|
10611
|
-
const out =
|
|
10788
|
+
const out = execFileSync2("find", [root, "-maxdepth", "2", "-name", "*.app", "-type", "d", "-print0"]);
|
|
10612
10789
|
const parts = out.toString("utf-8").split("\x00").filter((s) => s.length > 0);
|
|
10613
10790
|
parts.sort((a, b) => a.split("/").length - b.split("/").length);
|
|
10614
10791
|
return parts[0] ?? null;
|
|
@@ -10624,7 +10801,7 @@ async function extractBundleFromDmg(dmgPath, stagingDir) {
|
|
|
10624
10801
|
const source = findTopLevelApp(mountPoint);
|
|
10625
10802
|
if (!source)
|
|
10626
10803
|
throw new Error(`No .app found in disk image: ${dmgPath}`);
|
|
10627
|
-
const appPath =
|
|
10804
|
+
const appPath = join4(staging, basename2(source));
|
|
10628
10805
|
await dittoBundle(source, appPath);
|
|
10629
10806
|
staged = true;
|
|
10630
10807
|
return { appPath, stagingDir: staging };
|
|
@@ -10641,13 +10818,16 @@ async function extractBundleFromDmg(dmgPath, stagingDir) {
|
|
|
10641
10818
|
}
|
|
10642
10819
|
async function extractBundleFromZip(zipPath, stagingDir) {
|
|
10643
10820
|
const staging = stagingDir ?? createTempDir("craft-update-");
|
|
10644
|
-
const unpacked =
|
|
10821
|
+
const unpacked = join4(staging, "payload");
|
|
10645
10822
|
try {
|
|
10646
10823
|
await execFileAsync("ditto", ["-x", "-k", zipPath, unpacked]);
|
|
10647
10824
|
const source = findTopLevelApp(unpacked);
|
|
10648
|
-
if (
|
|
10649
|
-
|
|
10650
|
-
|
|
10825
|
+
if (source)
|
|
10826
|
+
return { appPath: source, stagingDir: staging };
|
|
10827
|
+
const images = readdirSync3(unpacked, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".dmg"));
|
|
10828
|
+
if (images.length !== 1)
|
|
10829
|
+
throw new Error(`Archive must contain one .app or one disk image: ${zipPath}`);
|
|
10830
|
+
return await extractBundleFromDmg(join4(unpacked, images[0].name), staging);
|
|
10651
10831
|
} catch (error) {
|
|
10652
10832
|
if (!stagingDir)
|
|
10653
10833
|
removeTempDir(staging);
|
|
@@ -10666,8 +10846,8 @@ async function swapBundle(stagedPath, installedPath) {
|
|
|
10666
10846
|
throw new Error(`Staged bundle is missing: ${stagedPath}`);
|
|
10667
10847
|
const parent = dirname2(installedPath);
|
|
10668
10848
|
const stamp = `${Date.now()}.${process.pid}`;
|
|
10669
|
-
const incoming =
|
|
10670
|
-
const retired =
|
|
10849
|
+
const incoming = join4(parent, `.${basename2(installedPath)}.${stamp}.incoming`);
|
|
10850
|
+
const retired = join4(parent, `.${basename2(installedPath)}.${stamp}.retired`);
|
|
10671
10851
|
try {
|
|
10672
10852
|
renameSync(stagedPath, incoming);
|
|
10673
10853
|
} catch {
|
|
@@ -10708,7 +10888,7 @@ function canReplaceBundle(installedPath) {
|
|
|
10708
10888
|
const parent = dirname2(installedPath);
|
|
10709
10889
|
accessSync(parent, constants.W_OK);
|
|
10710
10890
|
if (existsSync4(installedPath))
|
|
10711
|
-
|
|
10891
|
+
statSync3(installedPath);
|
|
10712
10892
|
return true;
|
|
10713
10893
|
} catch {
|
|
10714
10894
|
return false;
|
|
@@ -10747,7 +10927,7 @@ class AutoUpdater extends EventEmitter3 {
|
|
|
10747
10927
|
downloadDir() {
|
|
10748
10928
|
if (this.config.downloadDir)
|
|
10749
10929
|
return this.config.downloadDir;
|
|
10750
|
-
return
|
|
10930
|
+
return join5(tmpdir2(), "craft-updates", basename3(this.config.appPath) || "app");
|
|
10751
10931
|
}
|
|
10752
10932
|
getLastError() {
|
|
10753
10933
|
return this.lastError;
|
|
@@ -10852,7 +11032,7 @@ class AutoUpdater extends EventEmitter3 {
|
|
|
10852
11032
|
const downloadDir = this.downloadDir();
|
|
10853
11033
|
mkdirSync2(downloadDir, { recursive: true });
|
|
10854
11034
|
const fileName = basename3(url);
|
|
10855
|
-
this.downloadPath =
|
|
11035
|
+
this.downloadPath = join5(downloadDir, fileName);
|
|
10856
11036
|
try {
|
|
10857
11037
|
const response = await fetch(url);
|
|
10858
11038
|
if (!response.ok) {
|
|
@@ -10915,11 +11095,11 @@ class AutoUpdater extends EventEmitter3 {
|
|
|
10915
11095
|
throw new Error("Download verification failed: hash mismatch");
|
|
10916
11096
|
}
|
|
10917
11097
|
if (deltaUpdate) {
|
|
10918
|
-
if (!
|
|
11098
|
+
if (!statSync4(this.config.appPath).isFile()) {
|
|
10919
11099
|
throw new Error("Delta updates require appPath to point to the installed bundle file");
|
|
10920
11100
|
}
|
|
10921
11101
|
const patchPath = this.downloadPath;
|
|
10922
|
-
const reconstructedPath =
|
|
11102
|
+
const reconstructedPath = join5(downloadDir, `reconstructed-${basename3(platformUpdate.url)}`);
|
|
10923
11103
|
await DeltaGenerator.apply(this.config.appPath, patchPath, reconstructedPath);
|
|
10924
11104
|
const reconstructedHash = await this.computeFileHash(reconstructedPath);
|
|
10925
11105
|
if (reconstructedHash !== platformUpdate.sha256) {
|
|
@@ -11054,11 +11234,11 @@ class AutoUpdater extends EventEmitter3 {
|
|
|
11054
11234
|
}
|
|
11055
11235
|
async installMacOSPackage(downloadPath) {
|
|
11056
11236
|
try {
|
|
11057
|
-
|
|
11237
|
+
execFileSync3("pkgutil", ["--check-signature", downloadPath], { stdio: "pipe" });
|
|
11058
11238
|
} catch (e) {
|
|
11059
11239
|
throw new Error(`Refusing to install ${downloadPath}: pkgutil --check-signature failed. ` + "The .pkg is not signed by a trusted Apple certificate chain. " + `Underlying error: ${e.message}`);
|
|
11060
11240
|
}
|
|
11061
|
-
|
|
11241
|
+
execFileSync3("sudo", ["installer", "-pkg", downloadPath, "-target", "/"]);
|
|
11062
11242
|
}
|
|
11063
11243
|
async installWindowsUpdate() {
|
|
11064
11244
|
const downloadPath = this.downloadPath;
|
|
@@ -11074,7 +11254,7 @@ class AutoUpdater extends EventEmitter3 {
|
|
|
11074
11254
|
});
|
|
11075
11255
|
} else if (downloadPath.endsWith(".zip")) {
|
|
11076
11256
|
const appDir = dirname3(this.config.appPath);
|
|
11077
|
-
|
|
11257
|
+
execFileSync3("powershell", [
|
|
11078
11258
|
"-NoProfile",
|
|
11079
11259
|
"-NonInteractive",
|
|
11080
11260
|
"-Command",
|
|
@@ -11087,16 +11267,16 @@ class AutoUpdater extends EventEmitter3 {
|
|
|
11087
11267
|
async installLinuxUpdate() {
|
|
11088
11268
|
const downloadPath = this.downloadPath;
|
|
11089
11269
|
if (downloadPath.endsWith(".AppImage")) {
|
|
11090
|
-
|
|
11270
|
+
execFileSync3("chmod", ["+x", downloadPath]);
|
|
11091
11271
|
const { renameSync } = await import("fs");
|
|
11092
11272
|
renameSync(downloadPath, this.config.appPath);
|
|
11093
11273
|
} else if (downloadPath.endsWith(".deb")) {
|
|
11094
|
-
|
|
11274
|
+
execFileSync3("sudo", ["dpkg", "-i", downloadPath]);
|
|
11095
11275
|
} else if (downloadPath.endsWith(".rpm")) {
|
|
11096
|
-
|
|
11276
|
+
execFileSync3("sudo", ["rpm", "-U", downloadPath]);
|
|
11097
11277
|
} else if (downloadPath.endsWith(".tar.gz")) {
|
|
11098
11278
|
const appDir = dirname3(this.config.appPath);
|
|
11099
|
-
|
|
11279
|
+
execFileSync3("tar", ["-xzf", downloadPath, "-C", appDir]);
|
|
11100
11280
|
}
|
|
11101
11281
|
}
|
|
11102
11282
|
async restartApp() {
|
|
@@ -11191,7 +11371,7 @@ class DeltaGenerator {
|
|
|
11191
11371
|
return DeltaGenerator.supported;
|
|
11192
11372
|
DeltaGenerator.supported = ["bspatch", "xdelta3"].some((tool) => {
|
|
11193
11373
|
try {
|
|
11194
|
-
|
|
11374
|
+
execFileSync3("command", ["-v", tool], { stdio: "ignore", shell: true });
|
|
11195
11375
|
return true;
|
|
11196
11376
|
} catch {
|
|
11197
11377
|
return false;
|
|
@@ -11201,16 +11381,16 @@ class DeltaGenerator {
|
|
|
11201
11381
|
}
|
|
11202
11382
|
static async generate(oldPath, newPath, outputPath) {
|
|
11203
11383
|
try {
|
|
11204
|
-
|
|
11205
|
-
const stats =
|
|
11384
|
+
execFileSync3("bsdiff", [oldPath, newPath, outputPath]);
|
|
11385
|
+
const stats = statSync4(outputPath);
|
|
11206
11386
|
const hash = await DeltaGenerator.computeHash(outputPath);
|
|
11207
11387
|
return {
|
|
11208
11388
|
size: stats.size,
|
|
11209
11389
|
sha256: hash
|
|
11210
11390
|
};
|
|
11211
11391
|
} catch {
|
|
11212
|
-
|
|
11213
|
-
const stats =
|
|
11392
|
+
execFileSync3("xdelta3", ["-e", "-s", oldPath, newPath, outputPath]);
|
|
11393
|
+
const stats = statSync4(outputPath);
|
|
11214
11394
|
const hash = await DeltaGenerator.computeHash(outputPath);
|
|
11215
11395
|
return {
|
|
11216
11396
|
size: stats.size,
|
|
@@ -11220,9 +11400,9 @@ class DeltaGenerator {
|
|
|
11220
11400
|
}
|
|
11221
11401
|
static async apply(basePath, deltaPath, outputPath) {
|
|
11222
11402
|
try {
|
|
11223
|
-
|
|
11403
|
+
execFileSync3("bspatch", [basePath, outputPath, deltaPath]);
|
|
11224
11404
|
} catch {
|
|
11225
|
-
|
|
11405
|
+
execFileSync3("xdelta3", ["-d", "-s", basePath, deltaPath, outputPath]);
|
|
11226
11406
|
}
|
|
11227
11407
|
}
|
|
11228
11408
|
static async computeHash(filePath) {
|
|
@@ -11324,7 +11504,7 @@ ${update.releaseNotes}`);
|
|
|
11324
11504
|
releaseDate: new Date().toISOString(),
|
|
11325
11505
|
platforms: {}
|
|
11326
11506
|
};
|
|
11327
|
-
|
|
11507
|
+
writeFileSync4(outputPath, JSON.stringify(manifest, null, 2));
|
|
11328
11508
|
console.log(`Manifest generated: ${outputPath}`);
|
|
11329
11509
|
console.log("Edit the file to add platform-specific update URLs");
|
|
11330
11510
|
break;
|
|
@@ -11612,8 +11792,8 @@ class CraftApp {
|
|
|
11612
11792
|
} else {
|
|
11613
11793
|
const dir = createTempDir("craft-sidebar-");
|
|
11614
11794
|
this.sidebarConfigDir = dir;
|
|
11615
|
-
const path =
|
|
11616
|
-
|
|
11795
|
+
const path = join6(dir, "sidebar-config.json");
|
|
11796
|
+
writeFileSync5(path, json, "utf-8");
|
|
11617
11797
|
args.push("--sidebar-config-file", path);
|
|
11618
11798
|
}
|
|
11619
11799
|
}
|
|
@@ -11690,6 +11870,7 @@ export {
|
|
|
11690
11870
|
createApp,
|
|
11691
11871
|
createArcSidebar,
|
|
11692
11872
|
createClient,
|
|
11873
|
+
createDiskImage,
|
|
11693
11874
|
createFileBrowser,
|
|
11694
11875
|
createFileSidebar,
|
|
11695
11876
|
createMenubarApp,
|