mirror of
https://github.com/electron/electron.git
synced 2026-10-28 11:44:46 +08:00
ci: print TCC diagnostics when the macOS runner lacks capture/input access On a small share of hosted macOS VMs (about 0.5% of macos-x64 and 0.2% of macos-arm64 test jobs since late September, on the same runner image as the passing jobs) TCC denies the job screen capture, posting events and Accessibility from the first spec on. desktopCapturer.getSources then rejects with "Failed to get sources.", getUserMedia/getDisplayMedia with "Could not start video source", and the real-input specs time out with postEventAccess:false and accessibilityTrusted:false. Add a preflight before the specs that checks CGPreflightScreenCaptureAccess() and CGPreflightPostEventAccess() from the job's process chain. When access is missing it prints the process chain, the TCC.db rows for these services and tccd's attribution of the request, and emits a warning naming the runner host problem. It does not change TCC state or fail the step; that can follow once the attribution shows which process TCC holds responsible. Claude-Session: https://claude.ai/code/session_01LPMvJBQiyP6b7QjatACmnS Co-authored-by: Claude <noreply@anthropic.com>
579 lines
30 KiB
YAML
579 lines
30 KiB
YAML
name: Pipeline Segment - Electron Test
|
|
|
|
on:
|
|
workflow_call:
|
|
inputs:
|
|
target-platform:
|
|
type: string
|
|
description: 'Platform to run on, can be macos, win or linux'
|
|
required: true
|
|
target-arch:
|
|
type: string
|
|
description: 'Arch to build for, can be x64 or arm64'
|
|
required: true
|
|
test-runs-on:
|
|
type: string
|
|
description: 'What host to run the tests on'
|
|
required: true
|
|
test-container:
|
|
type: string
|
|
description: 'JSON container information for aks runs-on'
|
|
required: false
|
|
default: '{"image":null}'
|
|
is-asan:
|
|
description: 'Building the Address Sanitizer (ASan) Linux build'
|
|
required: false
|
|
type: boolean
|
|
default: false
|
|
is-ubsan:
|
|
description: 'Building the Undefined Behavior Sanitizer (UBSan) build'
|
|
required: false
|
|
type: boolean
|
|
default: false
|
|
enable-ssh:
|
|
description: 'Enable SSH debugging'
|
|
required: false
|
|
type: boolean
|
|
default: false
|
|
display-server:
|
|
description: 'Display backend for Linux tests: x11 or wayland'
|
|
required: false
|
|
type: string
|
|
default: x11
|
|
full-macos-tests:
|
|
description: 'Run the whole spec suite on every macOS leg. When false the x64 legs and the mas legs each run one shard over the allowlist in script/macos-{x64,mas}-spec-allowlist.txt; darwin arm64 always runs the whole suite'
|
|
required: false
|
|
type: boolean
|
|
default: true
|
|
secrets:
|
|
# ACTIONS_STEP_DEBUG is a reserved name for workflow_call secrets; the
|
|
# workflows that read it map this back to the ACTIONS_STEP_DEBUG env.
|
|
CI_STEP_DEBUG:
|
|
required: false
|
|
CHROMIUM_GIT_COOKIE:
|
|
required: false
|
|
CHROMIUM_GIT_COOKIE_WINDOWS_STRING:
|
|
required: false
|
|
CLOUDFLARE_TUNNEL_CERT:
|
|
required: false
|
|
CLOUDFLARE_USER_CA_CERT:
|
|
required: false
|
|
DD_API_KEY:
|
|
required: false
|
|
ELECTRON_RBE_JWT:
|
|
required: false
|
|
SSH_DEBUG_AUTHORIZED_USERS:
|
|
required: false
|
|
|
|
concurrency:
|
|
group: electron-test-${{ inputs.target-platform }}-${{ inputs.target-arch }}-${{ inputs.is-asan }}-${{ inputs.is-ubsan }}-${{ inputs.display-server }}-${{ github.ref_protected == true && github.run_id || github.ref }}
|
|
cancel-in-progress: ${{ github.ref_protected != true }}
|
|
|
|
permissions: {}
|
|
|
|
env:
|
|
CHROMIUM_GIT_COOKIE: ${{ secrets.CHROMIUM_GIT_COOKIE }}
|
|
CHROMIUM_GIT_COOKIE_WINDOWS_STRING: ${{ secrets.CHROMIUM_GIT_COOKIE_WINDOWS_STRING }}
|
|
ELECTRON_OUT_DIR: Default
|
|
ELECTRON_RBE_JWT: ${{ secrets.ELECTRON_RBE_JWT }}
|
|
ACTIONS_STEP_DEBUG: ${{ secrets.CI_STEP_DEBUG }}
|
|
# @sentry/cli is only needed by release upload-symbols.py; skip the ~17MB CDN download on test jobs
|
|
SENTRYCLI_SKIP_DOWNLOAD: 1
|
|
|
|
jobs:
|
|
test:
|
|
defaults:
|
|
run:
|
|
shell: bash
|
|
runs-on: ${{ inputs.test-runs-on }}
|
|
permissions:
|
|
contents: read
|
|
issues: read
|
|
pull-requests: read
|
|
container: ${{ fromJSON(inputs.test-container) }} # zizmor: ignore[unpinned-images] the caller passes ghcr.io/electron/build tagged by the content hash computed in build.yml; there is no static digest to pin
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
build-type: ${{ inputs.target-platform == 'macos' && (inputs.is-ubsan && fromJSON('["darwin"]') || fromJSON('["darwin","mas"]')) || (inputs.target-platform == 'win' && fromJSON('["win"]') || fromJSON('["linux"]')) }}
|
|
shard: ${{ case(inputs.display-server == 'wayland', fromJSON('[1]'), inputs.target-platform == 'macos' && inputs.is-ubsan, fromJSON('[1, 2, 3]'), inputs.target-platform == 'linux', fromJSON('[1, 2, 3]'), inputs.target-platform == 'macos' && !inputs.full-macos-tests && inputs.target-arch == 'x64', fromJSON('[1]'), inputs.target-platform == 'macos' && inputs.target-arch == 'x64', fromJSON('[1, 2, 3]'), fromJSON('[1, 2]')) }}
|
|
# The reduced macOS arm64 leg keeps both darwin shards and runs mas on
|
|
# a single one; the reduced x64 leg is already one shard per variant.
|
|
exclude: ${{ inputs.target-platform == 'macos' && !inputs.full-macos-tests && inputs.target-arch == 'arm64' && fromJSON('[{"build-type":"mas","shard":2}]') || fromJSON('[]') }}
|
|
env:
|
|
BUILD_TYPE: ${{ matrix.build-type }}
|
|
TARGET_ARCH: ${{ inputs.target-arch }}
|
|
ARTIFACT_KEY: ${{ matrix.build-type }}_${{ inputs.target-arch }}
|
|
# Set on the reduced macOS legs (x64, and mas on any arch): the file
|
|
# listing the spec files to run instead of the whole suite. Empty
|
|
# elsewhere. The mas list wins on mas_x64: its arch coverage comes from
|
|
# the darwin_x64 leg of the same run.
|
|
MACOS_SPEC_ALLOWLIST: ${{ inputs.target-platform == 'macos' && !inputs.full-macos-tests && (matrix.build-type == 'mas' && 'script/macos-mas-spec-allowlist.txt' || inputs.target-arch == 'x64' && 'script/macos-x64-spec-allowlist.txt') || '' }}
|
|
# depot_tools' unbootstrapped python3 wrapper (crrev.com/c/8162440) breaks system python3 resolution; bypass it
|
|
DEPOT_TOOLS_PYTHON_BYPASS: 1
|
|
steps:
|
|
- name: Use Cloudflare DNS (macOS)
|
|
if: ${{ inputs.target-platform == 'macos' }}
|
|
run: |
|
|
sudo networksetup -setdnsservers Ethernet 1.1.1.1 1.0.0.1
|
|
sudo dscacheutil -flushcache
|
|
sudo killall -HUP mDNSResponder
|
|
- name: Setup Node.js/npm
|
|
if: ${{ inputs.target-platform == 'win' }}
|
|
uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1
|
|
with:
|
|
node-version: 22.23.4
|
|
- name: Add TCC permissions on macOS
|
|
if: ${{ inputs.target-platform == 'macos' }}
|
|
run: |
|
|
epochdate=$(($(date +'%s * 1000 + %-N / 1000000')))
|
|
configure_user_tccdb () {
|
|
local values=$1
|
|
local dbPath="$HOME/Library/Application Support/com.apple.TCC/TCC.db"
|
|
local sqlQuery="INSERT OR REPLACE INTO access VALUES($values);"
|
|
sqlite3 "$dbPath" "$sqlQuery"
|
|
}
|
|
|
|
configure_sys_tccdb () {
|
|
local values=$1
|
|
local dbPath="/Library/Application Support/com.apple.TCC/TCC.db"
|
|
local sqlQuery="INSERT OR REPLACE INTO access VALUES($values);"
|
|
sudo sqlite3 "$dbPath" "$sqlQuery"
|
|
}
|
|
|
|
userValuesArray=(
|
|
"'kTCCServiceCamera','/usr/local/opt/runner/provisioner/provisioner',1,2,4,1,NULL,NULL,0,'UNUSED',NULL,0,1687786159"
|
|
"'kTCCServiceBluetoothAlways','/usr/local/opt/runner/provisioner/provisioner',1,2,4,1,NULL,NULL,0,'UNUSED',NULL,0,1687786159"
|
|
"'kTCCServiceAppleEvents','/usr/local/opt/runner/provisioner/provisioner',1,2,4,1,NULL,NULL,0,'UNUSED',NULL,0,1687786159"
|
|
"'kTCCServiceCamera','/opt/hca/hosted-compute-agent',1,2,4,1,NULL,NULL,0,'UNUSED',NULL,0,1687786159"
|
|
"'kTCCServiceBluetoothAlways','/opt/hca/hosted-compute-agent',1,2,4,1,NULL,NULL,0,'UNUSED',NULL,0,1687786159"
|
|
"'kTCCServiceScreenCapture','/bin/bash',1,2,3,1,NULL,NULL,NULL,'UNUSED',NULL,0,$epochdate"
|
|
)
|
|
for values in "${userValuesArray[@]}"; do
|
|
# Sonoma and higher have a few extra values
|
|
# Ref: https://github.com/actions/runner-images/blob/main/images/macos/scripts/build/configure-tccdb-macos.sh
|
|
if [ "$OSTYPE" = "darwin23" ] || [ "$OSTYPE" = "darwin24" ]; then
|
|
configure_user_tccdb "$values,NULL,NULL,'UNUSED',${values##*,}"
|
|
configure_sys_tccdb "$values,NULL,NULL,'UNUSED',${values##*,}"
|
|
else
|
|
configure_user_tccdb "$values"
|
|
configure_sys_tccdb "$values"
|
|
fi
|
|
done
|
|
- name: Turn off the unexpectedly quit dialog on macOS
|
|
if: ${{ inputs.target-platform == 'macos' }}
|
|
run: defaults write com.apple.CrashReporter DialogType server
|
|
|
|
- name: Turn off the analytics setup assistant prompts on macOS
|
|
if: ${{ inputs.target-platform == 'macos' }}
|
|
run: sudo su root -c "killall 'Setup Assistant'" || true
|
|
|
|
- name: Turn off the reopen windows prompt on macOS
|
|
if: ${{ inputs.target-platform == 'macos' }}
|
|
# Tests spawn Electron child apps and SIGINT them; after enough abnormal
|
|
# exits AppKit shows a modal "...unexpectedly quit while reopening
|
|
# windows. Do you want to try to reopen its windows again?" alert inside
|
|
# the open-application AppleEvent handler, which blocks before
|
|
# applicationDidFinishLaunching: and hangs app.whenReady() forever.
|
|
# See https://github.com/electron/electron/issues/51462.
|
|
run: defaults write com.github.Electron ApplePersistenceIgnoreState YES
|
|
- name: Set xcode to 16.4
|
|
if: ${{ inputs.target-platform == 'macos' }}
|
|
run: sudo xcode-select --switch /Applications/Xcode_16.4.app
|
|
- name: Checkout Electron
|
|
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
|
|
with:
|
|
path: src/electron
|
|
fetch-depth: 0
|
|
ref: ${{ github.event.pull_request.head.sha }}
|
|
persist-credentials: false
|
|
- name: Turn off screenshot nag on macOS
|
|
if: ${{ inputs.target-platform == 'macos' }}
|
|
run: |
|
|
defaults write ~/Library/Group\ Containers/group.com.apple.replayd/ScreenCaptureApprovals.plist "/bin/bash" -date "3024-09-23 12:00:00 +0000"
|
|
src/electron/script/actions/screencapture-nag-remover.sh -a $(which bash)
|
|
src/electron/script/actions/screencapture-nag-remover.sh -a /opt/hca/hosted-compute-agent
|
|
- name: Setup SSH Debugging
|
|
if: ${{ inputs.target-platform == 'macos' && (inputs.enable-ssh || env.ACTIONS_STEP_DEBUG == 'true') }}
|
|
uses: ./src/electron/.github/actions/ssh-debug
|
|
with:
|
|
tunnel: 'true'
|
|
env:
|
|
CLOUDFLARE_TUNNEL_CERT: ${{ secrets.CLOUDFLARE_TUNNEL_CERT }}
|
|
CLOUDFLARE_TUNNEL_HOSTNAME: ${{ vars.CLOUDFLARE_TUNNEL_HOSTNAME }}
|
|
CLOUDFLARE_USER_CA_CERT: ${{ secrets.CLOUDFLARE_USER_CA_CERT }}
|
|
AUTHORIZED_USERS: ${{ secrets.SSH_DEBUG_AUTHORIZED_USERS }}
|
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
- name: Set Chromium Git Cookie
|
|
uses: ./src/electron/.github/actions/set-chromium-cookie
|
|
- name: Load ASan specific environment variables
|
|
if: ${{ inputs.is-asan == true }}
|
|
run: |
|
|
echo "ARTIFACT_KEY=${BUILD_TYPE}_${TARGET_ARCH}_asan" >> $GITHUB_ENV
|
|
echo "DISABLE_CRASH_REPORTER_TESTS=true" >> $GITHUB_ENV
|
|
echo "IS_ASAN=true" >> $GITHUB_ENV
|
|
- name: Load UBSan specific environment variables
|
|
if: ${{ inputs.is-ubsan == true }}
|
|
run: |
|
|
echo "ARTIFACT_KEY=${BUILD_TYPE}_${TARGET_ARCH}_ubsan" >> $GITHUB_ENV
|
|
echo "DISABLE_CRASH_REPORTER_TESTS=true" >> $GITHUB_ENV
|
|
echo "IS_UBSAN=true" >> $GITHUB_ENV
|
|
# Safe to parallelize: each step writes a disjoint location (src/electron
|
|
# node_modules, ./depot_tools, ./generated_artifacts_*, ./src_artifacts_*,
|
|
# system CUPS state) and none reads another's output; the env/PATH changes
|
|
# they make are only consumed after the group's implicit wait.
|
|
# The download steps use a fork of actions/download-artifact: the runner
|
|
# rewrites $GITHUB_EVENT_PATH as each step in this group starts, and the
|
|
# upstream action parses that file at import, so it intermittently died
|
|
# with "JSON at position 8192" before downloading anything. The fork reads
|
|
# the payload lazily (it is never needed here). Drop it once actions/runner
|
|
# stops rewriting the file per step.
|
|
- parallel:
|
|
- name: Install Dependencies
|
|
uses: ./src/electron/.github/actions/install-dependencies
|
|
- name: Get Depot Tools
|
|
timeout-minutes: 5
|
|
run: |
|
|
git config --global core.filemode false
|
|
git config --global core.autocrlf false
|
|
git config --global branch.autosetuprebase always
|
|
git config --global core.fscache true
|
|
git config --global core.longpaths true
|
|
git config --global core.preloadindex true
|
|
git config --global core.longpaths true
|
|
# googlesource.com sometimes cuts a clone off mid-pack; retry it. A
|
|
# clone that fails during checkout leaves ./depot_tools behind, so
|
|
# have the retry remove it first.
|
|
RETRY_CLEAN_PATH=depot_tools bash src/electron/script/actions/retry.sh git clone --filter=tree:0 https://chromium.googlesource.com/chromium/tools/depot_tools.git
|
|
# Ensure depot_tools does not update.
|
|
test -d depot_tools && cd depot_tools
|
|
touch .disable_auto_update
|
|
- name: Download Generated Artifacts
|
|
uses: MarshallOfSound/download-artifact@bbfa5717ecb46c82dc6248b7d261d45f77a7c71a # v8.0.1 + lazy event payload read
|
|
with:
|
|
name: generated_artifacts_${{ env.ARTIFACT_KEY }}
|
|
path: ./generated_artifacts_${{ matrix.build-type }}_${{ inputs.target-arch }}
|
|
# The build job uploads one set of src artifacts for both macOS
|
|
# variants (they hold no variant-specific files), under the darwin key.
|
|
- name: Download Src Artifacts
|
|
uses: MarshallOfSound/download-artifact@bbfa5717ecb46c82dc6248b7d261d45f77a7c71a # v8.0.1 + lazy event payload read
|
|
with:
|
|
name: src_artifacts_${{ matrix.build-type == 'mas' && format('darwin_{0}', inputs.target-arch) || env.ARTIFACT_KEY }}
|
|
path: ./src_artifacts_${{ matrix.build-type }}_${{ inputs.target-arch }}
|
|
- name: Setup CUPS virtual printer (linux)
|
|
if: ${{ inputs.target-platform == 'linux' }}
|
|
# Provisions the silent virtual printer as ROOT here, rather than letting
|
|
# script/spec-runner.js do it, because the runner executes as builduser
|
|
# (via runuser) which cannot administer CUPS or keep ippeveprinter alive.
|
|
# The printer name is handed to the runner via ELECTRON_TEST_PRINTER_NAME;
|
|
# setupVirtualPrinter() honors that and skips its own provisioning.
|
|
#
|
|
# Best-effort: the spec self-skips when ELECTRON_TEST_PRINTER_NAME is unset
|
|
# or the printer is not enumerable, so this step must never fail the job
|
|
# (hence continue-on-error and the `|| true` guards under `bash -eo pipefail`).
|
|
continue-on-error: true
|
|
run: |
|
|
export DEBIAN_FRONTEND=noninteractive
|
|
apt-get update -y || true
|
|
# cups-daemon: cupsd | cups-client: lpadmin/lpstat | cups-ipp-utils: ippeveprinter
|
|
apt-get install -y --no-install-recommends cups cups-daemon cups-client cups-ipp-utils || true
|
|
# No systemd in the container, so start the scheduler directly.
|
|
mkdir -p /var/run/cups
|
|
(cupsd || /usr/sbin/cupsd) || true
|
|
for _ in $(seq 1 10); do lpstat -r >/dev/null 2>&1 && break; sleep 1; done
|
|
lpstat -r || true
|
|
|
|
IPPEVE="$(command -v ippeveprinter || echo /usr/sbin/ippeveprinter)"
|
|
LPADMIN="$(command -v lpadmin || echo /usr/sbin/lpadmin)"
|
|
PRINTER_NAME="electron-ci-test"
|
|
IPP_PORT=8631
|
|
SPOOL_DIR="$(mktemp -d)"
|
|
# setsid + nohup detach ippeveprinter into its own session so it keeps
|
|
# running for the test step rather than being reaped when this step ends.
|
|
setsid nohup "$IPPEVE" -f application/pdf -p "$IPP_PORT" -d "$SPOOL_DIR" \
|
|
-k "Electron CI Test Printer" > /tmp/ippeveprinter.log 2>&1 &
|
|
# Wait for the IPP endpoint to accept TCP connections before registering.
|
|
for _ in $(seq 1 20); do
|
|
if (exec 3<>/dev/tcp/127.0.0.1/$IPP_PORT) 2>/dev/null; then exec 3>&- 3<&-; break; fi
|
|
sleep 1
|
|
done
|
|
"$LPADMIN" -p "$PRINTER_NAME" -E -v "ipp://localhost:$IPP_PORT/ipp/print" -m everywhere || true
|
|
"$LPADMIN" -d "$PRINTER_NAME" || true
|
|
# Diagnostics to aid debugging if the printer fails to register.
|
|
lpstat -t || true
|
|
cat /tmp/ippeveprinter.log || true
|
|
# Only hand the printer to the runner if it actually registered.
|
|
if lpstat -p "$PRINTER_NAME" >/dev/null 2>&1; then
|
|
echo "ELECTRON_TEST_PRINTER_NAME=$PRINTER_NAME" >> "$GITHUB_ENV"
|
|
echo "Provisioned CUPS printer: $PRINTER_NAME"
|
|
else
|
|
echo "Failed to provision CUPS printer; the print settings test will self-skip."
|
|
fi
|
|
- name: Add Depot Tools to PATH
|
|
run: echo "$(pwd)/depot_tools" >> $GITHUB_PATH
|
|
# The real mouse input specs (spec/fixtures/native-addon/mouse-input) load
|
|
# libXtst at runtime. The x64 build image ships it; the arm64 test image
|
|
# does not. Runs after the parallel group so it cannot race the CUPS
|
|
# step's apt-get for the dpkg lock.
|
|
- name: Install libXtst runtime (linux)
|
|
if: ${{ inputs.target-platform == 'linux' }}
|
|
run: |
|
|
if ldconfig -p | grep -q 'libXtst\.so\.6'; then
|
|
echo "libXtst.so.6 already present"
|
|
else
|
|
export DEBIAN_FRONTEND=noninteractive
|
|
apt-get update -y
|
|
apt-get install -y --no-install-recommends libxtst6
|
|
fi
|
|
- name: Restore Generated Artifacts
|
|
run: ./src/electron/script/actions/restore-artifacts.sh
|
|
- name: Unzip Dist (win)
|
|
if: ${{ inputs.target-platform == 'win' }}
|
|
shell: powershell
|
|
run: |
|
|
Set-ExecutionPolicy Bypass -Scope Process -Force
|
|
cd src/out/Default
|
|
Expand-Archive -Force dist.zip -DestinationPath ./
|
|
if (Test-Path xcache.zip) { Expand-Archive -Force xcache.zip -DestinationPath ./ }
|
|
- name: Unzip Dist (unix)
|
|
if: ${{ inputs.target-platform != 'win' }}
|
|
run: |
|
|
cd src/out/Default
|
|
unzip -:o dist.zip
|
|
if [ -f xcache.zip ]; then unzip -:o xcache.zip electron_xcache; fi
|
|
- name: Import & Trust Self-Signed Codesigning Cert on MacOS
|
|
if: ${{ inputs.target-platform == 'macos' }}
|
|
run: |
|
|
cd src/electron
|
|
./script/codesign/generate-identity.sh
|
|
# Sign with our self-signed cert so that macOS system integrations
|
|
# (UNNotifications, dock bounce, etc.) work in tests on both architectures.
|
|
# Autoupdater tests sign their own fixture copies via signApp().
|
|
- name: Sign Electron.app for macOS tests
|
|
if: ${{ inputs.target-platform == 'macos' }}
|
|
run: |
|
|
identity=$(src/electron/script/codesign/get-trusted-identity.sh)
|
|
if [ -n "$identity" ]; then
|
|
codesign -s "$identity" --deep --force src/out/Default/Electron.app
|
|
fi
|
|
|
|
- name: Register Electron.app with LaunchServices
|
|
if: ${{ inputs.target-platform == 'macos' }}
|
|
run: |
|
|
/System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister \
|
|
-f src/out/Default/Electron.app
|
|
|
|
# Some hosted macOS VMs deny screen capture and synthetic input to the job
|
|
# even though the image and the TCC step above grant them. Print what TCC
|
|
# needs to explain why (process chain, TCC.db rows, tccd attribution) and
|
|
# warn; this step never changes TCC state or fails the job.
|
|
- name: Check TCC access on macOS
|
|
if: ${{ inputs.target-platform == 'macos' }}
|
|
timeout-minutes: 5
|
|
continue-on-error: true
|
|
run: src/electron/script/actions/check-macos-tcc.sh
|
|
|
|
# On GitHub-hosted Windows runners the agent's terminal window sits in the
|
|
# foreground and covers most of the desktop. Windows' native occlusion
|
|
# tracking then marks test windows beneath it as hidden, so they stop
|
|
# producing frames. Minimize it before the specs run.
|
|
- name: Minimize Runner Terminal Window (Windows)
|
|
if: ${{ inputs.target-platform == 'win' }}
|
|
shell: pwsh
|
|
run: |
|
|
Add-Type -Namespace ElectronCI -Name User32 -MemberDefinition @'
|
|
[DllImport("user32.dll")] public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
|
|
[DllImport("user32.dll")] public static extern bool IsWindowVisible(IntPtr hWnd);
|
|
[DllImport("user32.dll")] public static extern bool IsIconic(IntPtr hWnd);
|
|
[DllImport("user32.dll", CharSet = CharSet.Unicode)] public static extern IntPtr FindWindowEx(IntPtr parent, IntPtr after, string cls, string title);
|
|
'@
|
|
foreach ($cls in 'ConsoleWindowClass', 'CASCADIA_HOSTING_WINDOW_CLASS') {
|
|
$h = [IntPtr]::Zero
|
|
while (($h = [ElectronCI.User32]::FindWindowEx([IntPtr]::Zero, $h, $cls, [NullString]::Value)) -ne [IntPtr]::Zero) {
|
|
if ([ElectronCI.User32]::IsWindowVisible($h) -and -not [ElectronCI.User32]::IsIconic($h)) {
|
|
# 6 = SW_MINIMIZE
|
|
[void][ElectronCI.User32]::ShowWindow($h, 6)
|
|
Write-Host ("Minimized {0} window 0x{1:X}" -f $cls, $h.ToInt64())
|
|
}
|
|
}
|
|
}
|
|
|
|
- name: Dismiss first sign-in windows (Windows)
|
|
if: ${{ inputs.target-platform == 'win' }}
|
|
timeout-minutes: 5
|
|
# Best effort: if the window stays up, the specs report what covers
|
|
# them (describeWindowAtPoint) rather than failing here.
|
|
continue-on-error: true
|
|
uses: ./src/electron/.github/actions/dismiss-windows-first-sign-in
|
|
with:
|
|
minimizer-seconds: 3600
|
|
|
|
- name: Run Electron Tests
|
|
shell: bash
|
|
timeout-minutes: 60
|
|
env:
|
|
ELECTRON_DISABLE_SECURITY_WARNINGS: 1
|
|
DISPLAY: ':99.0'
|
|
NPM_CONFIG_MSVS_VERSION: '2022'
|
|
TARGET_PLATFORM: ${{ inputs.target-platform }}
|
|
DISPLAY_SERVER: ${{ inputs.display-server }}
|
|
SHARD: ${{ matrix.shard }}
|
|
SHARD_COUNT: ${{ case(inputs.display-server == 'wayland', 1, env.MACOS_SPEC_ALLOWLIST != '', 1, inputs.target-platform == 'macos' && inputs.is-ubsan, 3, inputs.target-platform == 'linux', 3, inputs.target-platform == 'macos' && inputs.target-arch == 'x64', 3, 2) }}
|
|
run: |
|
|
cd src/electron
|
|
export ELECTRON_TEST_RESULTS_DIR=`pwd`/junit
|
|
# Get which tests are on this shard
|
|
tests_files=$(node script/split-tests "$SHARD" "$SHARD_COUNT")
|
|
# The Wayland leg and the reduced macOS legs run only the spec files
|
|
# named in an allowlist (one path per line, # comments ignored).
|
|
allowlist_file=""
|
|
if [ "$DISPLAY_SERVER" = "wayland" ]; then
|
|
allowlist_file=script/wayland-test-allowlist.txt
|
|
elif [ -n "$MACOS_SPEC_ALLOWLIST" ]; then
|
|
allowlist_file="$MACOS_SPEC_ALLOWLIST"
|
|
# A renamed or deleted spec would otherwise silently drop out of the
|
|
# reduced run.
|
|
while IFS= read -r allowed; do
|
|
case "$allowed" in ''|'#'*) continue ;; esac
|
|
if [ ! -f "$allowed" ]; then
|
|
echo "::error file=$allowlist_file::lists $allowed, which does not exist"
|
|
exit 1
|
|
fi
|
|
done < "$allowlist_file"
|
|
fi
|
|
if [ -n "$allowlist_file" ]; then
|
|
echo "Running only the spec files listed in $allowlist_file"
|
|
filtered_tests=""
|
|
for test_file in $tests_files; do
|
|
if grep -Fxq "$test_file" "$allowlist_file"; then
|
|
filtered_tests="$filtered_tests $test_file"
|
|
fi
|
|
done
|
|
tests_files="${filtered_tests# }"
|
|
|
|
if [ -z "$tests_files" ]; then
|
|
echo "No tests matched $allowlist_file, skipping."
|
|
exit 0
|
|
fi
|
|
fi
|
|
|
|
# Run tests
|
|
if [ "$TARGET_PLATFORM" != "linux" ]; then
|
|
echo "About to start tests"
|
|
LOGGING_ARGS=--enable-logging
|
|
if [ "$TARGET_PLATFORM" = "win" ]; then
|
|
# Child processes can't write to our stderr pipe on Windows, and
|
|
# with plain --enable-logging each one opens a console window that
|
|
# can cover the test windows. Log to a file (uploaded with the test
|
|
# artifacts) and mirror the main process to stderr.
|
|
export ELECTRON_ALSO_LOG_TO_STDERR=1
|
|
mkdir -p spec/artifacts
|
|
LOGGING_ARGS="--enable-logging=file --log-file=$(pwd -W)/spec/artifacts/electron_debug.log"
|
|
fi
|
|
if [ "$IS_UBSAN" = "true" ]; then
|
|
export UBSAN_OPTIONS="print_stacktrace=1:halt_on_error=1:external_symbolizer_path=$PWD/../third_party/llvm-build/Release+Asserts/bin/llvm-symbolizer"
|
|
export ELECTRON_SPEC_TIMEOUT=180000
|
|
fi
|
|
node script/yarn.js test --runners=main --trace-uncaught $LOGGING_ARGS --files $tests_files
|
|
else
|
|
chown :builduser .. && chmod g+w ..
|
|
chown -R :builduser . && chmod -R g+w .
|
|
chmod 4755 ../out/Default/chrome-sandbox
|
|
runuser -u builduser -- git config --global --add safe.directory $(pwd)
|
|
if [ "$IS_ASAN" == "true" ]; then
|
|
cd ..
|
|
ASAN_SYMBOLIZE="$PWD/tools/valgrind/asan/asan_symbolize.py --executable-path=$PWD/out/Default/electron"
|
|
# symbolize=1 is required for leak suppressions to work: LSan can
|
|
# only match "leak:" entries against function and file names, and
|
|
# with symbolization off it has neither, which silently disables
|
|
# both Chromium's built-in list and lsan_suppressions.txt.
|
|
export ASAN_OPTIONS="detect_leaks=1 handle_abort=1 symbolize=1 strip_path_prefix=/../../ external_symbolizer_path=$PWD/third_party/llvm-build/Release+Asserts/bin/llvm-symbolizer"
|
|
export LSAN_OPTIONS="suppressions=$PWD/electron/build/lsan_suppressions.txt"
|
|
export G_SLICE=always-malloc
|
|
export NSS_DISABLE_ARENA_FREE_LIST=1
|
|
export NSS_DISABLE_UNLOAD=1
|
|
export LLVM_SYMBOLIZER_PATH=$PWD/third_party/llvm-build/Release+Asserts/bin/llvm-symbolizer
|
|
export ELECTRON_SPEC_TIMEOUT=180000
|
|
echo "Piping output to ASAN_SYMBOLIZE ($ASAN_SYMBOLIZE)"
|
|
cd electron
|
|
runuser -u builduser -- xvfb-run script/actions/run-tests.sh script/yarn.js test --runners=main --trace-uncaught --enable-logging --files $tests_files | $ASAN_SYMBOLIZE
|
|
elif [ "$IS_UBSAN" == "true" ]; then
|
|
cd ..
|
|
export UBSAN_OPTIONS="print_stacktrace=1:halt_on_error=1:external_symbolizer_path=$PWD/third_party/llvm-build/Release+Asserts/bin/llvm-symbolizer"
|
|
export ELECTRON_SPEC_TIMEOUT=180000
|
|
cd electron
|
|
runuser -u builduser -- xvfb-run script/actions/run-tests.sh script/yarn.js test --runners=main --trace-uncaught --enable-logging --files $tests_files
|
|
else
|
|
if [ "$DISPLAY_SERVER" = "wayland" ]; then
|
|
runuser -u builduser -- script/actions/run-tests-wayland.sh script/yarn.js test --runners=main --trace-uncaught --enable-logging --files $tests_files
|
|
else
|
|
runuser -u builduser -- xvfb-run script/actions/run-tests.sh script/yarn.js test --runners=main --trace-uncaught --enable-logging --files $tests_files
|
|
fi
|
|
fi
|
|
fi
|
|
- name: Print minimized WSL terminals (Windows)
|
|
if: ${{ always() && inputs.target-platform == 'win' }}
|
|
continue-on-error: true
|
|
shell: pwsh
|
|
run: |
|
|
$log = Join-Path $env:RUNNER_TEMP 'minimize-wsl-terminals.log'
|
|
foreach ($f in "$log.err", "$log.out", $log) {
|
|
if (Test-Path $f) { Write-Host "--- $f"; Get-Content $f | Write-Host }
|
|
}
|
|
- name: Verify custom mksnapshot blobs reach the main process
|
|
# electron-mksnapshot / LoadBrowserProcessSpecificV8Snapshot users replace
|
|
# the V8 snapshot blobs; check on one native leg that a build's own
|
|
# mksnapshot output is still picked up by the main process (the embedded
|
|
# Node startup snapshot must step aside for it). The build leg only
|
|
# exercises --create-snapshot-only.
|
|
if: ${{ inputs.target-platform == 'linux' && inputs.target-arch == 'x64' && inputs.is-asan != true && inputs.display-server != 'wayland' && matrix.shard == 1 }}
|
|
shell: bash
|
|
run: |
|
|
cd src
|
|
(cd out/Default && unzip -:o mksnapshot.zip)
|
|
chown -R :builduser out && chmod -R g+w out
|
|
runuser -u builduser -- xvfb-run python3 electron/script/verify-mksnapshot.py --source-root "$PWD" --build-dir out/Default
|
|
- name: Take screenshot on timeout or cancellation
|
|
if: ${{ inputs.target-platform != 'linux' && (cancelled() || failure()) }}
|
|
shell: bash
|
|
env:
|
|
TARGET_PLATFORM: ${{ inputs.target-platform }}
|
|
run: |
|
|
screenshot_dir="src/electron/spec/artifacts"
|
|
mkdir -p "$screenshot_dir"
|
|
screenshot_file="$screenshot_dir/screenshot-timeout-$(date +%Y%m%d%H%M%S).png"
|
|
if [ "$TARGET_PLATFORM" = "macos" ]; then
|
|
screencapture -x "$screenshot_file" || true
|
|
elif [ "$TARGET_PLATFORM" = "win" ]; then
|
|
powershell -command "Add-Type -AssemblyName System.Windows.Forms; \$screen = [System.Windows.Forms.Screen]::PrimaryScreen.Bounds; \$bitmap = New-Object System.Drawing.Bitmap(\$screen.Width, \$screen.Height); \$graphics = [System.Drawing.Graphics]::FromImage(\$bitmap); \$graphics.CopyFromScreen(\$screen.Location, [System.Drawing.Point]::Empty, \$screen.Size); \$bitmap.Save('$screenshot_file')" || true
|
|
fi
|
|
|
|
- name: Upload Test results to Datadog
|
|
env:
|
|
DD_ENV: ci
|
|
DD_SERVICE: electron
|
|
DD_API_KEY: ${{ secrets.DD_API_KEY }}
|
|
DD_CIVISIBILITY_LOGS_ENABLED: true
|
|
DD_TAGS: "os.architecture:${{ inputs.target-arch }},os.family:${{ inputs.target-platform }},os.platform:${{ inputs.target-platform }},asan:${{ inputs.is-asan }},ubsan:${{ inputs.is-ubsan }}"
|
|
run: |
|
|
if ! [ -z $DD_API_KEY ] && [ -f src/electron/junit/test-results-main.xml ]; then
|
|
cd src/electron
|
|
export DATADOG_PATH=`node script/yarn.js bin datadog-ci`
|
|
$DATADOG_PATH junit upload junit/test-results-main.xml
|
|
fi
|
|
if: always() && !cancelled()
|
|
- name: Upload Test Artifacts
|
|
if: always()
|
|
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 #v7.0.2
|
|
with:
|
|
name: ${{ inputs.target-platform == 'linux' && format('test_artifacts_{0}_{1}_{2}', env.ARTIFACT_KEY, inputs.display-server, matrix.shard) || format('test_artifacts_{0}_{1}', env.ARTIFACT_KEY, matrix.shard) }}
|
|
path: src/electron/spec/artifacts
|
|
if-no-files-found: ignore
|
|
- name: Wait for active SSH sessions
|
|
if: always() && !cancelled()
|
|
shell: bash
|
|
run: |
|
|
while [ -f /var/.ssh-lock ]
|
|
do
|
|
sleep 60
|
|
done
|