@nwutils/packager 0.3.0 → 0.5.0
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/README.md +67 -31
- package/package.json +13 -2
- package/src/appImage.js +307 -10
- package/src/index.js +5 -0
- package/src/util.js +109 -2
- package/types/appImage.d.ts +68 -5
- package/types/index.d.ts +5 -0
- package/types/util.d.ts +47 -0
package/README.md
CHANGED
|
@@ -21,8 +21,7 @@ Supported output formats, selected via `format`:
|
|
|
21
21
|
|
|
22
22
|
## Usage
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
that `@nwutils/builder` produced.
|
|
24
|
+
Build the application.
|
|
26
25
|
|
|
27
26
|
```js
|
|
28
27
|
import build from "@nwutils/builder";
|
|
@@ -41,33 +40,71 @@ await build({
|
|
|
41
40
|
categories: "Utility;",
|
|
42
41
|
},
|
|
43
42
|
});
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### AppImage
|
|
44
46
|
|
|
47
|
+
```js
|
|
45
48
|
const appImagePath = await packager({
|
|
46
49
|
format: "AppImage",
|
|
47
50
|
appDir: "./out",
|
|
48
51
|
appName: "Demo",
|
|
49
52
|
cacheDir: "./cache",
|
|
50
53
|
outDir: "./dist",
|
|
54
|
+
sign: true,
|
|
55
|
+
signKey: "ABCDEF1234567890",
|
|
51
56
|
});
|
|
52
57
|
|
|
53
58
|
console.log(`AppImage written to ${appImagePath}`);
|
|
54
59
|
```
|
|
55
60
|
|
|
56
|
-
`
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
+
Signing needs `gpg` installed on the host and the secret key imported into its
|
|
62
|
+
keyring. If the key is protected by a passphrase, set it in the
|
|
63
|
+
`APPIMAGETOOL_SIGN_PASSPHRASE` environment variable
|
|
64
|
+
|
|
65
|
+
### Self updating AppImages
|
|
66
|
+
|
|
67
|
+
Set `publish` to where releases are uploaded, and `version` to the app's
|
|
68
|
+
version. This follows electron-builder's approach:
|
|
69
|
+
|
|
70
|
+
- an `app-update.yml` describing `publish` is embedded next to the NW.js
|
|
71
|
+
executable inside the AppImage, so the running app knows where to look
|
|
72
|
+
- a `latest-linux.yml` update info file (`latest-linux-<arch>.yml` for
|
|
73
|
+
architectures other than `x64`) with the version, file name, size and SHA-512
|
|
74
|
+
of the AppImage is written next to it in `outDir`
|
|
75
|
+
|
|
76
|
+
Upload both the AppImage and the update info file to every release.
|
|
77
|
+
[`@nwutils/updater`](https://www.npmjs.com/package/@nwutils/updater) reads them
|
|
78
|
+
at runtime to check for, download and install updates.
|
|
79
|
+
|
|
80
|
+
```js
|
|
81
|
+
await packager({
|
|
82
|
+
format: "AppImage",
|
|
83
|
+
appDir: "./out",
|
|
84
|
+
appName: "Demo",
|
|
85
|
+
outDir: "./dist",
|
|
86
|
+
version: "1.2.0",
|
|
87
|
+
publish: { provider: "github", owner: "nwutils", repo: "demo" },
|
|
88
|
+
// Optional: also support zsync based updaters, see below.
|
|
89
|
+
updateInformation: true,
|
|
90
|
+
});
|
|
91
|
+
// ./dist/Demo-x86_64.AppImage
|
|
92
|
+
// ./dist/latest-linux.yml
|
|
93
|
+
// ./dist/Demo-x86_64.AppImage.zsync (only with updateInformation)
|
|
94
|
+
```
|
|
61
95
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
`
|
|
66
|
-
by default.
|
|
96
|
+
| `publish.provider` | Other keys | The latest release is looked up at |
|
|
97
|
+
| ------------------ | --------------- | ---------------------------------------------------------------- |
|
|
98
|
+
| `"github"` | `owner`, `repo` | The latest (non draft, non prerelease) GitHub release's assets |
|
|
99
|
+
| `"generic"` | `url` | `<url>/latest-linux.yml`, with the AppImage served from `<url>/` |
|
|
67
100
|
|
|
68
|
-
`
|
|
69
|
-
|
|
70
|
-
|
|
101
|
+
`updateInformation` additionally embeds AppImage update information, for zsync
|
|
102
|
+
based updaters such as AppImageUpdate, `appimageupdatetool` and AppImage
|
|
103
|
+
managers. `true` derives it from `publish`
|
|
104
|
+
(`gh-releases-zsync|<owner>|<repo>|latest|<AppImage>.zsync` or
|
|
105
|
+
`zsync|<url>/<AppImage>.zsync`); a string is embedded as-is. It needs
|
|
106
|
+
`zsyncmake` (usually packaged as `zsync`) installed on the host, and the
|
|
107
|
+
`.zsync` file must be uploaded alongside the AppImage.
|
|
71
108
|
|
|
72
109
|
## API Reference
|
|
73
110
|
|
|
@@ -84,25 +121,24 @@ Resolves with the path to the resulting packaged artifact.
|
|
|
84
121
|
|
|
85
122
|
Options
|
|
86
123
|
|
|
87
|
-
| Name
|
|
88
|
-
|
|
|
89
|
-
| appDir
|
|
90
|
-
| appName
|
|
91
|
-
| icon
|
|
92
|
-
| arch
|
|
93
|
-
| outDir
|
|
94
|
-
| cacheDir
|
|
95
|
-
| cache
|
|
96
|
-
| appImageToolUrl
|
|
124
|
+
| Name | Type | Description |
|
|
125
|
+
| ----------------- | ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
126
|
+
| appDir | `string` | Path to a built NW.js Linux application, ie. the `outDir` produced by `@nwutils/builder` for `platform: "linux"` |
|
|
127
|
+
| appName | `string` | Name of the application. Must match the `app.name` value used to build `appDir`. Must not contain `/` or control characters |
|
|
128
|
+
| icon | `string` | Path to a `.png` or `.svg` icon. Defaults to the `Icon` value read from `<appDir>/<appName>.desktop` |
|
|
129
|
+
| arch | `"ia32" \| "x64" \| "arm64"` | Target architecture. Defaults to the host architecture |
|
|
130
|
+
| outDir | `string` | Directory the resulting `.AppImage` file is written to. Defaults to the parent directory of `appDir` |
|
|
131
|
+
| cacheDir | `string` | Directory used to cache the downloaded `appimagetool` binary. Defaults to `"./cache"` |
|
|
132
|
+
| cache | `boolean` | If true, reuse a cached `appimagetool` binary. Otherwise redownload it. Defaults to `true` |
|
|
133
|
+
| appImageToolUrl | `string` | Base URL `appimagetool-<arch>.AppImage` is downloaded from. Defaults to the `AppImage/appimagetool` "continuous" release. Must be https, except for localhost |
|
|
134
|
+
| sign | `boolean` | If true, embed a GPG signature in the AppImage. Defaults to `false` |
|
|
135
|
+
| signKey | `string` | ID of the GPG key to sign with. Defaults to `gpg`'s default secret key. Requires `sign: true` |
|
|
136
|
+
| version | `string` | Version of the application. Required with `publish` |
|
|
137
|
+
| publish | `PublishOptions` | Where releases are published. Embeds `app-update.yml` and writes `latest-linux[-<arch>].yml` to `outDir` |
|
|
138
|
+
| updateInformation | `boolean \| string` | AppImage zsync update information. `true` derives it from `publish`. Requires `zsyncmake` on the host |
|
|
97
139
|
|
|
98
140
|
Resolves with the path to the resulting `.AppImage` file.
|
|
99
141
|
|
|
100
|
-
The application's desktop entry file (`<appDir>/<appName>.desktop`, as written
|
|
101
|
-
by `@nwutils/builder`) is copied into the AppImage with its `Exec` and `Icon`
|
|
102
|
-
keys rewritten to match the AppImage's own layout, and a `Categories` key
|
|
103
|
-
added (defaulting to `Utility;`) if one isn't already present, since
|
|
104
|
-
`appimagetool` refuses to build an AppImage without one.
|
|
105
|
-
|
|
106
142
|
## Contributing
|
|
107
143
|
|
|
108
144
|
### External contributor
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nwutils/packager",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Package NW.js applications for Linux, MacOS and Windows.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"NW.js",
|
|
@@ -47,5 +47,16 @@
|
|
|
47
47
|
"test:real": "node --test \"../../tests/specs/packager/appImage.real.test.js\""
|
|
48
48
|
},
|
|
49
49
|
"dependencies": {},
|
|
50
|
-
"
|
|
50
|
+
"devEngines": {
|
|
51
|
+
"runtime": {
|
|
52
|
+
"name": "node",
|
|
53
|
+
"version": "26.7.0",
|
|
54
|
+
"onFail": "warn"
|
|
55
|
+
},
|
|
56
|
+
"packageManager": {
|
|
57
|
+
"name": "npm",
|
|
58
|
+
"version": "12.0.2",
|
|
59
|
+
"onFail": "warn"
|
|
60
|
+
}
|
|
61
|
+
}
|
|
51
62
|
}
|
package/src/appImage.js
CHANGED
|
@@ -19,13 +19,40 @@ const APPIMAGETOOL_URL =
|
|
|
19
19
|
/**
|
|
20
20
|
* @typedef {object} AppImageOptions
|
|
21
21
|
* @property {string} appDir Path to a built NW.js Linux application, ie. the `outDir` produced by `@nwutils/builder` for `platform: "linux"`.
|
|
22
|
-
* @property {string} appName Name of the application. Must match the `app.name` value used to build `appDir` - the executable and desktop entry are expected at `<appDir>/<appName>` and `<appDir>/<appName>.desktop`.
|
|
22
|
+
* @property {string} appName Name of the application. Must match the `app.name` value used to build `appDir` - the executable and desktop entry are expected at `<appDir>/<appName>` and `<appDir>/<appName>.desktop`. Must not contain `/` or control characters.
|
|
23
23
|
* @property {string} [icon] Path to a `.png` or `.svg` icon. Defaults to the `Icon` value read from `<appDir>/<appName>.desktop`.
|
|
24
24
|
* @property {"ia32" | "x64" | "arm64"} [arch] Target architecture. Defaults to the host architecture. `appimagetool` runs as a native binary, so this must match the host unless the host has emulation (eg. QEMU/binfmt) configured for the target architecture.
|
|
25
25
|
* @property {string} [outDir] Directory the resulting `.AppImage` file is written to. Defaults to the parent directory of `appDir`.
|
|
26
26
|
* @property {string} [cacheDir="./cache"] Directory used to cache the downloaded `appimagetool` binary.
|
|
27
27
|
* @property {boolean} [cache=true] If true, reuse a cached `appimagetool` binary. Otherwise redownload it.
|
|
28
|
-
* @property {string} [appImageToolUrl] Base URL `appimagetool-<arch>.AppImage` is downloaded from. Defaults to the `AppImage/appimagetool` "continuous" GitHub release.
|
|
28
|
+
* @property {string} [appImageToolUrl] Base URL `appimagetool-<arch>.AppImage` is downloaded from. Defaults to the `AppImage/appimagetool` "continuous" GitHub release. Must be https, except for localhost.
|
|
29
|
+
* @property {boolean} [sign=false] If true, embed a GPG signature in the AppImage. Requires `gpg` and a usable secret key on the host. A passphrase-protected key reads its passphrase from the `APPIMAGETOOL_SIGN_PASSPHRASE` environment variable.
|
|
30
|
+
* @property {string} [signKey] ID of the GPG key to sign with. Defaults to `gpg`'s default secret key. Requires `sign` to be true.
|
|
31
|
+
* @property {string} [version] Version of the application, eg. `"1.2.0"`. Required when `publish` is set: it is written to the update info file `@nwutils/updater` compares against.
|
|
32
|
+
* @property {PublishOptions} [publish] Where releases are published. When set, an `app-update.yml` pointing at it is embedded in the AppImage and a `latest-linux[-<arch>].yml` update info file is written next to it, for `@nwutils/updater` to self update from.
|
|
33
|
+
* @property {string | boolean} [updateInformation] AppImage update information for zsync based updaters (eg. AppImageUpdate, `appimageupdatetool`, AppImage managers). `true` derives it from `publish`. A string is embedded as-is, eg. `"gh-releases-zsync|owner|repo|latest|Demo-x86_64.AppImage.zsync"`. Requires `zsyncmake` on the host to produce the `.zsync` file.
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Publish to a GitHub repository's releases. The AppImage and update info
|
|
38
|
+
* file must be uploaded as assets of the latest (non draft, non prerelease)
|
|
39
|
+
* release.
|
|
40
|
+
* @typedef {object} GitHubPublishOptions
|
|
41
|
+
* @property {"github"} provider
|
|
42
|
+
* @property {string} owner Owner of the GitHub repository.
|
|
43
|
+
* @property {string} repo Name of the GitHub repository.
|
|
44
|
+
*/
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Publish to any static file server. The AppImage and update info file must
|
|
48
|
+
* be served from `url`.
|
|
49
|
+
* @typedef {object} GenericPublishOptions
|
|
50
|
+
* @property {"generic"} provider
|
|
51
|
+
* @property {string} url Base URL the latest release's files are served from.
|
|
52
|
+
*/
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* @typedef {GitHubPublishOptions | GenericPublishOptions} PublishOptions
|
|
29
56
|
*/
|
|
30
57
|
|
|
31
58
|
/**
|
|
@@ -47,6 +74,11 @@ async function appImage({
|
|
|
47
74
|
cacheDir = "./cache",
|
|
48
75
|
cache = true,
|
|
49
76
|
appImageToolUrl = APPIMAGETOOL_URL,
|
|
77
|
+
sign = false,
|
|
78
|
+
signKey,
|
|
79
|
+
version,
|
|
80
|
+
publish,
|
|
81
|
+
updateInformation,
|
|
50
82
|
}) {
|
|
51
83
|
if (process.platform !== "linux") {
|
|
52
84
|
throw new Error(
|
|
@@ -69,6 +101,68 @@ async function appImage({
|
|
|
69
101
|
`Expected "options.appName" to be a non-empty string. Received: ${JSON.stringify(appName)}`,
|
|
70
102
|
);
|
|
71
103
|
}
|
|
104
|
+
/*
|
|
105
|
+
* `appName` names files inside `appDir` and `outDir`, and is written into
|
|
106
|
+
* the desktop entry and `AppRun`: a path separator or `..` would reach
|
|
107
|
+
* outside those directories, and a line break would inject desktop entries.
|
|
108
|
+
*/
|
|
109
|
+
const hasControlCharacter = [...appName].some((character) => {
|
|
110
|
+
const code = character.charCodeAt(0);
|
|
111
|
+
return code < 0x20 || code === 0x7f;
|
|
112
|
+
});
|
|
113
|
+
if (
|
|
114
|
+
appName.includes("/") ||
|
|
115
|
+
hasControlCharacter ||
|
|
116
|
+
appName === "." ||
|
|
117
|
+
appName === ".."
|
|
118
|
+
) {
|
|
119
|
+
throw new Error(
|
|
120
|
+
`Expected "options.appName" to be a file name without "/" or control characters. Received: ${JSON.stringify(appName)}`,
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (typeof sign !== "boolean") {
|
|
125
|
+
throw new Error(
|
|
126
|
+
`Expected "options.sign" to be a boolean. Received: ${JSON.stringify(sign)}`,
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (signKey !== undefined) {
|
|
131
|
+
if (typeof signKey !== "string" || signKey === "") {
|
|
132
|
+
throw new Error(
|
|
133
|
+
`Expected "options.signKey" to be a non-empty string. Received: ${JSON.stringify(signKey)}`,
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
if (sign === false) {
|
|
137
|
+
throw new Error(
|
|
138
|
+
'"options.signKey" was passed but "options.sign" is false. Set "options.sign" to true to sign the AppImage.',
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (publish !== undefined) {
|
|
144
|
+
validatePublish(publish);
|
|
145
|
+
if (typeof version !== "string" || version === "") {
|
|
146
|
+
throw new Error(
|
|
147
|
+
`Expected "options.version" to be a non-empty string when "options.publish" is set. Received: ${JSON.stringify(version)}`,
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (
|
|
153
|
+
updateInformation !== undefined &&
|
|
154
|
+
typeof updateInformation !== "boolean" &&
|
|
155
|
+
(typeof updateInformation !== "string" || updateInformation === "")
|
|
156
|
+
) {
|
|
157
|
+
throw new Error(
|
|
158
|
+
`Expected "options.updateInformation" to be a boolean or a non-empty string. Received: ${JSON.stringify(updateInformation)}`,
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
if (updateInformation === true && publish === undefined) {
|
|
162
|
+
throw new Error(
|
|
163
|
+
'"options.updateInformation" is true but "options.publish" is not set. Set "options.publish" to derive it, or pass the update information string directly.',
|
|
164
|
+
);
|
|
165
|
+
}
|
|
72
166
|
|
|
73
167
|
const resolvedArch =
|
|
74
168
|
/** @type {"ia32" | "x64" | "arm64" | undefined} */ (arch) ??
|
|
@@ -76,7 +170,10 @@ async function appImage({
|
|
|
76
170
|
/** @type {unknown} */ (process.arch)
|
|
77
171
|
);
|
|
78
172
|
|
|
79
|
-
|
|
173
|
+
/* `Object.hasOwn` so inherited keys such as "constructor" aren't mistaken for architectures. */
|
|
174
|
+
const appImageToolArch = Object.hasOwn(util.APPIMAGE_ARCH_KV, resolvedArch)
|
|
175
|
+
? util.APPIMAGE_ARCH_KV[resolvedArch]
|
|
176
|
+
: undefined;
|
|
80
177
|
if (appImageToolArch === undefined) {
|
|
81
178
|
throw new Error(
|
|
82
179
|
`Expected "options.arch" to be "ia32", "x64" or "arm64". Received: ${JSON.stringify(arch)}`,
|
|
@@ -153,7 +250,7 @@ async function appImage({
|
|
|
153
250
|
const appImageDesktopEntry = {
|
|
154
251
|
...desktopEntry,
|
|
155
252
|
Name: desktopEntry.Name ?? appName,
|
|
156
|
-
Exec: appName,
|
|
253
|
+
Exec: util.desktopExecArg(appName),
|
|
157
254
|
Icon: path.basename(iconFileName, path.extname(iconFileName)),
|
|
158
255
|
Categories: desktopEntry.Categories || "Utility;",
|
|
159
256
|
};
|
|
@@ -168,21 +265,79 @@ async function appImage({
|
|
|
168
265
|
[
|
|
169
266
|
"#!/bin/sh",
|
|
170
267
|
'HERE="$(dirname "$(readlink -f "${0}")")"',
|
|
171
|
-
|
|
268
|
+
/* Single quoted, so the app name is never expanded or run by the shell. */
|
|
269
|
+
`exec "\${HERE}"/${util.shellQuote(appName)} "$@"`,
|
|
172
270
|
"",
|
|
173
271
|
].join("\n"),
|
|
174
272
|
);
|
|
175
273
|
await fs.promises.chmod(appRunPath, 0o755);
|
|
176
274
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
275
|
+
/*
|
|
276
|
+
* Sits next to the NW.js executable, ie. at
|
|
277
|
+
* `path.dirname(process.execPath)` at runtime, which is where
|
|
278
|
+
* `@nwutils/updater` looks for it.
|
|
279
|
+
*/
|
|
280
|
+
if (publish !== undefined) {
|
|
281
|
+
await fs.promises.writeFile(
|
|
282
|
+
path.resolve(appImageDir, "app-update.yml"),
|
|
283
|
+
util.stringifyYaml(
|
|
284
|
+
publish.provider === "github"
|
|
285
|
+
? {
|
|
286
|
+
provider: publish.provider,
|
|
287
|
+
owner: publish.owner,
|
|
288
|
+
repo: publish.repo,
|
|
289
|
+
}
|
|
290
|
+
: { provider: publish.provider, url: publish.url },
|
|
291
|
+
),
|
|
292
|
+
);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
const appImageFileName = `${appName}-${appImageToolArch}.AppImage`;
|
|
296
|
+
const appImageFilePath = path.resolve(resolvedOutDir, appImageFileName);
|
|
297
|
+
const zsyncFilePath = `${appImageFilePath}.zsync`;
|
|
298
|
+
|
|
299
|
+
const resolvedUpdateInformation =
|
|
300
|
+
updateInformation === true
|
|
301
|
+
? deriveUpdateInformation(
|
|
302
|
+
/** @type {PublishOptions} */ (publish),
|
|
303
|
+
path.basename(zsyncFilePath),
|
|
304
|
+
)
|
|
305
|
+
: updateInformation || undefined;
|
|
306
|
+
|
|
307
|
+
/*
|
|
308
|
+
* Signing happens inside the same `appimagetool` run that builds the
|
|
309
|
+
* file: an embedded signature can't be added to an existing AppImage.
|
|
310
|
+
*/
|
|
311
|
+
const signArgs = sign
|
|
312
|
+
? ["--sign", ...(signKey === undefined ? [] : ["--sign-key", signKey])]
|
|
313
|
+
: [];
|
|
314
|
+
|
|
315
|
+
const updateInformationArgs =
|
|
316
|
+
resolvedUpdateInformation === undefined
|
|
317
|
+
? []
|
|
318
|
+
: ["--updateinformation", resolvedUpdateInformation];
|
|
319
|
+
|
|
320
|
+
if (resolvedUpdateInformation !== undefined) {
|
|
321
|
+
/* Don't let a `.zsync` file left over from a previous build pass the check below. */
|
|
322
|
+
await fs.promises.rm(zsyncFilePath, { force: true });
|
|
323
|
+
}
|
|
181
324
|
|
|
182
325
|
child_process.execFileSync(
|
|
183
326
|
appImageToolPath,
|
|
184
|
-
[
|
|
327
|
+
[
|
|
328
|
+
"--no-appstream",
|
|
329
|
+
...signArgs,
|
|
330
|
+
...updateInformationArgs,
|
|
331
|
+
appImageDir,
|
|
332
|
+
appImageFilePath,
|
|
333
|
+
],
|
|
185
334
|
{
|
|
335
|
+
/*
|
|
336
|
+
* `appimagetool` runs `zsyncmake` without an output path, which
|
|
337
|
+
* writes the `.zsync` file to the working directory - run from
|
|
338
|
+
* `outDir` so it lands next to the AppImage.
|
|
339
|
+
*/
|
|
340
|
+
cwd: resolvedOutDir,
|
|
186
341
|
env: {
|
|
187
342
|
...process.env,
|
|
188
343
|
ARCH: appImageToolArch,
|
|
@@ -197,12 +352,152 @@ async function appImage({
|
|
|
197
352
|
},
|
|
198
353
|
);
|
|
199
354
|
|
|
355
|
+
/*
|
|
356
|
+
* `appimagetool` only warns, and still exits successfully, when it
|
|
357
|
+
* can't find `zsyncmake` - leaving an AppImage that advertises a
|
|
358
|
+
* `.zsync` file which was never produced.
|
|
359
|
+
*/
|
|
360
|
+
if (
|
|
361
|
+
resolvedUpdateInformation !== undefined &&
|
|
362
|
+
(await util.fileExists(zsyncFilePath)) === false
|
|
363
|
+
) {
|
|
364
|
+
throw new Error(
|
|
365
|
+
`"appimagetool" did not produce ${zsyncFilePath}. Install "zsyncmake" (usually packaged as "zsync") on the host to build AppImages with "options.updateInformation".`,
|
|
366
|
+
);
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
if (publish !== undefined) {
|
|
370
|
+
const appImageSha512 = await util.sha512(appImageFilePath);
|
|
371
|
+
const { size } = await fs.promises.stat(appImageFilePath);
|
|
372
|
+
await fs.promises.writeFile(
|
|
373
|
+
path.resolve(resolvedOutDir, util.updateInfoFileName(resolvedArch)),
|
|
374
|
+
util.stringifyYaml({
|
|
375
|
+
version: /** @type {string} */ (version),
|
|
376
|
+
files: [{ url: appImageFileName, sha512: appImageSha512, size }],
|
|
377
|
+
path: appImageFileName,
|
|
378
|
+
sha512: appImageSha512,
|
|
379
|
+
releaseDate: new Date().toISOString(),
|
|
380
|
+
}),
|
|
381
|
+
);
|
|
382
|
+
}
|
|
383
|
+
|
|
200
384
|
return appImageFilePath;
|
|
201
385
|
} finally {
|
|
202
386
|
await fs.promises.rm(appImageDir, { recursive: true, force: true });
|
|
203
387
|
}
|
|
204
388
|
}
|
|
205
389
|
|
|
390
|
+
/**
|
|
391
|
+
* Throw if `publish` isn't a valid `PublishOptions` object.
|
|
392
|
+
* @param {unknown} publish
|
|
393
|
+
* @returns {asserts publish is PublishOptions}
|
|
394
|
+
*/
|
|
395
|
+
function validatePublish(publish) {
|
|
396
|
+
if (typeof publish !== "object" || publish === null) {
|
|
397
|
+
throw new Error(
|
|
398
|
+
`Expected "options.publish" to be an object. Received: ${JSON.stringify(publish)}`,
|
|
399
|
+
);
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
const { provider } = /** @type {{provider?: unknown}} */ (publish);
|
|
403
|
+
/** @type {string[]} */
|
|
404
|
+
let requiredKeys;
|
|
405
|
+
if (provider === "github") {
|
|
406
|
+
requiredKeys = ["owner", "repo"];
|
|
407
|
+
} else if (provider === "generic") {
|
|
408
|
+
requiredKeys = ["url"];
|
|
409
|
+
} else {
|
|
410
|
+
throw new Error(
|
|
411
|
+
`Expected "options.publish.provider" to be "github" or "generic". Received: ${JSON.stringify(provider)}`,
|
|
412
|
+
);
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
for (const key of requiredKeys) {
|
|
416
|
+
const value = /** @type {Record<string, unknown>} */ (publish)[key];
|
|
417
|
+
if (typeof value !== "string" || value === "") {
|
|
418
|
+
throw new Error(
|
|
419
|
+
`Expected "options.publish.${key}" to be a non-empty string for provider "${provider}". Received: ${JSON.stringify(value)}`,
|
|
420
|
+
);
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
if (provider === "github") {
|
|
425
|
+
/*
|
|
426
|
+
* The characters GitHub allows in owner and repository names. Anything
|
|
427
|
+
* else - eg. "|", the update information's field separator - is rejected.
|
|
428
|
+
*/
|
|
429
|
+
for (const key of requiredKeys) {
|
|
430
|
+
const value = /** @type {Record<string, string>} */ (publish)[key];
|
|
431
|
+
if (/^[A-Za-z0-9._-]+$/.test(value) === false) {
|
|
432
|
+
throw new Error(
|
|
433
|
+
`Expected "options.publish.${key}" to be a GitHub ${key} name (letters, digits, ".", "-" and "_"). Received: ${JSON.stringify(value)}`,
|
|
434
|
+
);
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
if (provider === "generic") {
|
|
440
|
+
const { url } = /** @type {GenericPublishOptions} */ (publish);
|
|
441
|
+
let protocol;
|
|
442
|
+
try {
|
|
443
|
+
protocol = new URL(url).protocol;
|
|
444
|
+
} catch {
|
|
445
|
+
protocol = undefined;
|
|
446
|
+
}
|
|
447
|
+
if (protocol !== "https:" && protocol !== "http:") {
|
|
448
|
+
throw new Error(
|
|
449
|
+
`Expected "options.publish.url" to be an http(s) URL. Received: ${JSON.stringify(url)}`,
|
|
450
|
+
);
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
/**
|
|
456
|
+
* Build the AppImage update information string for `publish`, in the format
|
|
457
|
+
* zsync based AppImage updaters understand.
|
|
458
|
+
* @param {PublishOptions} publish
|
|
459
|
+
* @param {string} zsyncFileName Name the `.zsync` file is published under.
|
|
460
|
+
* @returns {string}
|
|
461
|
+
*/
|
|
462
|
+
function deriveUpdateInformation(publish, zsyncFileName) {
|
|
463
|
+
if (publish.provider === "github") {
|
|
464
|
+
return `gh-releases-zsync|${publish.owner}|${publish.repo}|latest|${zsyncFileName}`;
|
|
465
|
+
}
|
|
466
|
+
/* Trim trailing slashes with a linear scan rather than a backtracking regular expression. */
|
|
467
|
+
let end = publish.url.length;
|
|
468
|
+
while (end > 0 && publish.url[end - 1] === "/") {
|
|
469
|
+
end--;
|
|
470
|
+
}
|
|
471
|
+
return `zsync|${publish.url.slice(0, end)}/${zsyncFileName}`;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
/**
|
|
475
|
+
* Throw unless `appImageToolUrl` is an https URL. The downloaded binary is
|
|
476
|
+
* executed, so a plain http download would let anyone on the network path
|
|
477
|
+
* replace it. http is only accepted for loopback hosts, eg. a local mirror.
|
|
478
|
+
* @param {string} appImageToolUrl
|
|
479
|
+
* @returns {void}
|
|
480
|
+
*/
|
|
481
|
+
function validateAppImageToolUrl(appImageToolUrl) {
|
|
482
|
+
let url;
|
|
483
|
+
try {
|
|
484
|
+
url = new URL(appImageToolUrl);
|
|
485
|
+
} catch {
|
|
486
|
+
url = undefined;
|
|
487
|
+
}
|
|
488
|
+
const isLoopback =
|
|
489
|
+
url !== undefined &&
|
|
490
|
+
["localhost", "127.0.0.1", "[::1]"].includes(url.hostname);
|
|
491
|
+
if (
|
|
492
|
+
url === undefined ||
|
|
493
|
+
(url.protocol !== "https:" && (url.protocol !== "http:" || !isLoopback))
|
|
494
|
+
) {
|
|
495
|
+
throw new Error(
|
|
496
|
+
`Expected "options.appImageToolUrl" to be an https URL (http is only allowed for localhost). Received: ${JSON.stringify(appImageToolUrl)}`,
|
|
497
|
+
);
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
|
|
206
501
|
/**
|
|
207
502
|
* Download (or reuse a cached) `appimagetool` binary for `appImageToolArch`.
|
|
208
503
|
* @async
|
|
@@ -220,6 +515,8 @@ async function getAppImageTool({
|
|
|
220
515
|
cacheDir,
|
|
221
516
|
cache,
|
|
222
517
|
}) {
|
|
518
|
+
validateAppImageToolUrl(appImageToolUrl);
|
|
519
|
+
|
|
223
520
|
const appImageToolPath = path.resolve(
|
|
224
521
|
cacheDir,
|
|
225
522
|
`appimagetool-${appImageToolArch}.AppImage`,
|
package/src/index.js
CHANGED
|
@@ -10,6 +10,11 @@ import appImage from "./appImage.js";
|
|
|
10
10
|
* @typedef {"AppImage" | "deb" | "rpm" | "msix" | "nsis" | "dmg"} PackageFormat
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
+
/**
|
|
14
|
+
* Where releases are published, for packagers that support self updating.
|
|
15
|
+
* @typedef {import("./appImage.js").PublishOptions} PublishOptions
|
|
16
|
+
*/
|
|
17
|
+
|
|
13
18
|
/**
|
|
14
19
|
* Package a built NW.js application.
|
|
15
20
|
* @async
|
package/src/util.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import crypto from "node:crypto";
|
|
1
2
|
import fs from "node:fs";
|
|
2
3
|
|
|
3
4
|
/**
|
|
@@ -71,16 +72,122 @@ function parseDesktopEntry(contents) {
|
|
|
71
72
|
function stringifyDesktopEntry(entries) {
|
|
72
73
|
let fileContent = "[Desktop Entry]\n";
|
|
73
74
|
for (const key of Object.keys(entries)) {
|
|
74
|
-
|
|
75
|
-
|
|
75
|
+
const value = entries[key];
|
|
76
|
+
if (value !== undefined) {
|
|
77
|
+
/* A line break would let the key or value inject further entries, eg. a second `Exec`. */
|
|
78
|
+
if (/[\r\n]/.test(key) || /[\r\n]/.test(value)) {
|
|
79
|
+
throw new Error(
|
|
80
|
+
`Desktop entry keys and values must not contain line breaks. Received: ${JSON.stringify(`${key}=${value}`)}`,
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
fileContent += `${key}=${value}\n`;
|
|
76
84
|
}
|
|
77
85
|
}
|
|
78
86
|
return fileContent;
|
|
79
87
|
}
|
|
80
88
|
|
|
89
|
+
/**
|
|
90
|
+
* Quote `value` as a single POSIX shell word. Inside single quotes nothing is
|
|
91
|
+
* expanded, so only single quotes themselves need escaping.
|
|
92
|
+
* @param {string} value
|
|
93
|
+
* @returns {string}
|
|
94
|
+
*/
|
|
95
|
+
function shellQuote(value) {
|
|
96
|
+
return `'${value.replaceAll("'", "'\\''")}'`;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Quote `value` as a single argument of a desktop entry's `Exec` key, per the
|
|
101
|
+
* Desktop Entry Specification: `%` is doubled so it isn't read as a field
|
|
102
|
+
* code, an argument with reserved characters is double quoted with `"`, `` ` ``,
|
|
103
|
+
* `$` and `\` backslash-escaped, and finally backslashes are escaped again as
|
|
104
|
+
* required for every string value.
|
|
105
|
+
* @param {string} value
|
|
106
|
+
* @returns {string}
|
|
107
|
+
*/
|
|
108
|
+
function desktopExecArg(value) {
|
|
109
|
+
let arg = value.replaceAll("%", "%%");
|
|
110
|
+
if (/[\s"'\\><~|&;$*?#()`]/.test(arg)) {
|
|
111
|
+
arg = `"${arg.replace(/["`$\\]/g, "\\$&")}"`;
|
|
112
|
+
}
|
|
113
|
+
return arg.replaceAll("\\", "\\\\");
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Name of the update info file `@nwutils/updater` downloads to find the
|
|
118
|
+
* latest release, following electron-builder's naming: `x64` gets the bare
|
|
119
|
+
* `latest-linux.yml`, every other architecture gets an `-<arch>` suffix.
|
|
120
|
+
* @param {"ia32" | "x64" | "arm64"} arch
|
|
121
|
+
* @returns {string}
|
|
122
|
+
*/
|
|
123
|
+
function updateInfoFileName(arch) {
|
|
124
|
+
return arch === "x64" ? "latest-linux.yml" : `latest-linux-${arch}.yml`;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Serialise `value` as a YAML scalar. Strings are written double-quoted via
|
|
129
|
+
* `JSON.stringify` - a JSON string literal is also a valid YAML double-quoted
|
|
130
|
+
* scalar, so this never needs YAML-specific escaping rules.
|
|
131
|
+
* @param {string | number | boolean} value
|
|
132
|
+
* @returns {string}
|
|
133
|
+
*/
|
|
134
|
+
function stringifyYamlScalar(value) {
|
|
135
|
+
return typeof value === "string" ? JSON.stringify(value) : String(value);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Serialise a map of scalars, and lists of maps of scalars, as YAML. This is
|
|
140
|
+
* the only shape the update info and `app-update.yml` files use, so it avoids
|
|
141
|
+
* pulling in a YAML library for a handful of lines.
|
|
142
|
+
* @param {Record<string, string | number | boolean | Record<string, string | number | boolean>[] | undefined>} entries
|
|
143
|
+
* @returns {string}
|
|
144
|
+
*/
|
|
145
|
+
function stringifyYaml(entries) {
|
|
146
|
+
let fileContent = "";
|
|
147
|
+
for (const [key, value] of Object.entries(entries)) {
|
|
148
|
+
if (value === undefined) {
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
if (Array.isArray(value)) {
|
|
152
|
+
fileContent += `${key}:\n`;
|
|
153
|
+
for (const item of value) {
|
|
154
|
+
let prefix = " - ";
|
|
155
|
+
for (const [itemKey, itemValue] of Object.entries(item)) {
|
|
156
|
+
fileContent += `${prefix}${itemKey}: ${stringifyYamlScalar(itemValue)}\n`;
|
|
157
|
+
prefix = " ";
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
} else {
|
|
161
|
+
fileContent += `${key}: ${stringifyYamlScalar(value)}\n`;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
return fileContent;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Compute the base64 encoded SHA-512 digest of the file at `filePath`, the
|
|
169
|
+
* same encoding electron-builder writes into its update info files.
|
|
170
|
+
* @async
|
|
171
|
+
* @function
|
|
172
|
+
* @param {string} filePath
|
|
173
|
+
* @returns {Promise<string>}
|
|
174
|
+
*/
|
|
175
|
+
async function sha512(filePath) {
|
|
176
|
+
const hash = crypto.createHash("sha512");
|
|
177
|
+
for await (const chunk of fs.createReadStream(filePath)) {
|
|
178
|
+
hash.update(chunk);
|
|
179
|
+
}
|
|
180
|
+
return hash.digest("base64");
|
|
181
|
+
}
|
|
182
|
+
|
|
81
183
|
export default {
|
|
82
184
|
APPIMAGE_ARCH_KV,
|
|
185
|
+
desktopExecArg,
|
|
83
186
|
fileExists,
|
|
84
187
|
parseDesktopEntry,
|
|
188
|
+
sha512,
|
|
189
|
+
shellQuote,
|
|
85
190
|
stringifyDesktopEntry,
|
|
191
|
+
stringifyYaml,
|
|
192
|
+
updateInfoFileName,
|
|
86
193
|
};
|
package/types/appImage.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ export type AppImageOptions = {
|
|
|
4
4
|
*/
|
|
5
5
|
appDir: string;
|
|
6
6
|
/**
|
|
7
|
-
* Name of the application. Must match the `app.name` value used to build `appDir` - the executable and desktop entry are expected at `<appDir>/<appName>` and `<appDir>/<appName>.desktop`.
|
|
7
|
+
* Name of the application. Must match the `app.name` value used to build `appDir` - the executable and desktop entry are expected at `<appDir>/<appName>` and `<appDir>/<appName>.desktop`. Must not contain `/` or control characters.
|
|
8
8
|
*/
|
|
9
9
|
appName: string;
|
|
10
10
|
/**
|
|
@@ -28,20 +28,83 @@ export type AppImageOptions = {
|
|
|
28
28
|
*/
|
|
29
29
|
cache?: boolean;
|
|
30
30
|
/**
|
|
31
|
-
* Base URL `appimagetool-<arch>.AppImage` is downloaded from. Defaults to the `AppImage/appimagetool` "continuous" GitHub release.
|
|
31
|
+
* Base URL `appimagetool-<arch>.AppImage` is downloaded from. Defaults to the `AppImage/appimagetool` "continuous" GitHub release. Must be https, except for localhost.
|
|
32
32
|
*/
|
|
33
33
|
appImageToolUrl?: string;
|
|
34
|
+
/**
|
|
35
|
+
* If true, embed a GPG signature in the AppImage. Requires `gpg` and a usable secret key on the host. A passphrase-protected key reads its passphrase from the `APPIMAGETOOL_SIGN_PASSPHRASE` environment variable.
|
|
36
|
+
*/
|
|
37
|
+
sign?: boolean;
|
|
38
|
+
/**
|
|
39
|
+
* ID of the GPG key to sign with. Defaults to `gpg`'s default secret key. Requires `sign` to be true.
|
|
40
|
+
*/
|
|
41
|
+
signKey?: string;
|
|
42
|
+
/**
|
|
43
|
+
* Version of the application, eg. `"1.2.0"`. Required when `publish` is set: it is written to the update info file `@nwutils/updater` compares against.
|
|
44
|
+
*/
|
|
45
|
+
version?: string;
|
|
46
|
+
/**
|
|
47
|
+
* Where releases are published. When set, an `app-update.yml` pointing at it is embedded in the AppImage and a `latest-linux[-<arch>].yml` update info file is written next to it, for `@nwutils/updater` to self update from.
|
|
48
|
+
*/
|
|
49
|
+
publish?: PublishOptions;
|
|
50
|
+
/**
|
|
51
|
+
* AppImage update information for zsync based updaters (eg. AppImageUpdate, `appimageupdatetool`, AppImage managers). `true` derives it from `publish`. A string is embedded as-is, eg. `"gh-releases-zsync|owner|repo|latest|Demo-x86_64.AppImage.zsync"`. Requires `zsyncmake` on the host to produce the `.zsync` file.
|
|
52
|
+
*/
|
|
53
|
+
updateInformation?: string | boolean;
|
|
54
|
+
};
|
|
55
|
+
export type GitHubPublishOptions = {
|
|
56
|
+
provider: "github";
|
|
57
|
+
/**
|
|
58
|
+
* Owner of the GitHub repository.
|
|
59
|
+
*/
|
|
60
|
+
owner: string;
|
|
61
|
+
/**
|
|
62
|
+
* Name of the GitHub repository.
|
|
63
|
+
*/
|
|
64
|
+
repo: string;
|
|
65
|
+
};
|
|
66
|
+
export type GenericPublishOptions = {
|
|
67
|
+
provider: "generic";
|
|
68
|
+
/**
|
|
69
|
+
* Base URL the latest release's files are served from.
|
|
70
|
+
*/
|
|
71
|
+
url: string;
|
|
34
72
|
};
|
|
73
|
+
export type PublishOptions = GitHubPublishOptions | GenericPublishOptions;
|
|
35
74
|
/**
|
|
36
75
|
* @typedef {object} AppImageOptions
|
|
37
76
|
* @property {string} appDir Path to a built NW.js Linux application, ie. the `outDir` produced by `@nwutils/builder` for `platform: "linux"`.
|
|
38
|
-
* @property {string} appName Name of the application. Must match the `app.name` value used to build `appDir` - the executable and desktop entry are expected at `<appDir>/<appName>` and `<appDir>/<appName>.desktop`.
|
|
77
|
+
* @property {string} appName Name of the application. Must match the `app.name` value used to build `appDir` - the executable and desktop entry are expected at `<appDir>/<appName>` and `<appDir>/<appName>.desktop`. Must not contain `/` or control characters.
|
|
39
78
|
* @property {string} [icon] Path to a `.png` or `.svg` icon. Defaults to the `Icon` value read from `<appDir>/<appName>.desktop`.
|
|
40
79
|
* @property {"ia32" | "x64" | "arm64"} [arch] Target architecture. Defaults to the host architecture. `appimagetool` runs as a native binary, so this must match the host unless the host has emulation (eg. QEMU/binfmt) configured for the target architecture.
|
|
41
80
|
* @property {string} [outDir] Directory the resulting `.AppImage` file is written to. Defaults to the parent directory of `appDir`.
|
|
42
81
|
* @property {string} [cacheDir="./cache"] Directory used to cache the downloaded `appimagetool` binary.
|
|
43
82
|
* @property {boolean} [cache=true] If true, reuse a cached `appimagetool` binary. Otherwise redownload it.
|
|
44
|
-
* @property {string} [appImageToolUrl] Base URL `appimagetool-<arch>.AppImage` is downloaded from. Defaults to the `AppImage/appimagetool` "continuous" GitHub release.
|
|
83
|
+
* @property {string} [appImageToolUrl] Base URL `appimagetool-<arch>.AppImage` is downloaded from. Defaults to the `AppImage/appimagetool` "continuous" GitHub release. Must be https, except for localhost.
|
|
84
|
+
* @property {boolean} [sign=false] If true, embed a GPG signature in the AppImage. Requires `gpg` and a usable secret key on the host. A passphrase-protected key reads its passphrase from the `APPIMAGETOOL_SIGN_PASSPHRASE` environment variable.
|
|
85
|
+
* @property {string} [signKey] ID of the GPG key to sign with. Defaults to `gpg`'s default secret key. Requires `sign` to be true.
|
|
86
|
+
* @property {string} [version] Version of the application, eg. `"1.2.0"`. Required when `publish` is set: it is written to the update info file `@nwutils/updater` compares against.
|
|
87
|
+
* @property {PublishOptions} [publish] Where releases are published. When set, an `app-update.yml` pointing at it is embedded in the AppImage and a `latest-linux[-<arch>].yml` update info file is written next to it, for `@nwutils/updater` to self update from.
|
|
88
|
+
* @property {string | boolean} [updateInformation] AppImage update information for zsync based updaters (eg. AppImageUpdate, `appimageupdatetool`, AppImage managers). `true` derives it from `publish`. A string is embedded as-is, eg. `"gh-releases-zsync|owner|repo|latest|Demo-x86_64.AppImage.zsync"`. Requires `zsyncmake` on the host to produce the `.zsync` file.
|
|
89
|
+
*/
|
|
90
|
+
/**
|
|
91
|
+
* Publish to a GitHub repository's releases. The AppImage and update info
|
|
92
|
+
* file must be uploaded as assets of the latest (non draft, non prerelease)
|
|
93
|
+
* release.
|
|
94
|
+
* @typedef {object} GitHubPublishOptions
|
|
95
|
+
* @property {"github"} provider
|
|
96
|
+
* @property {string} owner Owner of the GitHub repository.
|
|
97
|
+
* @property {string} repo Name of the GitHub repository.
|
|
98
|
+
*/
|
|
99
|
+
/**
|
|
100
|
+
* Publish to any static file server. The AppImage and update info file must
|
|
101
|
+
* be served from `url`.
|
|
102
|
+
* @typedef {object} GenericPublishOptions
|
|
103
|
+
* @property {"generic"} provider
|
|
104
|
+
* @property {string} url Base URL the latest release's files are served from.
|
|
105
|
+
*/
|
|
106
|
+
/**
|
|
107
|
+
* @typedef {GitHubPublishOptions | GenericPublishOptions} PublishOptions
|
|
45
108
|
*/
|
|
46
109
|
/**
|
|
47
110
|
* Package a built NW.js Linux application as an AppImage.
|
|
@@ -53,7 +116,7 @@ export type AppImageOptions = {
|
|
|
53
116
|
* @param {AppImageOptions} options
|
|
54
117
|
* @returns {Promise<string>} Path to the resulting `.AppImage` file.
|
|
55
118
|
*/
|
|
56
|
-
declare function appImage({ appDir, appName, icon, arch, outDir, cacheDir, cache, appImageToolUrl, }: AppImageOptions): Promise<string>;
|
|
119
|
+
declare function appImage({ appDir, appName, icon, arch, outDir, cacheDir, cache, appImageToolUrl, sign, signKey, version, publish, updateInformation, }: AppImageOptions): Promise<string>;
|
|
57
120
|
/**
|
|
58
121
|
* Download (or reuse a cached) `appimagetool` binary for `appImageToolArch`.
|
|
59
122
|
* @async
|
package/types/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export type PackageFormat = "AppImage" | "deb" | "rpm" | "msix" | "nsis" | "dmg";
|
|
2
|
+
export type PublishOptions = import("./appImage.js").PublishOptions;
|
|
2
3
|
/**
|
|
3
4
|
* Supported packaged output formats.
|
|
4
5
|
*
|
|
@@ -8,6 +9,10 @@ export type PackageFormat = "AppImage" | "deb" | "rpm" | "msix" | "nsis" | "dmg"
|
|
|
8
9
|
* land.
|
|
9
10
|
* @typedef {"AppImage" | "deb" | "rpm" | "msix" | "nsis" | "dmg"} PackageFormat
|
|
10
11
|
*/
|
|
12
|
+
/**
|
|
13
|
+
* Where releases are published, for packagers that support self updating.
|
|
14
|
+
* @typedef {import("./appImage.js").PublishOptions} PublishOptions
|
|
15
|
+
*/
|
|
11
16
|
/**
|
|
12
17
|
* Package a built NW.js application.
|
|
13
18
|
* @async
|
package/types/util.d.ts
CHANGED
|
@@ -19,10 +19,57 @@ declare function parseDesktopEntry(contents: string): Record<string, string>;
|
|
|
19
19
|
* @returns {string}
|
|
20
20
|
*/
|
|
21
21
|
declare function stringifyDesktopEntry(entries: Record<string, string | undefined>): string;
|
|
22
|
+
/**
|
|
23
|
+
* Quote `value` as a single POSIX shell word. Inside single quotes nothing is
|
|
24
|
+
* expanded, so only single quotes themselves need escaping.
|
|
25
|
+
* @param {string} value
|
|
26
|
+
* @returns {string}
|
|
27
|
+
*/
|
|
28
|
+
declare function shellQuote(value: string): string;
|
|
29
|
+
/**
|
|
30
|
+
* Quote `value` as a single argument of a desktop entry's `Exec` key, per the
|
|
31
|
+
* Desktop Entry Specification: `%` is doubled so it isn't read as a field
|
|
32
|
+
* code, an argument with reserved characters is double quoted with `"`, `` ` ``,
|
|
33
|
+
* `$` and `\` backslash-escaped, and finally backslashes are escaped again as
|
|
34
|
+
* required for every string value.
|
|
35
|
+
* @param {string} value
|
|
36
|
+
* @returns {string}
|
|
37
|
+
*/
|
|
38
|
+
declare function desktopExecArg(value: string): string;
|
|
39
|
+
/**
|
|
40
|
+
* Name of the update info file `@nwutils/updater` downloads to find the
|
|
41
|
+
* latest release, following electron-builder's naming: `x64` gets the bare
|
|
42
|
+
* `latest-linux.yml`, every other architecture gets an `-<arch>` suffix.
|
|
43
|
+
* @param {"ia32" | "x64" | "arm64"} arch
|
|
44
|
+
* @returns {string}
|
|
45
|
+
*/
|
|
46
|
+
declare function updateInfoFileName(arch: "ia32" | "x64" | "arm64"): string;
|
|
47
|
+
/**
|
|
48
|
+
* Serialise a map of scalars, and lists of maps of scalars, as YAML. This is
|
|
49
|
+
* the only shape the update info and `app-update.yml` files use, so it avoids
|
|
50
|
+
* pulling in a YAML library for a handful of lines.
|
|
51
|
+
* @param {Record<string, string | number | boolean | Record<string, string | number | boolean>[] | undefined>} entries
|
|
52
|
+
* @returns {string}
|
|
53
|
+
*/
|
|
54
|
+
declare function stringifyYaml(entries: Record<string, string | number | boolean | Record<string, string | number | boolean>[] | undefined>): string;
|
|
55
|
+
/**
|
|
56
|
+
* Compute the base64 encoded SHA-512 digest of the file at `filePath`, the
|
|
57
|
+
* same encoding electron-builder writes into its update info files.
|
|
58
|
+
* @async
|
|
59
|
+
* @function
|
|
60
|
+
* @param {string} filePath
|
|
61
|
+
* @returns {Promise<string>}
|
|
62
|
+
*/
|
|
63
|
+
declare function sha512(filePath: string): Promise<string>;
|
|
22
64
|
declare const _default: {
|
|
23
65
|
APPIMAGE_ARCH_KV: Record<"arm64" | "ia32" | "x64", string>;
|
|
66
|
+
desktopExecArg: typeof desktopExecArg;
|
|
24
67
|
fileExists: typeof fileExists;
|
|
25
68
|
parseDesktopEntry: typeof parseDesktopEntry;
|
|
69
|
+
sha512: typeof sha512;
|
|
70
|
+
shellQuote: typeof shellQuote;
|
|
26
71
|
stringifyDesktopEntry: typeof stringifyDesktopEntry;
|
|
72
|
+
stringifyYaml: typeof stringifyYaml;
|
|
73
|
+
updateInfoFileName: typeof updateInfoFileName;
|
|
27
74
|
};
|
|
28
75
|
export default _default;
|