up
This commit is contained in:
@@ -0,0 +1,145 @@
|
||||
const assert = require('node:assert/strict')
|
||||
const { createHash } = require('node:crypto')
|
||||
const {
|
||||
createReadStream,
|
||||
existsSync,
|
||||
readdirSync,
|
||||
readFileSync,
|
||||
writeFileSync,
|
||||
} = require('node:fs')
|
||||
const { join, relative } = require('node:path')
|
||||
const { execFileSync } = require('node:child_process')
|
||||
const asar = require('@electron/asar')
|
||||
const root = join(__dirname, '..')
|
||||
const metadata = require('../package.json')
|
||||
const sha = (bytes) => createHash('sha256').update(bytes).digest('hex')
|
||||
async function fileHash(path) {
|
||||
const hash = createHash('sha256')
|
||||
for await (const bytes of createReadStream(path)) hash.update(bytes)
|
||||
return hash.digest('hex')
|
||||
}
|
||||
function resourcesIn(directory) {
|
||||
const app = readdirSync(directory).find((name) => name.endsWith('.app'))
|
||||
return app ? join(directory, app, 'Contents', 'Resources') : join(directory, 'resources')
|
||||
}
|
||||
function verifyAsar(path) {
|
||||
const files = asar.listPackage(path).map((name) => name.replace(/^\//, ''))
|
||||
for (const name of files) {
|
||||
assert.ok(
|
||||
!/(^|\/)(?:\.git|\.env(?:\.[^/]*)?|credentials\.bin|windows\.json|selection\.json|__tests__)(?:\/|$)|\.test\.(?:js|ts)$|\.(?:pem|key|p12|pfx)$/.test(
|
||||
name,
|
||||
),
|
||||
`Forbidden packaged file: ${name}`,
|
||||
)
|
||||
assert.ok(
|
||||
/^(?:node_modules(?:\/|$)|dist(?:\/|$)|dist-electron(?:\/|$)|package\.json$)/.test(name),
|
||||
`Unexpected packaged file: ${name}`,
|
||||
)
|
||||
}
|
||||
for (const name of [
|
||||
'package.json',
|
||||
'dist/index.html',
|
||||
'dist-electron/electron/main.js',
|
||||
'dist-electron/electron/preload.js',
|
||||
'dist-electron/electron/online.js',
|
||||
'dist-electron/electron/diagnostics.js',
|
||||
])
|
||||
assert.ok(files.includes(name), `Missing ${name}`)
|
||||
const pkg = JSON.parse(asar.extractFile(path, 'package.json'))
|
||||
assert.equal(pkg.version, metadata.version)
|
||||
assert.equal(pkg.name, metadata.name)
|
||||
return pkg
|
||||
}
|
||||
function binaryArchitecture(path, platform) {
|
||||
const bytes = readFileSync(path)
|
||||
if (platform === 'linux') {
|
||||
assert.equal(bytes.subarray(0, 4).toString('hex'), '7f454c46')
|
||||
return { 62: 'x64', 183: 'arm64' }[bytes.readUInt16LE(18)]
|
||||
}
|
||||
if (platform === 'win32') {
|
||||
assert.equal(bytes.subarray(0, 2).toString(), 'MZ')
|
||||
const offset = bytes.readUInt32LE(0x3c)
|
||||
assert.equal(bytes.subarray(offset, offset + 4).toString(), 'PE\0\0')
|
||||
return { 0x8664: 'x64', 0xaa64: 'arm64' }[bytes.readUInt16LE(offset + 4)]
|
||||
}
|
||||
assert.equal(bytes.subarray(0, 4).toString('hex'), 'cffaedfe')
|
||||
return { 0x1000007: 'x64', 0x100000c: 'arm64' }[bytes.readUInt32LE(4)]
|
||||
}
|
||||
function executableIn(directory, platform) {
|
||||
if (platform === 'darwin') {
|
||||
const app = readdirSync(directory).find((name) => name.endsWith('.app'))
|
||||
return join(directory, app, 'Contents', 'MacOS', metadata.build.productName)
|
||||
}
|
||||
return join(directory, platform === 'win32' ? `${metadata.build.productName}.exe` : metadata.name)
|
||||
}
|
||||
async function verifyDirectory(directory) {
|
||||
const resources = resourcesIn(directory)
|
||||
const info = JSON.parse(readFileSync(join(resources, 'build-info.json')))
|
||||
assert.equal(info.formatVersion, 1)
|
||||
assert.equal(info.version, metadata.version)
|
||||
assert.ok(['linux', 'win32', 'darwin'].includes(info.platform))
|
||||
assert.ok(['x64', 'arm64'].includes(info.arch))
|
||||
verifyAsar(join(resources, 'app.asar'))
|
||||
assert.deepEqual(Object.keys(info.files).sort(), [
|
||||
'app.asar',
|
||||
'fixtures/audio.wav',
|
||||
'fixtures/video.mp4',
|
||||
])
|
||||
for (const [name, expected] of Object.entries(info.files))
|
||||
assert.equal(await fileHash(join(resources, name)), expected, `Hash mismatch: ${name}`)
|
||||
assert.deepEqual(readdirSync(join(resources, 'fixtures')).sort(), ['audio.wav', 'video.mp4'])
|
||||
assert.ok(
|
||||
!existsSync(join(resources, 'app.asar.unpacked')),
|
||||
'Unexpected unpacked application files',
|
||||
)
|
||||
assert.equal(binaryArchitecture(executableIn(directory, info.platform), info.platform), info.arch)
|
||||
return info
|
||||
}
|
||||
async function afterPack(context) {
|
||||
const resources = resourcesIn(context.appOutDir)
|
||||
verifyAsar(join(resources, 'app.asar'))
|
||||
const files = {}
|
||||
for (const name of ['app.asar', 'fixtures/audio.wav', 'fixtures/video.mp4'])
|
||||
files[name] = await fileHash(join(resources, name))
|
||||
let revision = null
|
||||
let dirty = true
|
||||
try {
|
||||
const result = execFileSync('git', ['rev-parse', 'HEAD'], {
|
||||
cwd: root,
|
||||
encoding: 'utf8',
|
||||
stdio: ['ignore', 'pipe', 'ignore'],
|
||||
}).trim()
|
||||
if (/^[a-f0-9]{40,64}$/.test(result)) revision = result
|
||||
dirty = !!execFileSync('git', ['status', '--porcelain'], { cwd: root, encoding: 'utf8' }).trim()
|
||||
} catch {
|
||||
/* Экспорт исходников без .git тоже может быть собран. */
|
||||
}
|
||||
const ffmpeg = execFileSync('ffmpeg', ['-version'], { encoding: 'utf8' }).split('\n')[0]
|
||||
const info = {
|
||||
formatVersion: 1,
|
||||
version: metadata.version,
|
||||
platform: context.electronPlatformName,
|
||||
arch: require('builder-util').Arch[context.arch],
|
||||
revision,
|
||||
dirty,
|
||||
tools: {
|
||||
node: process.versions.node,
|
||||
electron: context.packager.info.framework.version,
|
||||
builder: require('electron-builder/package.json').version,
|
||||
ffmpeg,
|
||||
},
|
||||
lockfileSha256: sha(readFileSync(join(root, 'package-lock.json'))),
|
||||
files,
|
||||
}
|
||||
writeFileSync(join(resources, 'build-info.json'), JSON.stringify(info, null, 2) + '\n')
|
||||
await verifyDirectory(context.appOutDir)
|
||||
console.log('RELEASE_CONTENTS_PASS', relative(root, context.appOutDir), info.platform, info.arch)
|
||||
}
|
||||
module.exports = afterPack
|
||||
Object.assign(module.exports, {
|
||||
fileHash,
|
||||
verifyAsar,
|
||||
verifyDirectory,
|
||||
binaryArchitecture,
|
||||
resourcesIn,
|
||||
})
|
||||
Reference in New Issue
Block a user