cross-packer 0.1.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/LICENSE +661 -0
- package/README.md +151 -0
- package/README.zh-CN.md +151 -0
- package/dist/archive/ar.d.ts +12 -0
- package/dist/archive/seven-za.d.ts +32 -0
- package/dist/archive/tar.d.ts +30 -0
- package/dist/archive/zip-reader.d.ts +17 -0
- package/dist/asar/dependency/collect.d.ts +2 -0
- package/dist/asar/dependency/detector.d.ts +8 -0
- package/dist/asar/dependency/hoister.d.ts +27 -0
- package/dist/asar/dependency/utils.d.ts +59 -0
- package/dist/asar/fileset/matcher.d.ts +12 -0
- package/dist/asar/fileset/platform-fileset.d.ts +36 -0
- package/dist/asar/fileset/transformer.d.ts +2 -0
- package/dist/asar/fileset/unpack-detector.d.ts +10 -0
- package/dist/asar/fileset/walker.d.ts +14 -0
- package/dist/asar/pack.d.ts +23 -0
- package/dist/asar/packer/app-collector.d.ts +2 -0
- package/dist/asar/packer/collector.d.ts +18 -0
- package/dist/asar/packer/concurrency.d.ts +1 -0
- package/dist/asar/packer/integrity.d.ts +8 -0
- package/dist/asar/packer/nm-collector.d.ts +7 -0
- package/dist/asar/packer/stream-builder.d.ts +18 -0
- package/dist/cli.d.ts +2 -0
- package/dist/config/index.d.ts +97 -0
- package/dist/electron/dist-zip.d.ts +18 -0
- package/dist/electron/download.d.ts +21 -0
- package/dist/index.d.ts +11 -0
- package/dist/packer/common/app-update-yml.d.ts +5 -0
- package/dist/packer/common/extra-resources.d.ts +39 -0
- package/dist/packer/common/staging.d.ts +3 -0
- package/dist/packer/common/template.d.ts +2 -0
- package/dist/packer/linux/linux.d.ts +2 -0
- package/dist/packer/mac/mac.d.ts +23 -0
- package/dist/packer/win/nsis.d.ts +24 -0
- package/dist/packer/win/resedit.d.ts +5 -0
- package/dist/packer/win/toolchain.d.ts +8 -0
- package/dist/packer/win/win.d.ts +12 -0
- package/dist/pipeline/pipeline.d.ts +41 -0
- package/dist/pipeline/targets.d.ts +28 -0
- package/dist/shared/hash.d.ts +2 -0
- package/dist/shared/logger.d.ts +39 -0
- package/dist/shared/platform.d.ts +26 -0
- package/dist/shared/types.d.ts +68 -0
- package/dist/update/blockmap.d.ts +13 -0
- package/dist/update/metadata.d.ts +20 -0
- package/dist/update/version.d.ts +7 -0
- package/package.json +71 -0
- package/src/archive/ar.ts +41 -0
- package/src/archive/seven-za.ts +160 -0
- package/src/archive/tar.ts +142 -0
- package/src/archive/zip-reader.ts +61 -0
- package/src/asar/dependency/collect.ts +437 -0
- package/src/asar/dependency/detector.ts +73 -0
- package/src/asar/dependency/hoister.ts +714 -0
- package/src/asar/dependency/utils.ts +225 -0
- package/src/asar/fileset/matcher.ts +101 -0
- package/src/asar/fileset/platform-fileset.ts +416 -0
- package/src/asar/fileset/transformer.ts +154 -0
- package/src/asar/fileset/unpack-detector.ts +38 -0
- package/src/asar/fileset/walker.ts +115 -0
- package/src/asar/pack.ts +186 -0
- package/src/asar/packer/app-collector.ts +74 -0
- package/src/asar/packer/collector.ts +134 -0
- package/src/asar/packer/concurrency.ts +24 -0
- package/src/asar/packer/integrity.ts +53 -0
- package/src/asar/packer/nm-collector.ts +194 -0
- package/src/asar/packer/stream-builder.ts +723 -0
- package/src/cli.ts +182 -0
- package/src/config/index.ts +365 -0
- package/src/electron/dist-zip.ts +65 -0
- package/src/electron/download.ts +121 -0
- package/src/index.ts +14 -0
- package/src/packer/common/app-update-yml.ts +21 -0
- package/src/packer/common/extra-resources.ts +83 -0
- package/src/packer/common/staging.ts +33 -0
- package/src/packer/common/template.ts +24 -0
- package/src/packer/linux/linux.ts +329 -0
- package/src/packer/mac/mac.ts +495 -0
- package/src/packer/win/nsis.ts +244 -0
- package/src/packer/win/resedit.ts +60 -0
- package/src/packer/win/toolchain.ts +63 -0
- package/src/packer/win/win.ts +421 -0
- package/src/pipeline/pipeline.ts +149 -0
- package/src/pipeline/targets.ts +67 -0
- package/src/shared/hash.ts +15 -0
- package/src/shared/logger.ts +91 -0
- package/src/shared/platform.ts +41 -0
- package/src/shared/types.ts +82 -0
- package/src/update/blockmap.ts +247 -0
- package/src/update/metadata.ts +97 -0
- package/src/update/version.ts +59 -0
- package/templates/linux/app.desktop +11 -0
- package/templates/linux/control +9 -0
- package/templates/linux/postinst +10 -0
- package/templates/linux/postrm +8 -0
- package/templates/linux/preinst +49 -0
- package/templates/linux/prerm +43 -0
- package/templates/mac/Info.plist +18 -0
- package/templates/mac/app-update.yml +4 -0
- package/templates/win/nsis/README.md +51 -0
- package/templates/win/nsis/THIRD-PARTY-NOTICES.md +20 -0
- package/templates/win/nsis/assistedInstaller.nsh +154 -0
- package/templates/win/nsis/assistedMessages.yml +368 -0
- package/templates/win/nsis/common.nsh +147 -0
- package/templates/win/nsis/empty-license.txt +1 -0
- package/templates/win/nsis/include/FileAssociation.nsh +123 -0
- package/templates/win/nsis/include/StdUtils.nsh +496 -0
- package/templates/win/nsis/include/StrContains.nsh +48 -0
- package/templates/win/nsis/include/UAC.nsh +299 -0
- package/templates/win/nsis/include/allowOnlyOneInstallerInstance.nsh +164 -0
- package/templates/win/nsis/include/extractAppPackage.nsh +138 -0
- package/templates/win/nsis/include/getProcessInfo.nsh +157 -0
- package/templates/win/nsis/include/installUtil.nsh +248 -0
- package/templates/win/nsis/include/installer.nsh +247 -0
- package/templates/win/nsis/include/nsProcess.nsh +28 -0
- package/templates/win/nsis/include/webPackage.nsh +64 -0
- package/templates/win/nsis/installSection.nsh +110 -0
- package/templates/win/nsis/installer.nsi +132 -0
- package/templates/win/nsis/messages.yml +240 -0
- package/templates/win/nsis/multiUser.nsh +134 -0
- package/templates/win/nsis/multiUserUi.nsh +230 -0
- package/templates/win/nsis/oneClick.nsh +19 -0
- package/templates/win/nsis/portable.nsi +91 -0
- package/templates/win/nsis/uninstaller.nsh +263 -0
- package/templates/win/nsis-header.nsh +57 -0
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Logging facade. All output is written through process.stdout.write or
|
|
3
|
+
* process.stderr.write; quiet mode suppresses every channel.
|
|
4
|
+
*/
|
|
5
|
+
import type { Arch, Platform } from './platform.ts'
|
|
6
|
+
|
|
7
|
+
interface LoggerOptions {
|
|
8
|
+
quiet?: boolean
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
interface BannerSummary {
|
|
12
|
+
platforms: Platform[]
|
|
13
|
+
arches: Arch[]
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
interface TargetResult {
|
|
17
|
+
platform: Platform
|
|
18
|
+
arch: Arch
|
|
19
|
+
output?: string
|
|
20
|
+
error?: string
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
interface SummaryResult {
|
|
24
|
+
targets: TargetResult[]
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Project identity fields shown in the banner; any config object with these fields works. */
|
|
28
|
+
export interface BannerInput {
|
|
29
|
+
productName: string
|
|
30
|
+
version: string
|
|
31
|
+
electronVersion: string
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface Logger {
|
|
35
|
+
info: (message: string) => void
|
|
36
|
+
warn: (message: string) => void
|
|
37
|
+
error: (message: string) => void
|
|
38
|
+
step: (label: string, message: string) => void
|
|
39
|
+
banner: (project: BannerInput, summary: BannerSummary) => void
|
|
40
|
+
summary: (result: SummaryResult) => void
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function createLogger(options: LoggerOptions = {}): Logger {
|
|
44
|
+
const { quiet = false } = options
|
|
45
|
+
|
|
46
|
+
const emit = (stream: NodeJS.WriteStream, prefix: string, message: string): void => {
|
|
47
|
+
if (quiet) return
|
|
48
|
+
stream.write(`${prefix ? `${prefix} ` : ''}${message}\n`)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return {
|
|
52
|
+
info: (message) => emit(process.stdout, '', message),
|
|
53
|
+
warn: (message) => emit(process.stderr, '[warn]', message),
|
|
54
|
+
error: (message) => emit(process.stderr, '[error]', message),
|
|
55
|
+
step: (label, message) => emit(process.stdout, ` [${label}]`, message),
|
|
56
|
+
|
|
57
|
+
banner(project, summary) {
|
|
58
|
+
if (quiet) return
|
|
59
|
+
const targets = summary.platforms.join(' + ')
|
|
60
|
+
const lines = [
|
|
61
|
+
'=========================================',
|
|
62
|
+
' cross-packer — Cross-platform packaging',
|
|
63
|
+
'=========================================',
|
|
64
|
+
` Project: ${project.productName} v${project.version}`,
|
|
65
|
+
` Electron: v${project.electronVersion}`,
|
|
66
|
+
` Targets: ${targets}`,
|
|
67
|
+
` Architectures: ${summary.arches.join(', ')}`,
|
|
68
|
+
'=========================================',
|
|
69
|
+
]
|
|
70
|
+
process.stdout.write(`${lines.join('\n')}\n`)
|
|
71
|
+
},
|
|
72
|
+
|
|
73
|
+
summary(result) {
|
|
74
|
+
if (quiet) return
|
|
75
|
+
const lines = [
|
|
76
|
+
'\n=========================================',
|
|
77
|
+
' Packaging Results',
|
|
78
|
+
'=========================================',
|
|
79
|
+
]
|
|
80
|
+
for (const r of result.targets) {
|
|
81
|
+
const status = r.error ? 'FAIL' : 'OK'
|
|
82
|
+
lines.push(` [${status}] ${r.platform}-${r.arch}: ${r.output || r.error}`)
|
|
83
|
+
}
|
|
84
|
+
lines.push('=========================================')
|
|
85
|
+
process.stdout.write(`${lines.join('\n')}\n`)
|
|
86
|
+
},
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** Shared default logger instance, used by packer/collector/util modules. */
|
|
91
|
+
export const logger: Logger = createLogger()
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cross-layer platform vocabulary: target platforms, architectures, and
|
|
3
|
+
* Node.js host platforms. Zero project dependencies; both config and the
|
|
4
|
+
* asar domain consume these enums.
|
|
5
|
+
*/
|
|
6
|
+
export enum Platform {
|
|
7
|
+
WIN = 'win',
|
|
8
|
+
MAC = 'mac',
|
|
9
|
+
LINUX = 'linux',
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export enum Arch {
|
|
13
|
+
AMD64 = 'amd64',
|
|
14
|
+
ARM64 = 'arm64',
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export enum NodePlatform {
|
|
18
|
+
WIN32 = 'win32',
|
|
19
|
+
DARWIN = 'darwin',
|
|
20
|
+
LINUX = 'linux',
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function nodeArchName(arch: Arch): 'x64' | 'arm64' {
|
|
24
|
+
return arch === Arch.AMD64 ? 'x64' : 'arm64'
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** All runtime values of NodePlatform, used for narrowing `process.platform`. */
|
|
28
|
+
const NODE_PLATFORMS: readonly NodePlatform[] = Object.values(NodePlatform)
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Narrow the host's `process.platform` to NodePlatform.
|
|
32
|
+
* Throws on unsupported hosts (node_modules collection is only defined
|
|
33
|
+
* for win32 / darwin / linux).
|
|
34
|
+
*/
|
|
35
|
+
export function nodePlatformOf(host: NodeJS.Platform): NodePlatform {
|
|
36
|
+
const narrow = NODE_PLATFORMS.find((p) => p === host)
|
|
37
|
+
if (narrow === undefined) {
|
|
38
|
+
throw new Error(`Unsupported host platform: ${host}`)
|
|
39
|
+
}
|
|
40
|
+
return narrow
|
|
41
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Zero-dependency shared types.
|
|
3
|
+
*
|
|
4
|
+
* This file may only import from `node:` builtins and external type
|
|
5
|
+
* packages — never from project modules. Every other layer depends on
|
|
6
|
+
* `shared/`, which keeps type-only dependencies acyclic at the module level.
|
|
7
|
+
*/
|
|
8
|
+
import type { Stats } from 'node:fs'
|
|
9
|
+
|
|
10
|
+
/** The kind of an asar-storable entry. */
|
|
11
|
+
export enum EntryKind {
|
|
12
|
+
FILE = 'file',
|
|
13
|
+
DIRECTORY = 'directory',
|
|
14
|
+
LINK = 'link',
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** A plain JSON object: arbitrary string keys, JSON-compatible values. */
|
|
18
|
+
export type JsonObject = Record<string, unknown>
|
|
19
|
+
|
|
20
|
+
/** A logical file set: an absolute source dir plus the collected files. */
|
|
21
|
+
export interface FileSet {
|
|
22
|
+
src: string
|
|
23
|
+
destination: string
|
|
24
|
+
files: string[]
|
|
25
|
+
metadata: Map<string, FileStat>
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** File filter predicate used across collectors. */
|
|
29
|
+
export type FileFilter = (file: string, stat: FileStat) => boolean
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Callback invoked for each collected node_modules file; returning true
|
|
33
|
+
* forces the file to be included even if it matches default exclusions.
|
|
34
|
+
*/
|
|
35
|
+
export type FileVisitor = (file: string) => boolean
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Collected entry metadata: the entry kind plus the module metadata that
|
|
39
|
+
* the collectors attach. Pure data with no behavior.
|
|
40
|
+
*/
|
|
41
|
+
export interface FileStat {
|
|
42
|
+
type: EntryKind
|
|
43
|
+
size: number
|
|
44
|
+
mode: number
|
|
45
|
+
/** Real on-disk source path when the packed file differs from the logical path. */
|
|
46
|
+
realFilePath?: string
|
|
47
|
+
/** Name of the node module the file belongs to. */
|
|
48
|
+
moduleName?: string
|
|
49
|
+
/** Root directory of the owning module. */
|
|
50
|
+
moduleRootPath?: string
|
|
51
|
+
/** Full path relative to the module root. */
|
|
52
|
+
moduleFullFilePath?: string
|
|
53
|
+
/** Symlink target relative to the module root. */
|
|
54
|
+
relativeLink?: string
|
|
55
|
+
/** Symlink target relative to the link's directory. */
|
|
56
|
+
linkRelativeToFile?: string
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** A resolved production dependency reported by the collector. */
|
|
60
|
+
export interface DependencyEntry {
|
|
61
|
+
name: string
|
|
62
|
+
dir: string
|
|
63
|
+
dependencies?: DependencyEntry[]
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** A parsed minimatch pattern (from the `minimatch` package). */
|
|
67
|
+
export interface MinimatchPattern {
|
|
68
|
+
match: (p: string, partial?: boolean) => boolean
|
|
69
|
+
set: Array<Array<string | object | symbol>>
|
|
70
|
+
negate: boolean
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Map an fs stat result to its entry kind, or null for special entries
|
|
75
|
+
* (sockets, FIFOs, devices) that the asar format cannot store.
|
|
76
|
+
*/
|
|
77
|
+
export function entryKind(stat: Stats): EntryKind | null {
|
|
78
|
+
if (stat.isSymbolicLink()) return EntryKind.LINK
|
|
79
|
+
if (stat.isFile()) return EntryKind.FILE
|
|
80
|
+
if (stat.isDirectory()) return EntryKind.DIRECTORY
|
|
81
|
+
return null
|
|
82
|
+
}
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure-JavaScript blockmap generator using Rabin fingerprinting.
|
|
3
|
+
* Ported from electron-builder v26.15.2 (app-builder-lib/out/targets/blockmap/blockmap.js),
|
|
4
|
+
* which replaced the Go-based app-builder.exe blockmap command.
|
|
5
|
+
*
|
|
6
|
+
* The output format is identical (blockmap v2 JSON), so the generated
|
|
7
|
+
* .blockmap files are fully compatible with electron-updater.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { createHash } from 'node:crypto'
|
|
11
|
+
import { createReadStream } from 'node:fs'
|
|
12
|
+
import { appendFile, writeFile } from 'node:fs/promises'
|
|
13
|
+
import zlib from 'node:zlib'
|
|
14
|
+
import { blake2b } from '@noble/hashes/blake2.js'
|
|
15
|
+
|
|
16
|
+
// Rabin fingerprinting constants from github.com/aclements/go-rabin:
|
|
17
|
+
// Poly64 = 0xbfe6b8a5bf378d83 (degree-63 irreducible polynomial over GF(2))
|
|
18
|
+
// Window = 64 bytes, Avg = 16 KB, Min = 8 KB, Max = 32 KB
|
|
19
|
+
// Boundary condition: (hash & (AVG-1)) === (AVG-1), i.e. all mask bits set.
|
|
20
|
+
//
|
|
21
|
+
// The tables below are precomputed offline (avoiding BigInt in production):
|
|
22
|
+
// PUSH[i] = (i * x^63 + (i * x^63 mod p)) truncated to 64 bits
|
|
23
|
+
// POP[i] = (i * x^(63*8)) mod p
|
|
24
|
+
// Each stored as signed Int32 hi/lo pair; hash = hi*2^32 + lo (unsigned 64-bit).
|
|
25
|
+
// prettier-ignore
|
|
26
|
+
const PUSH_HI = new Int32Array([
|
|
27
|
+
0, -1075398491, 2144170315, -1070872082, 1081891379, -6626666, 1068575608, -2141744163,
|
|
28
|
+
1058933955, -2131184538, 1087602056, -13253331, 2137151216, -1065030059, 11478971, -1085694178,
|
|
29
|
+
2117867910, -1042636509, 32598221, -1105735576, 1044399029, -2119763184, 1099772670, -26506661,
|
|
30
|
+
1092752709, -20664864, 1055876110, -2130060117, 22957942, -1095174189, 2123578941, -1049262440,
|
|
31
|
+
1134463913, -59231476, 1012136674, -2085273017, 65196442, -1140561601, 2083496145, -1010231180,
|
|
32
|
+
2088798058, -1016709169, 55440929, -1129623932, 1023204697, -2095421956, 1127328786, -53013321,
|
|
33
|
+
1034064431, -2109461878, 1114628964, -41329727, 2111752220, -1036488519, 34847063, -1108016654,
|
|
34
|
+
45915884, -1118165431, 2104618919, -1030269182, 1119929567, -47809414, 1024308628, -2098524879,
|
|
35
|
+
953921527, -2026039470, 1192681148, -118462951, 2024273348, -952021663, 124421263, -1198769110,
|
|
36
|
+
130392884, -1205658735, 2013844095, -940676390, 1203370247, -127975006, 947161164, -2020462359,
|
|
37
|
+
1189590641, -117371180, 959104826, -2033418337, 110881858, -1182968601, 2035719433, -961534548,
|
|
38
|
+
2046409394, -971046377, 104123385, -1177390244, 965075073, -2040309724, 1179161034, -106026641,
|
|
39
|
+
2068128862, -995910405, 76043541, -1150358096, 993623661, -2065709368, 1156845350, -82659453,
|
|
40
|
+
1146824861, -71462856, 999709142, -2072977037, 69694126, -1144927733, 2078933989, -1005798592,
|
|
41
|
+
91831768, -1163950723, 2058636435, -984419274, 1157982187, -85729458, 986191520, -2060538363,
|
|
42
|
+
979841307, -2055108162, 1168785488, -95618827, 2048617256, -973220979, 97917539, -1171217722,
|
|
43
|
+
1907843054, -833493173, 242888357, -1315138048, 835388893, -1909605000, 1309045910, -236925901,
|
|
44
|
+
1319719725, -246420600, 828645990, -1904043325, 248842526, -1322012229, 1897429077, -822165264,
|
|
45
|
+
260785768, -1334968627, 1883649827, -811561082, 1341594715, -267279106, 809135376, -1881352779,
|
|
46
|
+
815090347, -1888226802, 1331182560, -255950011, 1894322328, -821057475, 254042579, -1329407626,
|
|
47
|
+
841602119, -1915786014, 1306830092, -234742359, 1918209652, -844754337, 126847026, -1200980652,
|
|
48
|
+
1300297915, -237634370, 1906513758, -835821708, 237740260, -1327042663, 1911303428, -840027147,
|
|
49
|
+
126171951, -1202760392,
|
|
50
|
+
])
|
|
51
|
+
// prettier-ignore
|
|
52
|
+
const PUSH_LO = new Int32Array([
|
|
53
|
+
0, -1086878333, 2121210630, -1051158907, 1139391375, -52546036, 1032233097, -2102317814,
|
|
54
|
+
954530461, -2016184546, 1183572379, -105092072, 2064466194, -1002779503, 90331668, -1168779369,
|
|
55
|
+
1909060922, -822183751, 262598204, -1332649025, 840978101, -1927822538, 1280267699, -210184144,
|
|
56
|
+
1227689895, -166034908, 927077537, -2005559006, 180663336, -1242351189, 1957408558, -878959955,
|
|
57
|
+
1554278391, -476845452, 583761137, -1644367502, 525196408, -1602596357, 1629669246, -569029891,
|
|
58
|
+
1681956202, -612953879, 439322220, -1525150737, 665396965, -1734431898, 1506164195, -420368288,
|
|
59
|
+
762155725, -1839587506, 1392675275, -332069816, 1854155074, -776756031, 283849284, -1344487481,
|
|
60
|
+
361326672, -1430329901, 1810264918, -724435243, 1449186271, -380150180, 672123097, -1757919910,
|
|
61
|
+
109003373, -1186410514, 2014337387, -953690904, 1167522274, -90082207, 1006232292, -2066845849,
|
|
62
|
+
1050392816, -2119420557, 1089774582, -3985803, 2104623999, -1035628804, 52238457, -1138059782,
|
|
63
|
+
2008463191, -931054892, 165260369, -1225907758, 878644440, -1956085413, 1244665822, -184051107,
|
|
64
|
+
1330793930, -261767095, 826103500, -1911891121, 213644869, -1282638906, 1926556995, -840736576,
|
|
65
|
+
1524311450, -437475303, 615792284, -1685867745, 422747669, -1509616746, 1734182163, -664139632,
|
|
66
|
+
1648353031, -586657148, 475055105, -1553512062, 567698568, -1629361909, 1605992334, -527502835,
|
|
67
|
+
722653344, -1809490653, 1434307494, -364231131, 1761307439, -674437460, 378826793, -1448870486,
|
|
68
|
+
334899773, -1396594754, 1838756155, -760300360, 1344246194, -282584015, 779127476, -1857616073,
|
|
69
|
+
218006746, -1278670503, 1922146268, -844754337, 1326923605, -266292522, 829957203, -1907381808,
|
|
70
|
+
874151495, -1959922748, 1249207617, -180164414, 2012464584, -926660533, 161275598, -1230286003,
|
|
71
|
+
2100785632, -1040122781, 56126182, -1133516955, 1054788207, -2115418132, 1085395305, -7971606,
|
|
72
|
+
1171491709, -85719298, 1002213499, -2071257608, 104476914, -1190281871, 2018847732, -949836169,
|
|
73
|
+
1348133677, -278040914, 775288875, -1862109784, 330520738,
|
|
74
|
+
])
|
|
75
|
+
// prettier-ignore
|
|
76
|
+
const POP_HI = new Int32Array([
|
|
77
|
+
0, 2138251555, 1090583266, 1047780289, 1038568800, 1117023299, 2095560578, 60069537, 2077137601,
|
|
78
|
+
79543266, 986603555, 1170030848, 1177147297, 962262658, 120139075, 2019167328, 1216056615,
|
|
79
|
+
923315204, 159086533, 1980258022, 1973207111, 183492964, 882653861, 1273961350, 867506150,
|
|
80
|
+
1288058565, 1924525316, 231132199, 240278150, 1898019749, 1330815076, 807502151, 789684970,
|
|
81
|
+
1348633545, 1846630408, 291666219, 318173066, 1837485737, 1408636264, 746927179, 1423915051,
|
|
82
|
+
732698888, 366985929, 1789715434, 1765307723, 374035560, 674795433, 1464577674, 1735012301,
|
|
83
|
+
404295406, 644535599, 1494873100, 1519215277, 637420430, 462264399, 1694415212, 480556300,
|
|
84
|
+
1675072559, 1571049454, 584543949, 558102636, 1580259663, 1615004302, 523248557, 1579369941,
|
|
85
|
+
558916854, 522436407, 1615896084, 1673791669, 481765782, 583332439, 1572328308, 636346132,
|
|
86
|
+
1520369207, 1693263350, 463340757, 403346036, 1736029015, 1493854358, 645482933, 374917362,
|
|
87
|
+
1764485585, 1465397776, 673911603, 733971858, 1422697649, 1790934896, 365715027, 1838567987,
|
|
88
|
+
317027088, 748071121, 1407551986, 1349590867, 788676208, 292677041, 1845675154, 1899161407,
|
|
89
|
+
239191580, 808590813, 1329675518, 1289071199, 866552700, 232083645, 1923510686, 184319486,
|
|
90
|
+
1972329693, 1274840860, 881829439, 924528798, 1214779837, 1981532796, 157870943, 961112600,
|
|
91
|
+
1178225467, 2018087162, 121287129, 78522232, 2078082651, 1169087898, 987626681, 1116205273,
|
|
92
|
+
1039454714, 59181627, 2096376600, 2137046457, 1285274, 1046497115, 1091790456, 60830479,
|
|
93
|
+
2094207532, 1117833709, 1037297870, 1044872815, 1093951308, 2135393421, 3450286, 2020518350,
|
|
94
|
+
119376109, 963531564, 1176334863, 1166664878, 989513101, 76095052, 2079997807, 1272692264,
|
|
95
|
+
883465995, 182142154, 1973970409, 1983705928, 156226155, 926681514, 1213147273, 806692073,
|
|
96
|
+
1332086218, 1897258507, 241631016, 233990537, 1921075370, 1290965867, 864137800, 749834725,
|
|
97
|
+
1405268166, 1840343815, 314722852, 290905221, 1847983526, 1347823207, 790955844, 1467943716,
|
|
98
|
+
671885831, 377483718, 1762447589, 178,
|
|
99
|
+
])
|
|
100
|
+
// prettier-ignore
|
|
101
|
+
const POP_LO = new Int32Array([
|
|
102
|
+
0, -1826189856, -1720019389, 173808547, -1916624635, 518270181, 347617094, -2019640666, 461718026,
|
|
103
|
+
-2002530326, -2097163191, 299456937, -1773673713, 90271471, 255685964, -1675888468, -2009265769,
|
|
104
|
+
454788215, 289906644, -2107567564, 100640914, -1764153998, -1668989231, 262386481, -1816638563,
|
|
105
|
+
10405501, 180542942, -1713090498, 511371928, -1923324040, -2030011173, 338096443, -1353867603,
|
|
106
|
+
1013650253, 909576430, -1525578482, 579813288, -1314278840, -1141528085, 684925963, -1261904729,
|
|
107
|
+
669929799, 766659300, -1097545980, 956988834, -1439895486, -1603212319, 861300225, 661690170,
|
|
108
|
+
-1269425446, -1106640519, 757894297, -1431161281, 966048735, 868786300, -1595003492, 1022743856,
|
|
109
|
+
-1345103664, -1517337741, 917098131, -1321765835, 571603413, 676192886, -1150586986, 1587232090,
|
|
110
|
+
-843206470, -941511911, 1422337785, -749101985, 1082069439, 1243810332, -653948932, 1159626576,
|
|
111
|
+
-700910928, -597366509, 1329751283, -925049259, 1543132085, 1369851926, -1031748106, -693750579,
|
|
112
|
+
1166063917, 1339859598, -587583634, 1533318600, -935192536, -1038220405, 1362660971, -853313849,
|
|
113
|
+
1577450279, 1415176324, -947950236, 1088542658, -741909982, -644136575, 1253952609, -237591561,
|
|
114
|
+
1659907607, 1756116404, -74794924, 2081686258, -281899246, -445737807, 1984436561, -363601411,
|
|
115
|
+
2037738525, 1932097470, -535823778, 1737572600, -189281000, -18098501, 1842174811, 2045487712,
|
|
116
|
+
-355653760, -527352797, 1941418435, -198636699, 1729070725, 1834196262, -25882426, 1651435626,
|
|
117
|
+
-246913654, -82543063, 1748169673, -273921681, 2089469071, 1993793324, -437234996, 33683255,
|
|
118
|
+
-1859683625, -1686412940, 140373140, -1883023822, 484826066, 381272177, -2053157487, 428127549,
|
|
119
|
+
-1969080099, -2130828418, 332967582, -1807346632, 123771352, 222069371, -1642459237, -1975714144,
|
|
120
|
+
421163840, 323381475, -2141133565, 134111141, -1797729723, -1635464730, 228738054, -1850098518,
|
|
121
|
+
43987274, 147008233, -1679448311, 477830575, -1889693617, -2063496212, 371656204, -1387501158,
|
|
122
|
+
1047192698, 876019673, -1492094407, 546228383, -1635627768, 336483553, -1433766908, -2057987869,
|
|
123
|
+
82141755, -1254257515, -1910308274, 1756305977, -858749288, 1060136802, -1933640611,
|
|
124
|
+
])
|
|
125
|
+
|
|
126
|
+
const RABIN_MIN = 8192
|
|
127
|
+
const RABIN_AVG = 16384
|
|
128
|
+
const RABIN_MAX = 32768
|
|
129
|
+
const RABIN_WINDOW = 64
|
|
130
|
+
const RABIN_HASH_MASK = RABIN_AVG - 1 // 0x3FFF
|
|
131
|
+
|
|
132
|
+
export async function buildBlockMap(
|
|
133
|
+
inFile: string,
|
|
134
|
+
compressionFormat: string,
|
|
135
|
+
outFile?: string | null,
|
|
136
|
+
): Promise<{ sha512: string; size: number; blockMapSize?: number }> {
|
|
137
|
+
const fileHash = createHash('sha512')
|
|
138
|
+
const checksums: string[] = []
|
|
139
|
+
const sizes: number[] = []
|
|
140
|
+
let totalSize = 0
|
|
141
|
+
|
|
142
|
+
const chunkBuf = Buffer.allocUnsafe(RABIN_MAX)
|
|
143
|
+
let chunkN = 0
|
|
144
|
+
let hi = 0
|
|
145
|
+
let lo = 0
|
|
146
|
+
const win = new Uint8Array(RABIN_WINDOW)
|
|
147
|
+
let wpos = 0
|
|
148
|
+
|
|
149
|
+
function emitChunk(): void {
|
|
150
|
+
checksums.push(
|
|
151
|
+
Buffer.from(blake2b(chunkBuf.subarray(0, chunkN), { dkLen: 18 })).toString('base64'),
|
|
152
|
+
)
|
|
153
|
+
sizes.push(chunkN)
|
|
154
|
+
chunkN = 0
|
|
155
|
+
hi = 0
|
|
156
|
+
lo = 0
|
|
157
|
+
win.fill(0)
|
|
158
|
+
wpos = 0
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Feed one byte into the Rabin fingerprint window.
|
|
162
|
+
function processByte(b: number): void {
|
|
163
|
+
chunkBuf[chunkN++] = b
|
|
164
|
+
if (chunkN <= RABIN_MIN - RABIN_WINDOW) {
|
|
165
|
+
return
|
|
166
|
+
}
|
|
167
|
+
if (chunkN <= RABIN_MIN) {
|
|
168
|
+
win[wpos] = b
|
|
169
|
+
wpos = (wpos + 1) % RABIN_WINDOW
|
|
170
|
+
const top = (hi >>> 23) & 0xff
|
|
171
|
+
const nhi = ((hi << 8) | (lo >>> 24)) & 0x7fffffff
|
|
172
|
+
const nlo = ((lo << 8) | b) & 0xffffffff
|
|
173
|
+
hi = (nhi ^ PUSH_HI[top]) | 0
|
|
174
|
+
lo = (nlo ^ PUSH_LO[top]) | 0
|
|
175
|
+
if (chunkN === RABIN_MIN && (lo & RABIN_HASH_MASK) === RABIN_HASH_MASK) {
|
|
176
|
+
emitChunk()
|
|
177
|
+
}
|
|
178
|
+
return
|
|
179
|
+
}
|
|
180
|
+
const old = win[wpos]
|
|
181
|
+
win[wpos] = b
|
|
182
|
+
wpos = (wpos + 1) % RABIN_WINDOW
|
|
183
|
+
hi = (hi ^ POP_HI[old]) | 0
|
|
184
|
+
lo = (lo ^ POP_LO[old]) | 0
|
|
185
|
+
const top = (hi >>> 23) & 0xff
|
|
186
|
+
const nhi = ((hi << 8) | (lo >>> 24)) & 0x7fffffff
|
|
187
|
+
const nlo = ((lo << 8) | b) & 0xffffffff
|
|
188
|
+
hi = (nhi ^ PUSH_HI[top]) | 0
|
|
189
|
+
lo = (nlo ^ PUSH_LO[top]) | 0
|
|
190
|
+
if ((lo & RABIN_HASH_MASK) === RABIN_HASH_MASK || chunkN >= RABIN_MAX) {
|
|
191
|
+
emitChunk()
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Stream-read the file, feeding SHA-512 and the Rabin chunker simultaneously.
|
|
196
|
+
const done = new Promise<void>((resolve, reject) => {
|
|
197
|
+
const rs = createReadStream(inFile, { highWaterMark: 256 * 1024 })
|
|
198
|
+
rs.on('data', (chunk) => {
|
|
199
|
+
const buf = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)
|
|
200
|
+
fileHash.update(buf)
|
|
201
|
+
totalSize += buf.length
|
|
202
|
+
for (let i = 0; i < buf.length; i++) {
|
|
203
|
+
processByte(buf[i])
|
|
204
|
+
}
|
|
205
|
+
})
|
|
206
|
+
rs.on('end', () => {
|
|
207
|
+
if (chunkN > 0) {
|
|
208
|
+
emitChunk()
|
|
209
|
+
}
|
|
210
|
+
resolve()
|
|
211
|
+
})
|
|
212
|
+
rs.on('error', reject)
|
|
213
|
+
})
|
|
214
|
+
await done
|
|
215
|
+
|
|
216
|
+
const blockMap = {
|
|
217
|
+
version: '2',
|
|
218
|
+
files: [{ name: 'file', offset: 0, checksums, sizes }],
|
|
219
|
+
}
|
|
220
|
+
const compressed = compress(Buffer.from(JSON.stringify(blockMap)), compressionFormat)
|
|
221
|
+
|
|
222
|
+
if (outFile == null || outFile === '') {
|
|
223
|
+
const sizeHeader = Buffer.allocUnsafe(4)
|
|
224
|
+
sizeHeader.writeUInt32BE(compressed.length, 0)
|
|
225
|
+
const appended = Buffer.concat([compressed, sizeHeader])
|
|
226
|
+
await appendFile(inFile, appended)
|
|
227
|
+
fileHash.update(appended)
|
|
228
|
+
return {
|
|
229
|
+
size: totalSize + appended.length,
|
|
230
|
+
sha512: fileHash.digest('base64'),
|
|
231
|
+
blockMapSize: compressed.length,
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
await writeFile(outFile, compressed)
|
|
236
|
+
return {
|
|
237
|
+
size: totalSize,
|
|
238
|
+
sha512: fileHash.digest('base64'),
|
|
239
|
+
blockMapSize: compressed.length,
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
function compress(data: Buffer, format: string): Buffer {
|
|
244
|
+
return format === 'deflate'
|
|
245
|
+
? zlib.deflateRawSync(data, { level: 9 })
|
|
246
|
+
: zlib.gzipSync(data, { level: 9 })
|
|
247
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* electron-updater compatible metadata generation
|
|
3
|
+
* (latest.yml / latest-mac.yml / latest-linux.yml).
|
|
4
|
+
*/
|
|
5
|
+
import { existsSync, statSync, writeFileSync } from 'node:fs'
|
|
6
|
+
import path from 'node:path'
|
|
7
|
+
import { hashFile } from '../shared/hash.ts'
|
|
8
|
+
import { NodePlatform } from '../shared/platform.ts'
|
|
9
|
+
|
|
10
|
+
export interface UpdateMetadataOptions {
|
|
11
|
+
filePath: string
|
|
12
|
+
version: string
|
|
13
|
+
outputDir: string
|
|
14
|
+
platform: NodePlatform
|
|
15
|
+
blockMapSize?: number
|
|
16
|
+
channel?: string
|
|
17
|
+
extraFiles?: string[]
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Derive the update channel from a semver prerelease suffix
|
|
22
|
+
* (for example, 1.2.3-beta.1 yields "beta").
|
|
23
|
+
*/
|
|
24
|
+
export function getUpdateChannel(
|
|
25
|
+
version: string,
|
|
26
|
+
explicitChannel?: string,
|
|
27
|
+
defaultChannel = 'latest',
|
|
28
|
+
): string {
|
|
29
|
+
if (explicitChannel) return explicitChannel
|
|
30
|
+
const match = version.match(/^\d+\.\d+\.\d+-([0-9A-Za-z-]+)\.\d+$/)
|
|
31
|
+
return match ? match[1] : defaultChannel
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function getUpdateMetadataFileName(
|
|
35
|
+
version: string,
|
|
36
|
+
explicitChannel?: string,
|
|
37
|
+
platform: NodePlatform = NodePlatform.WIN32,
|
|
38
|
+
defaultChannel = 'latest',
|
|
39
|
+
): string {
|
|
40
|
+
const channel = getUpdateChannel(version, explicitChannel, defaultChannel)
|
|
41
|
+
if (platform === NodePlatform.DARWIN)
|
|
42
|
+
return `${channel === 'latest' ? 'latest' : channel}-mac.yml`
|
|
43
|
+
if (platform === NodePlatform.LINUX)
|
|
44
|
+
return `${channel === 'latest' ? 'latest' : channel}-linux.yml`
|
|
45
|
+
return `${channel}.yml`
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Get a file's size in bytes, 0 when missing. */
|
|
49
|
+
function getFileSize(filePath: string): number {
|
|
50
|
+
return existsSync(filePath) ? statSync(filePath).size : 0
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** Describe one artifact for the files[] array, with an optional inline blockMapSize. */
|
|
54
|
+
function fileEntryYaml(url: string, sha512: string, size: number, blockMapSize?: number): string {
|
|
55
|
+
const blockMapEntry = blockMapSize != null ? `\n blockMapSize: ${blockMapSize}` : ''
|
|
56
|
+
return ` - url: ${url}\n sha512: ${sha512}\n size: ${size}${blockMapEntry}`
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function generateUpdateMetadata({
|
|
60
|
+
filePath,
|
|
61
|
+
version,
|
|
62
|
+
outputDir,
|
|
63
|
+
platform,
|
|
64
|
+
blockMapSize,
|
|
65
|
+
channel,
|
|
66
|
+
extraFiles = [],
|
|
67
|
+
}: UpdateMetadataOptions): { metadataPath: string; metadataFileName: string } | null {
|
|
68
|
+
if (!existsSync(filePath)) return null
|
|
69
|
+
|
|
70
|
+
const url = path.basename(filePath)
|
|
71
|
+
const primaryHash = hashFile(filePath, 'sha512', 'base64')
|
|
72
|
+
const primarySize = getFileSize(filePath)
|
|
73
|
+
const filesYaml = [
|
|
74
|
+
fileEntryYaml(url, primaryHash, primarySize, blockMapSize),
|
|
75
|
+
...extraFiles
|
|
76
|
+
.filter((extra) => existsSync(extra) && extra !== filePath)
|
|
77
|
+
.map((extra) =>
|
|
78
|
+
fileEntryYaml(
|
|
79
|
+
path.basename(extra),
|
|
80
|
+
hashFile(extra, 'sha512', 'base64'),
|
|
81
|
+
getFileSize(extra),
|
|
82
|
+
),
|
|
83
|
+
),
|
|
84
|
+
].join('\n')
|
|
85
|
+
|
|
86
|
+
const metadataFileName = getUpdateMetadataFileName(version, channel, platform)
|
|
87
|
+
const content = `version: ${version}
|
|
88
|
+
files:
|
|
89
|
+
${filesYaml}
|
|
90
|
+
path: ${url}
|
|
91
|
+
sha512: ${primaryHash}
|
|
92
|
+
releaseDate: ${new Date().toISOString()}
|
|
93
|
+
`
|
|
94
|
+
const metadataPath = path.join(outputDir, metadataFileName)
|
|
95
|
+
writeFileSync(metadataPath, content, 'utf8')
|
|
96
|
+
return { metadataPath, metadataFileName }
|
|
97
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Convert a semver string into the four-part numeric build version.
|
|
3
|
+
* NSIS VIProductVersion and PE version resources require the X.X.X.X format.
|
|
4
|
+
* The fourth segment prefers: explicit buildNumber param > prerelease number
|
|
5
|
+
* in the version string > CI build number (only when CI=true).
|
|
6
|
+
*/
|
|
7
|
+
export function getBuildVersion(version: string, buildNumber?: number | string): string {
|
|
8
|
+
const ciBuildNumber = getCiBuildNumber()
|
|
9
|
+
const semverMatch = version.match(/^(\d+)\.(\d+)\.(\d+)(?:-(?:beta|rc)\.(\d+))?/i)
|
|
10
|
+
|
|
11
|
+
if (semverMatch) {
|
|
12
|
+
const major = Number.parseInt(semverMatch[1], 10)
|
|
13
|
+
const minor = Number.parseInt(semverMatch[2], 10)
|
|
14
|
+
const patch = Number.parseInt(semverMatch[3], 10)
|
|
15
|
+
|
|
16
|
+
if (buildNumber != null && /^\d+$/.test(String(buildNumber))) {
|
|
17
|
+
return `${major}.${minor}.${patch}.${Number.parseInt(String(buildNumber), 10)}`
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (ciBuildNumber && /^\d+$/.test(ciBuildNumber)) {
|
|
21
|
+
return `${major}.${minor}.${patch}.${Number.parseInt(ciBuildNumber, 10)}`
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const prereleaseBuild = semverMatch[4] ? Number.parseInt(semverMatch[4], 10) : 0
|
|
25
|
+
return `${major}.${minor}.${patch}.${prereleaseBuild}`
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const parts = version.replace(/-/g, '.').split('.')
|
|
29
|
+
const major = Number.parseInt(parts[0], 10) || 0
|
|
30
|
+
const minor = Number.parseInt(parts[1], 10) || 0
|
|
31
|
+
const patch = Number.parseInt(parts[2], 10) || 0
|
|
32
|
+
const fallbackBuild =
|
|
33
|
+
buildNumber != null && /^\d+$/.test(String(buildNumber))
|
|
34
|
+
? Number.parseInt(String(buildNumber), 10)
|
|
35
|
+
: Number.parseInt(parts[3], 10) || 0
|
|
36
|
+
const build =
|
|
37
|
+
ciBuildNumber && /^\d+$/.test(ciBuildNumber)
|
|
38
|
+
? Number.parseInt(ciBuildNumber, 10)
|
|
39
|
+
: fallbackBuild
|
|
40
|
+
|
|
41
|
+
return `${major}.${minor}.${patch}.${build}`
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Read the CI build number from well-known environment variables.
|
|
46
|
+
* Only consulted inside a CI runner (CI=true) so that a stray BUILD_NUMBER
|
|
47
|
+
* on a developer machine cannot silently alter artifact versions.
|
|
48
|
+
*/
|
|
49
|
+
function getCiBuildNumber(): string | undefined {
|
|
50
|
+
if (process.env.CI !== 'true') return undefined
|
|
51
|
+
return (
|
|
52
|
+
process.env.CI_PIPELINE_IID ||
|
|
53
|
+
process.env.BUILD_BUILDNUMBER ||
|
|
54
|
+
process.env.CIRCLE_BUILD_NUM ||
|
|
55
|
+
process.env.APPVEYOR_BUILD_NUMBER ||
|
|
56
|
+
process.env.TRAVIS_BUILD_NUMBER ||
|
|
57
|
+
process.env.BUILD_NUMBER
|
|
58
|
+
)
|
|
59
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
[Desktop Entry]
|
|
2
|
+
Name={{productName}}
|
|
3
|
+
{{desktopExtra}}Comment={{description}}
|
|
4
|
+
Exec="/opt/{{installDir}}/{{name}}" %U
|
|
5
|
+
Icon={{name}}
|
|
6
|
+
Type=Application
|
|
7
|
+
Categories=Utility;
|
|
8
|
+
{{mimeType}}
|
|
9
|
+
StartupWMClass={{name}}
|
|
10
|
+
StartupNotify=true
|
|
11
|
+
Terminal=false
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
chown root:root "/opt/{{installDir}}/chrome-sandbox"
|
|
3
|
+
chmod 4755 "/opt/{{installDir}}/chrome-sandbox"
|
|
4
|
+
chmod 755 "/opt/{{installDir}}/{{name}}"
|
|
5
|
+
if command -v gtk-update-icon-cache > /dev/null 2>&1; then
|
|
6
|
+
gtk-update-icon-cache -f -q /usr/share/icons/hicolor 2>/dev/null || true
|
|
7
|
+
fi
|
|
8
|
+
if command -v update-desktop-database > /dev/null 2>&1; then
|
|
9
|
+
update-desktop-database -q /usr/share/applications 2>/dev/null || true
|
|
10
|
+
fi
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
rm -rf "/opt/{{installDir}}"
|
|
3
|
+
if command -v gtk-update-icon-cache > /dev/null 2>&1; then
|
|
4
|
+
gtk-update-icon-cache -f -q /usr/share/icons/hicolor 2>/dev/null || true
|
|
5
|
+
fi
|
|
6
|
+
if command -v update-desktop-database > /dev/null 2>&1; then
|
|
7
|
+
update-desktop-database -q /usr/share/applications 2>/dev/null || true
|
|
8
|
+
fi
|