Skip to content

ci: unified desktop build pipeline — renderer once, package in parallel#10813

Draft
huhuanming wants to merge 1 commit intoxfrom
ci/desktop-unified-build-pipeline
Draft

ci: unified desktop build pipeline — renderer once, package in parallel#10813
huhuanming wants to merge 1 commit intoxfrom
ci/desktop-unified-build-pipeline

Conversation

@huhuanming
Copy link
Contributor

Summary

  • Add build-desktop-renderer.yml: reusable workflow that builds the desktop renderer (webpack) once on ubuntu-24.04
  • Add release-desktop-all.yml: orchestrator that calls the renderer build, then fans out to 3 platform packaging jobs in parallel

Pipeline architecture

renderer (ubuntu-24.04)        ← webpack build once
│
├─ package-mac (macos-26)      ← Phase 1: Mac DMG, Phase 2: MAS
├─ package-win (windows-2025)  ← Phase 1: Win NSIS, Phase 2: WinMS
└─ package-linux (ubuntu x2)   ← Phase 1: AppImage, Phase 2: Snap

Optimization

Metric Before (5 separate workflows) After (unified)
Renderer builds 5x 1x
yarn installs 7x 4x
macOS runners 2 jobs 1 job (mac + MAS)
Windows runners 2 jobs 1 job (win + winms)

Notes

  • Additive only — existing per-platform workflows (release-desktop, release-desktop-win, etc.) are untouched
  • Triggered via workflow_dispatch for now — can be wired to daily-build later
  • Artifact names use onekey-desktop-all-* prefix to avoid collision with existing artifacts

Test plan

  • Trigger release-desktop-all via workflow_dispatch
  • Verify renderer artifact is created and downloaded by all 3 platform jobs
  • Verify Mac DMG + MAS .pkg artifacts are produced
  • Verify Win NSIS + WinMS .exe artifacts are produced
  • Verify Linux AppImage + Snap artifacts are produced
  • Compare output artifacts with existing per-platform workflow outputs

Split desktop CI into two stages:
1. Build renderer once on ubuntu (fastest)
2. Fan out to 3 platform jobs that reuse the renderer artifact

This reduces renderer builds from 5x to 1x and yarn installs from 7x to 4x.
Existing per-platform workflows are untouched.
@revan-zhang
Copy link
Contributor

revan-zhang commented Mar 21, 2026

Snyk checks have passed. No issues have been found so far.

Status Scan Engine Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues
Licenses 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

@socket-security
Copy link

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Addedreact-native-get-random-values@​1.1.46100100100100100

View full report

@socket-security
Copy link

Warning

Review the following alerts detected in dependencies.

According to your organization's Security Policy, it is recommended to resolve "Warn" alerts. Learn more about Socket for GitHub.

Action Severity Alert  (click "▶" to expand/collapse)
Warn High
Obfuscated code: npm @polkadot/util-crypto is 91.0% likely obfuscated

Confidence: 0.91

Location: Package overview

From: ?npm/@polkadot/util-crypto@13.5.9

ℹ Read more on: This package | This alert | What is obfuscated code?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Packages should not obfuscate their code. Consider not using packages with obfuscated code.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/@polkadot/util-crypto@13.5.9. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

View full report

Comment on lines +41 to +104
runs-on: ubuntu-24.04
strategy:
matrix:
node-version: [24.x]
steps:
- name: Show executed time
run: |
echo "Executed at: $(date '+%Y-%m-%d %H:%M:%S')"

- name: Checkout Source Code
uses: actions/checkout@v4
with:
lfs: true

- name: Run Shared Env Setup
uses: ./.github/actions/shared-env
with:
env_file_name: '.env'
sentry_project: 'desktop'
covalent_key: ${{ secrets.COVALENT_KEY }}
sentry_token: ${{ secrets.SENTRY_TOKEN }}
sentry_dsn_react_native: ${{ secrets.SENTRY_DSN_REACT_NATIVE }}
sentry_dsn_web: ${{ secrets.SENTRY_DSN_WEB }}
sentry_dsn_desktop: ${{ secrets.SENTRY_DSN_DESKTOP }}
sentry_dsn_mas: ${{ secrets.SENTRY_DSN_MAS }}
sentry_dsn_snap: ${{ secrets.SENTRY_DSN_SNAP }}
sentry_dsn_winms: ${{ secrets.SENTRY_DSN_WINMS }}
sentry_dsn_ext: ${{ secrets.SENTRY_DSN_EXT }}

- name: Setup Node
uses: actions/setup-node@v6
with:
node-version: ${{ matrix.node-version }}
cache: 'yarn'
registry-url: 'https://npm.pkg.github.com'
scope: '@onekeyhq'

- name: Get yarn cache directory path
id: yarn-cache-dir-path
run: echo "dir=${{ github.workspace }}/.yarn" >> "$GITHUB_OUTPUT"

- name: Install Dep
env:
NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NODE_OPTIONS: '--max_old_space_size=8192'
run: |
yarn install --immutable

