Compare commits

...

6 Commits

Author SHA1 Message Date
bigjakk d17c1bc0a2 chore: bump version to 0.8.0
Build and Release / build-and-release (push) Successful in 17m21s
2026-04-16 10:30:56 -07:00
bigjakk b968ace8fa refactor: extract asar from electron-builder output instead of custom script 2026-04-16 10:30:44 -07:00
bigjakk 2cf5623186 chore: bump version to 0.7.9
Build and Release / build-and-release (push) Failing after 7m37s
2026-04-16 10:21:26 -07:00
bigjakk 5e7786d66e fix: resolve transitive deps in build-asar, use Move-Item in swap script 2026-04-16 10:21:14 -07:00
bigjakk 9a52d6b8fc chore: bump version to 0.7.8
Build and Release / build-and-release (push) Successful in 12m59s
2026-04-16 09:38:00 -07:00
bigjakk 255befd1b8 fix: use original-fs to bypass Electron asar interception 2026-04-16 09:38:00 -07:00
7 changed files with 26 additions and 187 deletions
+10 -2
View File
@@ -136,9 +136,17 @@ jobs:
if: steps.version-check.outputs.SKIP == 'false'
run: npx electron-builder --linux -c.electronDist=node_modules/electron/dist-linux --publish never
- name: Build asar for patch updates
- name: Extract asar for patch updates
if: steps.version-check.outputs.SKIP == 'false' && steps.release-type.outputs.TYPE == 'patch'
run: npm run build:asar
run: |
# The asar is a byproduct of the Linux build — platform-independent (just JS)
ASAR_SRC="out/linux-unpacked/resources/app.asar"
ASAR_DIR="out/asar"
mkdir -p "$ASAR_DIR"
cp "$ASAR_SRC" "$ASAR_DIR/app.asar"
sha256sum "$ASAR_DIR/app.asar" | awk '{print $1 " app.asar"}' > "$ASAR_DIR/checksums.sha256"
echo "Extracted asar: $(du -h "$ASAR_DIR/app.asar" | cut -f1)"
cat "$ASAR_DIR/checksums.sha256"
- name: Report build sizes
if: steps.version-check.outputs.SKIP == 'false'
+2 -76
View File
@@ -1,19 +1,18 @@
{
"name": "krunker-civilian-client",
"version": "0.7.5",
"version": "0.7.9",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "krunker-civilian-client",
"version": "0.7.5",
"version": "0.7.9",
"hasInstallScript": true,
"license": "GPL-3.0",
"dependencies": {
"electron-store": "^8.2.0"
},
"devDependencies": {
"@electron/asar": "^4.2.0",
"@eslint/js": "^10.0.1",
"@types/node": "^22.0.0",
"electron": "npm:electron-nightly@42.0.0-nightly.20260227",
@@ -45,69 +44,6 @@
"url": "https://opencollective.com/webpack"
}
},
"node_modules/@electron/asar": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/@electron/asar/-/asar-4.2.0.tgz",
"integrity": "sha512-npW1NW5yy8EB9XY/vEw9sUdgmq0sJEhmSBb6bqyFOAw1CSkrhvAvO6QWlW8CdIMo8VN1lkdF345l/MeW0LrY0Q==",
"dev": true,
"license": "MIT",
"dependencies": {
"commander": "^13.1.0",
"glob": "^13.0.2",
"minimatch": "^10.0.1"
},
"bin": {
"asar": "bin/asar.mjs"
},
"engines": {
"node": ">=22.12.0"
}
},
"node_modules/@electron/asar/node_modules/glob": {
"version": "13.0.6",
"resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz",
"integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==",
"dev": true,
"license": "BlueOak-1.0.0",
"dependencies": {
"minimatch": "^10.2.2",
"minipass": "^7.1.3",
"path-scurry": "^2.0.2"
},
"engines": {
"node": "18 || 20 || >=22"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/@electron/asar/node_modules/lru-cache": {
"version": "11.3.5",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.3.5.tgz",
"integrity": "sha512-NxVFwLAnrd9i7KUBxC4DrUhmgjzOs+1Qm50D3oF1/oL+r1NpZ4gA7xvG0/zJ8evR7zIKn4vLf7qTNduWFtCrRw==",
"dev": true,
"license": "BlueOak-1.0.0",
"engines": {
"node": "20 || >=22"
}
},
"node_modules/@electron/asar/node_modules/path-scurry": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz",
"integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==",
"dev": true,
"license": "BlueOak-1.0.0",
"dependencies": {
"lru-cache": "^11.0.0",
"minipass": "^7.1.2"
},
"engines": {
"node": "18 || 20 || >=22"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/@electron/fuses": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/@electron/fuses/-/fuses-1.8.0.tgz",
@@ -3157,16 +3093,6 @@
"node": ">= 0.8"
}
},
"node_modules/commander": {
"version": "13.1.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-13.1.0.tgz",
"integrity": "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=18"
}
},
"node_modules/compare-version": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/compare-version/-/compare-version-0.1.2.tgz",
+1 -3
View File
@@ -1,6 +1,6 @@
{
"name": "krunker-civilian-client",
"version": "0.7.7",
"version": "0.8.0",
"description": "Cross-platform Krunker game client",
"main": "dist/main/index.js",
"homepage": "https://github.com/bigjakk/Krunker-Civilian-Client",
@@ -17,7 +17,6 @@
"dist:win": "npm run build && electron-builder --win",
"dist:linux": "npm run build && electron-builder --linux",
"dist:all": "npm run build && electron-builder --win --linux",
"build:asar": "node scripts/build-asar.js",
"clean": "rimraf dist out",
"lint": "eslint src/",
"prepare": "husky"
@@ -29,7 +28,6 @@
"electron-store": "^8.2.0"
},
"devDependencies": {
"@electron/asar": "^4.2.0",
"@eslint/js": "^10.0.1",
"@types/node": "^22.0.0",
"electron": "npm:electron-nightly@42.0.0-nightly.20260227",
-96
View File
@@ -1,96 +0,0 @@
/**
* Build a standalone app.asar + checksums.sha256 for minor (patch) updates.
*
* Replicates what electron-builder packs into the asar (dist/ + package.json +
* required node_modules) without running the full installer build.
*
* Usage:
* npm run build:asar # default (github) update source
* UPDATE_SOURCE=gitea npm run build:asar # gitea update source
*/
const { execSync } = require('child_process');
const { cpSync, mkdirSync, rmSync, readFileSync, writeFileSync, createReadStream } = require('fs');
const { join } = require('path');
const { createHash } = require('crypto');
const asar = require('@electron/asar');
const ROOT = join(__dirname, '..');
const STAGING = join(ROOT, 'out', 'asar-staging');
const OUTPUT = join(ROOT, 'out', 'asar');
// Same node_modules list as electron-builder.yml
const NODE_MODULES = [
'electron-store', 'conf', 'dot-prop', 'type-fest', 'pkg-up',
'find-up', 'locate-path', 'p-locate', 'p-limit', 'yocto-queue',
'path-exists', 'env-paths', 'json-schema-typed', 'ajv', 'ajv-formats',
'atomically', 'debounce-fn', 'mimic-fn', 'semver', 'onetime',
];
function shouldExclude(src) {
return src.endsWith('.ts') || src.endsWith('.map');
}
async function main() {
// 1. Run Vite build (pass UPDATE_SOURCE through env)
const env = { ...process.env };
if (env.UPDATE_SOURCE) {
console.log(`Building with UPDATE_SOURCE=${env.UPDATE_SOURCE}`);
}
execSync('npm run build', { cwd: ROOT, stdio: 'inherit', env });
// 2. Prepare staging directory
rmSync(STAGING, { recursive: true, force: true });
mkdirSync(STAGING, { recursive: true });
// Copy dist/
cpSync(join(ROOT, 'dist'), join(STAGING, 'dist'), { recursive: true });
// Copy package.json (Electron reads "main" from it)
cpSync(join(ROOT, 'package.json'), join(STAGING, 'package.json'));
// Copy required node_modules (excluding .ts and .map files)
for (const mod of NODE_MODULES) {
const src = join(ROOT, 'node_modules', mod);
const dest = join(STAGING, 'node_modules', mod);
cpSync(src, dest, {
recursive: true,
filter: (s) => !shouldExclude(s),
});
}
// 3. Pack into asar
mkdirSync(OUTPUT, { recursive: true });
const asarPath = join(OUTPUT, 'app.asar');
await asar.createPackage(STAGING, asarPath);
console.log('Created:', asarPath);
// 4. Generate checksums.sha256
const hex = await fileHash(asarPath);
const checksumsPath = join(OUTPUT, 'checksums.sha256');
writeFileSync(checksumsPath, `${hex} app.asar\n`);
console.log(`SHA-256: ${hex}`);
console.log('Created:', checksumsPath);
// 5. Cleanup staging
rmSync(STAGING, { recursive: true, force: true });
// Print size
const size = readFileSync(asarPath).length;
console.log(`\napp.asar size: ${(size / 1024).toFixed(1)} KB`);
}
function fileHash(filePath) {
return new Promise((resolve, reject) => {
const hash = createHash('sha256');
const stream = createReadStream(filePath);
stream.on('data', (chunk) => hash.update(chunk));
stream.on('end', () => resolve(hash.digest('hex')));
stream.on('error', reject);
});
}
main().catch((err) => {
console.error('build-asar failed:', err);
process.exit(1);
});
+6 -5
View File
@@ -1,6 +1,7 @@
import { app, BrowserWindow, Menu, clipboard, ipcMain, safeStorage, session, shell } from 'electron';
import { join, dirname } from 'path';
import { existsSync, mkdirSync, unlinkSync, promises as fsp } from 'fs';
import { existsSync, mkdirSync, promises as fsp } from 'fs';
import { existsSync as origExistsSync, unlinkSync as origUnlinkSync } from 'original-fs';
import { get as httpsGet } from 'https';
import { execFile } from 'child_process';
import * as os from 'os';
@@ -179,8 +180,8 @@ app.whenReady().then(async () => {
// Clean up stale pending asar from a previous failed swap
const resourcesDir = join(dirname(app.getPath('exe')), 'resources');
const stalePending = join(resourcesDir, 'app-pending.asar');
if (existsSync(stalePending)) {
try { unlinkSync(stalePending); } catch { /* ignore */ }
if (origExistsSync(stalePending)) {
try { origUnlinkSync(stalePending); } catch { /* ignore */ }
}
try {
@@ -213,8 +214,8 @@ app.whenReady().then(async () => {
} catch (err) {
electronLog.error('[KCC] Patch download failed:', err);
// Clean up failed download
if (existsSync(pendingPath)) {
try { unlinkSync(pendingPath); } catch { /* ignore */ }
if (origExistsSync(pendingPath)) {
try { origUnlinkSync(pendingPath); } catch { /* ignore */ }
}
if (!updateWin.isDestroyed()) updateWin.close();
}
+6 -4
View File
@@ -1,5 +1,7 @@
import { get as httpsGet } from 'https';
import { createReadStream, createWriteStream, writeFileSync, renameSync, unlinkSync, existsSync, mkdirSync } from 'fs';
// Use original-fs to bypass Electron's asar interception — required for
// writing/renaming .asar files in the resources directory.
import { createReadStream, createWriteStream, writeFileSync, renameSync, unlinkSync, existsSync, mkdirSync } from 'original-fs';
import { join, dirname } from 'path';
import { createHash } from 'crypto';
import { spawn } from 'child_process';
@@ -78,12 +80,12 @@ if (-not (Test-Path $pending)) { exit 1 }
try {
if (Test-Path $backup) { Remove-Item $backup -Force }
Rename-Item $asar $backup -Force
Rename-Item $pending $asar -Force
Move-Item $asar $backup -Force
Move-Item $pending $asar -Force
if (Test-Path $backup) { Remove-Item $backup -Force }
} catch {
if ((Test-Path $backup) -and -not (Test-Path $asar)) {
Rename-Item $backup $asar -Force
Move-Item $backup $asar -Force
}
exit 1
}
+1 -1
View File
@@ -20,7 +20,7 @@ export default defineConfig({
outDir: 'dist/main',
emptyDirBefore: true,
rollupOptions: {
external: ['electron', 'electron-store', ...nodeBuiltins],
external: ['electron', 'electron-store', 'original-fs', ...nodeBuiltins],
},
target: 'node20',
minify: isProd,