From fc6a41ccd0c882007fd6b1b5b9eaa2b26a9aa25d Mon Sep 17 00:00:00 2001 From: skjnldsv Date: Wed, 8 Apr 2026 13:49:04 +0200 Subject: [PATCH 1/6] perf(ci): add build cancel concurrency Signed-off-by: skjnldsv --- .github/workflows/sphinxbuild.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/sphinxbuild.yml b/.github/workflows/sphinxbuild.yml index e85645e4ffd..a7d876d4746 100644 --- a/.github/workflows/sphinxbuild.yml +++ b/.github/workflows/sphinxbuild.yml @@ -10,6 +10,10 @@ on: permissions: contents: read +concurrency: + group: build-documentation-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + jobs: build: From 35a73000d946ef4c732cdec650c0265265d982db Mon Sep 17 00:00:00 2001 From: skjnldsv Date: Wed, 8 Apr 2026 13:50:05 +0200 Subject: [PATCH 2/6] fix(ci): deploy pages via pull request with auto-merge Signed-off-by: skjnldsv --- .github/workflows/sphinxbuild.yml | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/.github/workflows/sphinxbuild.yml b/.github/workflows/sphinxbuild.yml index a7d876d4746..ab206ed8dcc 100644 --- a/.github/workflows/sphinxbuild.yml +++ b/.github/workflows/sphinxbuild.yml @@ -248,15 +248,25 @@ jobs: mkdir -p server/${{ steps.branch.outputs.branch_name }}/user_manual mv user_manual/index.html server/${{ steps.branch.outputs.branch_name }}/user_manual/index.html - - name: Commit ${{ steps.branch.outputs.branch_name }} documentation and push to gh-pages - run: | - git config --local user.email "nextcloud-command@users.noreply.github.com" - git config --local user.name "nextcloud-command" - git add . - git diff --staged --quiet || git commit -m "chore: deploy documentation for ${{ steps.branch.outputs.branch_name }}" - # Ensure we are up to date with the remote gh-pages branch - git pull --rebase origin gh-pages || true - git push origin gh-pages || echo "Nothing to push (expected if no changes)" + - name: Create Pull Request for documentation deployment + uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0 # v8.1.0 + id: cpr + with: + token: ${{ secrets.COMMAND_BOT_PAT }} + commit-message: "chore: deploy documentation for ${{ steps.branch.outputs.branch_name }}" + committer: nextcloud-command + author: nextcloud-command + signoff: true + branch: "automated/deploy/documentation-${{ steps.branch.outputs.branch_name }}" + base: gh-pages + title: "Deploy documentation for ${{ steps.branch.outputs.branch_name }}" + body: "Automated documentation deployment from branch ${{ github.ref_name }}" + delete-branch: true + labels: "automated, 3. to review" + + - name: Enable Pull Request Automerge + run: gh pr merge --merge --auto "${{ steps.cpr.outputs.pull-request-number }}" + if: steps.cpr.outputs.pull-request-number != '' env: GH_TOKEN: ${{ secrets.COMMAND_BOT_PAT }} From ddd1cdc88bc9161e7965deeefb2054f1c10a12f9 Mon Sep 17 00:00:00 2001 From: skjnldsv Date: Wed, 8 Apr 2026 13:13:26 +0200 Subject: [PATCH 3/6] perf(ci): improve LaTeX caching with dedicated setup job Signed-off-by: skjnldsv --- .github/workflows/sphinxbuild.yml | 90 +++++++++++++++++++------------ 1 file changed, 57 insertions(+), 33 deletions(-) diff --git a/.github/workflows/sphinxbuild.yml b/.github/workflows/sphinxbuild.yml index ab206ed8dcc..6c2da88f9df 100644 --- a/.github/workflows/sphinxbuild.yml +++ b/.github/workflows/sphinxbuild.yml @@ -15,10 +15,43 @@ 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 + 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 + key: latex-apt-${{ runner.os }}-${{ runner.arch }}-ubuntu-24.04-texlive-2023 + restore-keys: | + latex-apt-${{ runner.os }}-${{ runner.arch }}-ubuntu-24.04- + latex-apt-${{ runner.os }}-${{ runner.arch }}- + + - name: Download LaTeX packages (cache miss only) + if: steps.cache-latex-apt.outputs.cache-hit != 'true' + run: | + sudo DEBIAN_FRONTEND=noninteractive apt-get update + sudo DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ + --download-only \ + python3-pil python3-pip texlive-fonts-recommended latexmk \ + texlive-latex-extra texlive-latex-recommended texlive-xetex \ + texlive-fonts-extra-links texlive-fonts-extra xindy + # Ensure downloaded packages are owned by the current user so they can be cached + sudo chown -R $(id -u):$(id -g) ${{ runner.temp }}/.cache/archives + build: name: Build ${{ matrix.manual.name }} runs-on: ubuntu-latest + needs: setup-latex-cache strategy: matrix: @@ -62,49 +95,40 @@ jobs: - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - name: Configure apt cache to use /dev/shm + - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 + with: + python-version: '3.12' + cache: 'pip' + + - name: Install pip dependencies + run: pip install -r requirements.txt + + - name: Configure apt cache if: ${{ matrix.manual.build_pdf_path }} run: | - mkdir -p /dev/shm/apt/cache/archives - echo 'Dir::Cache::archives "/dev/shm/apt/cache/archives";' | sudo tee /etc/apt/apt.conf.d/apt-cache-shm + mkdir -p ${{ runner.temp }}/.cache/archives + echo 'Dir::Cache::archives "${{ runner.temp }}/.cache/archives";' | sudo tee /etc/apt/apt.conf.d/apt-cache-tmp - - name: Cache LaTeX apt packages + - name: Restore LaTeX apt cache if: ${{ matrix.manual.build_pdf_path }} - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + uses: actions/cache/restore@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 with: - path: /dev/shm/apt/cache/archives - key: latex-apt-${{ runner.os }}-${{ hashFiles('.github/workflows/sphinxbuild.yml') }} + # Use relative path https://github.com/actions/cache/issues/1127 + path: ${{ runner.temp }}/.cache/archives + key: latex-apt-${{ runner.os }}-${{ runner.arch }}-ubuntu-24.04-texlive-2023 restore-keys: | - latex-apt-${{ runner.os }}- + latex-apt-${{ runner.os }}-${{ runner.arch }}-ubuntu-24.04- + latex-apt-${{ runner.os }}-${{ runner.arch }}- - - name: Install LaTeX dependencies + - name: Install LaTeX from cache if: ${{ matrix.manual.build_pdf_path }} run: | sudo DEBIAN_FRONTEND=noninteractive apt-get update - sudo DEBIAN_FRONTEND=noninteractive apt-get install -y \ - --no-install-recommends \ - python3-pil \ - python3-pip \ - texlive-fonts-recommended \ - latexmk \ - texlive-latex-extra \ - texlive-latex-recommended \ - texlive-xetex \ - texlive-fonts-extra-links \ - texlive-fonts-extra \ - xindy - - - name: Fix apt cache ownership for caching - if: ${{ matrix.manual.build_pdf_path }} - run: sudo chown -R $(id -u):$(id -g) /dev/shm/apt/cache/archives - - - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 - with: - python-version: '3.12' - cache: 'pip' - - - name: Install pip dependencies - run: pip install -r requirements.txt + sudo DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ + --no-download \ + python3-pil python3-pip texlive-fonts-recommended latexmk \ + texlive-latex-extra texlive-latex-recommended texlive-xetex \ + texlive-fonts-extra-links texlive-fonts-extra xindy - name: Build html documentation run: cd ${{ matrix.manual.directory }} && make ${{ matrix.manual.make_target }} From 14993a653666dc54f13fbf59a61425945a720138 Mon Sep 17 00:00:00 2001 From: skjnldsv Date: Thu, 9 Apr 2026 09:47:48 +0200 Subject: [PATCH 4/6] fix(ci): clean up staging artifact structure and deploy logic Signed-off-by: skjnldsv --- .github/workflows/sphinxbuild.yml | 583 +++++++++++++++++++----------- 1 file changed, 378 insertions(+), 205 deletions(-) diff --git a/.github/workflows/sphinxbuild.yml b/.github/workflows/sphinxbuild.yml index 6c2da88f9df..33da2a51f70 100644 --- a/.github/workflows/sphinxbuild.yml +++ b/.github/workflows/sphinxbuild.yml @@ -48,7 +48,6 @@ jobs: sudo chown -R $(id -u):$(id -g) ${{ runner.temp }}/.cache/archives build: - name: Build ${{ matrix.manual.name }} runs-on: ubuntu-latest needs: setup-latex-cache @@ -83,219 +82,394 @@ jobs: 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@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 - with: - python-version: '3.12' - cache: 'pip' - - - name: Install pip dependencies - run: pip install -r requirements.txt - - - name: Configure apt cache - if: ${{ matrix.manual.build_pdf_path }} - run: | - mkdir -p ${{ runner.temp }}/.cache/archives - echo 'Dir::Cache::archives "${{ runner.temp }}/.cache/archives";' | sudo tee /etc/apt/apt.conf.d/apt-cache-tmp - - - name: Restore LaTeX apt cache - if: ${{ matrix.manual.build_pdf_path }} - uses: actions/cache/restore@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 - with: - # Use relative path https://github.com/actions/cache/issues/1127 - path: ${{ runner.temp }}/.cache/archives - key: latex-apt-${{ runner.os }}-${{ runner.arch }}-ubuntu-24.04-texlive-2023 - 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 }} - run: | - sudo DEBIAN_FRONTEND=noninteractive apt-get update - sudo DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ - --no-download \ - python3-pil python3-pip texlive-fonts-recommended latexmk \ - texlive-latex-extra texlive-latex-recommended texlive-xetex \ - texlive-fonts-extra-links texlive-fonts-extra xindy - - - name: Build html documentation - run: cd ${{ matrix.manual.directory }} && make ${{ matrix.manual.make_target }} - - - name: Compute PDF release version - if: ${{ matrix.manual.build_pdf_path }} - id: pdf_version - run: | - branch="${GITHUB_REF#refs/heads/}" - if [[ "$branch" == stable* ]]; then - echo "release=${branch#stable}" >> $GITHUB_OUTPUT - else - echo "release=latest" >> $GITHUB_OUTPUT - fi - - - name: Build pdf documentation - if: ${{ matrix.manual.build_pdf_path }} - env: - DOCS_RELEASE: ${{ steps.pdf_version.outputs.release }} - run: | - set -e - 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 - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 - if: ${{ matrix.manual.publish }} - with: - name: ${{ matrix.manual.name }} - path: ${{ matrix.manual.directory }}/${{ matrix.manual.build_path }} + - 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 - deploy: - name: Deploy pages + - name: Checkout repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + + - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 + with: + python-version: "3.12" + cache: "pip" + + - name: Install pip dependencies + run: pip install -r requirements.txt + + - name: Configure apt cache + if: ${{ matrix.manual.build_pdf_path }} + run: | + mkdir -p ${{ runner.temp }}/.cache/archives + echo 'Dir::Cache::archives "${{ runner.temp }}/.cache/archives";' | sudo tee /etc/apt/apt.conf.d/apt-cache-tmp + + - name: Restore LaTeX apt cache + if: ${{ matrix.manual.build_pdf_path }} + uses: actions/cache/restore@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + with: + # Use relative path https://github.com/actions/cache/issues/1127 + path: ${{ runner.temp }}/.cache/archives + key: latex-apt-${{ runner.os }}-${{ runner.arch }}-ubuntu-24.04-texlive-2023 + 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 }} + run: | + sudo DEBIAN_FRONTEND=noninteractive apt-get update + sudo DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ + --no-download \ + python3-pil python3-pip texlive-fonts-recommended latexmk \ + texlive-latex-extra texlive-latex-recommended texlive-xetex \ + texlive-fonts-extra-links texlive-fonts-extra xindy + + - name: Build html documentation + run: cd ${{ matrix.manual.directory }} && make ${{ matrix.manual.make_target }} + + - name: Compute PDF release version + if: ${{ matrix.manual.build_pdf_path }} + id: pdf_version + run: | + branch="${GITHUB_REF#refs/heads/}" + if [[ "$branch" == stable* ]]; then + echo "release=${branch#stable}" >> $GITHUB_OUTPUT + else + echo "release=latest" >> $GITHUB_OUTPUT + fi + + - name: Build pdf documentation + if: ${{ matrix.manual.build_pdf_path }} + env: + DOCS_RELEASE: ${{ steps.pdf_version.outputs.release }} + run: | + set -e + 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 + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 + if: ${{ matrix.manual.publish }} + with: + name: ${{ matrix.manual.name }} + path: ${{ matrix.manual.directory }}/${{ matrix.manual.build_path }} + + # ============================================================================ + # STAGE AND VALIDATE + # ============================================================================ + # This job is responsible for: + # 1. Determining deployment target folder names (branch_name/version_name) + # 2. Organizing build artifacts into a clean structure + # 3. Validating the documentation (link checking) + # 4. Uploading a minimal staging artifact for the deploy job + # + # IMPORTANT: This job does NOT modify gh-pages. It only prepares and validates + # the artifacts that will be deployed. The actual deployment happens in the + # deploy job. + # ============================================================================ + stage-and-check: + name: Stage and check documentation needs: build runs-on: ubuntu-latest - if: github.event_name == 'push' # Only deploy on push, not PR + + outputs: + # branch_name: The primary deployment folder name for this branch + # - master → "latest" + # - stable (if highest) → "stable" + # - stable (if not highest) → "" (numeric version) + branch_name: ${{ steps.branch.outputs.branch_name }} + + # additional_deployment: ONLY set if deploying the highest stable branch + # - If this IS the highest stable → "" (numeric version, e.g. "32") + # - Otherwise → "" (empty string) + # + # This allows the highest stable to be deployed to TWO locations: + # server/stable/ (via branch_name) + # server// (via additional_deployment) + 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 + + - name: Download all artifacts + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + path: artifacts/ + + # ======================================================================== + # PREPARE FOR LINK VALIDATION: BUILD FULL PAGE CONTEXT + # ======================================================================== + # We need to validate links in the new documentation, but some links may + # point to other versions or branches that are already deployed. So we: + # 1. Fetch the existing gh-pages content (old versions) + # 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: + ref: gh-pages + fetch-depth: 1 + path: validation-context + + # ======================================================================== + # DETERMINE DEPLOYMENT TARGETS (branch_name and version_name) + # ======================================================================== + # Logic: + # 1. Determine current_branch: use GITHUB_REF if push, GITHUB_BASE_REF if PR + # 2. Find the highest numbered stable branch from git remotes + # 3. Map the current branch to deployment folder names: + # + # master → branch_name=latest (no version_name) + # + # stable where N is highest → branch_name=stable, version_name= + # (deployed to both server/stable/ and server//) + # + # stable where N is not highest → branch_name= (no version_name) + # (deployed only to server//) + # + # Any other branch → branch_name= (no version_name) + # ======================================================================== + - name: Determine deployment targets (branch_name and version_name) + id: branch + run: | + # Determine which branch we're building from + current_branch=${GITHUB_REF#refs/heads/} + if [ "$GITHUB_EVENT_NAME" = "pull_request" ]; then + current_branch=${GITHUB_BASE_REF} + fi + + # Find the highest numbered stable branch from the remote + # e.g., "stable30", "stable31", "stable32" → extract "32" + highest_stable=$(git ls-remote --heads origin | sed -n 's?.*refs/heads/stable\([0-9]\{2\}\)$?\1?p' | sort -n | tail -1) + highest_stable_branch="stable${highest_stable}" + + echo "Current branch: $current_branch" + echo "Highest stable branch found: $highest_stable_branch" + + # Map branch to deployment folder names + case "$current_branch" in + "master") + # master always deploys to "latest" + echo "branch_name=latest" >> $GITHUB_OUTPUT + ;; + "$highest_stable_branch") + # Highest stable gets TWO locations: both "stable" and "" + echo "branch_name=stable" >> $GITHUB_OUTPUT + echo "additional_deployment=${highest_stable}" >> $GITHUB_OUTPUT + ;; + *) + # Other branches (including older stable branches) get their branch name + # For stable where N is not highest: strip "stable" prefix to get just "" + branch_for_deploy="${current_branch#stable}" + echo "branch_name=$branch_for_deploy" >> $GITHUB_OUTPUT + ;; + esac + + - name: Log deployment targets + run: | + echo "Deployment target folder: ${{ steps.branch.outputs.branch_name }}" + echo "Additional deployment folder (if applicable): ${{ steps.branch.outputs.additional_deployment }}" + + # ======================================================================== + # ORGANIZE ARTIFACTS FOR DEPLOYMENT + # ======================================================================== + # Create a clean, minimal staging structure: + # - Deploy only the NEW artifacts for this branch + # - No need to include existing versions (we'll merge them during deploy) + # ======================================================================== + - name: Organize artifacts for deployment + id: organize + run: | + branch="${{ steps.branch.outputs.branch_name }}" + + # Create the branch folder directly + mkdir -p "deploy-staging/${branch}" + + # Copy artifacts preserving their manual folder structure + # Each artifact (user_manual, admin_manual, developer_manual) contains + # the build output that should be placed in a folder named after the artifact + for artifact in artifacts/*; do + if [ -d "$artifact" ]; then + manual_name="$(basename "$artifact")" + # Create the manual-specific folder + mkdir -p "deploy-staging/${branch}/${manual_name}" + # Copy artifact contents into the manual folder + cp -r "$artifact/"* "deploy-staging/${branch}/${manual_name}/" + fi + done + + # Move PDF files to the root of the branch folder for cleaner structure + find "deploy-staging/${branch}/" -maxdepth 2 -name "*.pdf" -type f -exec mv {} "deploy-staging/${branch}/" \; + + # Clean up empty directories + find deploy-staging -type d -empty -delete + + echo "Staged artifacts for ${branch}:" + find deploy-staging -type f | head -20 + + # ======================================================================== + # UPLOAD STAGING ARTIFACTS + # ======================================================================== + # Upload only the NEW artifacts in their branch structure. + # The deploy job will handle merging with existing versions on gh-pages. + # ======================================================================== + - name: Upload staged artifacts + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 + with: + name: staged-documentation-${{ steps.branch.outputs.branch_name }} + path: deploy-staging/ + + # ============================================================================ + # DEPLOY + # ============================================================================ + # This job is responsible for: + # 1. Downloading the staged artifacts from stage-and-check + # 2. Applying them to the gh-pages branch + # 3. Creating a pull request for the deployment + # + # This job ONLY runs on pushes (not on pull requests), since we only want + # to deploy when code is merged to master or a stable branch. + # ============================================================================ + deploy: + name: Deploy documentation for gh-pages + needs: stage-and-check + if: github.event_name == 'push' + runs-on: ubuntu-latest permissions: contents: write + 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 Github Pages branch - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - ref: gh-pages - fetch-depth: 0 - token: ${{ secrets.COMMAND_BOT_PAT }} - - - name: Download all ${{ needs.build.outputs.branch_name }} artifacts - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 - with: - path: artifacts/ - - - name: Get branch name and find latest stable - id: branch - run: | - current_branch="${GITHUB_REF#refs/heads/}" - - # Find the highest numbered stable branch from the remote - highest_stable=$(git ls-remote --heads origin | sed -n 's?.*refs/heads/stable\([0-9]\{2\}\)$?\1?p' | sort -n | tail -1) - highest_stable_branch="stable${highest_stable}" - - echo "Current branch: $current_branch" - echo "Highest stable branch found: $highest_stable_branch" - - # Map actual branch names to deployment folder names - case "$current_branch" in - "master") - echo "branch_name=latest" >> $GITHUB_OUTPUT - ;; - "$highest_stable_branch") - echo "branch_name=stable" >> $GITHUB_OUTPUT - # Also record the numeric version so we can publish to server// too - echo "version_name=${highest_stable}" >> $GITHUB_OUTPUT - ;; - *) - # Remove stable prefix for current branch - current_branch="${current_branch#stable}" - echo "branch_name=$current_branch" >> $GITHUB_OUTPUT - ;; - esac - - echo "Deployment folder name: ${{ steps.branch.outputs.branch_name }}" - echo "Version name for additional deployment (if applicable): ${{ steps.branch.outputs.version_name }}" - - - name: Merge ${{ steps.branch.outputs.branch_name }} documentation artifacts into gh-pages - run: | - # List artifacts - ls -la artifacts/*/ - - # Cleanup old documentation - rm -rf ${{ steps.branch.outputs.branch_name }} - rm -rf server/${{ steps.branch.outputs.branch_name }} - mkdir -p server/${{ steps.branch.outputs.branch_name }} - - # Copy all built documentation into dedicated subdirectories - for artifact in artifacts/*; do - if [ -d "$artifact" ]; then - manual_name="$(basename "$artifact")" - mkdir -p "server/${{ steps.branch.outputs.branch_name }}/$manual_name" - cp -r "$artifact/"* "server/${{ steps.branch.outputs.branch_name }}/$manual_name/" + - 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: + ref: gh-pages + fetch-depth: 1 + persist-credentials: false + + - name: Download staged artifacts + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: staged-documentation-${{ needs.stage-and-check.outputs.branch_name }} + path: stage/ + + # ======================================================================== + # APPLY STAGED ARTIFACTS TO GH-PAGES + # ======================================================================== + # Strategy: + # - Copy from stage// to server// + # - If version_name is set, ALSO copy to server// + # - This allows the highest stable to live in both locations + # ======================================================================== + - name: Apply staged artifacts to gh-pages + id: apply + run: | + branch="${{ needs.stage-and-check.outputs.branch_name }}" + additional="${{ needs.stage-and-check.outputs.additional_deployment }}" + + changed=0 + + # Deploy to primary branch folder + echo "Deploying to server/${branch}/" + if [ -d "stage/${branch}" ]; then + rm -rf "server/${branch}" + mkdir -p "server/${branch}" + cp -r "stage/${branch}/"* "server/${branch}/" || true + changed=1 fi - done - - # Move pdf files to the root of the branch_name - mv server/${{ steps.branch.outputs.branch_name }}/*/*.pdf server/${{ steps.branch.outputs.branch_name }}/ || true - - # If this is the highest stable branch, also deploy to its versioned folder - if [ -n "${{ steps.branch.outputs.version_name }}" ]; then - rm -rf server/${{ steps.branch.outputs.version_name }} - cp -r server/${{ steps.branch.outputs.branch_name }} server/${{ steps.branch.outputs.version_name }} - fi - - # Cleanup - find . -type d -empty -delete - rm -rf artifacts - - - name: Add various redirects for go.php and user_manual english version - run: | - # Fetch source branches so git checkout origin/... works from the gh-pages checkout - git fetch origin ${{ github.event.repository.default_branch }} ${{ github.ref_name }} - - # Generate go.php redirect from main branch - git checkout origin/${{ github.event.repository.default_branch }} -- go.php/index.html - mkdir -p server/${{ steps.branch.outputs.branch_name }}/go.php - mv go.php/index.html server/${{ steps.branch.outputs.branch_name }}/go.php/index.html - - # Generate user_manual english redirect - git checkout origin/${{ github.ref_name }} -- user_manual/index.html - mkdir -p server/${{ steps.branch.outputs.branch_name }}/user_manual - mv user_manual/index.html server/${{ steps.branch.outputs.branch_name }}/user_manual/index.html - - - name: Create Pull Request for documentation deployment - uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0 # v8.1.0 - id: cpr - with: - token: ${{ secrets.COMMAND_BOT_PAT }} - commit-message: "chore: deploy documentation for ${{ steps.branch.outputs.branch_name }}" - committer: nextcloud-command - author: nextcloud-command - signoff: true - branch: "automated/deploy/documentation-${{ steps.branch.outputs.branch_name }}" - base: gh-pages - title: "Deploy documentation for ${{ steps.branch.outputs.branch_name }}" - body: "Automated documentation deployment from branch ${{ github.ref_name }}" - delete-branch: true - labels: "automated, 3. to review" - - - name: Enable Pull Request Automerge - run: gh pr merge --merge --auto "${{ steps.cpr.outputs.pull-request-number }}" - if: steps.cpr.outputs.pull-request-number != '' - env: - GH_TOKEN: ${{ secrets.COMMAND_BOT_PAT }} + + # If this is the highest stable branch, also deploy to its versioned folder + if [ -n "${additional}" ]; then + echo "Also deploying to server/${additional}/ (additional versioned deployment)" + rm -rf "server/${additional}" + mkdir -p "server/${additional}" + cp -r "stage/${branch}/"* "server/${additional}/" || true + changed=1 + fi + + # Clean up empty directories + find . -type d -empty -delete + + # Log the final directory structure for debugging + echo "Final server/ structure:" + find server -type d -maxdepth 2 + + # Check if there are actual changes + if git diff --quiet HEAD; then + echo "has_changes=false" >> $GITHUB_OUTPUT + else + echo "has_changes=true" >> $GITHUB_OUTPUT + fi + + - name: Create Pull Request for documentation deployment + uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0 # v8.1.0 + id: cpr + if: steps.apply.outputs.has_changes == 'true' + with: + token: ${{ secrets.COMMAND_BOT_PAT }} + commit-message: "chore: update documentation for ${{ needs.stage-and-check.outputs.branch_name }}" + committer: nextcloud-command + author: nextcloud-command + signoff: true + branch: "automated/deploy/documentation-${{ needs.stage-and-check.outputs.branch_name }}" + base: gh-pages + title: "Documentation update for ${{ needs.stage-and-check.outputs.branch_name }}" + body: "Automated documentation update from branch ${{ github.ref_name }}" + delete-branch: true + labels: "automated, 3. to review" + + - name: Enable Pull Request Automerge + run: gh pr merge --merge --auto "${{ steps.cpr.outputs.pull-request-number }}" + if: steps.cpr.outputs.pull-request-number != '' + env: + GH_TOKEN: ${{ secrets.COMMAND_BOT_PAT }} summary: - needs: build + needs: [build, stage-and-check, deploy] runs-on: ubuntu-latest-low if: always() @@ -305,6 +479,5 @@ jobs: name: build-deploy-summary steps: - # Only check if the build was successful - name: Summary status - run: if ${{ needs.build.result != 'success' }}; then exit 1; fi + run: if ${{ needs.build.result != 'success' || needs.stage-and-check.result != 'success' || needs.deploy.result != 'success' }}; then exit 1; fi From ef03f9050ce9fa51657adbc883e6f7a17dcb9639 Mon Sep 17 00:00:00 2001 From: skjnldsv Date: Thu, 9 Apr 2026 11:28:15 +0200 Subject: [PATCH 5/6] fix(ci): use cache to bundle staged artifacts Signed-off-by: skjnldsv --- .github/workflows/sphinxbuild.yml | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/.github/workflows/sphinxbuild.yml b/.github/workflows/sphinxbuild.yml index 33da2a51f70..5d57ee38579 100644 --- a/.github/workflows/sphinxbuild.yml +++ b/.github/workflows/sphinxbuild.yml @@ -342,16 +342,16 @@ jobs: find deploy-staging -type f | head -20 # ======================================================================== - # UPLOAD STAGING ARTIFACTS + # CACHE STAGING ARTIFACTS # ======================================================================== - # Upload only the NEW artifacts in their branch structure. - # The deploy job will handle merging with existing versions on gh-pages. + # Cache the staging folder for use in deploy job. + # This avoids artifact storage overhead (saves ~1.8GB). # ======================================================================== - - name: Upload staged artifacts - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 + - name: Cache staged artifacts + uses: actions/cache/save@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 with: - name: staged-documentation-${{ steps.branch.outputs.branch_name }} path: deploy-staging/ + key: staged-docs-${{ github.sha }} # ============================================================================ # DEPLOY @@ -391,11 +391,12 @@ jobs: fetch-depth: 1 persist-credentials: false - - name: Download staged artifacts - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + - name: Restore staged artifacts from cache + uses: actions/cache/restore@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 with: - name: staged-documentation-${{ needs.stage-and-check.outputs.branch_name }} path: stage/ + key: staged-docs-${{ github.sha }} + fail-on-cache-miss: true # ======================================================================== # APPLY STAGED ARTIFACTS TO GH-PAGES From b735b478cfe282d26c655d9110585db736484fde Mon Sep 17 00:00:00 2001 From: skjnldsv Date: Thu, 9 Apr 2026 11:38:23 +0200 Subject: [PATCH 6/6] perf(ci): optimize latex caching Signed-off-by: skjnldsv --- .github/workflows/sphinxbuild.yml | 47 +++++++++++++++++++++++-------- 1 file changed, 36 insertions(+), 11 deletions(-) diff --git a/.github/workflows/sphinxbuild.yml b/.github/workflows/sphinxbuild.yml index 5d57ee38579..7eb391862a6 100644 --- a/.github/workflows/sphinxbuild.yml +++ b/.github/workflows/sphinxbuild.yml @@ -34,6 +34,7 @@ jobs: 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' @@ -47,6 +48,13 @@ jobs: # Ensure downloaded packages are owned by the current user so they can be cached sudo chown -R $(id -u):$(id -g) ${{ runner.temp }}/.cache/archives + - 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 + key: latex-apt-${{ runner.os }}-${{ runner.arch }}-ubuntu-24.04-texlive-2023 + build: name: Build ${{ matrix.manual.name }} runs-on: ubuntu-latest @@ -121,6 +129,7 @@ jobs: - name: Install LaTeX from cache if: ${{ matrix.manual.build_pdf_path }} + timeout-minutes: 3 run: | sudo DEBIAN_FRONTEND=noninteractive apt-get update sudo DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ @@ -317,7 +326,7 @@ jobs: branch="${{ steps.branch.outputs.branch_name }}" # Create the branch folder directly - mkdir -p "deploy-staging/${branch}" + mkdir -p "stage/${branch}" # Copy artifacts preserving their manual folder structure # Each artifact (user_manual, admin_manual, developer_manual) contains @@ -326,20 +335,20 @@ jobs: if [ -d "$artifact" ]; then manual_name="$(basename "$artifact")" # Create the manual-specific folder - mkdir -p "deploy-staging/${branch}/${manual_name}" + mkdir -p "stage/${branch}/${manual_name}" # Copy artifact contents into the manual folder - cp -r "$artifact/"* "deploy-staging/${branch}/${manual_name}/" + cp -r "$artifact/"* "stage/${branch}/${manual_name}/" fi done # Move PDF files to the root of the branch folder for cleaner structure - find "deploy-staging/${branch}/" -maxdepth 2 -name "*.pdf" -type f -exec mv {} "deploy-staging/${branch}/" \; + find "stage/${branch}/" -maxdepth 2 -name "*.pdf" -type f -exec mv {} "stage/${branch}/" \; # Clean up empty directories - find deploy-staging -type d -empty -delete + find stage -type d -empty -delete echo "Staged artifacts for ${branch}:" - find deploy-staging -type f | head -20 + find stage -type f | head -20 # ======================================================================== # CACHE STAGING ARTIFACTS @@ -350,7 +359,7 @@ jobs: - name: Cache staged artifacts uses: actions/cache/save@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 with: - path: deploy-staging/ + path: stage/ key: staged-docs-${{ github.sha }} # ============================================================================ @@ -446,20 +455,27 @@ jobs: echo "has_changes=true" >> $GITHUB_OUTPUT fi + # Remove the stage/ directory BEFORE creating the PR so it doesn't get committed + - name: Clean up staging cache before commit + run: rm -rf stage/ + - name: Create Pull Request for documentation deployment uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0 # v8.1.0 id: cpr if: steps.apply.outputs.has_changes == 'true' with: token: ${{ secrets.COMMAND_BOT_PAT }} - commit-message: "chore: update documentation for ${{ needs.stage-and-check.outputs.branch_name }}" + commit-message: "chore: update documentation for `${{ needs.stage-and-check.outputs.branch_name }}`" committer: nextcloud-command author: nextcloud-command signoff: true branch: "automated/deploy/documentation-${{ needs.stage-and-check.outputs.branch_name }}" base: gh-pages - title: "Documentation update for ${{ needs.stage-and-check.outputs.branch_name }}" - body: "Automated documentation update from branch ${{ github.ref_name }}" + title: "Documentation update for `${{ needs.stage-and-check.outputs.branch_name }}`" + body: | + This PR was automatically generated by the CI workflow and + includes the latest changes for the `${{ needs.stage-and-check.outputs.branch_name }}` branch. + delete-branch: true labels: "automated, 3. to review" @@ -481,4 +497,13 @@ jobs: steps: - name: Summary status - run: if ${{ needs.build.result != 'success' || needs.stage-and-check.result != 'success' || needs.deploy.result != 'success' }}; then exit 1; fi + 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 + 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 + fi +