- name: Build Renderer
env:
NODE_OPTIONS: '--max_old_space_size=8192'
run: |
cd apps/desktop
npx cross-env NODE_ENV=production yarn clean:build
npx cross-env NODE_ENV=production NODE_OPTIONS=--max-old-space-size=8192 webpack build
node scripts/finalize-renderer-assets.js

- name: Upload Renderer Artifact
uses: actions/upload-artifact@v4
with:
name: desktop-renderer-${{ github.sha }}
path: |
./apps/desktop/app/build/
retention-days: 1

Check warning

Code scanning / CodeQL

Workflow does not contain permissions Medium

Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {contents: read}

Copilot Autofix

AI about 4 hours ago

In general, the fix is to explicitly define a permissions: block that grants only the minimal scopes needed for this workflow. Since the workflow only checks out code, uses GitHub Packages as an npm registry, and uploads artifacts, it needs read access to repository contents and packages, and does not need any write scopes.

The best fix here is to add a workflow-level permissions: block near the top of .github/workflows/build-desktop-renderer.yml (for example, after name: and before on:). This block will then apply to all jobs (including build-renderer) that do not override permissions. A suitable minimal configuration is:

permissions:
  contents: read
  packages: read

No other changes are required to existing steps or functionality, and no imports or external dependencies are involved.

Suggested changeset 1
.github/workflows/build-desktop-renderer.yml

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/.github/workflows/build-desktop-renderer.yml b/.github/workflows/build-desktop-renderer.yml
--- a/.github/workflows/build-desktop-renderer.yml
+++ b/.github/workflows/build-desktop-renderer.yml
@@ -3,6 +3,10 @@
 # Reusable workflow: builds the desktop renderer (webpack) once on ubuntu,
 # then uploads the artifact for downstream platform-specific packaging jobs.
 
+permissions:
+  contents: read
+  packages: read
+
 on:
   workflow_call:
     inputs:
EOF
@@ -3,6 +3,10 @@
# Reusable workflow: builds the desktop renderer (webpack) once on ubuntu,
# then uploads the artifact for downstream platform-specific packaging jobs.

permissions:
contents: read
packages: read

on:
workflow_call:
inputs:
Copilot is powered by AI and may make mistakes. Always verify output.
Comment on lines +27 to +48
uses: ./.github/workflows/build-desktop-renderer.yml
with:
ONEKEY_ALLOW_SKIP_GPG_VERIFICATION: ${{ github.event.inputs.ONEKEY_ALLOW_SKIP_GPG_VERIFICATION || 'false' }}
secrets:
COVALENT_KEY: ${{ secrets.COVALENT_KEY }}
SENTRY_TOKEN: ${{ secrets.SENTRY_TOKEN }}
SENTRY_DSN_REACT_NATIVE: ${{ secrets.SENTRY_DSN_REACT_NATIVE }}
SENTRY_DSN_WEB: ${{ secrets.SENTRY_DSN_WEB }}
SENTRY_DSN_DESKTOP: ${{ secrets.SENTRY_DSN_DESKTOP }}
SENTRY_DSN_MAS: ${{ secrets.SENTRY_DSN_MAS }}
SENTRY_DSN_SNAP: ${{ secrets.SENTRY_DSN_SNAP }}
SENTRY_DSN_WINMS: ${{ secrets.SENTRY_DSN_WINMS }}
SENTRY_DSN_EXT: ${{ secrets.SENTRY_DSN_EXT }}

# ──────────────────────────────────────────────────
# Stage 2: Platform-specific packaging (3 parallel jobs)
# ──────────────────────────────────────────────────

# ═══════════════════════════════════════════════════
# macOS runner: mac DMG + MAS
# ═══════════════════════════════════════════════════
package-mac:

Check warning

Code scanning / CodeQL

Workflow does not contain permissions Medium

Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {}

Copilot Autofix

AI about 4 hours ago

In general, the fix is to add an explicit permissions: block that grants only the minimal scopes required for this workflow to run. For a release/build workflow that reads source and uses artifacts but does not push code or modify issues/PRs, contents: read is typically sufficient; if the workflow needs to download/upload artifacts only, that does not require extra scopes beyond contents: read. Because this workflow calls a reusable workflow (build-desktop-renderer.yml) and defines additional jobs, the safest and simplest change is to add a top‑level permissions: block so all jobs (including the called reusable workflow) default to least‑privilege settings.

Concretely, edit .github/workflows/release-desktop-all.yml near the top of the file, right after the on: block (line 10–18). Insert a root‑level permissions: section such as:

permissions:
  contents: read

This will apply to all jobs (including renderer, package-mac, and the other platform jobs) that do not override permissions. No imports or additional methods are required; this is purely a YAML configuration addition. If some unshown job genuinely requires write access in the future (for example to create releases), that job can override with its own permissions: while keeping the rest of the workflow restricted.

Suggested changeset 1
.github/workflows/release-desktop-all.yml

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/.github/workflows/release-desktop-all.yml b/.github/workflows/release-desktop-all.yml
--- a/.github/workflows/release-desktop-all.yml
+++ b/.github/workflows/release-desktop-all.yml
@@ -16,6 +16,9 @@
         type: boolean
         default: false
 
