Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,42 @@ jobs:
retention-days: 30
if-no-files-found: error

build-windows:
name: Build Windows
runs-on: windows-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0 # Required for git describe to find tags

- uses: ./.github/actions/setup-mux

- name: Install GNU Make (for build)
run: choco install -y make

- name: Verify tools
shell: bash
run: |
make --version
bun --version
magick --version | head -1

- name: Build application
run: bun run build

# No code signing - releases use release.yml (triggered by tag publish).
- name: Package for Windows
run: make dist-win

- name: Upload Windows exe
uses: actions/upload-artifact@v4
with:
name: windows-exe
path: release/*.exe
retention-days: 30
if-no-files-found: error

build-vscode-extension:
name: Build VS Code Extension
runs-on: ${{ github.repository_owner == 'coder' && 'depot-ubuntu-22.04-16' || 'ubuntu-latest' }}
Expand Down
44 changes: 44 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ on:

permissions:
contents: write # Required for electron-builder to upload release assets
id-token: write # Required for GCP workload identity authentication (Windows code signing)

env:
RELEASE_TAG: ${{ inputs.tag || github.event.release.tag_name || github.ref_name }}
Expand Down Expand Up @@ -168,7 +169,50 @@ jobs:
- name: Build application
run: bun run build

# Setup Java for jsign (EV code signing with GCP KMS)
- name: Setup Java
uses: actions/setup-java@3a4f6e1af504cf6a31855fa899c6aa5355ba6c12 # v4.7.0
with:
distribution: "zulu"
java-version: "11.0"

- name: Authenticate to Google Cloud
id: gcloud_auth
if: ${{ vars.GCP_WORKLOAD_ID_PROVIDER != '' }}
uses: google-github-actions/auth@71f986410dfbc7added4569d411d040a91dc6935 # v2.1.8
with:
workload_identity_provider: ${{ vars.GCP_WORKLOAD_ID_PROVIDER }}
service_account: ${{ vars.GCP_SERVICE_ACCOUNT }}
token_format: "access_token"

- name: Setup code signing
shell: pwsh
run: |
if (-not $env:EV_SIGNING_CERT) {
Write-Host "⚠️ No Windows code signing certificate provided - building unsigned"
exit 0
}

# Save EV certificate to temp file
$certPath = Join-Path $env:TEMP "ev_cert.pem"
Set-Content -Path $certPath -Value $env:EV_SIGNING_CERT
Add-Content -Path $env:GITHUB_ENV -Value "EV_CERTIFICATE_PATH=$certPath"

# Download jsign
$jsignPath = Join-Path $env:TEMP "jsign-6.0.jar"
Invoke-WebRequest -Uri "https://github.com/ebourg/jsign/releases/download/6.0/jsign-6.0.jar" -OutFile $jsignPath
Add-Content -Path $env:GITHUB_ENV -Value "JSIGN_PATH=$jsignPath"

Write-Host "✅ Windows EV code signing configured"
env:
EV_SIGNING_CERT: ${{ secrets.EV_SIGNING_CERT }}

- name: Package and publish for Windows (.exe)
run: bun x electron-builder --win --publish always
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# EV signing environment variables (used by custom sign script if configured)
EV_KEYSTORE: ${{ vars.EV_KEYSTORE }}
EV_KEY: ${{ vars.EV_KEY }}
EV_TSA_URL: ${{ vars.EV_TSA_URL }}
GCLOUD_ACCESS_TOKEN: ${{ steps.gcloud_auth.outputs.access_token }}
1 change: 0 additions & 1 deletion docs/system-prompt.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,5 +62,4 @@ You are in a git worktree at ${workspacePath}
}
```


{/* END SYSTEM_PROMPT_DOCS */}
6 changes: 5 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,11 @@
"win": {
"target": "nsis",
"icon": "build/icon.png",
"artifactName": "${productName}-${version}-${arch}.${ext}"
"artifactName": "${productName}-${version}-${arch}.${ext}",
"sign": "scripts/sign-windows.js",
"signingHashAlgorithms": [
"sha256"
]
},
"npmRebuild": false,
"buildDependenciesFromSource": false
Expand Down
78 changes: 78 additions & 0 deletions scripts/sign-windows.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/**
* Windows EV code signing script for electron-builder
* Uses jsign with GCP Cloud KMS for EV certificate signing
*
* Required environment variables:
* JSIGN_PATH - Path to jsign JAR file
* EV_KEYSTORE - GCP Cloud KMS keystore URL
* EV_KEY - Key alias in the keystore
* EV_CERTIFICATE_PATH - Path to the EV certificate PEM file
* EV_TSA_URL - Timestamp server URL
* GCLOUD_ACCESS_TOKEN - GCP access token for authentication
*/

const { execSync } = require("child_process");
const path = require("path");

/**
* @param {import("electron-builder").CustomWindowsSignTaskConfiguration} configuration
* @returns {Promise<void>}
*/
exports.default = async function sign(configuration) {
const filePath = configuration.path;

// Check if signing is configured
if (!process.env.JSIGN_PATH || !process.env.EV_KEYSTORE) {
console.log(
`⚠️ Windows code signing not configured - skipping signing for ${filePath}`
);
return;
}

// Validate required environment variables
const requiredVars = [
"JSIGN_PATH",
"EV_KEYSTORE",
"EV_KEY",
"EV_CERTIFICATE_PATH",
"EV_TSA_URL",
"GCLOUD_ACCESS_TOKEN",
];

for (const varName of requiredVars) {
if (!process.env[varName]) {
throw new Error(`Missing required environment variable: ${varName}`);
}
}

console.log(`Signing ${filePath} with EV certificate...`);

const jsignArgs = [
"-jar",
process.env.JSIGN_PATH,
"--storetype",
"GOOGLECLOUD",
"--storepass",
process.env.GCLOUD_ACCESS_TOKEN,
"--keystore",
process.env.EV_KEYSTORE,
"--alias",
process.env.EV_KEY,
"--certfile",
process.env.EV_CERTIFICATE_PATH,
"--tsmode",
"RFC3161",
"--tsaurl",
process.env.EV_TSA_URL,
filePath,
];

try {
execSync(`java ${jsignArgs.map((a) => `"${a}"`).join(" ")}`, {
stdio: "inherit",
});
console.log(`✅ Successfully signed ${filePath}`);
} catch (error) {
throw new Error(`Failed to sign ${filePath}: ${error.message}`);
}
};