diff --git a/.editorconfig b/.editorconfig index 229cce1a..984c2c6f 100644 --- a/.editorconfig +++ b/.editorconfig @@ -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 diff --git a/.github/workflows/build-helper.yml b/.github/workflows/build-helper.yml index 5ac0b887..b684c111 100644 --- a/.github/workflows/build-helper.yml +++ b/.github/workflows/build-helper.yml @@ -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: diff --git a/Taskfile.yml b/Taskfile.yml index e7cf9843..967aa34e 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -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 diff --git a/electron-builder.config.cjs b/electron-builder.config.cjs index e636b02a..4b4f6104 100644 --- a/electron-builder.config.cjs +++ b/electron-builder.config.cjs @@ -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;