Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit d9beaa9

Browse files
committedMar 21, 2025
ci: move to GitHub Actions
WORK IN PROGRESS Change-Id: I671dbbcbbe9d00a3ebf6f74edb01143fb2b05855
1 parent 64562d6 commit d9beaa9

19 files changed

+451
-716
lines changed
 

‎.github/workflows/test_runner.yml

+151
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
name: Perfetto CI
2+
on:
3+
# 1. continuous
4+
schedule:
5+
# Run every 6 hours
6+
- cron: "0 */6 * * *"
7+
8+
# 2. postsubmit
9+
push:
10+
branches:
11+
- main
12+
- test
13+
14+
# 3. presubmits
15+
pull_request:
16+
types: [opened, synchronize]
17+
branches:
18+
- main
19+
- test
20+
21+
22+
jobs:
23+
test:
24+
runs-on: self-hosted
25+
timeout-minutes: 45
26+
strategy:
27+
matrix:
28+
config:
29+
# - name: linux-clang-x86_64-debug
30+
# PERFETTO_TEST_GN_ARGS: 'is_debug=true is_hermetic_clang=false non_hermetic_clang_stdlib="libc++" enable_perfetto_merged_protos_check=true'
31+
# PERFETTO_TEST_SCRIPT: 'test/ci/linux_tests.sh'
32+
# PERFETTO_INSTALL_BUILD_DEPS_ARGS: ''
33+
# - name: linux-clang-x86_64-tsan
34+
# PERFETTO_TEST_GN_ARGS: 'is_debug=false is_tsan=true'
35+
# PERFETTO_TEST_SCRIPT: 'test/ci/linux_tests.sh'
36+
# PERFETTO_INSTALL_BUILD_DEPS_ARGS: ''
37+
# - name: linux-clang-x86_64-msan
38+
# PERFETTO_TEST_GN_ARGS: 'is_debug=false is_msan=true'
39+
# PERFETTO_TEST_SCRIPT: 'test/ci/linux_tests.sh'
40+
# PERFETTO_INSTALL_BUILD_DEPS_ARGS: ''
41+
# - name: linux-clang-x86_64-asan_lsan
42+
# PERFETTO_TEST_GN_ARGS: 'is_debug=false is_asan=true is_lsan=true'
43+
# PERFETTO_TEST_SCRIPT: 'test/ci/linux_tests.sh'
44+
# PERFETTO_INSTALL_BUILD_DEPS_ARGS: ''
45+
# - name: linux-clang-x86-release
46+
# PERFETTO_TEST_GN_ARGS: 'is_debug=false target_cpu="x86"'
47+
# PERFETTO_TEST_SCRIPT: 'test/ci/linux_tests.sh'
48+
# PERFETTO_INSTALL_BUILD_DEPS_ARGS: ''
49+
# - name: linux-gcc8-x86_64-release
50+
# PERFETTO_TEST_GN_ARGS: 'is_debug=false is_clang=false enable_perfetto_grpc=true cc="gcc-8" cxx="g++-8"'
51+
# PERFETTO_TEST_SCRIPT: 'test/ci/linux_tests.sh'
52+
# PERFETTO_INSTALL_BUILD_DEPS_ARGS: '--grpc'
53+
# - name: android-clang-arm-release
54+
# PERFETTO_TEST_GN_ARGS: 'is_debug=false target_os="android" target_cpu="arm"'
55+
# PERFETTO_TEST_SCRIPT: 'test/ci/android_tests.sh'
56+
# PERFETTO_INSTALL_BUILD_DEPS_ARGS: '--android'
57+
# - name: linux-clang-x86_64-libfuzzer
58+
# PERFETTO_TEST_GN_ARGS: 'is_debug=false is_fuzzer=true is_asan=true'
59+
# PERFETTO_TEST_SCRIPT: 'test/ci/fuzzer_tests.sh'
60+
# PERFETTO_INSTALL_BUILD_DEPS_ARGS: ''
61+
- name: linux-clang-x86_64-bazel
62+
PERFETTO_TEST_GN_ARGS: ''
63+
PERFETTO_TEST_SCRIPT: 'test/ci/bazel_tests.sh'
64+
PERFETTO_INSTALL_BUILD_DEPS_ARGS: '--bazel'
65+
- name: ui-clang-x86_64-release
66+
PERFETTO_TEST_GN_ARGS: 'is_debug=false'
67+
PERFETTO_TEST_SCRIPT: 'test/ci/ui_tests.sh'
68+
PERFETTO_INSTALL_BUILD_DEPS_ARGS: '--ui'
69+
70+
env:
71+
# /tmp/cache contains {ccache, bazelcache} and generally any other cache
72+
# that should be persisted across jobs, but only updated from the main
73+
# branch. This is populated by the "actions/cache/restore" step below.
74+
# TODO here check this between docker and this. DNS
75+
PERFETTO_CACHE_DIR: /ci/ramdisk
76+
PERFETTO_TEST_GN_ARGS: ${{ matrix.config.PERFETTO_TEST_GN_ARGS }}
77+
PERFETTO_TEST_SCRIPT: ${{ matrix.config.PERFETTO_TEST_SCRIPT }}
78+
PERFETTO_INSTALL_BUILD_DEPS_ARGS: ${{ matrix.config.PERFETTO_INSTALL_BUILD_DEPS_ARGS }}
79+
PERFETTO_TEST_JOB: gh-${{ env.GITHUB_RUN_ID }}-${{ matrix.config.name }}
80+
PERFETTO_TEST_NINJA_ARGS: # Deliberately empty, set in some other env
81+
steps:
82+
- name: Set up artifacts
83+
# TODO un-hardcode /ci/artifacts, pass through PERFETTO_ARTIFACTS_DIR
84+
run: |
85+
echo "PERFETTO_ARTIFACTS_DIR=/ci/artifacts/$PERFETTO_TEST_JOB" >> $GITHUB_ENV
86+
echo "Job id ${{ github.run_id }}-${{ github.job }}"
87+
88+
- name: Set up tmpfs dirs
89+
run: |
90+
mkdir -p "${{ env.PERFETTO_CACHE_DIR }}"
91+
mkdir -p "${{ env.PERFETTO_ARTIFACTS_DIR }}"
92+
93+
# Check out code.
94+
- uses: actions/checkout@v4
95+
with:
96+
fetch-depth: 2
97+
98+
- name: Set up cache
99+
# By default ccache uses the mtime of the compiler. This doesn't work
100+
# because our compilers are hermetic and their mtime is the time when we
101+
# run install-build-deps. Given that the toolchain is rolled via
102+
# install-build-deps we use that file as an identity function for the
103+
# compiler check.
104+
run: |
105+
mkdir -p "${{ env.PERFETTO_CACHE_DIR }}/ccache"
106+
DEPS_SHA=$(shasum "tools/install-build-deps" | awk '{print $1}')
107+
echo "DEPS_SHA=$DEPS_SHA" >> $GITHUB_ENV
108+
echo "CCACHE_COMPILERCHECK=string:$DEPS_SHA" >> $GITHUB_ENV
109+
echo "CCACHE_BASEDIR=${{ github.workspace }}" >> $GITHUB_ENV
110+
echo "CCACHE_DIR=${{ env.PERFETTO_CACHE_DIR }}/ccache" >> $GITHUB_ENV
111+
echo "CCACHE_MAXSIZE=8G" >> $GITHUB_ENV
112+
echo "CCACHE_SLOPPINESS=include_file_ctime,include_file_mtime" >> $GITHUB_ENV
113+
echo "CCACHE_NOCOMPRESS=1" >> $GITHUB_ENV
114+
echo "CCACHE_COMPILERCHECK=string:$(shasum tools/install-build-deps)" >> $GITHUB_ENV
115+
echo "CCACHE_UMASK=000" >> $GITHUB_ENV
116+
echo "CCACHE_DEPEND=1" >> $GITHUB_ENV
117+
118+
# TODO remove this
119+
- name: Print env vars
120+
run: pwd; ls -la; env;
121+
122+
- name: Restore cache and buildtools from GitHub cache
123+
uses: actions/cache/restore@v4
124+
with:
125+
path: |
126+
${{ env.PERFETTO_CACHE_DIR }}
127+
buildtools
128+
key: cache-${{ matrix.config.name }}-${{ env.DEPS_SHA }}
129+
130+
# TODO remove the two entries below, debug only.
131+
- name: ls buildtools
132+
run: ls -la buildtools/
133+
134+
- name: ls cache
135+
run: ls -laR ${{ env.PERFETTO_CACHE_DIR }}
136+
137+
- name: Build and test
138+
run: ${{ env.PERFETTO_TEST_SCRIPT }}
139+
shell: bash
140+
141+
- name: ccache stats
142+
run: ccache --show-stats
143+
144+
- name: Update cache (if on main)
145+
if: github.ref == 'refs/heads/main'
146+
uses: actions/cache/save@v4
147+
with:
148+
path: |
149+
${{ env.PERFETTO_CACHE_DIR }}
150+
buildtools
151+
key: cache-${{ matrix.config.name }}-${{ env.DEPS_SHA }}

