feat: use tar for bundles
release / build-web (push) Has been cancelled
release / build-bundles (aarch64-linux-gnu-gcc, arm64, linux, ubuntu-latest, linux-arm64) (push) Has been cancelled
release / build-bundles (amd64, darwin, macos-15-large, macos-x64) (push) Has been cancelled
release / build-bundles (amd64, linux, ubuntu-latest, linux-x64) (push) Has been cancelled
release / build-bundles (amd64, windows, windows-latest, windows-x64) (push) Has been cancelled
release / build-bundles (arm64, darwin, macos-14, macos-arm64) (push) Has been cancelled

This commit is contained in:
Carlos Florêncio
2026-02-16 10:00:54 +00:00
parent c4ef2d9ba1
commit cd7d554478
6 changed files with 69 additions and 55 deletions
+7 -8
View File
@@ -111,8 +111,8 @@ jobs:
mkdir -p dist/kandev/web
cp -R dist/web/. dist/kandev/web/
cd dist
zip -r "kandev-${{ matrix.platform }}.zip" kandev
shasum -a 256 "kandev-${{ matrix.platform }}.zip" > "kandev-${{ matrix.platform }}.zip.sha256"
tar -czf "kandev-${{ matrix.platform }}.tar.gz" kandev
shasum -a 256 "kandev-${{ matrix.platform }}.tar.gz" > "kandev-${{ matrix.platform }}.tar.gz.sha256"
- name: Package bundle (windows)
if: runner.os == 'Windows'
@@ -125,15 +125,14 @@ jobs:
New-Item -ItemType Directory -Force -Path dist/kandev/web | Out-Null
# -Force on Get-ChildItem includes dot-prefixed items (.next/)
Get-ChildItem dist/web -Force | Copy-Item -Destination dist/kandev/web/ -Recurse -Force
# Use 7z instead of Compress-Archive to handle long paths in node_modules
cd dist
7z a -tzip "kandev-${{ matrix.platform }}.zip" kandev
$hash = Get-FileHash "kandev-${{ matrix.platform }}.zip" -Algorithm SHA256
"$($hash.Hash) kandev-${{ matrix.platform }}.zip" | Out-File -FilePath "kandev-${{ matrix.platform }}.zip.sha256" -Encoding ascii
tar -czf "kandev-${{ matrix.platform }}.tar.gz" kandev
$hash = Get-FileHash "kandev-${{ matrix.platform }}.tar.gz" -Algorithm SHA256
"$($hash.Hash) kandev-${{ matrix.platform }}.tar.gz" | Out-File -FilePath "kandev-${{ matrix.platform }}.tar.gz.sha256" -Encoding ascii
- name: Upload release assets
uses: softprops/action-gh-release@v2
with:
files: |
dist/kandev-${{ matrix.platform }}.zip
dist/kandev-${{ matrix.platform }}.zip.sha256
dist/kandev-${{ matrix.platform }}.tar.gz
dist/kandev-${{ matrix.platform }}.tar.gz.sha256
+1 -2
View File
@@ -14,11 +14,10 @@
"dist"
],
"dependencies": {
"adm-zip": "^0.5.16",
"tar": "^7.5.0",
"tree-kill": "^1.2.2"
},
"devDependencies": {
"@types/adm-zip": "^0.5.7",
"@types/node": "^20",
"tsx": "^4.15.7",
"typescript": "^5"
+5 -14
View File
@@ -1,26 +1,17 @@
import AdmZip from "adm-zip";
import tar from "tar";
import fs from "node:fs";
import path from "node:path";
export function extractZip(zipPath: string, destDir: string): void {
const zip = new AdmZip(zipPath);
zip.extractAllTo(destDir, true);
// adm-zip doesn't preserve Unix file permissions, so mark binaries executable
const binDir = path.join(destDir, "kandev", "bin");
if (fs.existsSync(binDir)) {
for (const file of fs.readdirSync(binDir)) {
fs.chmodSync(path.join(binDir, file), 0o755);
}
}
export function extractTarGz(archivePath: string, destDir: string): void {
tar.extract({ file: archivePath, cwd: destDir, sync: true });
}
export function ensureExtracted(zipPath: string, destDir: string): void {
export function ensureExtracted(archivePath: string, destDir: string): void {
const marker = path.join(destDir, ".extracted");
if (fs.existsSync(marker)) {
return;
}
extractZip(zipPath, destDir);
extractTarGz(archivePath, destDir);
fs.writeFileSync(marker, "");
}
+3 -9
View File
@@ -43,10 +43,10 @@ async function prepareReleaseBundle({
const platformDir = getPlatformDir();
const release = await getRelease(version);
const tag = release.tag_name || "latest";
const assetName = `kandev-${platformDir}.zip`;
const assetName = `kandev-${platformDir}.tar.gz`;
const cacheDir = path.join(CACHE_DIR, tag, platformDir);
const zipPath = await ensureAsset(release, assetName, cacheDir, (downloaded, total) => {
const archivePath = await ensureAsset(release, assetName, cacheDir, (downloaded, total) => {
const percent = total ? Math.round((downloaded / total) * 100) : 0;
const mb = (downloaded / (1024 * 1024)).toFixed(1);
const totalMb = total ? (total / (1024 * 1024)).toFixed(1) : "?";
@@ -54,7 +54,7 @@ async function prepareReleaseBundle({
});
process.stderr.write("\n");
ensureExtracted(zipPath, cacheDir);
ensureExtracted(archivePath, cacheDir);
const bundleDir = findBundleRoot(cacheDir);
const backendBin = path.join(bundleDir, "bin", getBinaryName("kandev"));
@@ -67,12 +67,6 @@ async function prepareReleaseBundle({
throw new Error(`agentctl binary not found at ${agentctlBin}`);
}
// Ensure binaries are executable (adm-zip doesn't preserve Unix permissions)
if (process.platform !== "win32") {
fs.chmodSync(backendBin, 0o755);
fs.chmodSync(agentctlBin, 0o755);
}
const actualBackendPort = backendPort ?? (await pickAvailablePort(DEFAULT_BACKEND_PORT));
const actualWebPort = webPort ?? (await pickAvailablePort(DEFAULT_WEB_PORT));
const agentctlPort = await pickAvailablePort(DEFAULT_AGENTCTL_PORT);
+49 -19
View File
@@ -21,16 +21,13 @@ importers:
cli:
dependencies:
adm-zip:
specifier: ^0.5.16
version: 0.5.16
tar:
specifier: ^7.5.0
version: 7.5.9
tree-kill:
specifier: ^1.2.2
version: 1.2.2
devDependencies:
'@types/adm-zip':
specifier: ^0.5.7
version: 0.5.7
'@types/node':
specifier: ^20
version: 20.19.28
@@ -1310,6 +1307,10 @@ packages:
resolution: {integrity: sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==}
engines: {node: 20 || >=22}
'@isaacs/fs-minipass@4.0.1':
resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==}
engines: {node: '>=18.0.0'}
'@jest/schemas@29.6.3':
resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
@@ -2747,9 +2748,6 @@ packages:
'@tybys/wasm-util@0.10.1':
resolution: {integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==}
'@types/adm-zip@0.5.7':
resolution: {integrity: sha512-DNEs/QvmyRLurdQPChqq0Md4zGvPwHerAJYWk9l2jCbD1VPpnzRJorOdiq4zsw09NFbYnhfsoEhWtxIzXpn2yw==}
'@types/d3-array@3.2.2':
resolution: {integrity: sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==}
@@ -3167,10 +3165,6 @@ packages:
engines: {node: '>=0.4.0'}
hasBin: true
adm-zip@0.5.16:
resolution: {integrity: sha512-TGw5yVi4saajsSEgz25grObGHEUaDrniwvA2qwSC060KfqGPdglhvPMA2lPIoxs3PQIItj2iag35fONcQqgUaQ==}
engines: {node: '>=12.0'}
agent-base@7.1.4:
resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==}
engines: {node: '>= 14'}
@@ -3376,6 +3370,10 @@ packages:
chevrotain@11.0.3:
resolution: {integrity: sha512-ci2iJH6LeIkvP9eJW6gpueU8cnZhv85ELY8w8WiFtNjMHA5ad6pQLaJo9mEly/9qUyCpvqX8/POVUTf18/HFdw==}
chownr@3.0.0:
resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==}
engines: {node: '>=18'}
class-variance-authority@0.7.1:
resolution: {integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==}
@@ -5054,6 +5052,14 @@ packages:
minimist@1.2.8:
resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==}
minipass@7.1.2:
resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==}
engines: {node: '>=16 || 14 >=14.17'}
minizlib@3.1.0:
resolution: {integrity: sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==}
engines: {node: '>= 18'}
mlly@1.8.0:
resolution: {integrity: sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g==}
@@ -5913,6 +5919,10 @@ packages:
resolution: {integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==}
engines: {node: '>=6'}
tar@7.5.9:
resolution: {integrity: sha512-BTLcK0xsDh2+PUe9F6c2TlRp4zOOBMTkoQHQIWSIzI0R7KG46uEwq4OPk2W7bZcprBMsuaeFsqwYr7pjh6CuHg==}
engines: {node: '>=18'}
tiny-invariant@1.3.3:
resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==}
@@ -6318,6 +6328,10 @@ packages:
yallist@3.1.1:
resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==}
yallist@5.0.0:
resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==}
engines: {node: '>=18'}
yargs-parser@21.1.1:
resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==}
engines: {node: '>=12'}
@@ -7190,6 +7204,10 @@ snapshots:
dependencies:
'@isaacs/balanced-match': 4.0.1
'@isaacs/fs-minipass@4.0.1':
dependencies:
minipass: 7.1.2
'@jest/schemas@29.6.3':
dependencies:
'@sinclair/typebox': 0.27.8
@@ -8659,10 +8677,6 @@ snapshots:
tslib: 2.8.1
optional: true
'@types/adm-zip@0.5.7':
dependencies:
'@types/node': 20.19.28
'@types/d3-array@3.2.2': {}
'@types/d3-axis@3.0.6':
@@ -9114,8 +9128,6 @@ snapshots:
acorn@8.15.0: {}
adm-zip@0.5.16: {}
agent-base@7.1.4: {}
ajv-formats@3.0.1(ajv@8.17.1):
@@ -9356,6 +9368,8 @@ snapshots:
'@chevrotain/utils': 11.0.3
lodash-es: 4.17.21
chownr@3.0.0: {}
class-variance-authority@0.7.1:
dependencies:
clsx: 2.1.1
@@ -11427,6 +11441,12 @@ snapshots:
minimist@1.2.8: {}
minipass@7.1.2: {}
minizlib@3.1.0:
dependencies:
minipass: 7.1.2
mlly@1.8.0:
dependencies:
acorn: 8.15.0
@@ -12594,6 +12614,14 @@ snapshots:
tapable@2.3.0: {}
tar@7.5.9:
dependencies:
'@isaacs/fs-minipass': 4.0.1
chownr: 3.0.0
minipass: 7.1.2
minizlib: 3.1.0
yallist: 5.0.0
tiny-invariant@1.3.3: {}
tinybench@2.9.0: {}
@@ -13054,6 +13082,8 @@ snapshots:
yallist@3.1.1: {}
yallist@5.0.0: {}
yargs-parser@21.1.1: {}
yargs@17.7.2:
+4 -3
View File
@@ -18,9 +18,10 @@ rm -rf "$OUT_DIR"
mkdir -p "$OUT_DIR"
# Copy with symlinks dereferenced so pnpm's .pnpm/ structure survives
# zip/unzip (adm-zip can't handle symlinks). The standalone output may
# contain broken symlinks from partial tracing — --safe-links skips those
# and rsync exits with code 23 (partial transfer), which is expected.
# archiving (symlinks cause issues on Windows even inside tar). The
# standalone output may contain broken symlinks from partial tracing —
# --safe-links skips those and rsync exits with code 23 (partial transfer),
# which is expected.
rsync -a --copy-links --safe-links "$WEB_DIR/.next/standalone/" "$OUT_DIR/" || {
rc=$?
if [ "$rc" -eq 23 ]; then