Files
electron/.github/workflows/pipeline-segment-electron-test.yml
T
claude[bot]andClaude 96a2dc1f28 ci: print TCC diagnostics when macOS runner lacks capture/input access (#55237)
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>
2026-10-24 07:16:37 +00:00

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