From a037e99cffbefb48187389d30fd28ab2a8e26a53 Mon Sep 17 00:00:00 2001 From: Leon Morival Date: Mon, 17 Aug 2026 10:35:49 +0200 Subject: [PATCH] ci: harden Composer downloads against rate limits --- .gitea/workflows/ci.yml | 43 +++++++++++++++++++++++++++++++++++------ Dockerfile | 29 +++++++++++++++++++++------ 2 files changed, 60 insertions(+), 12 deletions(-) diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index 9b1c8a1..0d4889f 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -17,6 +17,7 @@ jobs: env: DB_CONNECTION: sqlite DB_DATABASE: database/database.sqlite + COMPOSER_MAX_PARALLEL_HTTP: 4 steps: - name: 📥 Checkout uses: actions/checkout@v4 @@ -28,18 +29,45 @@ jobs: extensions: bcmath,ctype,curl,dom,fileinfo,intl,mbstring,openssl,pdo,tokenizer,xml,zip coverage: none + - name: Configure Composer authentication + env: + COMPOSER_GITHUB_TOKEN: ${{ secrets.COMPOSER_GITHUB_TOKEN }} + run: | + if [ -n "$COMPOSER_GITHUB_TOKEN" ]; then + composer config \ + --global \ + --auth github-oauth.github.com \ + "$COMPOSER_GITHUB_TOKEN" + fi + + - name: Get Composer cache directory + id: composer-cache + run: echo "dir=$(composer config cache-files-dir)" >> "$GITHUB_OUTPUT" + - name: Cache Composer uses: actions/cache@v4 with: - path: ~/.composer/cache - key: composer-cache + path: ${{ steps.composer-cache.outputs.dir }} + key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} + restore-keys: ${{ runner.os }}-composer- - name: Install Composer dependencies run: | - composer install \ - --no-interaction \ - --prefer-dist \ - --no-progress + attempt=1 + + until composer install \ + --no-interaction \ + --prefer-dist \ + --no-progress; do + if [ "$attempt" -ge 4 ]; then + exit 1 + fi + + delay=$((attempt * 15)) + echo "Composer install failed (attempt $attempt/4), retrying in ${delay}s..." + sleep "$delay" + attempt=$((attempt + 1)) + done - name: ⚙️ Prepare Laravel run: | @@ -71,8 +99,11 @@ jobs: -p "${{ secrets.REGISTRY_PASSWORD }}" - name: 🏗️ Build image + env: + COMPOSER_GITHUB_TOKEN: ${{ secrets.COMPOSER_GITHUB_TOKEN }} run: | docker build \ + --secret id=composer_github_token,env=COMPOSER_GITHUB_TOKEN \ -t git.leonmorival.xyz/leonm/daily-meal-api:latest \ -t git.leonmorival.xyz/leonm/daily-meal-api:${{ github.sha }} \ . diff --git a/Dockerfile b/Dockerfile index 6ffc520..87d7708 100644 --- a/Dockerfile +++ b/Dockerfile @@ -17,12 +17,29 @@ COPY --from=composer:2 /usr/bin/composer /usr/bin/composer COPY composer.json composer.lock ./ -RUN composer install \ - --no-dev \ - --no-interaction \ - --prefer-dist \ - --optimize-autoloader \ - --no-scripts +RUN --mount=type=cache,target=/tmp/composer-cache \ + --mount=type=secret,id=composer_github_token,required=false \ + set -eu; \ + if [ -s /run/secrets/composer_github_token ]; then \ + export COMPOSER_AUTH="{\"github-oauth\":{\"github.com\":\"$(cat /run/secrets/composer_github_token)\"}}"; \ + fi; \ + export COMPOSER_CACHE_DIR=/tmp/composer-cache; \ + export COMPOSER_MAX_PARALLEL_HTTP=4; \ + attempt=1; \ + until composer install \ + --no-dev \ + --no-interaction \ + --prefer-dist \ + --optimize-autoloader \ + --no-scripts; do \ + if [ "$attempt" -ge 4 ]; then \ + exit 1; \ + fi; \ + delay=$((attempt * 15)); \ + echo "Composer install failed (attempt $attempt/4), retrying in ${delay}s..."; \ + sleep "$delay"; \ + attempt=$((attempt + 1)); \ + done COPY . .