diff --git a/.docker/sphinx-latex/Dockerfile b/.docker/sphinx-latex/Dockerfile index d4c28364b85..0ac6a4ca836 100644 --- a/.docker/sphinx-latex/Dockerfile +++ b/.docker/sphinx-latex/Dockerfile @@ -25,6 +25,6 @@ RUN apt-get update \ && mktexlsr \ && rm -rf /var/lib/apt/lists/* -# Allow pip to install packages system-wide inside the container (used by -# actions/setup-python and plain pip invocations running as root) +# Allow pip to install packages system-wide inside the container when running +# as root (avoids PEP 668 "externally managed environment" error) RUN printf '[global]\nbreak-system-packages = true\n' > /etc/pip.conf diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml index a6839cef93b..5a600952ba3 100644 --- a/.github/workflows/docker-build.yml +++ b/.github/workflows/docker-build.yml @@ -16,8 +16,8 @@ permissions: packages: write jobs: - build-and-push: - name: Build and push sphinx-latex image + build-and-publish: + name: Build and publish sphinx-latex Docker image runs-on: ubuntu-latest steps: @@ -44,7 +44,7 @@ jobs: type=raw,value=latest,enable={{is_default_branch}} type=sha,prefix=sha- - - name: Build and push Docker image + - name: Build and publish Docker image uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0 with: context: .docker/sphinx-latex diff --git a/.github/workflows/sphinxbuild.yml b/.github/workflows/sphinxbuild.yml index 13a837a93c6..f6426bd15a0 100644 --- a/.github/workflows/sphinxbuild.yml +++ b/.github/workflows/sphinxbuild.yml @@ -15,62 +15,15 @@ concurrency: cancel-in-progress: true jobs: - setup-latex-cache: - name: Cache LaTeX packages - runs-on: ubuntu-latest - - steps: - - name: Configure apt cache - run: | - mkdir -p ${{ runner.temp }}/.cache/archives - mkdir -p ${{ runner.temp }}/.cache/lists - echo 'Dir::Cache::archives "${{ runner.temp }}/.cache/archives";' | sudo tee /etc/apt/apt.conf.d/apt-cache-tmp - - - name: Cache LaTeX apt packages - id: cache-latex-apt - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 - with: - path: | - ${{ runner.temp }}/.cache/archives - ${{ runner.temp }}/.cache/lists - key: latex-apt-${{ runner.os }}-${{ runner.arch }}-ubuntu-24.04-texlive-2023-v2 - restore-keys: | - latex-apt-${{ runner.os }}-${{ runner.arch }}-ubuntu-24.04- - latex-apt-${{ runner.os }}-${{ runner.arch }}- - lookup-only: true - - - name: Download LaTeX packages (cache miss only) - if: steps.cache-latex-apt.outputs.cache-hit != 'true' - run: | - for i in 1 2 3; do - sudo DEBIAN_FRONTEND=noninteractive apt-get update \ - -o Dir::State::lists="${{ runner.temp }}/.cache/lists" \ - -o Acquire::Retries=3 && break - echo "apt-get update failed (attempt $i), retrying in 15s..." - sleep 15 - done - sudo DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ - --download-only \ - -o Dir::State::lists="${{ runner.temp }}/.cache/lists" \ - python3-pil python3-pip texlive-fonts-recommended latexmk \ - texlive-latex-extra texlive-latex-recommended texlive-xetex \ - texlive-fonts-extra-links texlive-fonts-extra xindy tex-gyre - # Ensure downloaded packages are owned by the current user so they can be cached - sudo chown -R $(id -u):$(id -g) ${{ runner.temp }}/.cache/ - - - name: Save LaTeX apt cache (cache miss only) - if: steps.cache-latex-apt.outputs.cache-hit != 'true' - uses: actions/cache/save@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 - with: - path: | - ${{ runner.temp }}/.cache/archives - ${{ runner.temp }}/.cache/lists - key: latex-apt-${{ runner.os }}-${{ runner.arch }}-ubuntu-24.04-texlive-2023-v2 - - build: - name: Build ${{ matrix.manual.name }} + # ============================================================================ + # BUILD HTML + # ============================================================================ + # Builds the HTML documentation for all manuals. No LaTeX required. + # Starts immediately without waiting for any setup job. + # ============================================================================ + build-html: + name: Building ${{ matrix.manual.name }} HTML runs-on: ubuntu-latest - needs: setup-latex-cache strategy: fail-fast: false @@ -80,7 +33,6 @@ jobs: directory: "user_manual" make_target: "html" build_path: "_build/html" - build_pdf_path: "_build/latex" publish: true - name: "user_manual-en" @@ -99,68 +51,73 @@ jobs: directory: "admin_manual" make_target: "html" build_path: "_build/html/com" - build_pdf_path: "_build/latex" publish: true steps: - - name: Cache git metadata - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 - with: - path: .git - key: git-metadata-${{ github.sha }} - restore-keys: | - git-metadata-${{ github.sha }} - git-metadata - - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + - name: Set up Python + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: "3.13" cache: "pip" - name: Install pip dependencies - run: pip install -r requirements.txt + run: python -m pip install -r requirements.txt - - name: Configure apt cache - if: ${{ matrix.manual.build_pdf_path }} - run: | - mkdir -p ${{ runner.temp }}/.cache/archives - mkdir -p ${{ runner.temp }}/.cache/lists - echo 'Dir::Cache::archives "${{ runner.temp }}/.cache/archives";' | sudo tee /etc/apt/apt.conf.d/apt-cache-tmp + - name: Build html documentation + run: cd ${{ matrix.manual.directory }} && make ${{ matrix.manual.make_target }} - - name: Restore LaTeX apt cache - if: ${{ matrix.manual.build_pdf_path }} - uses: actions/cache/restore@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + - name: Upload static documentation + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + if: ${{ matrix.manual.publish }} with: - # Use relative path https://github.com/actions/cache/issues/1127 - path: | - ${{ runner.temp }}/.cache/archives - ${{ runner.temp }}/.cache/lists - key: latex-apt-${{ runner.os }}-${{ runner.arch }}-ubuntu-24.04-texlive-2023-v2 - restore-keys: | - latex-apt-${{ runner.os }}-${{ runner.arch }}-ubuntu-24.04- - latex-apt-${{ runner.os }}-${{ runner.arch }}- - - - name: Install LaTeX from cache - if: ${{ matrix.manual.build_pdf_path }} - timeout-minutes: 5 - run: | - debs=(${{ runner.temp }}/.cache/archives/*.deb) - if [ ! -e "${debs[0]}" ]; then - echo "No .deb files found in cache archives — cache may be empty or missing." >&2 - exit 1 - fi - sudo dpkg -i --force-depends "${debs[@]}" - sudo DEBIAN_FRONTEND=noninteractive apt-get install -f -y --no-install-recommends \ - -o Dir::State::lists="${{ runner.temp }}/.cache/lists" + name: ${{ matrix.manual.name }} + path: ${{ matrix.manual.directory }}/${{ matrix.manual.build_path }} - - name: Build html documentation - run: cd ${{ matrix.manual.directory }} && make ${{ matrix.manual.make_target }} + # ============================================================================ + # BUILD PDF + # ============================================================================ + # Builds the PDF documentation using the pre-built sphinx-latex Docker image. + # The image already contains all LaTeX packages, so no apt install is needed. + # Starts immediately without waiting for any setup job. + # ============================================================================ + build-pdf: + name: Building ${{ matrix.manual.name }} PDF + runs-on: ubuntu-latest + # Use the pre-built sphinx-latex image which has all LaTeX packages pre-installed. + # The image is built from .docker/sphinx-latex/Dockerfile by the docker-build.yml workflow + # and published to GHCR. Using latest ensures we always use the current image for this repo. + container: ghcr.io/nextcloud/documentation/sphinx-latex:latest + + strategy: + fail-fast: false + matrix: + manual: + - name: "user_manual" + directory: "user_manual" + build_pdf_path: "_build/latex/Nextcloud_User_Manual.pdf" + + - name: "admin_manual" + directory: "admin_manual" + build_pdf_path: "_build/latex/Nextcloud_Server_Administration_Manual.pdf" + + steps: + - name: Checkout repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + + - name: Set up Python + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + with: + python-version: "3.13" + # pip cache is not compatible with the Docker container + # cache: "pip" + + - name: Install pip dependencies + run: python -m pip install -r requirements.txt - name: Compute PDF release version - if: ${{ matrix.manual.build_pdf_path }} id: pdf_version run: | branch="${GITHUB_REF#refs/heads/}" @@ -171,7 +128,6 @@ jobs: fi - name: Build pdf documentation - if: ${{ matrix.manual.build_pdf_path }} env: DOCS_RELEASE: ${{ steps.pdf_version.outputs.release }} run: | @@ -179,14 +135,12 @@ jobs: cd ${{ matrix.manual.directory }} make latexpdf ls -la ${{ matrix.manual.build_pdf_path }} - cp ${{ matrix.manual.build_pdf_path }}/*.pdf ${{ matrix.manual.build_path }}/ - - name: Upload static documentation + - name: Upload PDF documentation uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 - if: ${{ matrix.manual.publish }} with: - name: ${{ matrix.manual.name }} - path: ${{ matrix.manual.directory }}/${{ matrix.manual.build_path }} + name: ${{ matrix.manual.name }}-pdf + path: ${{ matrix.manual.directory }}/${{ matrix.manual.build_pdf_path }} # ============================================================================ # STAGE AND VALIDATE @@ -203,7 +157,7 @@ jobs: # ============================================================================ stage-and-check: name: Stage and check documentation - needs: build + needs: [build-html, build-pdf] runs-on: ubuntu-latest outputs: @@ -223,15 +177,6 @@ jobs: additional_deployment: ${{ steps.branch.outputs.additional_deployment }} steps: - - name: Cache git metadata - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 - with: - path: .git - key: git-metadata-${{ github.sha }} - restore-keys: | - git-metadata-${{ github.sha }} - git-metadata - - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 @@ -249,22 +194,6 @@ jobs: # 2. Merge in the new artifacts (what we're about to deploy) # 3. Run link checks only on the NEW content, but with full context # ======================================================================== - # ======================================================================== - # FETCH GH-PAGES FOR LINK VALIDATION CONTEXT - # ======================================================================== - # We need the existing gh-pages content (old versions) so that link - # validation can resolve cross-version references. We use git cache - # to make this fast. - # ======================================================================== - - name: Cache git metadata for gh-pages - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 - with: - path: .git - key: git-metadata-${{ github.sha }} - restore-keys: | - git-metadata-${{ github.sha }} - git-metadata - - name: Checkout gh-pages branch for validation context uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: @@ -402,15 +331,6 @@ jobs: pull-requests: write steps: - - name: Cache git metadata - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 - with: - path: .git - key: git-metadata-${{ github.sha }} - restore-keys: | - git-metadata-${{ github.sha }} - git-metadata - - name: Checkout gh-pages branch uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: @@ -526,7 +446,7 @@ jobs: GH_TOKEN: ${{ secrets.COMMAND_BOT_PAT }} summary: - needs: [build, stage-and-check, deploy] + needs: [build-html, build-pdf, stage-and-check, deploy] runs-on: ubuntu-latest-low if: always() @@ -540,10 +460,10 @@ jobs: run: | if ${{ github.event_name == 'pull_request' }} then - echo "This workflow ran for a pull request. We need build and stage-and-check to succeed, but deploy will be skipped" - if ${{ needs.build.result != 'success' || needs.stage-and-check.result != 'success' || needs.deploy.result != 'skipped' }}; then exit 1; fi + echo "This workflow ran for a pull request. We need build-html, build-pdf and stage-and-check to succeed, but deploy will be skipped" + if ${{ needs.build-html.result != 'success' || needs.build-pdf.result != 'success' || needs.stage-and-check.result != 'success' || needs.deploy.result != 'skipped' }}; then exit 1; fi else echo "This workflow ran for a push. We need all jobs to succeed, including deploy" - if ${{ needs.build.result != 'success' || needs.stage-and-check.result != 'success' || needs.deploy.result != 'success' }}; then exit 1; fi + if ${{ needs.build-html.result != 'success' || needs.build-pdf.result != 'success' || needs.stage-and-check.result != 'success' || needs.deploy.result != 'success' }}; then exit 1; fi fi