CLI: Replace the default icon and program info for Windows build (#127)

This commit is contained in:
Michał Leszczyński
2023-03-21 00:14:05 +01:00
committed by GitHub
parent 97d4e511f2
commit 7af924bbc8
6 changed files with 155 additions and 1 deletions

View File

@@ -90,7 +90,19 @@ jobs:
if: matrix.os == 'windows-latest'
run: |
cd cli
node win_fix_binary.js cli
$env:PKG_PATCHED_BIN = '1'
$env:PKG_CACHE_PATH = './.pkg-cache/'
$env:PKG_IGNORE_TAG = '1'
node_modules/.bin/pkg --compress GZip -t node16-win-x64 -c package.json -o dist/halocli.exe entry_cli.js
- name: Package HaLo Bridge tool (Windows)
if: matrix.os == 'windows-latest'
run: |
cd cli
node win_fix_binary.js bridge
$env:PKG_PATCHED_BIN = '1'
$env:PKG_CACHE_PATH = './.pkg-cache/'
$env:PKG_IGNORE_TAG = '1'
node_modules/.bin/pkg --compress GZip -t node16-win-x64 -c package.json -o dist/halo-bridge.exe entry_bridge.js
- name: Package HaLo CLI tool (MacOS)
if: matrix.os == 'macos-latest'

1
cli/.gitignore vendored
View File

@@ -1 +1,2 @@
assets/static/ws_client.js*
.pkg-cache/

BIN
cli/arx.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

25
cli/package-lock.json generated
View File

@@ -21,6 +21,8 @@
},
"devDependencies": {
"pkg": "^5.8.0",
"pkg-fetch": "^3.4.2",
"resedit": "^2.0.0",
"webpack": "^5.76.2",
"webpack-cli": "^5.0.1"
}
@@ -2658,6 +2660,16 @@
"node": ">=8"
}
},
"node_modules/pe-library": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/pe-library/-/pe-library-1.0.0.tgz",
"integrity": "sha512-yZ+4d3YHKUjO0BX03oXFfHRKLdYKDO2HmCt1RcApPxme/P5ASPbbKnuQkzFrmT482wi2kfO+sPgqasrz5QeU1w==",
"dev": true,
"engines": {
"node": ">=14",
"npm": ">=7"
}
},
"node_modules/picocolors": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
@@ -2976,6 +2988,19 @@
"node": ">=0.10.0"
}
},
"node_modules/resedit": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/resedit/-/resedit-2.0.0.tgz",
"integrity": "sha512-vrrJCabKxAW4MT1QivtAAb0poGp8KT2qhnSzfN9tFIxb2rQu1hRHNn1VgGSZR7nmxGaW5Yz0YeW1bjgvRfNoKA==",
"dev": true,
"dependencies": {
"pe-library": "^1.0.0"
},
"engines": {
"node": ">=14",
"npm": ">=7"
}
},
"node_modules/resolve": {
"version": "1.22.1",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz",

View File

@@ -29,7 +29,7 @@
"bin": "cli.js",
"pkg": {
"targets": [
"node18"
"node16"
],
"outputPath": "dist",
"assets": [
@@ -51,6 +51,8 @@
},
"devDependencies": {
"pkg": "^5.8.0",
"pkg-fetch": "^3.4.2",
"resedit": "^2.0.0",
"webpack": "^5.76.2",
"webpack-cli": "^5.0.1"
}

114
cli/win_fix_binary.js Normal file
View File

@@ -0,0 +1,114 @@
const path = require("path");
const fs = require("fs");
const { readFileSync, writeFileSync } = require('fs');
const { need, system } = require('pkg-fetch');
const package_json = require('./package.json');
const crypto = require("crypto");
const {
hostArch,
hostPlatform
} = system;
function computeSha256(filePath) {
return new Promise((resolve, reject) => {
fs.createReadStream(filePath)
.pipe(crypto.createHash('sha256').setEncoding('hex'))
.on('error', function (err) {
reject(err);
})
.on('finish', function () {
resolve(this.read());
});
});
}
async function fixBinary(name, bin_name) {
// unable to normally require, this is ES6 module
const ResEdit = await import('resedit');
if (package_json['pkg']['targets'].length !== 1) {
throw Error("Only one pkg target is supported");
}
const nodeBinPath = await need({
dryRun: false,
forceBuild: false,
nodeRange: package_json['pkg']['targets'][0],
platform: hostPlatform,
arch: hostArch
});
const language = {
lang: 1033, // en-us
codepage: 1200 // UTF-16
};
// Modify .exe w/ ResEdit
const data = readFileSync(nodeBinPath);
const executable = ResEdit.NtExecutable.from(data);
const res = ResEdit.NtExecutableResource.from(executable);
const vi = ResEdit.Resource.VersionInfo.fromEntries(res.entries)[0];
// Remove original filename
vi.removeStringValue(language, 'OriginalFilename');
vi.removeStringValue(language, 'InternalName');
vi.setProductVersion(1, 0, 0, 0, language.lang);
vi.setFileVersion(1, 0, 0, 0, language.lang);
vi.setStringValues(language, {
FileDescription: name,
ProductName: name,
LegalCopyright: 'Arx Research Inc.',
OriginalFilename: bin_name
});
vi.outputToResourceEntries(res.entries);
// Add icon
const iconFile = ResEdit.Data.IconFile.from(readFileSync("arx.ico"));
ResEdit.Resource.IconGroupEntry.replaceIconsForResource(
res.entries,
1,
language.lang,
iconFile.icons.map(item => item.data)
);
// Regenerate and write to .exe
res.outputResource(executable);
if (!fs.existsSync(".pkg-cache")){
fs.mkdirSync(".pkg-cache");
}
const nodeBinBase = path.basename(nodeBinPath);
const nodeHashKey = nodeBinBase.replace('fetched-', 'node-');
const outPath = path.join(".pkg-cache", nodeBinBase);
writeFileSync(outPath, Buffer.from(executable.generate()));
const fileHash = await computeSha256(outPath);
fs.appendFileSync('node_modules\\pkg-fetch\\lib-es5\\expected.js', '\n/** PATCHED **/ if (process.env.PKG_PATCHED_BIN === "1") {exports.EXPECTED_HASHES[\'' + nodeHashKey + '\'] = \'' + fileHash + '\';}');
}
let name = null;
let bin_name = null;
if (process.argv.length < 3) {
throw Error("Binary type not specified in argv.");
} else if (process.argv[2] === "cli") {
name = 'HaLo CLI';
bin_name = 'halocli.exe';
} else if (process.argv[2] === "bridge") {
name = 'HaLo Bridge Server';
bin_name = 'halo-bridge.exe';
} else {
throw Error("Unknown binary type specified.");
}
fixBinary(name, bin_name);
// run pkg with:
// $env:PKG_PATCHED_BIN = 1
// $env:PKG_CACHE_PATH = './.pkg-cache/'
// $env:PKG_IGNORE_TAG = 1