+permissions:
+  contents: read
+
 env:
   ONEKEY_ALLOW_SKIP_GPG_VERIFICATION: ${{ github.event.inputs.ONEKEY_ALLOW_SKIP_GPG_VERIFICATION || 'false' }}
 
EOF
@@ -16,6 +16,9 @@
type: boolean
default: false

permissions:
contents: read

env:
ONEKEY_ALLOW_SKIP_GPG_VERIFICATION: ${{ github.event.inputs.ONEKEY_ALLOW_SKIP_GPG_VERIFICATION || 'false' }}

Copilot is powered by AI and may make mistakes. Always verify output.
Comment on lines +49 to +284
needs: renderer
runs-on: macos-26
strategy:
matrix:
node-version: [24.x]
env:
NODE_ENV: production
YARN_ENABLE_GLOBAL_CACHE: true
steps:
- name: Checkout Source Code
uses: actions/checkout@v4
with:
lfs: true

- name: Run Shared Env Setup
uses: ./.github/actions/shared-env
with:
env_file_name: '.env'
sentry_project: 'desktop'
covalent_key: ${{ secrets.COVALENT_KEY }}
sentry_token: ${{ secrets.SENTRY_TOKEN }}
sentry_dsn_react_native: ${{ secrets.SENTRY_DSN_REACT_NATIVE }}
sentry_dsn_web: ${{ secrets.SENTRY_DSN_WEB }}
sentry_dsn_desktop: ${{ secrets.SENTRY_DSN_DESKTOP }}
sentry_dsn_mas: ${{ secrets.SENTRY_DSN_MAS }}
sentry_dsn_snap: ${{ secrets.SENTRY_DSN_SNAP }}
sentry_dsn_winms: ${{ secrets.SENTRY_DSN_WINMS }}
sentry_dsn_ext: ${{ secrets.SENTRY_DSN_EXT }}

- name: Warn if GPG verification is skipped
if: ${{ env.ONEKEY_ALLOW_SKIP_GPG_VERIFICATION == 'true' }}
run: |
echo "::warning::GPG verification is SKIPPED for this build. This should only be used in CI/dev builds."

- name: 'Setup ENV'
run: |
eval "$(node -e 'const v=require("./apps/desktop/package.json").version; console.log("pkg_version="+v)')"
echo "PKG_VERSION=$pkg_version" >> $GITHUB_ENV
artifacts_url="$GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID"
echo "ARTIFACTS_URL=$artifacts_url" >> $GITHUB_ENV

- name: Setup Node
uses: actions/setup-node@v6
with:
node-version: ${{ matrix.node-version }}
cache: 'yarn'
registry-url: 'https://npm.pkg.github.com'
scope: '@onekeyhq'

- name: Get yarn cache directory path
id: yarn-cache-dir-path
run: echo "dir=${{ github.workspace }}/.yarn" >> "$GITHUB_OUTPUT"

- name: Install Dep
env:
NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NODE_OPTIONS: '--max_old_space_size=8192'
run: |
yarn install --immutable

- name: Download Renderer Artifact
uses: actions/download-artifact@v4
with:
name: desktop-renderer-${{ github.sha }}
path: ./apps/desktop/app/build/

- name: Compile macOS Liquid Glass Icon
run: |
cd apps/desktop
bash scripts/compile-liquid-icon.sh

- name: Verify Liquid Glass Icon
run: |
cd apps/desktop
bash scripts/verify-icon-compatibility.sh

# ── Phase 1: Mac + Linux (Developer ID) ──

- name: Setup Developer ID Code Signing
run: |
echo ${{ secrets.DESKTOP_KEYS_SECRET }} | base64 -d > apps/desktop/sign.p12

- name: Install Developer ID provisioning profile for CloudKit
env:
DESKTOP_ISO_PROVISION_PROFILE_BASE64: ${{ secrets.DESKTOP_ISO_PROVISION_PROFILE_BASE64 }}
run: |
PP_PATH=./apps/desktop/OneKey_Desktop_DeveloperId.provisionprofile
echo -n "$DESKTOP_ISO_PROVISION_PROFILE_BASE64" | base64 --decode > $PP_PATH
mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles
cp $PP_PATH ~/Library/MobileDevice/Provisioning\ Profiles

- name: 'Build Main Process'
env:
NODE_OPTIONS: '--max_old_space_size=8192'
run: |
cd apps/desktop
yarn build:main
yarn install-app-deps

- name: 'Package Mac'
env:
NODE_OPTIONS: '--max_old_space_size=8192'
APPLEID: ${{ secrets.APPLEID }}
APPLEIDPASS: ${{ secrets.APPLEIDPASS }}
ASC_PROVIDER: ${{ secrets.ASC_PROVIDER }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CSC_KEY_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }}
CSC_LINK: './sign.p12'
run: |
cd apps/desktop
yarn build:electron:mac --publish never

