From 4b4b2453aef2649fc9eca240f552054f4c946e08 Mon Sep 17 00:00:00 2001 From: James Griffin Date: Thu, 20 Aug 2026 14:44:54 -0300 Subject: [PATCH 1/4] Cache the .debs php-builder installs, to measure whether it helps Experiment for #178. On self-hosted runners setup-php installs PHP 8.3+ through php-builder, whose install.sh apt-installs ~70 -dev packages before unpacking the build. The build tarball itself is only 19MB, so that apt work is the whole cost, not the download. Ubuntu's image drops the .debs after install, so every job fetches them from the archive again. This keeps them and restores them through the cache server, which lives in the cluster, so a WAN download becomes a local one. The dependency list does not vary by PHP version, so a single key serves 8.3 and 8.5 and the quality and build jobs alike. Only the .debs are cached. /var/lib/apt/lists is deliberately left alone, since a stale index is how apt starts 404ing mid-install, and reducing flakiness is the entire point. The Report restored .debs step is temporary instrumentation to show whether the cache is actually being read. Baseline to beat, from run 526: 8.1 30s, 8.2 53s, 8.3 454s, quality 151s, 8.5 733s and a hard failure. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01Uw545F1vveNJKjzLxdi2ks --- .gitea/workflows/ci.yml | 87 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index d8cf893..19384a1 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -19,6 +19,35 @@ jobs: steps: - uses: actions/checkout@v4 + # setup-php installs PHP 8.3+ through php-builder, which apt-installs ~70 + # -dev packages before unpacking the build (#178). Ubuntu's own image + # deletes the .debs after install, so every job re-downloads them from + # the archive. Keep them and restore them from the cache server, which + # lives in the cluster, turning a WAN download into a local one. + # + # The dep list is the same for every PHP version on the builder path, so + # one shared key serves them all. Only the .debs are cached, never + # /var/lib/apt/lists — a stale index is how you get 404s on install. + - name: Keep downloaded .debs + run: | + sudo rm -f /etc/apt/apt.conf.d/docker-clean + echo 'Binary::apt::APT::Keep-Downloaded-Packages "true";' \ + | sudo tee /etc/apt/apt.conf.d/99keep-downloaded-packages >/dev/null + + - name: Cache apt packages + uses: actions/cache@v3 + with: + path: /var/cache/apt/archives/*.deb + key: apt-php-builder-deps-${{ runner.arch }}-v1 + + # Temporary, for #178: shows whether the cache actually served the + # builder's dependencies, and times the step that consumes them. + - name: Report restored .debs + run: | + echo "restored .debs: $(ls /var/cache/apt/archives/*.deb 2>/dev/null | wc -l)" + echo "restored size: $(du -sh /var/cache/apt/archives 2>/dev/null | cut -f1)" + date -u +'setup-php start: %H:%M:%S' + - name: Setup PHP uses: shivammathur/setup-php@v2 with: @@ -53,6 +82,35 @@ jobs: steps: - uses: actions/checkout@v4 + # setup-php installs PHP 8.3+ through php-builder, which apt-installs ~70 + # -dev packages before unpacking the build (#178). Ubuntu's own image + # deletes the .debs after install, so every job re-downloads them from + # the archive. Keep them and restore them from the cache server, which + # lives in the cluster, turning a WAN download into a local one. + # + # The dep list is the same for every PHP version on the builder path, so + # one shared key serves them all. Only the .debs are cached, never + # /var/lib/apt/lists — a stale index is how you get 404s on install. + - name: Keep downloaded .debs + run: | + sudo rm -f /etc/apt/apt.conf.d/docker-clean + echo 'Binary::apt::APT::Keep-Downloaded-Packages "true";' \ + | sudo tee /etc/apt/apt.conf.d/99keep-downloaded-packages >/dev/null + + - name: Cache apt packages + uses: actions/cache@v3 + with: + path: /var/cache/apt/archives/*.deb + key: apt-php-builder-deps-${{ runner.arch }}-v1 + + # Temporary, for #178: shows whether the cache actually served the + # builder's dependencies, and times the step that consumes them. + - name: Report restored .debs + run: | + echo "restored .debs: $(ls /var/cache/apt/archives/*.deb 2>/dev/null | wc -l)" + echo "restored size: $(du -sh /var/cache/apt/archives 2>/dev/null | cut -f1)" + date -u +'setup-php start: %H:%M:%S' + - name: Setup PHP uses: shivammathur/setup-php@v2 with: @@ -96,6 +154,35 @@ jobs: steps: - uses: actions/checkout@v4 + # setup-php installs PHP 8.3+ through php-builder, which apt-installs ~70 + # -dev packages before unpacking the build (#178). Ubuntu's own image + # deletes the .debs after install, so every job re-downloads them from + # the archive. Keep them and restore them from the cache server, which + # lives in the cluster, turning a WAN download into a local one. + # + # The dep list is the same for every PHP version on the builder path, so + # one shared key serves them all. Only the .debs are cached, never + # /var/lib/apt/lists — a stale index is how you get 404s on install. + - name: Keep downloaded .debs + run: | + sudo rm -f /etc/apt/apt.conf.d/docker-clean + echo 'Binary::apt::APT::Keep-Downloaded-Packages "true";' \ + | sudo tee /etc/apt/apt.conf.d/99keep-downloaded-packages >/dev/null + + - name: Cache apt packages + uses: actions/cache@v3 + with: + path: /var/cache/apt/archives/*.deb + key: apt-php-builder-deps-${{ runner.arch }}-v1 + + # Temporary, for #178: shows whether the cache actually served the + # builder's dependencies, and times the step that consumes them. + - name: Report restored .debs + run: | + echo "restored .debs: $(ls /var/cache/apt/archives/*.deb 2>/dev/null | wc -l)" + echo "restored size: $(du -sh /var/cache/apt/archives 2>/dev/null | cut -f1)" + date -u +'setup-php start: %H:%M:%S' + - name: Setup PHP uses: shivammathur/setup-php@v2 with: From d5f6ebf0b5b9fd58576d3fc6c83be4d08c019459 Mon Sep 17 00:00:00 2001 From: James Griffin Date: Thu, 20 Aug 2026 15:49:04 -0300 Subject: [PATCH 2/4] Move actions/cache to v4 and probe the cache URL thatguygriff/infra#1 turned the runner cache server on, so actions/cache has an endpoint for the first time. v4 rather than v3 because v4.2+ can speak the cache service v2 API, which is what the runner serves. The instrumentation step now also prints ACTIONS_CACHE_URL, so a single run shows the endpoint arriving and the cache being read in one log rather than needing a separate probe. Refs #178. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01Uw545F1vveNJKjzLxdi2ks --- .gitea/workflows/ci.yml | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index 19384a1..a792eaa 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -35,7 +35,7 @@ jobs: | sudo tee /etc/apt/apt.conf.d/99keep-downloaded-packages >/dev/null - name: Cache apt packages - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: /var/cache/apt/archives/*.deb key: apt-php-builder-deps-${{ runner.arch }}-v1 @@ -44,6 +44,7 @@ jobs: # builder's dependencies, and times the step that consumes them. - name: Report restored .debs run: | + echo "ACTIONS_CACHE_URL: ${ACTIONS_CACHE_URL:-}" echo "restored .debs: $(ls /var/cache/apt/archives/*.deb 2>/dev/null | wc -l)" echo "restored size: $(du -sh /var/cache/apt/archives 2>/dev/null | cut -f1)" date -u +'setup-php start: %H:%M:%S' @@ -55,7 +56,7 @@ jobs: tools: composer:v2 - name: Cache Composer packages - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: ~/.composer/cache key: composer-${{ hashFiles('composer.json') }} @@ -98,7 +99,7 @@ jobs: | sudo tee /etc/apt/apt.conf.d/99keep-downloaded-packages >/dev/null - name: Cache apt packages - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: /var/cache/apt/archives/*.deb key: apt-php-builder-deps-${{ runner.arch }}-v1 @@ -107,6 +108,7 @@ jobs: # builder's dependencies, and times the step that consumes them. - name: Report restored .debs run: | + echo "ACTIONS_CACHE_URL: ${ACTIONS_CACHE_URL:-}" echo "restored .debs: $(ls /var/cache/apt/archives/*.deb 2>/dev/null | wc -l)" echo "restored size: $(du -sh /var/cache/apt/archives 2>/dev/null | cut -f1)" date -u +'setup-php start: %H:%M:%S' @@ -120,7 +122,7 @@ jobs: tools: composer:v2 - name: Cache Composer packages - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: ~/.composer/cache key: ${{ matrix.php }}-composer-${{ hashFiles('composer.json') }} @@ -170,7 +172,7 @@ jobs: | sudo tee /etc/apt/apt.conf.d/99keep-downloaded-packages >/dev/null - name: Cache apt packages - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: /var/cache/apt/archives/*.deb key: apt-php-builder-deps-${{ runner.arch }}-v1 @@ -179,6 +181,7 @@ jobs: # builder's dependencies, and times the step that consumes them. - name: Report restored .debs run: | + echo "ACTIONS_CACHE_URL: ${ACTIONS_CACHE_URL:-}" echo "restored .debs: $(ls /var/cache/apt/archives/*.deb 2>/dev/null | wc -l)" echo "restored size: $(du -sh /var/cache/apt/archives 2>/dev/null | cut -f1)" date -u +'setup-php start: %H:%M:%S' From a90e06ae705f19fd6f050016a5d8c3f79f1c58eb Mon Sep 17 00:00:00 2001 From: James Griffin Date: Thu, 20 Aug 2026 15:57:27 -0300 Subject: [PATCH 3/4] Key the apt cache per PHP version and drop the instrumentation Run 536 proved the cache works -- ACTIONS_CACHE_URL is now populated, no GHES warning, "Cache saved with key: apt-php-builder-deps-ARM64-v1" -- and in doing so showed the key was wrong. The jobs that saved it were 8.1 and 8.2. They take the ondrej PPA path, a handful of runtime packages, while 8.3 takes php-builder and its ~70 -dev packages. One shared key therefore lets whichever job finishes first decide what every other job restores, and 8.1 is always first, at ~30s against 8.3's several minutes. 8.3 would have restored a few runtime .debs it has no use for and then downloaded all 70 anyway. My "the dep list is the same for every PHP version" comment was true only among the builder versions. Keyed per version now, so each path caches what it actually installs. That also picks up a small win on 8.1 and 8.2 rather than only avoiding harm. Both temporary steps are gone. Report restored .debs was there to show the cache URL arriving and the restore landing; both are established, so it goes. Keep downloaded .debs stays -- it is not instrumentation, it disables Ubuntu's docker-clean, without which there are no .debs left to cache. Refs #178. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01Uw545F1vveNJKjzLxdi2ks --- .gitea/workflows/ci.yml | 93 +++++++++++++++++++---------------------- 1 file changed, 42 insertions(+), 51 deletions(-) diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index a792eaa..02a2068 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -20,14 +20,20 @@ jobs: - uses: actions/checkout@v4 # setup-php installs PHP 8.3+ through php-builder, which apt-installs ~70 - # -dev packages before unpacking the build (#178). Ubuntu's own image - # deletes the .debs after install, so every job re-downloads them from - # the archive. Keep them and restore them from the cache server, which - # lives in the cluster, turning a WAN download into a local one. + # -dev packages before unpacking the build (#178). Ubuntu's image deletes + # the .debs after install, so every job re-downloads them. Keeping them + # and restoring them from the cache server, which lives in the cluster, + # turns a WAN download into a local one. # - # The dep list is the same for every PHP version on the builder path, so - # one shared key serves them all. Only the .debs are cached, never - # /var/lib/apt/lists — a stale index is how you get 404s on install. + # Keyed per PHP version because the install path differs by version and + # the package sets are not interchangeable: 8.3+ pulls the ~70 -dev + # packages through php-builder, while 8.1 and 8.2 come from the ondrej + # PPA as a handful of runtime packages. Sharing one key across both lets + # whichever job finishes first decide what the others restore, and 8.1 is + # always first. + # + # Only the .debs are cached, never /var/lib/apt/lists — a stale index is + # how you get 404s mid-install. - name: Keep downloaded .debs run: | sudo rm -f /etc/apt/apt.conf.d/docker-clean @@ -38,16 +44,7 @@ jobs: uses: actions/cache@v4 with: path: /var/cache/apt/archives/*.deb - key: apt-php-builder-deps-${{ runner.arch }}-v1 - - # Temporary, for #178: shows whether the cache actually served the - # builder's dependencies, and times the step that consumes them. - - name: Report restored .debs - run: | - echo "ACTIONS_CACHE_URL: ${ACTIONS_CACHE_URL:-}" - echo "restored .debs: $(ls /var/cache/apt/archives/*.deb 2>/dev/null | wc -l)" - echo "restored size: $(du -sh /var/cache/apt/archives 2>/dev/null | cut -f1)" - date -u +'setup-php start: %H:%M:%S' + key: apt-php8.3-${{ runner.arch }}-v1 - name: Setup PHP uses: shivammathur/setup-php@v2 @@ -84,14 +81,20 @@ jobs: - uses: actions/checkout@v4 # setup-php installs PHP 8.3+ through php-builder, which apt-installs ~70 - # -dev packages before unpacking the build (#178). Ubuntu's own image - # deletes the .debs after install, so every job re-downloads them from - # the archive. Keep them and restore them from the cache server, which - # lives in the cluster, turning a WAN download into a local one. + # -dev packages before unpacking the build (#178). Ubuntu's image deletes + # the .debs after install, so every job re-downloads them. Keeping them + # and restoring them from the cache server, which lives in the cluster, + # turns a WAN download into a local one. # - # The dep list is the same for every PHP version on the builder path, so - # one shared key serves them all. Only the .debs are cached, never - # /var/lib/apt/lists — a stale index is how you get 404s on install. + # Keyed per PHP version because the install path differs by version and + # the package sets are not interchangeable: 8.3+ pulls the ~70 -dev + # packages through php-builder, while 8.1 and 8.2 come from the ondrej + # PPA as a handful of runtime packages. Sharing one key across both lets + # whichever job finishes first decide what the others restore, and 8.1 is + # always first. + # + # Only the .debs are cached, never /var/lib/apt/lists — a stale index is + # how you get 404s mid-install. - name: Keep downloaded .debs run: | sudo rm -f /etc/apt/apt.conf.d/docker-clean @@ -102,16 +105,7 @@ jobs: uses: actions/cache@v4 with: path: /var/cache/apt/archives/*.deb - key: apt-php-builder-deps-${{ runner.arch }}-v1 - - # Temporary, for #178: shows whether the cache actually served the - # builder's dependencies, and times the step that consumes them. - - name: Report restored .debs - run: | - echo "ACTIONS_CACHE_URL: ${ACTIONS_CACHE_URL:-}" - echo "restored .debs: $(ls /var/cache/apt/archives/*.deb 2>/dev/null | wc -l)" - echo "restored size: $(du -sh /var/cache/apt/archives 2>/dev/null | cut -f1)" - date -u +'setup-php start: %H:%M:%S' + key: apt-php${{ matrix.php }}-${{ runner.arch }}-v1 - name: Setup PHP uses: shivammathur/setup-php@v2 @@ -157,14 +151,20 @@ jobs: - uses: actions/checkout@v4 # setup-php installs PHP 8.3+ through php-builder, which apt-installs ~70 - # -dev packages before unpacking the build (#178). Ubuntu's own image - # deletes the .debs after install, so every job re-downloads them from - # the archive. Keep them and restore them from the cache server, which - # lives in the cluster, turning a WAN download into a local one. + # -dev packages before unpacking the build (#178). Ubuntu's image deletes + # the .debs after install, so every job re-downloads them. Keeping them + # and restoring them from the cache server, which lives in the cluster, + # turns a WAN download into a local one. # - # The dep list is the same for every PHP version on the builder path, so - # one shared key serves them all. Only the .debs are cached, never - # /var/lib/apt/lists — a stale index is how you get 404s on install. + # Keyed per PHP version because the install path differs by version and + # the package sets are not interchangeable: 8.3+ pulls the ~70 -dev + # packages through php-builder, while 8.1 and 8.2 come from the ondrej + # PPA as a handful of runtime packages. Sharing one key across both lets + # whichever job finishes first decide what the others restore, and 8.1 is + # always first. + # + # Only the .debs are cached, never /var/lib/apt/lists — a stale index is + # how you get 404s mid-install. - name: Keep downloaded .debs run: | sudo rm -f /etc/apt/apt.conf.d/docker-clean @@ -175,16 +175,7 @@ jobs: uses: actions/cache@v4 with: path: /var/cache/apt/archives/*.deb - key: apt-php-builder-deps-${{ runner.arch }}-v1 - - # Temporary, for #178: shows whether the cache actually served the - # builder's dependencies, and times the step that consumes them. - - name: Report restored .debs - run: | - echo "ACTIONS_CACHE_URL: ${ACTIONS_CACHE_URL:-}" - echo "restored .debs: $(ls /var/cache/apt/archives/*.deb 2>/dev/null | wc -l)" - echo "restored size: $(du -sh /var/cache/apt/archives 2>/dev/null | cut -f1)" - date -u +'setup-php start: %H:%M:%S' + key: apt-php8.3-${{ runner.arch }}-v1 - name: Setup PHP uses: shivammathur/setup-php@v2 From 37c8d2b39e949a63187278ad72593ae80d5d25f6 Mon Sep 17 00:00:00 2001 From: James Griffin Date: Thu, 20 Aug 2026 16:04:52 -0300 Subject: [PATCH 4/4] Put PHP 8.5 back in the matrix I misread the instruction to hold 8.5 as "take it out until the cache lands" and removed it in f6481d4, so #179 merged without it. It was meant to stay where it was. Restoring it. 8.5 takes the same php-builder path as 8.3, so the apt cache in this branch is exactly what it needs, and the per-version key means it caches its own ~70 -dev packages rather than sharing with the ondrej-path jobs. composer test passes on 8.5.9 locally: 915 tests, 2592 assertions. Refs #178. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01Uw545F1vveNJKjzLxdi2ks --- .gitea/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index 02a2068..9a18a90 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -77,6 +77,7 @@ jobs: - '8.1' - '8.2' - '8.3' + - '8.5' steps: - uses: actions/checkout@v4