‎docs/design-docs/continuous-integration.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -272,7 +272,7 @@ docker run worker-N ...
272272
- It streams the container stdout/stderr to the DB.
273273
- It upload the build artifacts to GCS.
274274

275-
### [testrunner.sh](/infra/ci/sandbox/testrunner.sh)
275+
### testrunner.sh
276276

277277
- It is pinned in the container image. Does NOT depend on the particular
278278
revision being tested.

‎infra/ci/Makefile

+5-6
Original file line numberDiff line numberDiff line change
@@ -77,11 +77,10 @@ clean:
7777
--maintenance-policy=MIGRATE \
7878
--service-account=gce-ci-worker@${PROJECT}.iam.gserviceaccount.com \
7979
--scopes=${GCE_SCOPES} \
80-
--image=cos-85-13310-1209-10 \
81-
--image-project=cos-cloud \
80+
--image=projects/cos-cloud/global/images/cos-117-18613-164-81 \
8281
--boot-disk-size=100GB \
8382
--boot-disk-type=pd-ssd \
84-
--boot-disk-device-name=ci-worker-template \
83+
--boot-disk-device-name=${GCE_TEMPLATE} \
8584
--local-ssd=interface=NVME \
8685
--local-ssd=interface=NVME
8786
touch $@
@@ -102,7 +101,7 @@ gcloud compute --project=${PROJECT} \
102101
instance-groups managed create ${GCE_GROUP_NAME}-$1 \
103102
--region=$1 \
104103
--base-instance-name=ci-$1 \
105-
--template=ci-worker-template \
104+
--template=${GCE_TEMPLATE} \
106105
--size=1
107106
gcloud compute --quiet --project=$(PROJECT) \
108107
instance-groups managed set-autoscaling ${GCE_GROUP_NAME}-$1 \
@@ -137,14 +136,14 @@ stop-worker-for-testing:
137136
gcloud compute --quiet \
138137
--project ${PROJECT} \
139138
instances delete ${GCE_VM_NAME} \
140-
--zone us-central1-f
139+
--zone us-west1-c
141140

142141
.PHONY: start-worker-for-testing
143142
start-worker-for-testing: .deps/gce-template
144143
gcloud compute --quiet \
145144
--project ${PROJECT} \
146145
instances create ${GCE_VM_NAME} \
147-
--zone us-central1-f \
146+
--zone us-west1-c \
148147
--source-instance-template=${GCE_TEMPLATE}
149148

150149
# Debugging client to make OAuth2 authenticated requests manually.

‎infra/ci/config.py

+16-65
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,20 @@
3030
GERRIT_VOTING_ENABLED = True
3131
LOGLEVEL = 'info'
3232

33+
34+
# IDs for the Perfetto CI GitHub app.
35+
GITHUB_REPO = 'google/perfetto'
36+
GITHUB_APP_ID = 1184402
37+
GITHUB_APP_INSTALLATION_ID = 62928975
38+
3339
# Cloud config (GCE = Google Compute Engine, GAE = Google App Engine)
3440
PROJECT = 'perfetto-ci'
3541

3642
GAE_VERSION = 'prod'
3743
DB_ROOT = 'https://%s.firebaseio.com' % PROJECT
3844
DB = DB_ROOT + '/ci'
39-
SANDBOX_IMG = 'us-docker.pkg.dev/%s/containers/sandbox' % PROJECT
40-
WORKER_IMG = 'us-docker.pkg.dev/%s/containers/worker' % PROJECT
45+
SANDBOX_IMG = 'us-docker.pkg.dev/%s/containers/gh-sandbox' % PROJECT
46+
WORKER_IMG = 'us-docker.pkg.dev/%s/containers/gh-worker' % PROJECT
4147
CI_SITE = 'https://ci.perfetto.dev'
4248
GCS_ARTIFACTS = 'perfetto-ci-artifacts'
4349