- name: Clean up Developer ID provisioning profile
if: ${{ always() }}
run: |
rm -f ~/Library/MobileDevice/Provisioning\ Profiles/OneKey_Desktop_DeveloperId.provisionprofile

- name: Upload Artifacts latest.yml
uses: actions/upload-artifact@v4
with:
name: onekey-desktop-all-mac-yml-${{ env.BUILD_APP_VERSION }}-${{ env.BUILD_NUMBER }}-${{ env.BUILD_BUNDLE_VERSION }}-${{ github.sha }}
path: |
./apps/desktop/build-electron/*.yml

- name: Upload universal Mac DMG
uses: actions/upload-artifact@v4
with:
name: onekey-desktop-all-mac-universal-dmg-${{ env.BUILD_APP_VERSION }}-${{ env.BUILD_NUMBER }}-${{ env.BUILD_BUNDLE_VERSION }}-${{ github.sha }}
path: |
./apps/desktop/build-electron/*-universal.dmg

- name: Upload x64 Mac DMG
uses: actions/upload-artifact@v4
with:
name: onekey-desktop-all-mac-x64-dmg-${{ env.BUILD_APP_VERSION }}-${{ env.BUILD_NUMBER }}-${{ env.BUILD_BUNDLE_VERSION }}-${{ github.sha }}
path: |
./apps/desktop/build-electron/*-x64.dmg

- name: Upload arm64 Mac DMG
uses: actions/upload-artifact@v4
with:
name: onekey-desktop-all-mac-arm64-dmg-${{ env.BUILD_APP_VERSION }}-${{ env.BUILD_NUMBER }}-${{ env.BUILD_BUNDLE_VERSION }}-${{ github.sha }}
path: |
./apps/desktop/build-electron/*-arm64.dmg

- name: Upload Mac Release Artifacts
uses: actions/upload-artifact@v4
with:
name: onekey-desktop-all-mac-${{ env.BUILD_APP_VERSION }}-${{ env.BUILD_NUMBER }}-${{ env.BUILD_BUNDLE_VERSION }}-${{ github.sha }}
path: |
./apps/desktop/build-electron/*
!./apps/desktop/build-electron/mac-arm64
!./apps/desktop/build-electron/mac
!./apps/desktop/build-electron/builder-debug.yml

# ── Phase 2: MAS (App Store signing) ──

- name: Clean build-electron for MAS
run: rm -rf apps/desktop/build-electron

- name: Install the Apple certificate and provisioning profile for MAS
env:
MAC_INSTALL_P12_BASE64: ${{ secrets.MAC_INSTALL_P12_BASE64 }}
MAC_INSTALL_P12_PASSWORD: ${{ secrets.MAC_INSTALL_P12_PASSWORD }}
APPLE_DISTRIBUTION_P12_BASE64: ${{ secrets.APPLE_DISTRIBUTION_P12_BASE64 }}
APPLE_DISTRIBUTION_P12_PASSWORD: ${{ secrets.APPLE_DISTRIBUTION_P12_PASSWORD }}
PROVISION_PROFILE_BASE64: ${{ secrets.PROVISION_PROFILE_BASE64 }}
KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }}
run: |
MAC_INSTALL_P12_PATH=$RUNNER_TEMP/mac_install_certificate.p12
APPLE_DISTRIBUTION_P12_PATH=$RUNNER_TEMP/apple_distribution_certificate.p12
PP_PATH=./apps/desktop/OneKey_Mac_App.provisionprofile
KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db

echo -n "$MAC_INSTALL_P12_BASE64" | base64 --decode > $MAC_INSTALL_P12_PATH
echo -n "$APPLE_DISTRIBUTION_P12_BASE64" | base64 --decode > $APPLE_DISTRIBUTION_P12_PATH
echo -n "$PROVISION_PROFILE_BASE64" | base64 --decode > $PP_PATH

security create-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
security set-keychain-settings -lut 21600 $KEYCHAIN_PATH
security unlock-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH

security import $MAC_INSTALL_P12_PATH -P "$MAC_INSTALL_P12_PASSWORD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH
security import $APPLE_DISTRIBUTION_P12_PATH -P "$APPLE_DISTRIBUTION_P12_PASSWORD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH
security list-keychain -d user -s $KEYCHAIN_PATH

mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles
cp $PP_PATH ~/Library/MobileDevice/Provisioning\ Profiles

- name: 'Package MAS'
env:
NODE_OPTIONS: '--max_old_space_size=8192'
APPLEID: ${{ secrets.APPLEID }}
APPLEIDPASS: ${{ secrets.APPLEIDPASS }}
ASC_PROVIDER: ${{ secrets.ASC_PROVIDER }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
cd apps/desktop
npx electron-builder build -m --config electron-builder-mas.config.js --publish never

- name: Clean up MAS keychain and provisioning profile
if: ${{ always() }}
run: |
security delete-keychain $RUNNER_TEMP/app-signing.keychain-db 2>/dev/null || true
rm -f ~/Library/MobileDevice/Provisioning\ Profiles/OneKey_Mac_App.provisionprofile

- name: Upload MAS Artifacts
uses: actions/upload-artifact@v4
with:
name: onekey-desktop-all-mas-${{ env.BUILD_APP_VERSION }}-${{ env.BUILD_NUMBER }}-${{ env.BUILD_BUNDLE_VERSION }}-${{ github.sha }}
path: |
./apps/desktop/build-electron/mas-universal/*.pkg

- name: Validate MAS for Testflight
env:
APPLEID: ${{ secrets.APPLEID }}
APPLEIDPASS: ${{ secrets.APPLEIDPASS }}
run: |
PKG_FILE=$(ls ./apps/desktop/build-electron/mas-universal/*.pkg)
echo "Found package: $PKG_FILE"
xcrun altool --validate-app --file "$PKG_FILE" -t macOS -u "$APPLEID" -p "$APPLEIDPASS" --show-progress

- name: Upload MAS to Testflight
continue-on-error: true
env:
APPLEID: ${{ secrets.APPLEID }}
APPLEIDPASS: ${{ secrets.APPLEIDPASS }}
run: |
PKG_FILE=$(ls ./apps/desktop/build-electron/mas-universal/*.pkg)
echo "Uploading package: $PKG_FILE"
xcrun altool --upload-app --file "$PKG_FILE" -t macOS -u "$APPLEID" -p "$APPLEIDPASS" --show-progress

# ═══════════════════════════════════════════════════
# Windows runner: win NSIS + winms (Microsoft Store)
# ═══════════════════════════════════════════════════
package-win:

Check warning

Code scanning / CodeQL

Workflow does not contain permissions Medium

Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {contents: read}

Copilot Autofix

AI about 4 hours ago

To fix the problem, explicitly define permissions for this workflow so that the GITHUB_TOKEN has only the minimal scopes required. Since the visible parts of this workflow merely check out code, download and upload artifacts, and access packages via NODE_AUTH_TOKEN (which uses secrets.GITHUB_TOKEN), contents: read and packages: read are sufficient as a safe baseline. These can be applied at the top (workflow) level so they affect all jobs, including renderer, package-mac, and the later package-win/Linux jobs referenced but not shown.

The single best minimal‑change fix is: add a workflow‑level permissions block just after the on: block (after line 18 and before the env: section). This sets default permissions for all jobs that do not override them, without modifying any job definitions or steps. No imports or additional methods are needed, since this is YAML configuration only. We do not change any existing behavior of the jobs; we only constrain the implicit token capabilities.

Suggested changeset 1
.github/workflows/release-desktop-all.yml

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/.github/workflows/release-desktop-all.yml b/.github/workflows/release-desktop-all.yml
--- a/.github/workflows/release-desktop-all.yml
+++ b/.github/workflows/release-desktop-all.yml
@@ -16,6 +16,10 @@
         type: boolean
         default: false
 
+permissions:
+  contents: read
+  packages: read
+
 env:
   ONEKEY_ALLOW_SKIP_GPG_VERIFICATION: ${{ github.event.inputs.ONEKEY_ALLOW_SKIP_GPG_VERIFICATION || 'false' }}
 
EOF
@@ -16,6 +16,10 @@
type: boolean
default: false

permissions:
contents: read
packages: read

env:
ONEKEY_ALLOW_SKIP_GPG_VERIFICATION: ${{ github.event.inputs.ONEKEY_ALLOW_SKIP_GPG_VERIFICATION || 'false' }}

Copilot is powered by AI and may make mistakes. Always verify output.
Comment on lines +285 to +455
needs: renderer
runs-on: windows-2025
strategy:
matrix:
node-version: [24.x]
env:
NODE_ENV: production
YARN_ENABLE_GLOBAL_CACHE: true
steps:
- name: Checkout Source Code
uses: actions/checkout@v4
with:
lfs: true

- name: Run Shared Env Setup
uses: ./.github/actions/shared-env
with:
env_file_name: '.env'
sentry_project: 'desktop'
covalent_key: ${{ secrets.COVALENT_KEY }}
sentry_token: ${{ secrets.SENTRY_TOKEN }}
sentry_dsn_react_native: ${{ secrets.SENTRY_DSN_REACT_NATIVE }}
sentry_dsn_web: ${{ secrets.SENTRY_DSN_WEB }}
sentry_dsn_desktop: ${{ secrets.SENTRY_DSN_DESKTOP }}
sentry_dsn_mas: ${{ secrets.SENTRY_DSN_MAS }}
sentry_dsn_snap: ${{ secrets.SENTRY_DSN_SNAP }}
sentry_dsn_winms: ${{ secrets.SENTRY_DSN_WINMS }}
sentry_dsn_ext: ${{ secrets.SENTRY_DSN_EXT }}

- name: Warn if GPG verification is skipped
if: ${{ env.ONEKEY_ALLOW_SKIP_GPG_VERIFICATION == 'true' }}
run: |
Write-Host "::warning::GPG verification is SKIPPED for this build. This should only be used in CI/dev builds."

- name: 'Setup ENV'
run: |
$pkg_version = node -e "console.log(require('./apps/desktop/package.json').version)"
$pkg_version = $pkg_version.Trim()
Write-Host "pkg_version=$pkg_version"
"PKG_VERSION=$pkg_version" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
$artifacts_url = "$env:GITHUB_SERVER_URL/$env:GITHUB_REPOSITORY/actions/runs/$env:GITHUB_RUN_ID"
"ARTIFACTS_URL=$artifacts_url" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append

- name: Setup Node
uses: actions/setup-node@v6
with:
node-version: ${{ matrix.node-version }}
cache: 'yarn'
registry-url: 'https://npm.pkg.github.com'
scope: '@onekeyhq'

- name: Get yarn cache directory path
id: yarn-cache-dir-path
shell: pwsh
run: echo "dir=${{ github.workspace }}/.yarn" >> "$GITHUB_OUTPUT"

- name: Install Dep
env:
NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NODE_OPTIONS: '--max_old_space_size=8192'
run: |
yarn install --immutable

- name: Download Renderer Artifact
uses: actions/download-artifact@v4
with:
name: desktop-renderer-${{ github.sha }}
path: ./apps/desktop/app/build/

- name: Setup Code Signing file
run: |
[IO.File]::WriteAllBytes("apps/desktop/sign.p12", [Convert]::FromBase64String("${{ secrets.DESKTOP_KEYS_SECRET }}"))

# ── Phase 1: Windows NSIS installer ──

- name: 'Build Main Process (win)'
env:
NODE_OPTIONS: '--max_old_space_size=8192'
run: |
cd apps/desktop
yarn build:main
yarn install-app-deps

- name: 'Package Windows NSIS'
env:
NODE_OPTIONS: '--max_old_space_size=8192'
APPLEID: ${{ secrets.APPLEID }}
APPLEIDPASS: ${{ secrets.APPLEIDPASS }}
ASC_PROVIDER: ${{ secrets.ASC_PROVIDER }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CSC_KEY_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }}
CSC_LINK: './sign.p12'
run: |
cd apps/desktop
npx electron-builder build -w --config electron-builder-win.config.js --publish never

- name: Upload x64 Windows exe
uses: actions/upload-artifact@v4
with:
name: onekey-desktop-all-win-x64-${{ env.BUILD_APP_VERSION }}-${{ env.BUILD_NUMBER }}-${{ env.BUILD_BUNDLE_VERSION }}-${{ github.sha }}
path: |
./apps/desktop/build-electron/*-x64.exe

- name: Upload arm64 Windows exe
uses: actions/upload-artifact@v4
with:
name: onekey-desktop-all-win-arm64-${{ env.BUILD_APP_VERSION }}-${{ env.BUILD_NUMBER }}-${{ env.BUILD_BUNDLE_VERSION }}-${{ github.sha }}
path: |
./apps/desktop/build-electron/*-arm64.exe

- name: Upload Artifacts latest.yml
uses: actions/upload-artifact@v4
with:
name: onekey-desktop-all-win-yml-${{ env.BUILD_APP_VERSION }}-${{ env.BUILD_NUMBER }}-${{ env.BUILD_BUNDLE_VERSION }}-${{ github.sha }}
path: |
./apps/desktop/build-electron/*.yml

- name: Upload Windows NSIS Release Artifacts
uses: actions/upload-artifact@v4
with:
name: onekey-desktop-all-win-${{ env.BUILD_APP_VERSION }}-${{ env.BUILD_NUMBER }}-${{ env.BUILD_BUNDLE_VERSION }}-${{ github.sha }}
path: |
./apps/desktop/build-electron/*
!./apps/desktop/build-electron/win-unpacked
!./apps/desktop/build-electron/win-arm64-unpacked
!./apps/desktop/build-electron/mac-arm64
!./apps/desktop/build-electron/mac
!./apps/desktop/build-electron/linux-unpacked
!./apps/desktop/build-electron/builder-debug.yml

# ── Phase 2: Windows Microsoft Store ──

- name: Clean build-electron for WinMS
shell: pwsh
run: Remove-Item -Recurse -Force apps/desktop/build-electron -ErrorAction SilentlyContinue

- name: 'Rebuild Main Process (winms, DESK_CHANNEL=ms-store)'
env:
NODE_OPTIONS: '--max_old_space_size=8192'
DESK_CHANNEL: ms-store
run: |
cd apps/desktop
yarn build:main

- name: 'Package Windows Microsoft Store'
env:
NODE_OPTIONS: '--max_old_space_size=8192'
APPLEID: ${{ secrets.APPLEID }}
APPLEIDPASS: ${{ secrets.APPLEIDPASS }}
ASC_PROVIDER: ${{ secrets.ASC_PROVIDER }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CSC_KEY_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }}
DESK_CHANNEL: ms-store
CSC_LINK: './sign.p12'
run: |
cd apps/desktop
npx electron-builder build -w --config electron-builder-ms.config.js --publish never

- name: Upload WinMS universal exe
uses: actions/upload-artifact@v4
with:
name: onekey-desktop-all-winms-universal-${{ env.BUILD_APP_VERSION }}-${{ env.BUILD_NUMBER }}-${{ env.BUILD_BUNDLE_VERSION }}-${{ github.sha }}
path: |
./apps/desktop/build-electron/*.exe
!./apps/desktop/build-electron/*-x64.exe
!./apps/desktop/build-electron/*-arm64.exe

# ═══════════════════════════════════════════════════
# Linux runner: AppImage + Snap (amd64 + arm64)
# ═══════════════════════════════════════════════════
package-linux:

Check warning

Code scanning / CodeQL

Workflow does not contain permissions Medium

Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {contents: read}

Copilot Autofix

AI about 4 hours ago

To fix the problem, explicitly declare a permissions block in the workflow so the GITHUB_TOKEN used in all jobs is restricted to the least privileges required. Since the workflow primarily checks out code, downloads/upload artifacts, and uses GITHUB_TOKEN as an auth token for package installation and build tooling, the minimal safe and generic setting is contents: read at the workflow level. This applies to all jobs that do not override permissions, including renderer, package-mac, package-win, and package-linux.

The best fix without altering functionality is to add a top-level permissions block just after the on: section (or immediately before/after env:) in .github/workflows/release-desktop-all.yml. This will not interfere with any secrets or steps and will simply constrain the automatically provided GITHUB_TOKEN to read-only repository contents, which is sufficient for actions/checkout, actions/download-artifact, actions/upload-artifact, and actions/setup-node with GitHub Packages. No additional imports or external dependencies are required.

Concretely:

  • Edit .github/workflows/release-desktop-all.yml.

  • Add:

    permissions:
      contents: read

    at the workflow root level (aligned with name, on, and env), between the existing on: block and the env: block (or just above jobs: if you prefer). No other lines need to change.

Suggested changeset 1
.github/workflows/release-desktop-all.yml

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/.github/workflows/release-desktop-all.yml b/.github/workflows/release-desktop-all.yml
--- a/.github/workflows/release-desktop-all.yml
+++ b/.github/workflows/release-desktop-all.yml
@@ -16,6 +16,9 @@
         type: boolean
         default: false
 
+permissions:
+  contents: read
+
 env:
   ONEKEY_ALLOW_SKIP_GPG_VERIFICATION: ${{ github.event.inputs.ONEKEY_ALLOW_SKIP_GPG_VERIFICATION || 'false' }}
 
EOF
@@ -16,6 +16,9 @@
type: boolean
default: false

permissions:
contents: read

env:
ONEKEY_ALLOW_SKIP_GPG_VERIFICATION: ${{ github.event.inputs.ONEKEY_ALLOW_SKIP_GPG_VERIFICATION || 'false' }}

Copilot is powered by AI and may make mistakes. Always verify output.
Comment on lines +456 to +604
needs: renderer
strategy:
matrix:
node-version: [24.x]
arch: [amd64, arm64]
include:
- arch: amd64
runs-on: ubuntu-24.04
build-flag: --x64
- arch: arm64
runs-on: ubuntu-24.04-arm
build-flag: --arm64
runs-on: ${{ matrix.runs-on }}
env:
NODE_ENV: production
YARN_ENABLE_GLOBAL_CACHE: true
steps:
- name: Checkout Source Code
uses: actions/checkout@v4
with:
lfs: true

- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install -y libudev-dev

- name: Run Shared Env Setup
uses: ./.github/actions/shared-env
with:
env_file_name: '.env'
sentry_project: 'desktop'
covalent_key: ${{ secrets.COVALENT_KEY }}
sentry_token: ${{ secrets.SENTRY_TOKEN }}
sentry_dsn_react_native: ${{ secrets.SENTRY_DSN_REACT_NATIVE }}
sentry_dsn_web: ${{ secrets.SENTRY_DSN_WEB }}
sentry_dsn_desktop: ${{ secrets.SENTRY_DSN_DESKTOP }}
sentry_dsn_mas: ${{ secrets.SENTRY_DSN_MAS }}
sentry_dsn_snap: ${{ secrets.SENTRY_DSN_SNAP }}
sentry_dsn_winms: ${{ secrets.SENTRY_DSN_WINMS }}
sentry_dsn_ext: ${{ secrets.SENTRY_DSN_EXT }}

- name: Warn if GPG verification is skipped
if: ${{ env.ONEKEY_ALLOW_SKIP_GPG_VERIFICATION == 'true' }}
run: |
echo "::warning::GPG verification is SKIPPED for this build. This should only be used in CI/dev builds."

- name: Install Snapcraft
uses: samuelmeuli/action-snapcraft@v2

- name: Install snap dependencies for snapcraft build
run: |
sudo snap install core24
sudo snap install gnome-46-2404
sudo snap install mesa-2404
sudo snap install gtk-common-themes

- name: Configure Snapcraft destructive mode
run: |
echo "SNAP_DESTRUCTIVE_MODE=true" >> $GITHUB_ENV

- name: 'Setup ENV'
run: |
eval "$(node -e 'const v=require("./apps/desktop/package.json").version; console.log("pkg_version="+v)')"
echo "PKG_VERSION=$pkg_version" >> $GITHUB_ENV
artifacts_url="$GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID"
echo "ARTIFACTS_URL=$artifacts_url" >> $GITHUB_ENV

- name: Setup Node
uses: actions/setup-node@v6
with:
node-version: ${{ matrix.node-version }}
cache: 'yarn'
registry-url: 'https://npm.pkg.github.com'
scope: '@onekeyhq'

- name: Get yarn cache directory path
id: yarn-cache-dir-path
run: echo "dir=${{ github.workspace }}/.yarn" >> "$GITHUB_OUTPUT"

- name: Install Dep
env:
NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NODE_OPTIONS: '--max_old_space_size=8192'
run: |
yarn install --immutable

- name: Download Renderer Artifact
uses: actions/download-artifact@v4
with:
name: desktop-renderer-${{ github.sha }}
path: ./apps/desktop/app/build/

# ── Phase 1: AppImage (no SNAP) ──

- name: 'Build Main Process (AppImage)'
env:
NODE_OPTIONS: '--max_old_space_size=8192'
run: |
cd apps/desktop
yarn build:main
yarn install-app-deps

- name: 'Package AppImage'
env:
NODE_OPTIONS: '--max_old_space_size=8192'
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
cd apps/desktop
npx electron-builder build -l --config electron-builder.config.js ${{ matrix.build-flag }} --publish never

- name: Upload AppImage Artifacts
uses: actions/upload-artifact@v4
with:
name: onekey-desktop-all-linux-appimage-${{ matrix.arch }}-${{ env.BUILD_APP_VERSION }}-${{ env.BUILD_NUMBER }}-${{ env.BUILD_BUNDLE_VERSION }}-${{ github.sha }}
path: |
./apps/desktop/build-electron/*.AppImage

# ── Phase 2: Snap (SNAP=true) ──

- name: Clean build-electron for Snap
run: rm -rf apps/desktop/build-electron

- name: 'Rebuild Main Process (Snap, SNAP=true)'
env:
NODE_OPTIONS: '--max_old_space_size=8192'
SNAP: true
run: |
cd apps/desktop
yarn build:main

- name: 'Package Snap'
env:
NODE_OPTIONS: '--max_old_space_size=8192'
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SNAP: true
run: |
cd apps/desktop
npx electron-builder build -l --config electron-builder-snap.config.js ${{ matrix.build-flag }} --publish never

- name: Upload Snap Artifacts
uses: actions/upload-artifact@v4
with:
name: onekey-desktop-all-snap-${{ matrix.arch }}-${{ env.BUILD_APP_VERSION }}-${{ env.BUILD_NUMBER }}-${{ env.BUILD_BUNDLE_VERSION }}-${{ github.sha }}
path: |
./apps/desktop/build-electron/*
!./apps/desktop/build-electron/linux-unpacked
!./apps/desktop/build-electron/linux-arm64-unpacked
!./apps/desktop/build-electron/builder-debug.yml

Check warning

Code scanning / CodeQL

Workflow does not contain permissions Medium

Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {contents: read}

Copilot Autofix

AI about 4 hours ago

In general, you fix this by explicitly specifying a permissions: block either at the workflow root (to apply to all jobs that don’t override it) or per job, limiting the GITHUB_TOKEN to the least privileges needed. For this workflow, the jobs shown only need to read repository contents and possibly packages; they don’t push commits, create releases, or modify issues/PRs. Thus, a root-level permissions: block with contents: read is an appropriate minimal baseline. If the workflow also needs to read from GitHub Packages via the token, adding packages: read is also safe and minimal.

The single best fix without changing functionality is to add a root-level permissions: section just after the on: block and before env:. This will apply to all jobs (including package-linux at line 455 and the Windows/mac jobs referenced in the snippet) and satisfies the CodeQL requirement. The block should specify contents: read (and optionally packages: read if you want to be explicit for private registries). No additional imports, methods, or code changes are needed elsewhere, as this is purely a YAML configuration change.

Suggested changeset 1
.github/workflows/release-desktop-all.yml

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/.github/workflows/release-desktop-all.yml b/.github/workflows/release-desktop-all.yml
--- a/.github/workflows/release-desktop-all.yml
+++ b/.github/workflows/release-desktop-all.yml
@@ -16,6 +16,10 @@
         type: boolean
         default: false
 
+permissions:
+  contents: read
+  packages: read
+
 env:
   ONEKEY_ALLOW_SKIP_GPG_VERIFICATION: ${{ github.event.inputs.ONEKEY_ALLOW_SKIP_GPG_VERIFICATION || 'false' }}
 
EOF
@@ -16,6 +16,10 @@
type: boolean
default: false

permissions:
contents: read
packages: read

env:
ONEKEY_ALLOW_SKIP_GPG_VERIFICATION: ${{ github.event.inputs.ONEKEY_ALLOW_SKIP_GPG_VERIFICATION || 'false' }}

Copilot is powered by AI and may make mistakes. Always verify output.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants