From 4e702bfa4572ef5ef2c667da8dfa29c79d24b0ea Mon Sep 17 00:00:00 2001 From: WuYongJie <549727955@qq.com> Date: Wed, 26 Aug 2026 13:24:59 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=EF=BC=9A=E4=BF=9D=E7=95=99?= =?UTF-8?q?=E7=94=9F=E4=BA=A7=E6=9E=84=E5=BB=BA=E5=9F=BA=E7=A1=80=E4=BE=9D?= =?UTF-8?q?=E8=B5=96=E7=BC=93=E5=AD=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- deploy/Dockerfile | 38 +++++++++++++------ scripts/deploy-from-workstation.ps1 | 14 +++++-- .../ProductionBuildCachePolicyTest.php | 37 ++++++++++++++++++ 3 files changed, 75 insertions(+), 14 deletions(-) create mode 100644 tests/Unit/Deployment/ProductionBuildCachePolicyTest.php diff --git a/deploy/Dockerfile b/deploy/Dockerfile index 4dccd336..2024a960 100644 --- a/deploy/Dockerfile +++ b/deploy/Dockerfile @@ -1,9 +1,9 @@ # syntax=docker/dockerfile:1.7 -FROM php:8.3-apache-bookworm AS php-base +FROM php:8.3-apache-bookworm@sha256:fa8852a2e01747ffe8c8768bfd6bbc2f296f974aa0de90ee157a66664996d263 AS php-base -RUN apt-get update \ - && apt-get install -y --no-install-recommends \ +RUN apt-get -o Acquire::Retries=3 update \ + && apt-get -o Acquire::Retries=3 install -y --no-install-recommends \ curl \ libcurl4-openssl-dev \ libfreetype6-dev \ @@ -37,22 +37,38 @@ RUN apt-get update \ && a2enmod expires headers rewrite \ && rm -rf /var/lib/apt/lists/* +FROM composer:2.8@sha256:5248900ab8b5f7f880c2d62180e40960cd87f60149ec9a1abfd62ac72a02577c AS composer-bin + FROM php-base AS vendor -COPY --from=composer:2.8 /usr/bin/composer /usr/local/bin/composer +COPY --from=composer-bin /usr/bin/composer /usr/local/bin/composer WORKDIR /build COPY composer.json composer.lock ./ RUN --mount=type=cache,target=/tmp/composer-cache \ - COMPOSER_CACHE_DIR=/tmp/composer-cache composer install \ - --prefer-dist \ - --no-dev \ - --no-interaction \ - --no-progress \ - --no-scripts \ - --optimize-autoloader + set -eu; \ + attempt=1; \ + while true; do \ + if COMPOSER_CACHE_DIR=/tmp/composer-cache \ + COMPOSER_MAX_PARALLEL_HTTP=4 \ + composer install \ + --prefer-dist \ + --no-dev \ + --no-interaction \ + --no-progress \ + --no-scripts \ + --optimize-autoloader; then \ + break; \ + fi; \ + if [ "$attempt" -ge 3 ]; then \ + exit 1; \ + fi; \ + attempt=$((attempt + 1)); \ + echo "Composer install failed; retrying ($attempt/3)." >&2; \ + sleep 3; \ + done FROM php-base AS runtime diff --git a/scripts/deploy-from-workstation.ps1 b/scripts/deploy-from-workstation.ps1 index 08fe8a64..0021d8c2 100644 --- a/scripts/deploy-from-workstation.ps1 +++ b/scripts/deploy-from-workstation.ps1 @@ -14,6 +14,10 @@ param( [string]$DatabaseImage = 'mariadb:11.4', [string]$DatabaseMirrorImage = 'm.daocloud.io/docker.io/library/mariadb:11.4', [int]$KeepImages = 2, + [ValidatePattern('^\d+(KB|MB|GB|TB)$')] + [string]$BuildCacheMaxUsedSpace = '24GB', + [ValidateRange(24, 8760)] + [int]$BuildCacheMaxAgeHours = 720, [switch]$BootstrapServer, [switch]$ConfigureHttps, [switch]$AllowNonMain, @@ -536,13 +540,17 @@ finally { ssh -i $keyFullPath -o BatchMode=yes -o ConnectTimeout=5 $sshTarget "rm -f '$bootstrapRemote'" 2>$null | Out-Null } + # Keep the compiled PHP base and Composer vendor layers between releases. + # The production build cache is normally about 20 GB; pruning it to 3 GB made + # every deployment rebuild PHP extensions and download all dependencies again. $pruneHelp = docker buildx prune --help 2>$null | Out-String - docker buildx prune --builder $BuilderName --filter 'until=168h' --force 2>$null | Out-Null + $buildCacheMaxAge = "${BuildCacheMaxAgeHours}h" + docker buildx prune --builder $BuilderName --filter "until=$buildCacheMaxAge" --force 2>$null | Out-Null if ($pruneHelp -match '--max-used-space') { - docker buildx prune --builder $BuilderName --max-used-space 3GB --force 2>$null | Out-Null + docker buildx prune --builder $BuilderName --max-used-space $BuildCacheMaxUsedSpace --force 2>$null | Out-Null } elseif ($pruneHelp -match '--keep-storage') { - docker buildx prune --builder $BuilderName --keep-storage 3GB --force 2>$null | Out-Null + docker buildx prune --builder $BuilderName --keep-storage $BuildCacheMaxUsedSpace --force 2>$null | Out-Null } docker system df diff --git a/tests/Unit/Deployment/ProductionBuildCachePolicyTest.php b/tests/Unit/Deployment/ProductionBuildCachePolicyTest.php new file mode 100644 index 00000000..4a27d66b --- /dev/null +++ b/tests/Unit/Deployment/ProductionBuildCachePolicyTest.php @@ -0,0 +1,37 @@ +assertStringContainsString( + 'FROM php:8.3-apache-bookworm@sha256:fa8852a2e01747ffe8c8768bfd6bbc2f296f974aa0de90ee157a66664996d263 AS php-base', + $dockerfile, + ); + $this->assertStringContainsString( + 'FROM composer:2.8@sha256:5248900ab8b5f7f880c2d62180e40960cd87f60149ec9a1abfd62ac72a02577c AS composer-bin', + $dockerfile, + ); + $this->assertStringContainsString('Acquire::Retries=3', $dockerfile); + $this->assertStringContainsString('COMPOSER_MAX_PARALLEL_HTTP=4', $dockerfile); + $this->assertStringContainsString('if [ "$attempt" -ge 3 ]', $dockerfile); + } + + public function testWorkstationDeploymentRetainsReusableBuildLayers(): void + { + $script = file_get_contents(base_path('scripts/deploy-from-workstation.ps1')); + + $this->assertStringContainsString("[string]\$BuildCacheMaxUsedSpace = '24GB'", $script); + $this->assertStringContainsString('[int]$BuildCacheMaxAgeHours = 720', $script); + $this->assertStringContainsString('--max-used-space $BuildCacheMaxUsedSpace', $script); + $this->assertStringContainsString('--keep-storage $BuildCacheMaxUsedSpace', $script); + $this->assertStringNotContainsString('--max-used-space 3GB', $script); + $this->assertStringNotContainsString('--keep-storage 3GB', $script); + } +}