@@ -47,11 +53,12 @@
4753
TRUSTED_EMAILS = '^.*@google.com$'
4854

4955
GCE_REGIONS = 'us-west1'
50-
GCE_VM_NAME = 'ci-worker'
56+
GCE_VM_NAME = 'gh-worker'
5157
GCE_VM_TYPE = 'c2d-standard-32'
52-
GCE_TEMPLATE = 'ci-worker-template'
58+
GCE_TEMPLATE = 'gh-worker-template'
5359
GCE_GROUP_NAME = 'ci'
54-
MAX_VMS_PER_REGION = 8
60+
# MAX_VMS_PER_REGION = 8 TODO restore to 8 after killing old CI
61+
MAX_VMS_PER_REGION = 1
5562
NUM_WORKERS_PER_VM = 4
5663

5764
GCE_SCOPES = [
@@ -64,66 +71,10 @@
6471
'https://www.googleapis.com/auth/userinfo.email',
6572
]
6673

67-
# Only variables starting with PERFETTO_ are propagated into the sandbox.
68-
JOB_CONFIGS = {
69-
'linux-clang-x86_64-debug': {
70-
'PERFETTO_TEST_GN_ARGS': 'is_debug=true is_hermetic_clang=false '
71-
'non_hermetic_clang_stdlib="libc++" '
72-
'enable_perfetto_merged_protos_check=true',
73-
'PERFETTO_TEST_SCRIPT': 'test/ci/linux_tests.sh',
74-
'PERFETTO_INSTALL_BUILD_DEPS_ARGS': '',
75-
},
76-
'linux-clang-x86_64-tsan': {
77-
'PERFETTO_TEST_GN_ARGS': 'is_debug=false is_tsan=true',
78-
'PERFETTO_TEST_SCRIPT': 'test/ci/linux_tests.sh',
79-
'PERFETTO_INSTALL_BUILD_DEPS_ARGS': '',
80-
},
81-
'linux-clang-x86_64-msan': {
82-
'PERFETTO_TEST_GN_ARGS': 'is_debug=false is_msan=true',
83-
'PERFETTO_TEST_SCRIPT': 'test/ci/linux_tests.sh',
84-
'PERFETTO_INSTALL_BUILD_DEPS_ARGS': '',
85-
},
86-
'linux-clang-x86_64-asan_lsan': {
87-
'PERFETTO_TEST_GN_ARGS': 'is_debug=false is_asan=true is_lsan=true',
88-
'PERFETTO_TEST_SCRIPT': 'test/ci/linux_tests.sh',
89-
'PERFETTO_INSTALL_BUILD_DEPS_ARGS': '',
90-
},
91-
'linux-clang-x86-release': {
92-
'PERFETTO_TEST_GN_ARGS': 'is_debug=false target_cpu="x86"',
93-
'PERFETTO_TEST_SCRIPT': 'test/ci/linux_tests.sh',
94-
'PERFETTO_INSTALL_BUILD_DEPS_ARGS': '',
95-
},
96-
'linux-gcc8-x86_64-release': {
97-
'PERFETTO_TEST_GN_ARGS':
98-
'is_debug=false is_clang=false enable_perfetto_grpc=true '
99-
'cc="gcc-8" cxx="g++-8"',
100-
'PERFETTO_TEST_SCRIPT': 'test/ci/linux_tests.sh',
101-
'PERFETTO_INSTALL_BUILD_DEPS_ARGS': '--grpc',
102-
},
103-
'android-clang-arm-release': {
104-
'PERFETTO_TEST_GN_ARGS':
105-
'is_debug=false target_os="android" target_cpu="arm"',
106-
'PERFETTO_TEST_SCRIPT':
107-
'test/ci/android_tests.sh',
108-
'PERFETTO_INSTALL_BUILD_DEPS_ARGS':
109-
'--android',
110-
},
111-
'linux-clang-x86_64-libfuzzer': {
112-
'PERFETTO_TEST_GN_ARGS': 'is_debug=false is_fuzzer=true is_asan=true',
113-
'PERFETTO_TEST_SCRIPT': 'test/ci/fuzzer_tests.sh',
114-
'PERFETTO_INSTALL_BUILD_DEPS_ARGS': '',
115-
},
116-
'linux-clang-x86_64-bazel': {
117-
'PERFETTO_TEST_GN_ARGS': '',
118-
'PERFETTO_TEST_SCRIPT': 'test/ci/bazel_tests.sh',
119-
'PERFETTO_INSTALL_BUILD_DEPS_ARGS': '--bazel',
120-
},
121-
'ui-clang-x86_64-release': {
122-
'PERFETTO_TEST_GN_ARGS': 'is_debug=false',
123-
'PERFETTO_TEST_SCRIPT': 'test/ci/ui_tests.sh',
124-
'PERFETTO_INSTALL_BUILD_DEPS_ARGS': '--ui',
125-
},
126-
}
74+
SANDBOX_SVC_ACCOUNT = 'gce-ci-sandbox@perfetto-ci.iam.gserviceaccount.com'
75+
76+
# TODO remove this
77+
JOB_CONFIGS = {}
12778

12879
if __name__ == '__main__':
12980
import os

‎infra/ci/sandbox/Dockerfile

