Skip to content

Commit acab171

Browse files
Desktop: Build and Sign Mac and Windows Bundles in CI (#3728)
* Desktop: Build and Sign Mac and Windows Bundles in CI * Desktop: Remove unnecessary CEF files from Windows Bundle * Desktop: Use a temp file for license generation on Windows to avoid PowerShell modifying stdout
1 parent 5efa81d commit acab171

File tree

5 files changed

+376
-10
lines changed

5 files changed

+376
-10
lines changed
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
name: Build Mac Bundle
2+
3+
on:
4+
push:
5+
branches:
6+
- master
7+
8+
jobs:
9+
build:
10+
runs-on: macos-latest
11+
12+
env:
13+
WASM_BINDGEN_CLI_VERSION: "0.2.100"
14+
15+
steps:
16+
- name: Checkout
17+
uses: actions/checkout@v4
18+
19+
- name: Setup Rust
20+
uses: actions-rust-lang/setup-rust-toolchain@v1
21+
with:
22+
toolchain: stable
23+
override: true
24+
rustflags: ""
25+
target: wasm32-unknown-unknown
26+
27+
- name: Cache Cargo
28+
uses: actions/cache@v4
29+
with:
30+
path: |
31+
~/.cargo/registry
32+
~/.cargo/git
33+
target
34+
key: cargo-${{ runner.os }}-${{ hashFiles('**/Cargo.lock') }}
35+
36+
- name: Setup Node
37+
uses: actions/setup-node@v4
38+
with:
39+
node-version-file: .nvmrc
40+
cache: npm
41+
cache-dependency-path: |
42+
package-lock.json
43+
frontend/package-lock.json
44+
45+
- name: Install Native Dependencies
46+
env:
47+
GITHUB_TOKEN: ${{ github.token }}
48+
BINSTALL_DISABLE_TELEMETRY: "true"
49+
run: |
50+
brew update
51+
brew install \
52+
pkg-config \
53+
openssl@3 \
54+
binaryen \
55+
llvm \
56+
cargo-binstall
57+
58+
echo "OPENSSL_DIR=$(brew --prefix openssl@3)" >> $GITHUB_ENV
59+
echo "PKG_CONFIG_PATH=$(brew --prefix openssl@3)/lib/pkgconfig" >> $GITHUB_ENV
60+
echo "$(brew --prefix llvm)/bin" >> $GITHUB_PATH
61+
62+
cargo binstall --no-confirm --force wasm-pack
63+
cargo binstall --no-confirm --force cargo-about
64+
cargo binstall --no-confirm --force "wasm-bindgen-cli@${WASM_BINDGEN_CLI_VERSION}"
65+
66+
- name: Build Mac Bundle
67+
env:
68+
CARGO_TERM_COLOR: always
69+
run: npm run build-desktop
70+
71+
- name: Stage Artifacts
72+
shell: bash
73+
run: |
74+
rm -rf target/artifacts
75+
mkdir -p target/artifacts
76+
cp -R target/release/Graphite.app target/artifacts/Graphite.app
77+
78+
- name: Upload Mac Bundle
79+
uses: actions/upload-artifact@v4
80+
with:
81+
name: graphite-mac-bundle
82+
path: target/artifacts
83+
84+
- name: Sign and Notarize Mac Bundle Preparation
85+
env:
86+
APPLE_CERT_BASE64: ${{ secrets.APPLE_CERT_BASE64 }}
87+
APPLE_CERT_PASSWORD: ${{ secrets.APPLE_CERT_PASSWORD }}
88+
run: |
89+
mkdir -p .sign
90+
echo "$APPLE_CERT_BASE64" | base64 --decode > .sign/certificate.p12
91+
92+
security create-keychain -p "" .sign/main.keychain
93+
security default-keychain -s .sign/main.keychain
94+
security unlock-keychain -p "" .sign/main.keychain
95+
security set-keychain-settings -t 3600 -u .sign/main.keychain
96+
97+
security import .sign/certificate.p12 -k .sign/main.keychain -P "$APPLE_CERT_PASSWORD" -T /usr/bin/codesign -T /usr/bin/productsign
98+
security set-key-partition-list -S apple-tool:,apple: -s -k "" .sign/main.keychain
99+
100+
cat > .sign/entitlements.plist <<'EOF'
101+
<?xml version="1.0" encoding="UTF-8"?>
102+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
103+
<plist version="1.0">
104+
<dict>
105+
<key>com.apple.security.cs.allow-jit</key>
106+
<true/>
107+
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
108+
<true/>
109+
<key>com.apple.security.cs.disable-executable-page-protection</key>
110+
<true/>
111+
<key>com.apple.security.cs.disable-library-validation</key>
112+
<true/>
113+
</dict>
114+
</plist>
115+
EOF
116+
117+
- name: Sign and Notarize Mac Bundle
118+
env:
119+
APPLE_EMAIL: ${{ secrets.APPLE_EMAIL }}
120+
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
121+
APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
122+
APPLE_CERT_NAME: ${{ secrets.APPLE_CERT_NAME }}
123+
run: |
124+
CERTIFICATE="$APPLE_CERT_NAME"
125+
ENTITLEMENTS=".sign/entitlements.plist"
126+
APP_PATH="target/artifacts/Graphite.app"
127+
ZIP_PATH=".sign/Graphite.zip"
128+
129+
codesign --force --options runtime --entitlements "$ENTITLEMENTS" --sign "$CERTIFICATE" "$APP_PATH/Contents/Frameworks/Graphite Helper.app"
130+
codesign --force --options runtime --entitlements "$ENTITLEMENTS" --sign "$CERTIFICATE" "$APP_PATH/Contents/Frameworks/Graphite Helper (GPU).app"
131+
codesign --force --options runtime --entitlements "$ENTITLEMENTS" --sign "$CERTIFICATE" "$APP_PATH/Contents/Frameworks/Graphite Helper (Renderer).app"
132+
codesign --force --options runtime --entitlements "$ENTITLEMENTS" --sign "$CERTIFICATE" "$APP_PATH/Contents/Frameworks/Chromium Embedded Framework.framework"
133+
codesign --force --options runtime --entitlements "$ENTITLEMENTS" --sign "$CERTIFICATE" "$APP_PATH/Contents/Frameworks/Chromium Embedded Framework.framework/Libraries/libcef_sandbox.dylib"
134+
codesign --force --options runtime --entitlements "$ENTITLEMENTS" --sign "$CERTIFICATE" "$APP_PATH/Contents/Frameworks/Chromium Embedded Framework.framework/Libraries/libEGL.dylib"
135+
codesign --force --options runtime --entitlements "$ENTITLEMENTS" --sign "$CERTIFICATE" "$APP_PATH/Contents/Frameworks/Chromium Embedded Framework.framework/Libraries/libGLESv2.dylib"
136+
codesign --force --options runtime --entitlements "$ENTITLEMENTS" --sign "$CERTIFICATE" "$APP_PATH/Contents/Frameworks/Chromium Embedded Framework.framework/Libraries/libvk_swiftshader.dylib"
137+
codesign --force --options runtime --entitlements "$ENTITLEMENTS" --sign "$CERTIFICATE" "$APP_PATH" --deep
138+
139+
codesign --verify --deep --strict --verbose=4 "$APP_PATH"
140+
141+
ditto -c -k --keepParent "$APP_PATH" "$ZIP_PATH"
142+
xcrun notarytool submit "$ZIP_PATH" --wait --apple-id "$APPLE_EMAIL" --team-id "$APPLE_TEAM_ID" --password "$APPLE_PASSWORD"
143+
rm "$ZIP_PATH"
144+
145+
xcrun stapler staple -v "$APP_PATH"
146+
147+
spctl -a -vv "$APP_PATH"
148+
149+
- name: Upload Mac Bundle Signed
150+
uses: actions/upload-artifact@v4
151+
with:
152+
name: graphite-mac-bundle-signed
153+
path: target/artifacts
Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
name: Build Windows Bundle
2+
3+
on:
4+
push:
5+
branches:
6+
- master
7+
8+
permissions:
9+
contents: read
10+
id-token: write
11+
12+
jobs:
13+
build:
14+
runs-on: windows-latest
15+
16+
env:
17+
WASM_BINDGEN_CLI_VERSION: "0.2.100"
18+
19+
steps:
20+
- name: Checkout
21+
uses: actions/checkout@v4
22+
23+
- name: Setup Rust
24+
uses: actions-rust-lang/setup-rust-toolchain@v1
25+
with:
26+
toolchain: stable
27+
override: true
28+
rustflags: ""
29+
target: wasm32-unknown-unknown
30+
31+
- name: Cache Cargo
32+
uses: actions/cache@v4
33+
with:
34+
path: |
35+
${{ env.USERPROFILE }}\.cargo\registry
36+
${{ env.USERPROFILE }}\.cargo\git
37+
target
38+
key: cargo-${{ runner.os }}-${{ hashFiles('**/Cargo.lock') }}
39+
40+
- name: Setup Node
41+
uses: actions/setup-node@v4
42+
with:
43+
node-version-file: .nvmrc
44+
cache: npm
45+
cache-dependency-path: |
46+
package-lock.json
47+
frontend/package-lock.json
48+
49+
- name: Setup Cargo Binstall
50+
uses: cargo-bins/cargo-binstall@main
51+
52+
- name: Install Native Dependencies
53+
shell: pwsh
54+
env:
55+
GITHUB_TOKEN: ${{ github.token }}
56+
BINSTALL_DISABLE_TELEMETRY: "true"
57+
run: |
58+
winget install --id LLVM.LLVM -e --accept-package-agreements --accept-source-agreements
59+
winget install --id Kitware.CMake -e --accept-package-agreements --accept-source-agreements
60+
winget install --id OpenSSL.OpenSSL -e --accept-package-agreements --accept-source-agreements
61+
winget install --id WebAssembly.Binaryen -e --accept-package-agreements --accept-source-agreements
62+
winget install --id GnuWin32.PkgConfig -e --accept-package-agreements --accept-source-agreements
63+
64+
"OPENSSL_DIR=C:\Program Files\OpenSSL-Win64" | Out-File -FilePath $env:GITHUB_ENV -Append
65+
"PKG_CONFIG_PATH=C:\Program Files\OpenSSL-Win64\lib\pkgconfig" | Out-File -FilePath $env:GITHUB_ENV -Append
66+
67+
cargo binstall --no-confirm --force wasm-pack
68+
cargo binstall --no-confirm --force cargo-about
69+
cargo binstall --no-confirm --force "wasm-bindgen-cli@$env:WASM_BINDGEN_CLI_VERSION"
70+
71+
- name: Build Windows Bundle
72+
env:
73+
CARGO_TERM_COLOR: always
74+
run: npm run build-desktop
75+
76+
- name: Stage Artifacts
77+
shell: bash
78+
run: |
79+
rm -rf target/artifacts
80+
mkdir -p target/artifacts
81+
cp -R target/release/Graphite target/artifacts/Graphite
82+
83+
- name: Upload Windows Bundle
84+
uses: actions/upload-artifact@v4
85+
with:
86+
name: graphite-windows-bundle
87+
path: target/artifacts
88+
89+
- name: Azure login
90+
uses: azure/login@v1
91+
with:
92+
client-id: ${{ secrets.AZURE_CLIENT_ID }}
93+
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
94+
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
95+
enable-AzPSSession: true
96+
97+
- name: Sign
98+
uses: azure/artifact-signing-action@v1
99+
with:
100+
endpoint: https://eus.codesigning.azure.net/
101+
signing-account-name: Graphite
102+
certificate-profile-name: Graphite
103+
files: |
104+
${{ github.workspace }}\target\artifacts\Graphite\Graphite.exe
105+
${{ github.workspace }}\target\artifacts\Graphite\libcef.dll
106+
${{ github.workspace }}\target\artifacts\Graphite\chrome_elf.dll
107+
${{ github.workspace }}\target\artifacts\Graphite\vulkan-1.dll
108+
${{ github.workspace }}\target\artifacts\Graphite\dxcompiler.dll
109+
${{ github.workspace }}\target\artifacts\Graphite\libEGL.dll
110+
${{ github.workspace }}\target\artifacts\Graphite\libGLESv2.dll
111+
${{ github.workspace }}\target\artifacts\Graphite\vk_swiftshader.dll
112+
file-digest: SHA256
113+
timestamp-rfc3161: http://timestamp.acs.microsoft.com
114+
timestamp-digest: SHA256
115+
correlation-id: ${{ github.sha }}
116+
117+
- name: Verify Signatures
118+
shell: pwsh
119+
run: |
120+
$ErrorActionPreference = "Stop"
121+
122+
$TargetDir = "target\artifacts\Graphite"
123+
124+
if (-not (Test-Path $TargetDir)) {
125+
throw "TargetDir not found: $TargetDir"
126+
}
127+
128+
$UnsignedOrBad = @()
129+
130+
Get-ChildItem -Path $TargetDir -Recurse -File -Include *.exe,*.dll | ForEach-Object {
131+
$sig = Get-AuthenticodeSignature -FilePath $_.FullName
132+
133+
if ($sig.Status -ne 'Valid') {
134+
$UnsignedOrBad += "$($_.FullName) (Status=$($sig.Status))"
135+
}
136+
}
137+
138+
if ($UnsignedOrBad.Count -gt 0) {
139+
Write-Host "Unsigned or invalid binaries detected:"
140+
$UnsignedOrBad | ForEach-Object {
141+
Write-Host "::error::$_"
142+
}
143+
144+
if ($env:GITHUB_STEP_SUMMARY) {
145+
"### ❌ Unsigned or invalid binaries detected" |
146+
Out-File $env:GITHUB_STEP_SUMMARY -Append -Encoding utf8
147+
"" | Out-File $env:GITHUB_STEP_SUMMARY -Append -Encoding utf8
148+
$UnsignedOrBad | ForEach-Object {
149+
"* `$_" | Out-File $env:GITHUB_STEP_SUMMARY -Append -Encoding utf8
150+
}
151+
}
152+
153+
exit 1
154+
}
155+
156+
Write-Host "All binaries are signed and valid."
157+
158+
if ($env:GITHUB_STEP_SUMMARY) {
159+
"### ✅ All binaries are signed and valid" |
160+
Out-File $env:GITHUB_STEP_SUMMARY -Append -Encoding utf8
161+
}
162+
163+
- name: Upload Windows Bundle Signed
164+
uses: actions/upload-artifact@v4
165+
with:
166+
name: graphite-windows-bundle-signed
167+
path: target/artifacts

desktop/bundle/src/win.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,33 @@ fn bundle(out_dir: &Path, app_bin: &Path) -> PathBuf {
2727

2828
copy_dir(&cef_path(), &app_dir);
2929

30+
if let Err(e) = remove_unnecessary_cef_files(&app_dir) {
31+
eprintln!("Failed to remove unnecessary CEF files: {}", e);
32+
}
33+
3034
let bin_path = app_dir.join(EXECUTABLE);
3135
fs::copy(app_bin, &bin_path).unwrap();
3236

3337
bin_path
3438
}
39+
40+
fn remove_unnecessary_cef_files(app_dir: &Path) -> Result<(), Box<dyn Error>> {
41+
fs::remove_dir_all(app_dir.join("cmake"))?;
42+
fs::remove_dir_all(app_dir.join("include"))?;
43+
fs::remove_dir_all(app_dir.join("libcef_dll"))?;
44+
45+
for entry in fs::read_dir(app_dir.join("locales"))? {
46+
let path = entry?.path();
47+
if path.is_file() && path.file_name() != Some("en-US.pak".as_ref()) {
48+
fs::remove_file(path)?;
49+
}
50+
}
51+
52+
fs::remove_file(app_dir.join("archive.json"))?;
53+
fs::remove_file(app_dir.join("CMakeLists.txt"))?;
54+
fs::remove_file(app_dir.join("bootstrapc.exe"))?;
55+
fs::remove_file(app_dir.join("bootstrap.exe"))?;
56+
fs::remove_file(app_dir.join("libcef.lib"))?;
57+
58+
Ok(())
59+
}

desktop/src/app.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -635,7 +635,7 @@ impl ApplicationHandler for App {
635635
}
636636
}
637637

638-
fn new_events(&mut self, event_loop: &dyn ActiveEventLoop, cause: winit::event::StartCause) {
638+
fn new_events(&mut self, _event_loop: &dyn ActiveEventLoop, cause: winit::event::StartCause) {
639639
if let StartCause::ResumeTimeReached { .. } = cause
640640
&& let Some(window) = &self.window
641641
{

0 commit comments

Comments
 (0)