Set up Windows build pipeline (#292)

This adds a new job to the Build Helper pipeline for building for
Windows. This includes code signing via DigiCert. Right now, we can only
build for x64 on Windows as wavesrv fails to build for arm64 in the
default runner and the Windows ARM runner images are missing a bunch of
tooling.

This also adds new separated arm64 and x64 for macOS for those who don't
want to use the universal binary.

This also improves the general code quality of the Taskfile.yml and the
build-helper.yml files.
This commit is contained in:
Evan Simkowitz 2024-08-30 10:13:40 -07:00 committed by GitHub
parent d1656de44d
commit a2695e8c08
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 168 additions and 93 deletions

View File

@ -4,7 +4,7 @@ root = true
end_of_line = lf
insert_final_newline = true
[*.{js,jsx,ts,tsx,json,yml,css,less}]
[*.{js,jsx,ts,tsx,cjs,json,yml,yaml,css,less}]
charset = utf-8
indent_style = space
indent_size = 4

View File

@ -1,3 +1,7 @@
# Build Helper workflow - Builds, signs, and packages binaries for each supported platform, then uploads to a staging bucket in S3 for wider distribution.
# For more information on the macOS signing and notarization, see https://www.electron.build/code-signing and https://www.electron.build/configuration/mac
# For more information on the Windows Code Signing, see https://docs.digicert.com/en/digicert-keylocker/ci-cd-integrations/plugins/github-custom-action-for-keypair-signing.html and https://docs.digicert.com/en/digicert-keylocker/signing-tools/sign-authenticode-with-electron-builder-using-ksp-integration.html
name: "Build Helper"
on: workflow_dispatch
env:
@ -9,17 +13,15 @@ jobs:
matrix:
include:
- platform: "darwin"
arch: "universal"
runner: "macos-latest-xlarge"
- platform: "linux"
arch: "amd64"
runner: "ubuntu-latest"
- platform: "linux"
arch: "arm64"
runner: ubuntu-24.04-arm64-16core
- platform: "windows"
runner: "windows-latest"
# - platform: "windows"
# arch: "amd64"
# runner: "windows-latest"
# runner: "windows-11-arm64-16core"
runs-on: ${{ matrix.runner }}
steps:
- uses: actions/checkout@v4
@ -28,11 +30,21 @@ jobs:
run: |
sudo apt-get update
sudo apt-get install --no-install-recommends -y libarchive-tools libopenjp2-tools rpm squashfs-tools
- name: Upgrade AWS CLI (Mac only) # The pre-installed version of the AWS CLI has a segfault problem so we'll install it via Homebrew instead.
# The pre-installed version of the AWS CLI has a segfault problem so we'll install it via Homebrew instead.
- name: Upgrade AWS CLI (Mac only)
if: matrix.platform == 'darwin'
run: brew update && brew install awscli
- name: Install FPM # The version of FPM that comes bundled with electron-builder doesn't include a Linux ARM target. Installing Gems onto the runner is super quick so we'll just do this for all targets.
# The version of FPM that comes bundled with electron-builder doesn't include a Linux ARM target. Installing Gems onto the runner is super quick so we'll just do this for all targets.
- name: Install FPM (not Windows)
if: matrix.platform != 'windows'
run: sudo gem install fpm
- name: Install FPM (Windows only)
if: matrix.platform == 'windows'
run: gem install fpm
# General build dependencies
- uses: actions/setup-go@v5
with:
go-version: ${{env.GO_VERSION}}
@ -53,7 +65,40 @@ jobs:
- name: Set Version
id: set-version
run: echo "WAVETERM_VERSION=$(node "./version.cjs")" >> "$GITHUB_OUTPUT"
- name: Build ${{ matrix.platform }}/${{ matrix.arch }}
shell: bash
# Windows Code Signing Setup
- name: Set up certificate (Windows only)
if: matrix.platform == 'windows'
run: |
echo "${{ secrets.SM_CLIENT_CERT_FILE_B64 }}" | base64 --decode > /d/Certificate_pkcs12.p12
shell: bash
- name: Set signing variables (Windows only)
if: matrix.platform == 'windows'
id: variables
run: |
echo "SM_HOST=${{ secrets.SM_HOST }}" >> "$GITHUB_ENV"
echo "SM_API_KEY=${{ secrets.SM_API_KEY }}" >> "$GITHUB_ENV"
echo "SM_CODE_SIGNING_CERT_SHA1_HASH=${{ secrets.SM_CODE_SIGNING_CERT_SHA1_HASH }}" >> "$GITHUB_ENV"
echo "SM_CLIENT_CERT_FILE=D:\\Certificate_pkcs12.p12" >> "$GITHUB_ENV"
echo "SM_CLIENT_CERT_FILE=D:\\Certificate_pkcs12.p12" >> "$GITHUB_OUTPUT"
echo "SM_CLIENT_CERT_PASSWORD=${{ secrets.SM_CLIENT_CERT_PASSWORD }}" >> "$GITHUB_ENV"
echo "C:\Program Files (x86)\Windows Kits\10\App Certification Kit" >> $GITHUB_PATH
echo "C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.8 Tools" >> $GITHUB_PATH
echo "C:\Program Files\DigiCert\DigiCert Keylocker Tools" >> $GITHUB_PATH
shell: bash
- name: Setup Keylocker KSP (Windows only)
if: matrix.platform == 'windows'
run: |
curl -X GET https://one.digicert.com/signingmanager/api-ui/v1/releases/Keylockertools-windows-x64.msi/download -H "x-api-key:%SM_API_KEY%" -o Keylockertools-windows-x64.msi
msiexec /i Keylockertools-windows-x64.msi /quiet /qn
C:\Windows\System32\certutil.exe -csp "DigiCert Signing Manager KSP" -key -user
smctl windows certsync
shell: cmd
# Build and upload packages
- name: Build (not Windows)
if: matrix.platform != 'windows'
run: task package
env:
USE_SYSTEM_FPM: true # Ensure that the installed version of FPM is used rather than the bundled one.
@ -62,6 +107,14 @@ jobs:
APPLE_ID: ${{ matrix.platform == 'darwin' && secrets.PROD_MACOS_NOTARIZATION_APPLE_ID }}
APPLE_APP_SPECIFIC_PASSWORD: ${{ matrix.platform == 'darwin' && secrets.PROD_MACOS_NOTARIZATION_PWD }}
APPLE_TEAM_ID: ${{ matrix.platform == 'darwin' && secrets.PROD_MACOS_NOTARIZATION_TEAM_ID }}
- name: Build (Windows only)
if: matrix.platform == 'windows'
run: task package
env:
USE_SYSTEM_FPM: true # Ensure that the installed version of FPM is used rather than the bundled one.
CSC_LINK: ${{ steps.variables.outputs.SM_CLIENT_CERT_FILE }}
CSC_KEY_PASSWORD: ${{ secrets.SM_CLIENT_CERT_PASSWORD }}
shell: powershell # electron-builder's Windows code signing package has some compatibility issues with pwsh, so we need to use Windows Powershell
- name: Upload to S3 staging
run: aws s3 cp make/ s3://waveterm-github-artifacts/staging-w2/${{ steps.set-version.outputs.WAVETERM_VERSION }}/ --recursive --exclude "*/*" --exclude "builder-*.yml"
env:

View File

@ -10,7 +10,7 @@ vars:
sh: node version.cjs
RM: '{{if eq OS "windows"}}cmd --% /c del /S{{else}}rm {{end}}'
RMRF: '{{if eq OS "windows"}}powershell Remove-Item -Force -Recurse{{else}}rm -rf{{end}}'
DATE: '{{if eq OS "windows"}}powershell date -UFormat{{else}}date{{end}}'
DATE: '{{if eq OS "windows"}}powershell Get-Date -UFormat{{else}}date{{end}}'
tasks:
electron:dev:
@ -52,11 +52,12 @@ tasks:
desc: Build the wavesrv component.
deps:
- generate
- build:server:darwin
- build:server:other
- build:server:linux
- build:server:macos
- build:server:windows
build:server:darwin:
desc: Build the wavesrv component for Darwin platforms (generates artifacts for both arm64 and amd64).
build:server:macos:
desc: Build the wavesrv component for macOS (Darwin) platforms (generates artifacts for both arm64 and amd64).
status:
- exit {{if eq OS "darwin"}}1{{else}}0{{end}}
cmds:
@ -64,35 +65,48 @@ tasks:
ignore_error: true
- task: build:server:internal
vars:
GOARCH: arm64
- task: build:server:internal
vars:
GOARCH: amd64
ARCHS: arm64,amd64
build:server:other:
desc: Build the wavesrv component for non-Darwin platforms (only generates artifacts for the current architecture).
build:server:windows:
desc: Build the wavesrv component for Windows platforms (only generates artifacts for the current architecture).
status:
- exit {{if eq OS "darwin"}}0{{else}}1{{end}}
- exit {{if eq OS "windows"}}1{{else}}0{{end}}
cmds:
- cmd: "{{.RM}} dist/bin/wavesrv*"
ignore_error: true
- task: build:server:internal
vars:
GOARCH:
ARCHS:
sh: echo {{if eq "arm" ARCH}}arm64{{else}}{{ARCH}}{{end}}
build:server:linux:
desc: Build the wavesrv component for Linux platforms (only generates artifacts for the current architecture).
status:
- exit {{if eq OS "linux"}}1{{else}}0{{end}}
cmds:
- cmd: "{{.RM}} dist/bin/wavesrv*"
ignore_error: true
- task: build:server:internal
vars:
ARCHS:
sh: echo {{if eq "arm" ARCH}}arm64{{else}}{{ARCH}}{{end}}
GO_LDFLAGS: -linkmode 'external' -extldflags=-static
build:server:internal:
requires:
vars:
- GOARCH
- ARCHS
cmds:
- CGO_ENABLED=1 GOARCH={{.GOARCH}} go build -tags "osusergo,netgo,sqlite_omit_load_extension" -ldflags "{{.GO_LDFLAGS}} -X main.BuildTime=$({{.DATE}} +'%Y%m%d%H%M') -X main.WaveVersion={{.VERSION}}" -o dist/bin/wavesrv.{{.GOARCH}}{{exeExt}} cmd/server/main-server.go
- cmd: CGO_ENABLED=1 GOARCH={{.ARCH}} go build -tags "osusergo,netgo,sqlite_omit_load_extension" -ldflags "{{.GO_LDFLAGS}} -X main.BuildTime=$({{.DATE}} +'%Y%m%d%H%M') -X main.WaveVersion={{.VERSION}}" -o dist/bin/wavesrv.{{.ARCH}}{{exeExt}} cmd/server/main-server.go
for:
var: ARCHS
split: ","
as: ARCH
sources:
- "cmd/server/*.go"
- "pkg/**/*.go"
generates:
- dist/bin/wavesrv.{{.GOARCH}}{{exeExt}}
- dist/bin/wavesrv.*{{exeExt}}
deps:
- go:mod:tidy
internal: true

View File

@ -7,76 +7,84 @@ const path = require("path");
* @see https://www.electron.build/configuration/configuration
*/
const config = {
appId: pkg.build.appId,
productName: pkg.productName,
artifactName: "${productName}-${platform}-${arch}-${version}.${ext}",
npmRebuild: false,
nodeGypRebuild: false,
electronCompile: false,
files: [
{
from: "./dist",
to: "./dist",
filter: ["**/*"],
},
{
from: ".",
to: ".",
filter: ["package.json"],
},
"!node_modules", // We don't need electron-builder to package in Node modules as Vite has already bundled any code that our program is using.
],
directories: {
output: "make",
},
asarUnpack: [
"dist/bin/**/*", // wavesrv and wsh binaries
],
mac: {
target: [
{
target: "zip",
arch: "universal",
},
{
target: "dmg",
arch: "universal",
},
appId: pkg.build.appId,
productName: pkg.productName,
artifactName: "${productName}-${platform}-${arch}-${version}.${ext}",
npmRebuild: false,
nodeGypRebuild: false,
electronCompile: false,
files: [
{
from: "./dist",
to: "./dist",
filter: ["**/*"],
},
{
from: ".",
to: ".",
filter: ["package.json"],
},
"!node_modules", // We don't need electron-builder to package in Node modules as Vite has already bundled any code that our program is using.
],
icon: "build/icons.icns",
category: "public.app-category.developer-tools",
minimumSystemVersion: "10.15.0",
notarize: process.env.APPLE_TEAM_ID
? {
teamId: process.env.APPLE_TEAM_ID,
}
: false,
binaries: fs
.readdirSync("dist/bin", { recursive: true, withFileTypes: true })
.filter((f) => f.isFile() && (f.name.startsWith("wavesrv") || f.name.includes("darwin")))
.map((f) => path.resolve(f.path, f.name)),
},
linux: {
executableName: pkg.productName,
category: "TerminalEmulator",
icon: "build/icons.icns",
target: ["zip", "deb", "rpm", "AppImage", "pacman"],
synopsis: pkg.description,
description: null,
desktop: {
Name: pkg.productName,
Comment: pkg.description,
Keywords: "developer;terminal;emulator;",
category: "Development;Utility;",
directories: {
output: "make",
},
asarUnpack: [
"dist/bin/**/*", // wavesrv and wsh binaries
],
mac: {
target: [
{
target: "zip",
arch: ["universal", "arm64", "x64"],
},
{
target: "dmg",
arch: ["universal", "arm64", "x64"],
},
],
icon: "build/icons.icns",
category: "public.app-category.developer-tools",
minimumSystemVersion: "10.15.0",
notarize: process.env.APPLE_TEAM_ID
? {
teamId: process.env.APPLE_TEAM_ID,
}
: false,
binaries: fs
.readdirSync("dist/bin", { recursive: true, withFileTypes: true })
.filter((f) => f.isFile() && (f.name.startsWith("wavesrv") || f.name.includes("darwin")))
.map((f) => path.resolve(f.parentPath, f.name)),
},
linux: {
executableName: pkg.productName,
category: "TerminalEmulator",
icon: "build/icons.icns",
target: ["zip", "deb", "rpm", "AppImage", "pacman"],
synopsis: pkg.description,
description: null,
desktop: {
Name: pkg.productName,
Comment: pkg.description,
Keywords: "developer;terminal;emulator;",
category: "Development;Utility;",
},
},
win: {
icon: "build/icons.icns",
publisherName: "Command Line, Inc.",
target: ["nsis", "msi", "zip"],
certificateSubjectName: "Command Line Inc",
certificateSha1: process.env.SM_CODE_SIGNING_CERT_SHA1_HASH,
signingHashAlgorithms: ["sha256"],
},
appImage: {
license: "LICENSE",
},
publish: {
provider: "generic",
url: "https://dl.waveterm.dev/releases-w2",
},
},
appImage: {
license: "LICENSE",
},
publish: {
provider: "generic",
url: "https://dl.waveterm.dev/releases-w2",
},
};
module.exports = config;