#!/usr/bin/env bash# Sync phase: clone/update depot_tools, clone the chromium-fork checkout,# gclient sync. Run via `hetzner-runner exec -script sync.sh` against a box# already provisioned by a separate `up` step -- see ../../.gitea/workflows# in chromium-fork for the phase breakdown this belongs to.set -euo pipefail: "": "": "": "": ""retry() {local desc="$1"; shiftlocal attempt=1local max=5until "$@"; doif [ "$attempt" -ge "$max" ]; thenecho "$desc failed after $attempt attempts, giving up"return 1fiecho "$desc failed (attempt $attempt/$max); retrying in 5s"attempt=$((attempt + 1))sleep 5done}redact() {sed -E 's#x-access-token:[^@]*@#x-access-token:REDACTED@#g'}echo "===== base packages (file, strace) ====="# `file` is a hard requirement: install-build-deps.py's dev_list() shells# out to it (via `file -L <path>`) to detect ELF architecture, and this# minimal Ubuntu cloud image doesn't ship it by default (confirmed: ci run# #61, 2026-08-05, FileNotFoundError: 'file'). strace is a diagnostic# nicety for stall-watch.py, not a build requirement -- best-effort only.if ! command -v file >/dev/null 2>&1 || ! command -v strace >/dev/null 2>&1; thenapt-get update -qqficommand -v file >/dev/null 2>&1 || apt-get install -y filecommand -v strace >/dev/null 2>&1 || apt-get install -y strace \|| echo "strace install failed, continuing without it"echo "===== depot_tools ====="if [ ! -d "$HOME/depot_tools" ]; thenretry "clone depot_tools" git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git "$HOME/depot_tools"fiexport PATH="$HOME/depot_tools:$PATH"# Explicit bootstrap before anything else touches depot_tools' `python3`# wrapper. That wrapper hard-requires python3_bin_reldir.txt (see# depot_tools/python3), which is normally created by `update_depot_tools`# -- but update_depot_tools itself refuses to do anything as root# ("Running depot tools as root is sad", see depot_tools/update_depot_tools)# and we're root on this box. gclient sync still works without this# (confirmed: ci run #54, 2026-08-04, sync completed fully) because it goes# through vpython3, a separately-bootstrapped wrapper -- but any later# direct python3 invocation, e.g. install-build-deps.py's shebang, fails# with exactly this error otherwise. cipd_bin_setup and bootstrap_python3# are the two functions update_depot_tools calls after its root check;# calling them straight sidesteps that check entirely.## set +u around this block: these are third-party scripts (depot_tools'# own cipd/cipd_bin_setup.sh), not written to be safe under nounset --# confirmed cipd itself references an unset var with no default and dies# under set -u. They're sourced (not exec'd), so they inherit our shell's# flags; restore -u immediately after.set +usource "$HOME/depot_tools/cipd_bin_setup.sh"cipd_bin_setupsource "$HOME/depot_tools/bootstrap_python3"bootstrap_python3set -umkdir -p "$CHROMIUM_ROOT"cd "$CHROMIUM_ROOT"REPO_URL="https://x-access-token:@forge.quinefoundation.com/.git"# VK-GL-CTS against chromium.googlesource.com directly was the single# biggest source of sync flakiness in chromium-fork's original CI workflow# (corrupted packs, silent stalls). Redirect to the org's own mirror.VK_GL_CTS_UPSTREAM="https://chromium.googlesource.com/external/github.com/KhronosGroup/VK-GL-CTS"git config --global \url."https://x-access-token:@forge.quinefoundation.com/Cold-Air-Networks/mirror-vk-gl-cts.git".insteadOf \"$VK_GL_CTS_UPSTREAM"echo "===== registered url.insteadOf mappings ====="git config --global --get-regexp '^url\.' | redactecho "===== end registered url.insteadOf mappings ====="cat > .gclient <<EOFsolutions = [{"name": "src","url": "$REPO_URL","custom_deps": {},"custom_vars": {},},]EOFecho "===== fetching $GITEA_REPO@$GITEA_REF ====="if [ ! -d src/.git ]; thenretry "clone src" git clone "$REPO_URL" srcficd srcgit remote set-url origin "$REPO_URL"retry "fetch $GITEA_REF" git fetch origin "$GITEA_REF"git checkout --force FETCH_HEAD# --shallow is deliberately absent: chromium-fork's original CI workflow# traced every VK-GL-CTS stall to --shallow itself (git_cache.py turns it# into --depth 10000, a much harder reachability problem across VK-GL-CTS's# 150+ tags/branches than an unlimited-depth fetch of the same refs) --# removing it is what got that workflow's sync to actually succeed.echo "===== gclient sync ====="# Wrapped in stall-watch.py: ci run #56 (2026-08-04) stalled here for ~3h# with zero output (last thing seen: devtools-frontend's# sync_rollup_libs.py hook) and left no diagnostic trail. 5 minutes of true# silence is well outside anything a legitimate hook/clone/cipd-deploy# should ever go without printing *something* -- if it happens again this# dumps the process tree, network state, and a live strace sample of# whatever's actually stuck, before killing it.## ci run #57 (2026-08-05) then spent 90+ minutes cycling through this loop# with no clear signal from the outside about which attempt it was on or# whether stall-watch had even fired -- Gitea's log capture visibly lags# real execution by a lot on long steps (confirmed on both runs), so# "no new log lines" doesn't mean "no progress". Two fixes for that,# neither of which depends on the log catching up in real time:# - explicit timestamped start/end/duration per attempt, so whatever log# *does* eventually land is unambiguous about where the time went.# - a hard wall-clock budget across the whole retry loop (not just per# attempt) -- run #57 never got a chance to hit this because it was# killed manually; now the loop itself will give up and report# instead of running indefinitely regardless of individual attempts# each staying under the stall threshold.SYNC_LOOP_START=SYNC_LOOP_BUDGET=2400 # 40 minutes total across all attemptsATTEMPT=1MAX_ATTEMPTS=5while true; doATTEMPT_START=echo "----- gclient sync attempt $ATTEMPT/$MAX_ATTEMPTS starting at -----"if python3 "$HOME/stall-watch.py" --stall-timeout 300 -- \gclient sync --with_branch_heads --with_tags -j"" --verbose --force 2>&1 | redact; thenATTEMPT_ELAPSED=$(( - ATTEMPT_START ))echo "----- gclient sync attempt $ATTEMPT/$MAX_ATTEMPTS succeeded after s -----"breakfiATTEMPT_ELAPSED=$(( - ATTEMPT_START ))LOOP_ELAPSED=$(( - SYNC_LOOP_START ))echo "----- gclient sync attempt $ATTEMPT/$MAX_ATTEMPTS failed after s (s total so far) -----"if [ "$ATTEMPT" -ge "$MAX_ATTEMPTS" ]; thenecho "gclient sync failed after $ATTEMPT attempts, giving up"exit 1fiif [ "$LOOP_ELAPSED" -ge "$SYNC_LOOP_BUDGET" ]; thenecho "gclient sync retry loop exceeded its s wall-clock budget after attempt $ATTEMPT, giving up rather than continuing indefinitely"exit 1fiecho "retrying ($((ATTEMPT + 1))/$MAX_ATTEMPTS)"ATTEMPT=$((ATTEMPT + 1))done