diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 20bae272a0..d31dfb1167 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -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 diff --git a/apps/cli/package.json b/apps/cli/package.json index 93e3679d8e..be69211355 100644 --- a/apps/cli/package.json +++ b/apps/cli/package.json @@ -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" diff --git a/apps/cli/src/bundle.ts b/apps/cli/src/bundle.ts index d47424a218..fe671626a3 100644 --- a/apps/cli/src/bundle.ts +++ b/apps/cli/src/bundle.ts @@ -1,26 +1,17 @@ -import AdmZip from "adm-zip"; +import * as 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, ""); } diff --git a/apps/cli/src/run.ts b/apps/cli/src/run.ts index fd1bf1b1e9..fe4eed5755 100644 --- a/apps/cli/src/run.ts +++ b/apps/cli/src/run.ts @@ -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); diff --git a/apps/pnpm-lock.yaml b/apps/pnpm-lock.yaml index 76a180a06a..c3dcbd5f3e 100644 --- a/apps/pnpm-lock.yaml +++ b/apps/pnpm-lock.yaml @@ -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: diff --git a/scripts/release/package-web.sh b/scripts/release/package-web.sh index 041752813f..5e8d69ccef 100755 --- a/scripts/release/package-web.sh +++ b/scripts/release/package-web.sh @@ -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 @@ -29,6 +30,16 @@ rsync -a --copy-links --safe-links "$WEB_DIR/.next/standalone/" "$OUT_DIR/" || { exit "$rc" fi } +# Next.js require-hook.js resolves styled-jsx at startup, but pnpm's .pnpm +# virtual store layout puts it out of reach from web/node_modules/next/. +# Copy it next to next so Node's module resolution finds it. +NEXT_MODULES="$OUT_DIR/web/node_modules" +STYLED_JSX_SRC="$OUT_DIR/node_modules/.pnpm/node_modules/styled-jsx" +if [ -d "$STYLED_JSX_SRC" ] && [ -d "$NEXT_MODULES/next" ] && [ ! -d "$NEXT_MODULES/styled-jsx" ]; then + cp -R "$STYLED_JSX_SRC" "$NEXT_MODULES/styled-jsx" + echo "Hoisted styled-jsx into web/node_modules/" +fi + mkdir -p "$OUT_DIR/.next" cp -R "$WEB_DIR/.next/static" "$OUT_DIR/.next/"