+26-14
Original file line numberDiff line numberDiff line change
@@ -30,16 +30,12 @@ RUN set -ex; \
3030
openjdk-11-jdk; \
3131
apt-get -y install libc++-8-dev libc++abi-8-dev clang-8; \
3232
update-alternatives --install /usr/bin/python python /usr/bin/python3.7 1; \
33+
pip3 install protobuf pandas grpcio; \
3334
gcc-8 --version; \
3435
g++-8 --version; \
3536
clang-8 --version; \
3637
clang++-8 --version; \
37-
java --version; \
38-
pip3 install protobuf pandas grpcio; \
39-
groupadd -g 1337 perfetto; \
40-
useradd -d /ci/ramdisk -u 1337 -g perfetto perfetto; \
41-
apt-get -y autoremove; \
42-
rm -rf /var/lib/apt/lists/* /usr/share/man/* /usr/share/doc/*;
38+
java --version;
4339

4440
# Chrome/puppeteer deps.
4541
RUN set -ex; \
@@ -55,16 +51,32 @@ RUN set -ex; \
5551
xdg-utils fonts-liberation fonts-ipafont-gothic fonts-wqy-zenhei \
5652
fonts-thai-tlwg fonts-kacst fonts-freefont-ttf
5753

58-
# Cleanup to reduce image size
59-
RUN apt-get -y autoremove; \
60-
rm -rf /var/lib/apt/lists/* /usr/share/man/* /usr/share/doc/*; \
61-
rm -rf /root/.cache/;
54+
RUN set -ex; \
55+
export DEBIAN_FRONTEND=noninteractive; \
56+
apt-get -y install jq python3-jwt python3-requests
57+
6258

63-
COPY testrunner.sh /ci/testrunner.sh
64-
COPY init.sh /ci/init.sh
59+
RUN set -ex; \
60+
mkdir -p /ci; \
61+
groupadd -g 1001 perfetto; \
62+
useradd -m -d /ci/ramdisk -u 1001 -g perfetto perfetto; \
63+
apt-get -y autoremove; \
64+
rm -rf /var/lib/apt/lists/* /usr/share/man/* /usr/share/doc/*; \
65+
rm -rf /var/lib/apt/lists/* /usr/share/man/* /usr/share/doc/*; \
66+
rm -rf /root/.cache/;
67+
68+
COPY sandbox.sh /ci/sandbox.sh
6569
RUN chmod -R a+rx /ci/
6670

71+
# Download GitHub Actions runner
72+
RUN set -ex; \
73+
mkdir /opt/github-action-runner; \
74+
cd /opt/github-action-runner; \
75+
RUNNER_URL=$(curl -s https://api.github.com/repos/actions/runner/releases/latest | jq -r .assets[].browser_download_url | grep "linux-x64"); \
76+
curl -o actions-runner.tar.gz -L "$RUNNER_URL"; \
77+
tar xzf actions-runner.tar.gz; \
78+
rm -f actions-runner.tar.gz;
79+
6780
VOLUME [ "/ci/cache", "/ci/ramdisk", "/ci/artifacts" ]
6881
ENTRYPOINT [ "tini", "-g", "--" ]
69-
CMD [ "bash", "/ci/init.sh" ]
70-
82+
CMD [ "bash", "/ci/sandbox.sh" ]

‎infra/ci/sandbox/init.sh ‎infra/ci/sandbox/sandbox.sh

+20-3
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,28 @@
1313
# See the License for the specific language governing permissions and
1414
# limitations under the License.
1515

16-
# Performs initialization that requires root, then runs the test as non-root.
16+
# Performs initialization that requires root, then runs the GitHub action runner
17+
# as non-root.
1718

18-
set -eux
19+
set -eu
1920
chmod 777 /ci/cache /ci/artifacts
2021
chown perfetto.perfetto /ci/ramdisk
2122
cd /ci/ramdisk
2223
ls -A1 | xargs rm -rf
23-
exec sudo -u perfetto -g perfetto -EH bash /ci/testrunner.sh
24+
25+
cat << EOF > /ci/run.sh
26+
cd /opt/github-action-runner
27+
28+
# Configure the runner (replace with org or repo URL as required)
29+
./config.sh --unattended --ephemeral --replace \
30+
--url "https://github.com/$GITHUB_REPO" \
31+
--token "$GITHUB_TOKEN"
32+
33+
# Run the GitHub Action Runner
34+
GITHUB_TOKEN="" ./run.sh
35+
36+
EOF
37+
38+
chmod 755 /ci/run.sh
39+
40+
exec sudo -u perfetto -g perfetto -EH /ci/run.sh

‎infra/ci/sandbox/testrunner.sh

-99
This file was deleted.

‎infra/ci/worker/Dockerfile

+38-20
Original file line numberDiff line numberDiff line change
@@ -12,36 +12,54 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15-
# docker:stable is an Alpine-based distro.
16-
FROM docker:stable
15+
FROM debian:bookworm
1716

18-
RUN apk update && apk add python3 py-pip sudo tini
19-
RUN pip3 install oauth2client httplib2 google-auth google-cloud requests;
17+
RUN set -ex; \
18+
export DEBIAN_FRONTEND=noninteractive; \
19+
apt-get update; \
20+
apt-get -y install ca-certificates curl gnupg lsb-release sudo tini \
21+
python3 python3-jwt python3-requests python3-oauth2client \
22+
python3-httplib2 python3-google-auth python3-google-auth-oauthlib \
23+
python3-googleapi
2024

25+
# Install docker
26+
RUN curl -fsSL https://download.docker.com/linux/debian/gpg | \
27+
gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg; \
28+
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] \
29+
https://download.docker.com/linux/debian $(lsb_release -cs) stable" | \
30+
tee /etc/apt/sources.list.d/docker.list > /dev/null; \
31+
apt-get update && apt-get install -y docker-ce docker-ce-cli containerd.io
32+
33+
34+
# Install gcloud
35+
RUN curl -fsSL https://packages.cloud.google.com/apt/doc/apt-key.gpg | \
36+
gpg --dearmor -o /usr/share/keyrings/cloud.google.gpg; \
37+
echo "deb [signed-by=/usr/share/keyrings/cloud.google.gpg] https://packages.cloud.google.com/apt cloud-sdk main" \
38+
| tee -a /etc/apt/sources.list.d/google-cloud-sdk.list; \
39+
apt-get update && apt-get install -y google-cloud-cli
2140

2241
# Unfortunately Docker doesn't allow to copy a file from ../. So we copy instead
2342
# the config files into tmp/ from the Makefile that runs docker build.
24-
COPY tmp/config.py /home/perfetto/config.py
25-
COPY tmp/common_utils.py /home/perfetto/common_utils.py
26-
COPY artifacts_uploader.py /home/perfetto/
27-
COPY perf_metrics_uploader.py /home/perfetto/
28-
COPY run_job.py /home/perfetto/
29-
COPY worker.py /home/perfetto/
43+
COPY tmp/config.py /home/worker/config.py
44+
COPY tmp/common_utils.py /home/worker/common_utils.py
45+
COPY artifacts_uploader.py /home/worker/
46+
COPY worker.py /home/worker/
47+
COPY get_github_token.py /home/worker/
3048

3149
# Allow the worker to spawn new docker containers (for the jobs' sandboxes).
32-
# This makes the worker container highly priviledged (effectiveely can run any
50+
# This makes the worker container highly priviledged (effectiveely can run any
3351
# commands on the GCE vm). The worker container is trusted and must never run
34-
# code from a tryjob (which instead is run into the sandbox containers).
52+
# code from a GitHub action (which instead is run into the sandbox containers).
3553
RUN set -e; \
3654
echo 'root ALL=(ALL) ALL' /etc/sudoers; \
37-
echo 'perfetto ALL=(ALL) NOPASSWD: ALL' >> /etc/sudoers; \
38-
addgroup -S --gid 1337 perfetto; \
39-
adduser -S --uid 1337 -h /home/perfetto perfetto perfetto; \
40-
chown perfetto.perfetto -R /home/perfetto; \
41-
chmod -R 755 /home/perfetto;
55+
echo 'worker ALL=(ALL) NOPASSWD: ALL' >> /etc/sudoers; \
56+
groupadd -g 1091 worker; \
57+
useradd -d /home/worker -u 1091 -g worker worker; \
58+
chown worker:worker -R /home/worker; \
59+
chmod -R 755 /home/worker;
4260

43-
USER perfetto:perfetto
44-
WORKDIR /home/perfetto
61+
USER worker:worker
62+
WORKDIR /home/worker
4563

4664
ENTRYPOINT [ "tini", "--" ]
47-
CMD [ "python3", "/home/perfetto/worker.py" ]
65+
CMD [ "python3", "/home/worker/worker.py" ]

‎infra/ci/worker/artifacts_uploader.py

+10-21
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,9 @@ def get_http_obj():
4949
return tls.http
5050

5151

52-
def upload_one_file(fpath):
52+
def upload_one_file(fpath, base):
5353
http = get_http_obj()
54-
relpath = os.path.relpath(fpath, os.getenv('ARTIFACTS_DIR'))
54+
relpath = os.path.relpath(fpath, base)
5555
logging.debug('Uploading %s', relpath)
5656
assert (os.path.exists(fpath))
5757
fsize = os.path.getsize(fpath)
@@ -71,9 +71,9 @@ def upload_one_file(fpath):
7171
return fsize
7272

7373

74-
def upload_one_file_with_retries(fpath):
74+
def upload_one_file_with_retries(fpath, base):
7575
for retry in [0.5, 1.5, 3]:
76-
res = upload_one_file(fpath)
76+
res = upload_one_file(fpath, base)
7777
if res >= 0:
7878
return res
7979
logging.warning('Upload of %s failed, retrying in %s seconds', fpath, retry)
@@ -88,15 +88,6 @@ def list_files(path):
8888
yield fpath
8989

9090

91-
def scan_and_upload_perf_folder(job_id, dirpath):
92-
perf_folder = os.path.join(dirpath, 'perf')
93-
if not os.path.isdir(perf_folder):
94-
return
95-
uploader = os.path.join(CUR_DIR, 'perf_metrics_uploader.py')
96-
for path in list_files(perf_folder):
97-
subprocess.call([uploader, '--job-id', job_id, path])
98-
99-
10091
def main():
10192
init_logging()
10293
signal.alarm(WATCHDOG_SEC)
@@ -105,13 +96,12 @@ def main():
10596
parser = argparse.ArgumentParser()
10697
parser.add_argument('--rm', action='store_true', help='Removes the directory')
10798
parser.add_argument(
108-
'--job-id',
99+
'--dir',
109100
type=str,
110101
required=True,
111-
help='The Perfetto CI job ID to tie this upload to')
102+
help='The directory containing the artifacts')
112103
args = parser.parse_args()
113-
job_id = args.job_id
114-
dirpath = os.path.join(os.getenv('ARTIFACTS_DIR', default=os.curdir), job_id)
104+
dirpath = args.dir
115105
if not os.path.isdir(dirpath):
116106
logging.error('Directory not found: %s', dirpath)
117107
return 1
@@ -126,15 +116,14 @@ def main():
126116
failures = 0
127117
files = list_files(dirpath)
128118
pool = ThreadPool(processes=10)
129-
for upl_size in pool.imap_unordered(upload_one_file_with_retries, files):
119+
upload_fn = lambda x: upload_one_file_with_retries(x, dirpath)
120+
for upl_size in pool.imap_unordered(upload_fn, files):
130121
uploads += 1 if upl_size >= 0 else 0
131122
failures += 1 if upl_size < 0 else 0
132123
total_size += max(upl_size, 0)
133124

134125
logging.info('Uploaded artifacts for %s: %d files, %s failures, %d KB',
135-
job_id, uploads, failures, total_size / 1e3)
136-
137-
scan_and_upload_perf_folder(job_id, dirpath)
126+
dirpath, uploads, failures, total_size / 1e3)
138127

139128
if args.rm:
140129
subprocess.call(['sudo', 'rm', '-rf', dirpath])

‎infra/ci/worker/gce-startup-script.sh

+5-1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ SANDBOX_IMG=$(curl --silent --fail -H'Metadata-Flavor:Google' $URL)
2727
URL="$ATTRS/worker-img"
2828
WORKER_IMG=$(curl --silent --fail -H'Metadata-Flavor:Google' $URL)
2929

30+
# We largely use tmpfs for checkout + build + cache. Add swap so we can exceed
31+
# physical ram. It's still faster than operating on a real filesystem because in
32+
# most cases we make very limited use of swap.
3033
for SSD in /dev/nvme0n*; do
3134
mkswap $SSD
3235
swapon -p -1 $SSD
@@ -84,7 +87,8 @@ docker run -d \
8487
--env SANDBOX_TMP="$SANDBOX_TMP" \
8588
--env WORKER_HOST="$(hostname)" \
8689
--name worker-$i \
87-
--hostname worker-$i \
90+
--hostname $(hostname)-worker-$i \
91+
--privileged \
8892
--log-driver gcplogs \
8993
$WORKER_IMG
9094
done

‎infra/ci/worker/get_github_token.py

+81
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
#!/usr/bin/env python3
2+
# Copyright (C) 2025 The Android Open Source Project
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the 'License');
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an 'AS IS' BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
""" This script does all the token dance to register a GitHub action runner
16+
17+
- The script impersonates the Perfetto CI App GitHub App using the app private
18+
key.
19+
- It then obtains the app installation token, which binds the app to the
20+
Perfetto GitHub repo.
21+
- From that obtains the "Runner registration token".
22+
- This token is then passed to the sandbox, so it can run the
23+
GitHub Action Runner (./config --unmanned --token=...)
24+
"""
25+
26+
import jwt
27+
import time
28+
import requests
29+
import subprocess
30+
31+
from config import PROJECT, GITHUB_REPO, GITHUB_APP_ID, GITHUB_APP_INSTALLATION_ID
32+
33+
GITHUB_API_URL = 'https://api.github.com'
34+
35+
36+
def generate_jwt():
37+
private_key = subprocess.check_output([
38+
'gcloud', '--project', PROJECT, 'secrets', 'versions', 'access', 'latest',
39+
'--secret=perfetto_ci_github_private_key'
40+
]).decode()
41+
42+
now = int(time.time())
43+
payload = {
44+
'iat': now,
45+
'exp': now + (10 * 60), # JWT valid for 10 minutes
46+
'iss': GITHUB_APP_ID
47+
}
48+
return jwt.encode(payload, private_key, algorithm='RS256')
49+
50+
51+
def get_installation_token(jwt_token, installation_id):
52+
url = f'{GITHUB_API_URL}/app/installations/{installation_id}/access_tokens'
53+
headers = {
54+
'Authorization': f'Bearer {jwt_token}',
55+
'Accept': 'application/vnd.github.v3+json'
56+
}
57+
response = requests.post(url, headers=headers)
58+
response.raise_for_status()
59+
return response.json()['token']
60+
61+
62+
def get_runner_registration_token(inst_token):
63+
url = f'{GITHUB_API_URL}/repos/{GITHUB_REPO}/actions/runners/registration-token'
64+
headers = {
65+
'Authorization': f'token {inst_token}',
66+
'Accept': 'application/vnd.github.v3+json'
67+
}
68+
response = requests.post(url, headers=headers)
69+
response.raise_for_status()
70+
return response.json()['token']
71+
72+
73+
def get_github_token():
74+
jwt_token = generate_jwt()
75+
inst_token = get_installation_token(jwt_token, GITHUB_APP_INSTALLATION_ID)
76+
registration_token = get_runner_registration_token(inst_token)
77+
return registration_token
78+
79+
80+
if __name__ == '__main__':
81+
print(get_github_token)

‎infra/ci/worker/perf_metrics_uploader.py

-136
This file was deleted.

‎infra/ci/worker/run_job.py

-197
This file was deleted.

‎infra/ci/worker/worker.py

+83-144
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,10 @@
1212
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1313
# See the License for the specific language governing permissions and
1414
# limitations under the License.
15-
''' Worker main loop. Pulls jobs from the DB and runs them in the sandbox
15+
''' Worker main loop.
1616
17-
It also handles timeouts and graceful container termination.
17+
Runs a github action ephemeral runner in a nested sandboxed docker container.
18+
It also handles graceful container termination.
1819
'''
1920

2021
import logging
@@ -26,174 +27,112 @@
2627
import time
2728
import traceback
2829

29-
from config import DB, JOB_TIMEOUT_SEC
30-
from common_utils import req, utc_now_iso, init_logging
31-
from common_utils import ConcurrentModificationError, SCOPES
30+
from config import DB, SANDBOX_IMG, GITHUB_REPO, SANDBOX_SVC_ACCOUNT
3231

33-
CUR_DIR = os.path.dirname(__file__)
34-
SCOPES.append('https://www.googleapis.com/auth/firebase.database')
35-
SCOPES.append('https://www.googleapis.com/auth/userinfo.email')
36-
WORKER_NAME = '%s-%s' % (os.getenv(
37-
'WORKER_HOST', 'local').split('-')[-1], socket.gethostname())
38-
sigterm = threading.Event()
39-
40-
41-
def try_acquire_job(job_id):
42-
''' Transactionally acquire the given job.
43-
44-
Returns the job JSON object if it managed to acquire and put it into the
45-
STARTED state, None if another worker got there first.
46-
'''
47-
logging.debug('Trying to acquire job %s', job_id)
32+
from get_github_token import get_github_token
4833

49-
uri = '%s/jobs/%s.json' % (DB, job_id)
50-
job, etag = req('GET', uri, req_etag=True)
51-
if job['status'] != 'QUEUED':
52-
return None # Somebody else took it or the job is CANCELLED/INTERRUPTED
53-
try:
54-
job['status'] = 'STARTED'
55-
job['time_started'] = utc_now_iso()
56-
job['worker'] = WORKER_NAME
57-
req('PUT', uri, body=job, etag=etag)
58-
return job
59-
except ConcurrentModificationError:
60-
return None
34+
CUR_DIR = os.path.dirname(__file__)
6135

36+
# The container name will be deadb33f-sandbox-N.
37+
SANDBOX_NAME = socket.gethostname().replace('-worker-', '-sandbox-')
6238

63-
def make_worker_obj(status, job_id=None):
64-
return {
65-
'job_id': job_id,
66-
'status': status,
67-
'last_update': utc_now_iso(),
68-
'host': os.getenv('WORKER_HOST', '')
69-
}
39+
sigterm = threading.Event()
7040

7141

7242
def worker_loop():
73-
''' Pulls a job from the queue and runs it invoking run_job.py '''
74-
uri = '%s/jobs_queued.json?orderBy="$key"&limitToLast=100' % DB
75-
jobs = req('GET', uri)
76-
if not jobs:
77-
return
78-
79-
# Work out the worker number from the hostname. We try to distribute the load
80-
# (via the time.sleep below) so that we fill first all the worker-1 of each
81-
# vm, then worker-2 and so on. This is designed so that if there is only one
82-
# CL (hence N jobs) in the queue, each VM gets only one job, maximizing the
83-
# cpu efficiency of each VM.
84-
try:
85-
worker_num = int(socket.gethostname().split('-')[-1])
86-
except ValueError:
87-
worker_num = 1
88-
89-
# Transactionally acquire a job. Deal with races (two workers trying to
90-
# acquire the same job).
91-
job = None
92-
job_id = None
93-
for job_id in sorted(jobs.keys(), reverse=True):
94-
job = try_acquire_job(job_id)
95-
if job is not None:
96-
break
97-
time.sleep(worker_num)
98-
if job is None:
99-
logging.error('Failed to acquire a job')
100-
return
101-
102-
logging.info('Starting job %s', job_id)
103-
104-
# Update the db, move the job to the running queue.
105-
patch_obj = {
106-
'jobs_queued/' + job_id: {}, # = DELETE
107-
'jobs_running/' + job_id: {
108-
'worker': WORKER_NAME
109-
},
110-
'workers/' + WORKER_NAME: make_worker_obj('RUNNING', job_id=job_id)
111-
}
112-
req('PATCH', '%s.json' % DB, body=patch_obj)
113-
114-
cmd = [os.path.join(CUR_DIR, 'run_job.py'), job_id]
115-
116-
# Propagate the worker's PERFETTO_ vars and merge with the job-specific vars.
117-
env = dict(os.environ, **{k: str(v) for (k, v) in job['env'].items()})
118-
job_runner = subprocess.Popen(cmd, env=env)
119-
120-
# Run the job in a python subprocess, to isolate the main loop from logs
121-
# uploader failures.
122-
res = None
123-
cancelled = False
124-
timed_out = False
125-
time_started = time.time()
126-
time_last_db_poll = time_started
127-
polled_status = 'STARTED'
128-
while res is None:
129-
time.sleep(0.25)
130-
res = job_runner.poll()
131-
now = time.time()
132-
if now - time_last_db_poll > 10: # Throttle DB polling.
133-
polled_status = req('GET', '%s/jobs/%s/status.json' % (DB, job_id))
134-
time_last_db_poll = now
135-
if now - time_started > JOB_TIMEOUT_SEC:
136-
logging.info('Job %s timed out, terminating', job_id)
137-
timed_out = True
138-
job_runner.terminate()
139-
if (sigterm.is_set() or polled_status != 'STARTED') and not cancelled:
140-
logging.info('Job %s cancelled, terminating', job_id)
141-
cancelled = True
142-
job_runner.terminate()
143-
144-
status = ('INTERRUPTED' if sigterm.is_set() else 'CANCELLED' if cancelled else
145-
'TIMED_OUT' if timed_out else 'COMPLETED' if res == 0 else 'FAILED')
146-
logging.info('Job %s %s with code %s', job_id, status, res)
147-
148-
# Update the DB, unless the job has been cancelled. The "is not None"
149-
# condition deals with a very niche case, that is, avoid creating a partial
150-
# job entry after doing a full clear of the DB (which is super rare, happens
151-
# only when re-deploying the CI).
152-
if polled_status is not None:
153-
patch = {
154-
'jobs/%s/status' % job_id: status,
155-
'jobs/%s/exit_code' % job_id: {} if res is None else res,
156-
'jobs/%s/time_ended' % job_id: utc_now_iso(),
157-
'jobs_running/%s' % job_id: {}, # = DELETE
158-
}
159-
req('PATCH', '%s.json' % (DB), body=patch)
43+
# Remove stale jobs, if any.
44+
subprocess.call(['sudo', 'docker', 'rm', '-f', SANDBOX_NAME])
45+
46+
# Impersonate the sandbox service account. This creates a temporary downgraded
47+
# service account that we pass to the sandbox. The sandbox service account
48+
# is allowed only storage object creation for untrusted CI artifacts.
49+
# sandbox_svc_token = subprocess.check_output([
50+
# 'gcloud', 'auth', 'application-default',
51+
# 'print-access-token'
52+
# '--impersonate-service-account=%s' % SANDBOX_SVC_ACCOUNT,
53+
# ])
54+
55+
# Run the nested docker container that will execute the ephemeral GitHub
56+
# action runner in the sandbox image.
57+
cmd = [
58+
'sudo', 'docker', 'run', '--rm', '--name', SANDBOX_NAME, '--hostname',
59+
SANDBOX_NAME, '--cap-add', 'SYS_PTRACE', '--tmpfs', '/tmp:exec'
60+
]
61+
62+
# Obtain the (short-lived) token to register the Github Action Runner and
63+
# pass it to the sandbox.
64+
github_token = get_github_token()
65+
cmd += ['--env', 'GITHUB_TOKEN=%s' % github_token]
66+
cmd += ['--env', 'GITHUB_REPO=%s' % GITHUB_REPO]
67+
# cmd += ['--env', 'SANDBOX_SVC_TOKEN=%s' % sandbox_svc_token]
68+
69+
# Propagate PERFETTO_ environment variables
70+
for kv in [kv for kv in os.environ.items() if kv[0].startswith('PERFETTO_')]:
71+
cmd += ['--env', '%s=%s' % kv]
72+
73+
# We use the tmpfs mount created by gce-startup-script.sh, if present. The
74+
# problem is that Docker doesn't allow to both override the tmpfs-size and
75+
# prevent the "-o noexec". In turn the default tmpfs-size depends on the host
76+
# phisical memory size.
77+
if os.getenv('SANDBOX_TMP'):
78+
cmd += ['-v', '%s:/ci/ramdisk' % os.getenv('SANDBOX_TMP')]
79+
else:
80+
cmd += ['--tmpfs', '/ci/ramdisk:exec']
81+
82+
# Rationale for the conditional branches below: when running in the real GCE
83+
# environment, the gce-startup-script.sh mounts these directories in the right
84+
# locations, so that they are shared between all workers.
85+
# When running the worker container outside of GCE (i.e.for local testing) we
86+
# leave these empty. The VOLUME directive in the dockerfile will cause docker
87+
# to automatically mount a scratch volume for those.
88+
# This is so that the CI containers can be tested without having to do the
89+
# work that gce-startup-script.sh does.
90+
if os.getenv('SHARED_WORKER_CACHE'):
91+
cmd += ['--volume=%s:/ci/cache' % os.getenv('SHARED_WORKER_CACHE')]
92+
93+
artifacts_dir = None
94+
if os.getenv('ARTIFACTS_DIR'):
95+
artifacts_dir = os.path.join(os.getenv('ARTIFACTS_DIR'), SANDBOX_NAME)
96+
subprocess.call(['sudo', 'rm', '-rf', artifacts_dir])
97+
os.mkdir(artifacts_dir)
98+
cmd += ['--volume=%s:/ci/artifacts' % artifacts_dir]
99+
100+
cmd += os.getenv('SANDBOX_NETWORK_ARGS', '').split()
101+
cmd += [SANDBOX_IMG]
102+
103+
# This spawns the sandbox that runs one ephemeral GitHub Action job and
104+
# terminates when done.
105+
subprocess.call(cmd)
106+
107+
if artifacts_dir:
108+
artifacts_uploader = os.path.join(CUR_DIR, 'artifacts_uploader.py')
109+
cmd = ['setsid', artifacts_uploader, '--dir=' + artifacts_dir, '--rm']
110+
subprocess.call(cmd)
160111

161112

162113
def sig_handler(_, __):
163114
logging.warning('Interrupted by signal, exiting worker')
115+
subprocess.call(['sudo', 'docker', 'kill', SANDBOX_NAME])
164116
sigterm.set()
165117

166118

167119
def main():
168-
init_logging()
120+
logging.basicConfig(
121+
format='%(levelname)-8s %(asctime)s %(message)s',
122+
level=logging.DEBUG if os.getenv('VERBOSE') else logging.INFO,
123+
datefmt=r'%Y-%m-%d %H:%M:%S')
169124
logging.info('Worker started')
170125
signal.signal(signal.SIGTERM, sig_handler)
171126
signal.signal(signal.SIGINT, sig_handler)
172127

173128
while not sigterm.is_set():
174-
logging.debug('Starting poll cycle')
175129
try:
176130
worker_loop()
177-
req('PUT',
178-
'%s/workers/%s.json' % (DB, WORKER_NAME),
179-
body=make_worker_obj('IDLE'))
180131
except:
181132
logging.error('Exception in worker loop:\n%s', traceback.format_exc())
182133
if sigterm.is_set():
183134
break
184-
185-
# Synchronize sleeping with the wall clock. This is so all VMs wake up at
186-
# the same time. See comment on distributing load above in this file.
187-
poll_time_sec = 5
188-
time.sleep(poll_time_sec - (time.time() % poll_time_sec))
189-
190-
# The use case here is the VM being terminated by the GCE infrastructure.
191-
# We mark the worker as terminated and the job as cancelled so we don't wait
192-
# forever for it.
193-
logging.warning('Exiting the worker loop, got signal: %s', sigterm.is_set())
194-
req('PUT',
195-
'%s/workers/%s.json' % (DB, WORKER_NAME),
196-
body=make_worker_obj('TERMINATED'))
135+
time.sleep(1)
197136

198137

199138
if __name__ == '__main__':

‎test/ci/bazel_tests.sh

+2-2
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ echo "skipping build + test runs"
2222
exit 0
2323
fi
2424

25-
BAZEL_DISK_CACHE_FOLDER="/ci/cache/bazel-disk-cache-$(hostname)"
25+
BAZEL_DISK_CACHE_FOLDER="$PERFETTO_CACHE_DIR/bazel-disk-cache-$(hostname)"
2626
readonly BAZEL_DISK_CACHE_FOLDER
2727
# Cleanup the cache if any of the two conditions are true.
2828
BAZEL_DISK_CACHE_GC_OPTIONS="--experimental_disk_cache_gc_max_age=7d --experimental_disk_cache_gc_max_size=10G"
@@ -43,7 +43,7 @@ tools/bazel build //python:all ${BAZEL_DISK_CACHE_FLAGS} --verbose_failures
4343
./bazel-bin/traced &
4444
./bazel-bin/traced_probes &
4545
sleep 5
46-
TRACE=/ci/artifacts/bazel.trace
46+
TRACE="$PERFETTO_ARTIFACTS_DIR/bazel.trace"
4747
./bazel-bin/perfetto -c :test -o $TRACE
4848
kill $(jobs -p)
4949
./bazel-bin/trace_processor_shell -q <(echo 'select count(1) from sched') $TRACE

‎test/ci/common.sh

+6
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,12 @@ OUT_PATH="out/dist"
2020

2121
export PYTHONUNBUFFERED=1
2222

23+
export PERFETTO_CACHE_DIR="${PERFETTO_CACHE_DIR:-/tmp/cache}"
24+
mkdir -p "$PERFETTO_CACHE_DIR"
25+
26+
export PERFETTO_ARTIFACTS_DIR="${PERFETTO_ARTIFACTS_DIR:-/tmp/artifacts}"
27+
mkdir -p "$PERFETTO_ARTIFACTS_DIR"
28+
2329
tools/install-build-deps $PERFETTO_INSTALL_BUILD_DEPS_ARGS
2430

2531
# Assumes Linux. Windows should use /win/clang instead.

‎test/ci/linux_tests.sh

+2-2
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,10 @@ if [ ! -f ${HOST_OUT_PATH}/trace_processor_shell ]; then
4242
HOST_OUT_PATH=${OUT_PATH}
4343
fi
4444

45-
mkdir -p /ci/artifacts/perf
45+
mkdir -p "$PERFETTO_ARTIFACTS_DIR/perf"
4646

4747
tools/diff_test_trace_processor.py \
48-
--perf-file=/ci/artifacts/perf/tp-perf-all.json \
48+
--perf-file=$PERFETTO_ARTIFACTS_DIR/perf/tp-perf-all.json \
4949
${HOST_OUT_PATH}/trace_processor_shell
5050

5151
python/run_tests.py ${HOST_OUT_PATH}

‎test/ci/ui_tests.sh

+4-4
Original file line numberDiff line numberDiff line change
@@ -21,16 +21,16 @@ infra/perfetto.dev/build
2121

2222
ui/build --out ${OUT_PATH}
2323

24-
cp -a ${OUT_PATH}/ui/dist/ /ci/artifacts/ui
24+
cp -a ${OUT_PATH}/ui/dist/ "$PERFETTO_ARTIFACTS_DIR/ui"
2525

2626
ui/run-unittests --out ${OUT_PATH} --no-build
2727

2828
set +e
2929

3030
# Install chrome
3131
(
32-
mkdir /ci/ramdisk/chrome
33-
cd /ci/ramdisk/chrome
32+
mkdir /tmp/chrome
33+
cd /tmp/chrome
3434
CHROME_VERSION=134.0.6998.35
3535
curl -Ls -o chrome.deb https://dl.google.com/linux/chrome/deb/pool/main/g/google-chrome-stable/google-chrome-stable_${CHROME_VERSION}-1_amd64.deb
3636
dpkg-deb -x chrome.deb .
@@ -42,7 +42,7 @@ set +x
4242

4343
# Copy the output of screenshots diff testing.
4444
if [ -d ${OUT_PATH}/ui-test-artifacts ]; then
45-
cp -a ${OUT_PATH}/ui-test-artifacts /ci/artifacts/ui-test-artifacts
45+
cp -a ${OUT_PATH}/ui-test-artifacts "$PERFETTO_ARTIFACTS_DIR/ui-test-artifacts"
4646
echo "UI integration test report with screnshots:"
4747
echo "https://storage.googleapis.com/perfetto-ci-artifacts/$PERFETTO_TEST_JOB/ui-test-artifacts/index.html"
4848
echo ""

‎ui/playwright.config.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ const isCi = Boolean(process.env.CI);
2020
const outDir = process.env.OUT_DIR ?? '../out/ui';
2121

2222
// Installed by test/ci/ui_tests.sh
23-
const ciChromePath = '/ci/ramdisk/chrome/opt/google/chrome/google-chrome';
23+
const ciChromePath = '/tmp/chrome/opt/google/chrome/google-chrome';
2424

2525
export default defineConfig({
2626
testDir: './src',

0 commit comments

Comments
 (0)
Please sign in to comment.