Skip to content

Commit 168944c

Browse files
author
evgyur
committed
Add portable Windows release workflow
1 parent 6728dc7 commit 168944c

3 files changed

Lines changed: 176 additions & 0 deletions

File tree

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
name: release-portable
2+
3+
on:
4+
workflow_dispatch:
5+
push:
6+
tags:
7+
- "v*"
8+
9+
permissions:
10+
contents: write
11+
12+
jobs:
13+
build-windows-portable:
14+
runs-on: windows-latest
15+
steps:
16+
- name: Checkout
17+
uses: actions/checkout@v4
18+
19+
- name: Setup Node
20+
uses: actions/setup-node@v4
21+
with:
22+
node-version: 22
23+
cache: "npm"
24+
25+
- name: Install dependencies
26+
run: npm install --legacy-peer-deps
27+
env:
28+
VERCEL: "1"
29+
30+
- name: Download Claude binary
31+
run: npm run claude:download
32+
33+
- name: Build app
34+
run: npm run build
35+
36+
- name: Package portable
37+
run: npm run package:win:portable
38+
39+
- name: Upload release assets
40+
uses: softprops/action-gh-release@v2
41+
with:
42+
files: release/*.exe

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
"package": "node scripts/package-windows.mjs",
1616
"package:mac": "electron-builder --mac",
1717
"package:win": "electron-builder --win",
18+
"package:win:portable": "node scripts/package-windows-portable.mjs",
1819
"package:linux": "electron-builder --linux",
1920
"dist": "electron-builder",
2021
"dist:manifest": "node scripts/generate-update-manifest.mjs",
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
#!/usr/bin/env node
2+
/**
3+
* Windows portable packaging script that suppresses winCodeSign extraction errors.
4+
* This mirrors package-windows.mjs but produces a portable .exe.
5+
*/
6+
7+
import { spawn } from "child_process"
8+
import { fileURLToPath } from "url"
9+
import { dirname, join } from "path"
10+
import { existsSync } from "fs"
11+
12+
const __filename = fileURLToPath(import.meta.url)
13+
const __dirname = dirname(__filename)
14+
const projectRoot = join(__dirname, "..")
15+
16+
// Set environment variables to prevent code signing attempts
17+
const env = {
18+
...process.env,
19+
CSC_IDENTITY_AUTO_SELECT: "false",
20+
WIN_CSC_LINK: "",
21+
WIN_CSC_KEY_PASSWORD: "",
22+
}
23+
24+
// Track if we detect file lock errors
25+
let hasFileLockError = false
26+
27+
// Filter out winCodeSign/darwin symlink errors from output
28+
const filterOutput = (data) => {
29+
const lines = data.toString().split("\n")
30+
const filtered = lines.filter((line) => {
31+
const lower = line.toLowerCase()
32+
33+
// Detect file lock/overwrite errors in stdout too
34+
if (
35+
lower.includes("ebusy") ||
36+
lower.includes("eacces") ||
37+
lower.includes("eperm") ||
38+
lower.includes("file is in use") ||
39+
lower.includes("cannot overwrite") ||
40+
lower.includes("access is denied") ||
41+
lower.includes("the process cannot access the file") ||
42+
lower.includes("being used by another process") ||
43+
lower.includes("file is locked") ||
44+
lower.includes("cannot delete") ||
45+
lower.includes("sharing violation")
46+
) {
47+
hasFileLockError = true
48+
}
49+
50+
return (
51+
!lower.includes("wincodesign") &&
52+
!lower.includes("darwin") &&
53+
!lower.includes("symbolic link") &&
54+
!lower.includes("libcrypto.dylib") &&
55+
!lower.includes("libssl.dylib") &&
56+
!lower.includes("cannot create symbolic link")
57+
)
58+
})
59+
if (filtered.length > 0) {
60+
process.stdout.write(filtered.join("\n"))
61+
}
62+
}
63+
64+
const electronBuilder = spawn("npx", ["electron-builder", "--win", "portable"], {
65+
cwd: projectRoot,
66+
shell: true,
67+
env,
68+
})
69+
70+
// Filter stdout
71+
electronBuilder.stdout?.on("data", filterOutput)
72+
73+
// Filter stderr but keep important errors
74+
electronBuilder.stderr?.on("data", (data) => {
75+
const text = data.toString()
76+
const lower = text.toLowerCase()
77+
78+
// Detect file lock/overwrite errors in stderr too
79+
if (
80+
lower.includes("ebusy") ||
81+
lower.includes("eacces") ||
82+
lower.includes("eperm") ||
83+
lower.includes("file is in use") ||
84+
lower.includes("cannot overwrite") ||
85+
lower.includes("access is denied") ||
86+
lower.includes("the process cannot access the file") ||
87+
lower.includes("being used by another process") ||
88+
lower.includes("file is locked") ||
89+
lower.includes("cannot delete") ||
90+
lower.includes("sharing violation")
91+
) {
92+
hasFileLockError = true
93+
}
94+
95+
// Only show errors that aren't related to winCodeSign/darwin
96+
if (
97+
!lower.includes("wincodesign") &&
98+
!lower.includes("darwin") &&
99+
!lower.includes("symbolic link") &&
100+
!lower.includes("libcrypto.dylib") &&
101+
!lower.includes("libssl.dylib")
102+
) {
103+
process.stderr.write(data)
104+
}
105+
})
106+
107+
electronBuilder.on("error", (error) => {
108+
console.error("Failed to start electron-builder:", error)
109+
process.exit(1)
110+
})
111+
112+
electronBuilder.on("close", (code) => {
113+
const releaseDir = join(projectRoot, "release")
114+
const exeCandidates = [
115+
join(releaseDir, "1Code.exe"),
116+
]
117+
const exePath = exeCandidates.find((p) => existsSync(p))
118+
119+
if (hasFileLockError) {
120+
console.log("\n❌ Build failed: Cannot overwrite files (app may be running)")
121+
console.log(" Please close 1Code.exe and try again")
122+
process.exit(1)
123+
} else if (exePath) {
124+
console.log("\n✅ Build completed successfully!")
125+
console.log(` Executable: ${exePath}`)
126+
process.exit(0)
127+
} else if (code === 0) {
128+
console.log("\n⚠️ Build completed but executable not found in release/")
129+
process.exit(1)
130+
} else {
131+
process.exit(code || 1)
132+
}
133+
})

0 commit comments

Comments
 (0)