diff --git a/.github/actions/apt-install/action.yml b/.github/actions/apt-install/action.yml new file mode 100644 index 000000000..3f20230e6 --- /dev/null +++ b/.github/actions/apt-install/action.yml @@ -0,0 +1,53 @@ +name: Install apt packages +description: > + Installs apt packages with a per-attempt timeout and retries. The hosted runner's Azure apt mirror intermittently serves archives at a crawl, which apt's own timeout does not catch. A stuck attempt is killed and retried, which usually lands on a healthy mirror node. + +inputs: + packages: + description: Space separated list of packages to install + required: true + attempts: + description: Maximum number of attempts + default: "3" + attempt-timeout: + description: Seconds before a single attempt is killed + default: "180" +runs: + using: composite + steps: + - name: Install ${{ inputs.packages }} + shell: bash + env: + PACKAGES: ${{ inputs.packages }} + ATTEMPTS: ${{ inputs.attempts }} + ATTEMPT_TIMEOUT: ${{ inputs.attempt-timeout }} + run: | + SUDO="" + if [ "$(id -u)" -ne 0 ]; then + SUDO="sudo" + fi + + attempt_install() { + $SUDO apt-get update -q + # Intentionally unquoted, this is a list of package names + $SUDO apt-get install -q --yes --no-install-recommends $PACKAGES + } + export -f attempt_install + export SUDO PACKAGES + + for attempt in $(seq 1 "${ATTEMPTS}"); do + echo "::group::apt install attempt ${attempt}/${ATTEMPTS}" + start=$(date +%s) + if timeout --kill-after=10 "${ATTEMPT_TIMEOUT}" bash -c attempt_install; then + echo "::endgroup::" + echo "Installed in $(( $(date +%s) - start ))s" + exit 0 + fi + echo "::endgroup::" + echo "::warning::apt install attempt ${attempt}/${ATTEMPTS} failed or exceeded ${ATTEMPT_TIMEOUT}s" + # A killed attempt can leave dpkg half configured + $SUDO dpkg --configure -a || true + done + + echo "::error::apt install failed after ${ATTEMPTS} attempts" + exit 1 diff --git a/.github/workflows/ci-backend.yml b/.github/workflows/ci-backend.yml index 1f5b6e82a..893c1b3d9 100644 --- a/.github/workflows/ci-backend.yml +++ b/.github/workflows/ci-backend.yml @@ -108,11 +108,10 @@ jobs: enable-cache: true python-version: ${{ steps.setup-python.outputs.python-version }} - name: Install system dependencies - timeout-minutes: 10 - run: | - sudo apt-get update -qq - sudo apt-get install -qq --no-install-recommends \ - unpaper tesseract-ocr imagemagick ghostscript poppler-utils + timeout-minutes: 12 + uses: ./.github/actions/apt-install + with: + packages: unpaper tesseract-ocr imagemagick ghostscript poppler-utils - name: Configure ImageMagick run: | sudo cp docker/rootfs/etc/ImageMagick-6/paperless-policy.xml /etc/ImageMagick-6/policy.xml diff --git a/.github/workflows/ci-frontend.yml b/.github/workflows/ci-frontend.yml index 8bdab8f71..af764cc83 100644 --- a/.github/workflows/ci-frontend.yml +++ b/.github/workflows/ci-frontend.yml @@ -231,9 +231,10 @@ jobs: - name: Install dependencies run: cd src-ui && pnpm install --frozen-lockfile - name: Install backend system dependencies - run: | - apt-get update - apt-get install --yes --quiet --no-install-recommends libmagic1 + timeout-minutes: 12 + uses: ./.github/actions/apt-install + with: + packages: libmagic1 - name: Install backend dependencies env: PYTHON_VERSION: ${{ steps.setup-python.outputs.python-version }} diff --git a/.github/workflows/ci-release.yml b/.github/workflows/ci-release.yml index 2c998456c..bdeec68b5 100644 --- a/.github/workflows/ci-release.yml +++ b/.github/workflows/ci-release.yml @@ -70,9 +70,10 @@ jobs: run: | uv sync --python "${PYTHON_VERSION}" --dev --frozen - name: Install system dependencies - run: | - sudo apt-get update -qq - sudo apt-get install -qq --no-install-recommends gettext liblept5 + timeout-minutes: 12 + uses: ./.github/actions/apt-install + with: + packages: gettext liblept5 # ---- Build Documentation ---- - name: Build documentation env: diff --git a/.github/workflows/translate-strings.yml b/.github/workflows/translate-strings.yml index 0bfc7ba1f..ed1504a5d 100644 --- a/.github/workflows/translate-strings.yml +++ b/.github/workflows/translate-strings.yml @@ -25,9 +25,10 @@ jobs: id: setup-python uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 - name: Install system dependencies - run: | - sudo apt-get update -qq - sudo apt-get install -qq --no-install-recommends gettext + timeout-minutes: 12 + uses: ./.github/actions/apt-install + with: + packages: gettext - name: Install uv uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0 with: