From 1b996aeef2cb2ea702c93973fd16cbb300296932 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 17 Apr 2026 08:12:33 +0000 Subject: [PATCH 01/15] feat: add netlify-preview job to sphinxbuild workflow Agent-Logs-Url: https://github.com/nextcloud/documentation/sessions/12ab3238-1214-4ff1-b0cf-67836a1abc12 Co-authored-by: skjnldsv <14975046+skjnldsv@users.noreply.github.com> --- .github/workflows/sphinxbuild.yml | 84 ++++++++++++++++++++++++++++++- 1 file changed, 82 insertions(+), 2 deletions(-) diff --git a/.github/workflows/sphinxbuild.yml b/.github/workflows/sphinxbuild.yml index 150e989d9d0..f4669252341 100644 --- a/.github/workflows/sphinxbuild.yml +++ b/.github/workflows/sphinxbuild.yml @@ -503,8 +503,86 @@ jobs: env: GH_TOKEN: ${{ secrets.COMMAND_BOT_PAT }} + # ============================================================================ + # NETLIFY PREVIEW + # ============================================================================ + # This job deploys a per-PR documentation preview to Netlify. + # It only runs on pull_request events (never on push/merge). + # + # The stable preview URL for PR #N is: + # https://pr---.netlify.app/ + # + # Required repository secrets: + # NETLIFY_AUTH_TOKEN – Netlify personal access token + # NETLIFY_SITE_ID – ID of the target Netlify site + # ============================================================================ + netlify-preview: + name: Deploy preview to Netlify + needs: stage-and-check + if: github.event_name == 'pull_request' + runs-on: ubuntu-latest + + permissions: + contents: read + pull-requests: write + + steps: + - name: Restore staged artifacts from cache + uses: actions/cache/restore@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + with: + path: stage/ + key: staged-docs-${{ github.sha }} + fail-on-cache-miss: true + + - name: Assemble Netlify deploy directory + run: | + branch="${{ needs.stage-and-check.outputs.branch_name }}" + mkdir -p netlify-deploy + + # Flatten stage/// into netlify-deploy// + for manual_dir in "stage/${branch}/"/*/; do + if [ -d "$manual_dir" ]; then + manual_name="$(basename "$manual_dir")" + cp -r "$manual_dir" "netlify-deploy/${manual_name}" + fi + done + + # Create a root index linking to each manual + cat > netlify-deploy/index.html << 'ENDHTML' + + + + + + Nextcloud Documentation Preview + + +

Nextcloud Documentation Preview

+ + + + ENDHTML + + - name: Deploy to Netlify + uses: nwtgck/actions-netlify@4cbaf4c08f1a7bfa537d6113472ef4424e4eb654 # v3.0 + with: + publish-dir: './netlify-deploy' + production-deploy: false + github-token: ${{ secrets.GITHUB_TOKEN }} + deploy-message: "Preview for PR #${{ github.event.number }}" + alias: pr-${{ github.event.number }} + enable-pull-request-comment: true + enable-commit-comment: false + env: + NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }} + NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }} + summary: - needs: [build, stage-and-check, deploy] + needs: [build, stage-and-check, deploy, netlify-preview] runs-on: ubuntu-latest-low if: always() @@ -520,8 +598,10 @@ jobs: 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 + if ${{ needs.netlify-preview.result != 'success' && needs.netlify-preview.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.netlify-preview.result != 'skipped' }}; then exit 1; fi fi - + From 1d35c340acdc2d5e89983698889f1bedd1df800d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 17 Apr 2026 08:27:31 +0000 Subject: [PATCH 02/15] feat: deploy full merged documentation to Netlify preview Agent-Logs-Url: https://github.com/nextcloud/documentation/sessions/131cd607-27ec-4bd2-abed-7ede744acad9 Co-authored-by: skjnldsv <14975046+skjnldsv@users.noreply.github.com> --- .github/workflows/sphinxbuild.yml | 99 +++++++++++++++++++------------ 1 file changed, 62 insertions(+), 37 deletions(-) diff --git a/.github/workflows/sphinxbuild.yml b/.github/workflows/sphinxbuild.yml index f4669252341..c787d9dd42a 100644 --- a/.github/workflows/sphinxbuild.yml +++ b/.github/workflows/sphinxbuild.yml @@ -380,6 +380,64 @@ jobs: path: stage/ key: staged-docs-${{ github.sha }} + # ======================================================================== + # ASSEMBLE FULL NETLIFY PREVIEW CONTENT + # ======================================================================== + # Combine the existing gh-pages content (all versions) with the newly + # built artifacts so the Netlify preview shows the complete site. + # + # Structure of the merged output (netlify-full/): + # netlify-full/ + # ├── index.html ← generated version listing + # ├── latest/ ← from gh-pages or newly built + # ├── stable/ ← from gh-pages or newly built + # └── / ← older versions from gh-pages + # ======================================================================== + - name: Assemble full documentation for Netlify preview + run: | + branch="${{ steps.branch.outputs.branch_name }}" + additional="${{ steps.branch.outputs.additional_deployment }}" + + mkdir -p netlify-full + + # Start with the full existing gh-pages content as the base + if [ -d "validation-context/server" ]; then + cp -r validation-context/server/. netlify-full/ + fi + + # Override the current branch's folder with the freshly built content + rm -rf "netlify-full/${branch}" + cp -r "stage/${branch}" "netlify-full/${branch}" + + # For the highest stable branch, also override its versioned folder + if [ -n "${additional}" ]; then + rm -rf "netlify-full/${additional}" + cp -r "stage/${branch}" "netlify-full/${additional}" + fi + + # Generate a root index.html listing all version folders + echo '' > netlify-full/index.html + echo '' >> netlify-full/index.html + echo '' >> netlify-full/index.html + echo '' >> netlify-full/index.html + echo 'Nextcloud Documentation Preview' >> netlify-full/index.html + echo '' >> netlify-full/index.html + echo '

Nextcloud Documentation Preview

    ' >> netlify-full/index.html + for version_dir in netlify-full/*/; do + version="$(basename "$version_dir")" + echo "
  • ${version}
  • " >> netlify-full/index.html + done + echo '
' >> netlify-full/index.html + + echo "Full Netlify deploy structure:" + find netlify-full -maxdepth 2 -type d + + - name: Cache full documentation for Netlify preview + uses: actions/cache/save@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + with: + path: netlify-full/ + key: netlify-full-docs-${{ github.sha }} + # ============================================================================ # DEPLOY # ============================================================================ @@ -527,50 +585,17 @@ jobs: pull-requests: write steps: - - name: Restore staged artifacts from cache + - name: Restore full documentation from cache uses: actions/cache/restore@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 with: - path: stage/ - key: staged-docs-${{ github.sha }} + path: netlify-full/ + key: netlify-full-docs-${{ github.sha }} fail-on-cache-miss: true - - name: Assemble Netlify deploy directory - run: | - branch="${{ needs.stage-and-check.outputs.branch_name }}" - mkdir -p netlify-deploy - - # Flatten stage/// into netlify-deploy// - for manual_dir in "stage/${branch}/"/*/; do - if [ -d "$manual_dir" ]; then - manual_name="$(basename "$manual_dir")" - cp -r "$manual_dir" "netlify-deploy/${manual_name}" - fi - done - - # Create a root index linking to each manual - cat > netlify-deploy/index.html << 'ENDHTML' - - - - - - Nextcloud Documentation Preview - - -

Nextcloud Documentation Preview

- - - - ENDHTML - - name: Deploy to Netlify uses: nwtgck/actions-netlify@4cbaf4c08f1a7bfa537d6113472ef4424e4eb654 # v3.0 with: - publish-dir: './netlify-deploy' + publish-dir: './netlify-full' production-deploy: false github-token: ${{ secrets.GITHUB_TOKEN }} deploy-message: "Preview for PR #${{ github.event.number }}" From aec4ec00c55768a7465bc21fbcb39e23cad71473 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 17 Apr 2026 09:31:19 +0000 Subject: [PATCH 03/15] Initial plan From 0bdd6b8017ac9733b17be1501adf269afa1b658c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 17 Apr 2026 09:35:37 +0000 Subject: [PATCH 04/15] refactor: move Netlify preview into deploy job, avoiding 2GB cache round-trip Agent-Logs-Url: https://github.com/nextcloud/documentation/sessions/1f6603ff-c52e-4e41-bf59-d23447600bfb Co-authored-by: skjnldsv <14975046+skjnldsv@users.noreply.github.com> --- .github/workflows/sphinxbuild.yml | 125 +++++------------------------- 1 file changed, 20 insertions(+), 105 deletions(-) diff --git a/.github/workflows/sphinxbuild.yml b/.github/workflows/sphinxbuild.yml index c787d9dd42a..fa6f2516058 100644 --- a/.github/workflows/sphinxbuild.yml +++ b/.github/workflows/sphinxbuild.yml @@ -380,79 +380,25 @@ jobs: path: stage/ key: staged-docs-${{ github.sha }} - # ======================================================================== - # ASSEMBLE FULL NETLIFY PREVIEW CONTENT - # ======================================================================== - # Combine the existing gh-pages content (all versions) with the newly - # built artifacts so the Netlify preview shows the complete site. - # - # Structure of the merged output (netlify-full/): - # netlify-full/ - # ├── index.html ← generated version listing - # ├── latest/ ← from gh-pages or newly built - # ├── stable/ ← from gh-pages or newly built - # └── / ← older versions from gh-pages - # ======================================================================== - - name: Assemble full documentation for Netlify preview - run: | - branch="${{ steps.branch.outputs.branch_name }}" - additional="${{ steps.branch.outputs.additional_deployment }}" - - mkdir -p netlify-full - - # Start with the full existing gh-pages content as the base - if [ -d "validation-context/server" ]; then - cp -r validation-context/server/. netlify-full/ - fi - - # Override the current branch's folder with the freshly built content - rm -rf "netlify-full/${branch}" - cp -r "stage/${branch}" "netlify-full/${branch}" - - # For the highest stable branch, also override its versioned folder - if [ -n "${additional}" ]; then - rm -rf "netlify-full/${additional}" - cp -r "stage/${branch}" "netlify-full/${additional}" - fi - - # Generate a root index.html listing all version folders - echo '' > netlify-full/index.html - echo '' >> netlify-full/index.html - echo '' >> netlify-full/index.html - echo '' >> netlify-full/index.html - echo 'Nextcloud Documentation Preview' >> netlify-full/index.html - echo '' >> netlify-full/index.html - echo '

Nextcloud Documentation Preview

    ' >> netlify-full/index.html - for version_dir in netlify-full/*/; do - version="$(basename "$version_dir")" - echo "
  • ${version}
  • " >> netlify-full/index.html - done - echo '
' >> netlify-full/index.html - - echo "Full Netlify deploy structure:" - find netlify-full -maxdepth 2 -type d - - - name: Cache full documentation for Netlify preview - uses: actions/cache/save@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 - with: - path: netlify-full/ - key: netlify-full-docs-${{ github.sha }} - # ============================================================================ # 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 handles two scenarios: + # + # Push to master/stable: + # Applies the staged artifacts to the gh-pages branch and creates a PR. + # + # Pull request: + # Applies the staged artifacts on top of gh-pages content and deploys the + # result to a Netlify preview (no gh-pages PR is created). # - # 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. + # Required repository secrets for Netlify previews: + # NETLIFY_AUTH_TOKEN – Netlify personal access token + # NETLIFY_SITE_ID – ID of the target Netlify site # ============================================================================ deploy: - name: Deploy documentation for gh-pages + name: Deploy documentation needs: stage-and-check - if: github.event_name == 'push' runs-on: ubuntu-latest permissions: @@ -533,12 +479,13 @@ jobs: # Remove the stage/ directory BEFORE creating the PR so it doesn't get committed - name: Clean up staging cache before commit + if: github.event_name == 'push' 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' + if: github.event_name == 'push' && 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 }}`" @@ -557,45 +504,15 @@ jobs: - name: Enable Pull Request Automerge run: gh pr merge --merge --auto "${{ steps.cpr.outputs.pull-request-number }}" - if: steps.cpr.outputs.pull-request-number != '' + if: github.event_name == 'push' && steps.cpr.outputs.pull-request-number != '' env: GH_TOKEN: ${{ secrets.COMMAND_BOT_PAT }} - # ============================================================================ - # NETLIFY PREVIEW - # ============================================================================ - # This job deploys a per-PR documentation preview to Netlify. - # It only runs on pull_request events (never on push/merge). - # - # The stable preview URL for PR #N is: - # https://pr---.netlify.app/ - # - # Required repository secrets: - # NETLIFY_AUTH_TOKEN – Netlify personal access token - # NETLIFY_SITE_ID – ID of the target Netlify site - # ============================================================================ - netlify-preview: - name: Deploy preview to Netlify - needs: stage-and-check - if: github.event_name == 'pull_request' - runs-on: ubuntu-latest - - permissions: - contents: read - pull-requests: write - - steps: - - name: Restore full documentation from cache - uses: actions/cache/restore@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 - with: - path: netlify-full/ - key: netlify-full-docs-${{ github.sha }} - fail-on-cache-miss: true - - name: Deploy to Netlify uses: nwtgck/actions-netlify@4cbaf4c08f1a7bfa537d6113472ef4424e4eb654 # v3.0 + if: github.event_name == 'pull_request' with: - publish-dir: './netlify-full' + publish-dir: './server' production-deploy: false github-token: ${{ secrets.GITHUB_TOKEN }} deploy-message: "Preview for PR #${{ github.event.number }}" @@ -607,7 +524,7 @@ jobs: NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }} summary: - needs: [build, stage-and-check, deploy, netlify-preview] + needs: [build, stage-and-check, deploy] runs-on: ubuntu-latest-low if: always() @@ -621,12 +538,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 - if ${{ needs.netlify-preview.result != 'success' && needs.netlify-preview.result != 'skipped' }}; then exit 1; fi + echo "This workflow ran for a pull request. We need build, stage-and-check, and deploy (Netlify preview) to succeed" + if ${{ needs.build.result != 'success' || needs.stage-and-check.result != 'success' || needs.deploy.result != 'success' }}; 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.netlify-preview.result != 'skipped' }}; then exit 1; fi fi From b3fa78823c433d5a9a5835f8d334be9f58c8c1dc Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 17 Apr 2026 10:29:36 +0000 Subject: [PATCH 05/15] refactor: use netlify-cli directly, preview current branch only with simple index.html Agent-Logs-Url: https://github.com/nextcloud/documentation/sessions/af4f573e-5b1e-4d54-bae0-dbfec917b30c Co-authored-by: skjnldsv <14975046+skjnldsv@users.noreply.github.com> --- .github/workflows/sphinxbuild.yml | 159 ++++++++++++++++++++++++------ 1 file changed, 129 insertions(+), 30 deletions(-) diff --git a/.github/workflows/sphinxbuild.yml b/.github/workflows/sphinxbuild.yml index fa6f2516058..29ab9fef660 100644 --- a/.github/workflows/sphinxbuild.yml +++ b/.github/workflows/sphinxbuild.yml @@ -383,22 +383,18 @@ jobs: # ============================================================================ # DEPLOY # ============================================================================ - # This job handles two scenarios: - # - # Push to master/stable: - # Applies the staged artifacts to the gh-pages branch and creates a PR. - # - # Pull request: - # Applies the staged artifacts on top of gh-pages content and deploys the - # result to a Netlify preview (no gh-pages PR is created). + # 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 # - # Required repository secrets for Netlify previews: - # NETLIFY_AUTH_TOKEN – Netlify personal access token - # NETLIFY_SITE_ID – ID of the target Netlify site + # 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 + name: Deploy documentation for gh-pages needs: stage-and-check + if: github.event_name == 'push' runs-on: ubuntu-latest permissions: @@ -479,13 +475,12 @@ jobs: # Remove the stage/ directory BEFORE creating the PR so it doesn't get committed - name: Clean up staging cache before commit - if: github.event_name == 'push' run: rm -rf stage/ - name: Create Pull Request for documentation deployment uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0 # v8.1.0 id: cpr - if: github.event_name == 'push' && steps.apply.outputs.has_changes == 'true' + 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 }}`" @@ -504,27 +499,129 @@ jobs: - name: Enable Pull Request Automerge run: gh pr merge --merge --auto "${{ steps.cpr.outputs.pull-request-number }}" - if: github.event_name == 'push' && steps.cpr.outputs.pull-request-number != '' + if: steps.cpr.outputs.pull-request-number != '' env: GH_TOKEN: ${{ secrets.COMMAND_BOT_PAT }} + # ============================================================================ + # NETLIFY PREVIEW + # ============================================================================ + # This job deploys a per-PR documentation preview to Netlify. + # It only runs on pull_request events (never on push/merge). + # + # Only the artifacts built for the current branch are deployed, with a simple + # index.html listing the available manuals. + # + # The stable preview URL for PR #N is: + # https://pr---.netlify.app/ + # + # Required repository secrets: + # NETLIFY_AUTH_TOKEN – Netlify personal access token + # NETLIFY_SITE_ID – ID of the target Netlify site + # ============================================================================ + netlify-preview: + name: Deploy preview to Netlify + needs: stage-and-check + if: github.event_name == 'pull_request' + runs-on: ubuntu-latest + + permissions: + contents: read + pull-requests: write + + steps: + - name: Restore staged artifacts from cache + uses: actions/cache/restore@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + with: + path: stage/ + key: staged-docs-${{ github.sha }} + fail-on-cache-miss: true + + - name: Generate index.html for preview + run: | + branch="${{ needs.stage-and-check.outputs.branch_name }}" + preview_dir="stage/${branch}" + + cat > "${preview_dir}/index.html" <<'EOF' + + + + + + Nextcloud Documentation Preview + + +

Nextcloud Documentation Preview

+
    + EOF + + for manual_dir in "${preview_dir}"/*/; do + [ -d "$manual_dir" ] || continue + manual="$(basename "$manual_dir")" + echo "
  • ${manual}
  • " >> "${preview_dir}/index.html" + done + + cat >> "${preview_dir}/index.html" <<'EOF' +
+ + + EOF + + - name: Install Netlify CLI + run: npm install -g netlify-cli + - name: Deploy to Netlify - uses: nwtgck/actions-netlify@4cbaf4c08f1a7bfa537d6113472ef4424e4eb654 # v3.0 - if: github.event_name == 'pull_request' + id: netlify + run: | + branch="${{ needs.stage-and-check.outputs.branch_name }}" + output=$(netlify deploy \ + --dir="stage/${branch}" \ + --site="${{ secrets.NETLIFY_SITE_ID }}" \ + --auth="${{ secrets.NETLIFY_AUTH_TOKEN }}" \ + --alias="pr-${{ github.event.number }}" \ + --message="Preview for PR #${{ github.event.number }}" \ + --json) + deploy_url=$(echo "$output" | jq -r '.deploy_url // .url') + if [ -z "$deploy_url" ] || [ "$deploy_url" = "null" ]; then + echo "Failed to get deploy URL from Netlify output:" >&2 + echo "$output" >&2 + exit 1 + fi + echo "deploy_url=${deploy_url}" >> $GITHUB_OUTPUT + + - name: Comment preview URL on PR + uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 with: - publish-dir: './server' - production-deploy: false - github-token: ${{ secrets.GITHUB_TOKEN }} - deploy-message: "Preview for PR #${{ github.event.number }}" - alias: pr-${{ github.event.number }} - enable-pull-request-comment: true - enable-commit-comment: false - env: - NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }} - NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }} + script: | + const deployUrl = '${{ steps.netlify.outputs.deploy_url }}'; + const marker = ''; + const body = `${marker}\n:rocket: **Netlify preview deployed:** ${deployUrl}`; + + const { data: comments } = await github.rest.issues.listComments({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + }); + + const existing = comments.find(c => c.body.includes(marker)); + if (existing) { + await github.rest.issues.updateComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: existing.id, + body, + }); + } else { + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + body, + }); + } summary: - needs: [build, stage-and-check, deploy] + needs: [build, stage-and-check, deploy, netlify-preview] runs-on: ubuntu-latest-low if: always() @@ -538,10 +635,12 @@ jobs: run: | if ${{ github.event_name == 'pull_request' }} then - echo "This workflow ran for a pull request. We need build, stage-and-check, and deploy (Netlify preview) to succeed" - if ${{ needs.build.result != 'success' || needs.stage-and-check.result != 'success' || needs.deploy.result != 'success' }}; then exit 1; fi + 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 + if ${{ needs.netlify-preview.result != 'success' && needs.netlify-preview.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.netlify-preview.result != 'skipped' }}; then exit 1; fi fi From 937510e352a55330162d0e0a294ce891ef82c132 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 17 Apr 2026 10:57:23 +0000 Subject: [PATCH 06/15] refactor: merge netlify preview into deploy job, make it optional Agent-Logs-Url: https://github.com/nextcloud/documentation/sessions/fd9abf90-6eb8-4986-ae43-4ce3c769083e Co-authored-by: skjnldsv <14975046+skjnldsv@users.noreply.github.com> --- .github/workflows/sphinxbuild.yml | 109 ++++++++---------------------- 1 file changed, 30 insertions(+), 79 deletions(-) diff --git a/.github/workflows/sphinxbuild.yml b/.github/workflows/sphinxbuild.yml index 29ab9fef660..1d31866e66d 100644 --- a/.github/workflows/sphinxbuild.yml +++ b/.github/workflows/sphinxbuild.yml @@ -383,18 +383,20 @@ jobs: # ============================================================================ # 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 handles two cases based on the event type: + # + # push → Apply staged artifacts to gh-pages and open a PR for the update. + # + # pull_request → Check out gh-pages, overlay the new artifacts, and deploy + # the full server/ tree to Netlify as a preview (optional – + # Netlify failure does NOT block the PR). # - # 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. + # In both cases the job checks out gh-pages first, so the full existing + # documentation is available before the new artifacts are applied. # ============================================================================ deploy: name: Deploy documentation for gh-pages needs: stage-and-check - if: github.event_name == 'push' runs-on: ubuntu-latest permissions: @@ -480,7 +482,7 @@ jobs: - 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' + if: steps.apply.outputs.has_changes == 'true' && github.event_name == 'push' with: token: ${{ secrets.COMMAND_BOT_PAT }} commit-message: "chore: update documentation for `${{ needs.stage-and-check.outputs.branch_name }}`" @@ -503,79 +505,26 @@ jobs: env: GH_TOKEN: ${{ secrets.COMMAND_BOT_PAT }} - # ============================================================================ - # NETLIFY PREVIEW - # ============================================================================ - # This job deploys a per-PR documentation preview to Netlify. - # It only runs on pull_request events (never on push/merge). - # - # Only the artifacts built for the current branch are deployed, with a simple - # index.html listing the available manuals. - # - # The stable preview URL for PR #N is: - # https://pr---.netlify.app/ - # - # Required repository secrets: - # NETLIFY_AUTH_TOKEN – Netlify personal access token - # NETLIFY_SITE_ID – ID of the target Netlify site - # ============================================================================ - netlify-preview: - name: Deploy preview to Netlify - needs: stage-and-check - if: github.event_name == 'pull_request' - runs-on: ubuntu-latest - - permissions: - contents: read - pull-requests: write - - steps: - - name: Restore staged artifacts from cache - uses: actions/cache/restore@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 - with: - path: stage/ - key: staged-docs-${{ github.sha }} - fail-on-cache-miss: true - - - name: Generate index.html for preview - run: | - branch="${{ needs.stage-and-check.outputs.branch_name }}" - preview_dir="stage/${branch}" - - cat > "${preview_dir}/index.html" <<'EOF' - - - - - - Nextcloud Documentation Preview - - -

Nextcloud Documentation Preview

-
    - EOF - - for manual_dir in "${preview_dir}"/*/; do - [ -d "$manual_dir" ] || continue - manual="$(basename "$manual_dir")" - echo "
  • ${manual}
  • " >> "${preview_dir}/index.html" - done - - cat >> "${preview_dir}/index.html" <<'EOF' -
- - - EOF - + # ======================================================================== + # NETLIFY PREVIEW (pull_request events only) + # ======================================================================== + # At this point the working directory is the gh-pages checkout with the + # newly staged artifacts already applied, so server/ is a complete, + # up-to-date snapshot of the full documentation site. + # Netlify failure is non-fatal – the step uses continue-on-error so it + # does not block the PR. + # ======================================================================== - name: Install Netlify CLI + if: github.event_name == 'pull_request' run: npm install -g netlify-cli - name: Deploy to Netlify id: netlify + if: github.event_name == 'pull_request' + continue-on-error: true run: | - branch="${{ needs.stage-and-check.outputs.branch_name }}" output=$(netlify deploy \ - --dir="stage/${branch}" \ + --dir=server \ --site="${{ secrets.NETLIFY_SITE_ID }}" \ --auth="${{ secrets.NETLIFY_AUTH_TOKEN }}" \ --alias="pr-${{ github.event.number }}" \ @@ -590,6 +539,7 @@ jobs: echo "deploy_url=${deploy_url}" >> $GITHUB_OUTPUT - name: Comment preview URL on PR + if: github.event_name == 'pull_request' && steps.netlify.outputs.deploy_url != '' uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 with: script: | @@ -620,8 +570,11 @@ jobs: }); } + # ============================================================================ + # SUMMARY + # ============================================================================ summary: - needs: [build, stage-and-check, deploy, netlify-preview] + needs: [build, stage-and-check, deploy] runs-on: ubuntu-latest-low if: always() @@ -635,12 +588,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 - if ${{ needs.netlify-preview.result != 'success' && needs.netlify-preview.result != 'skipped' }}; then exit 1; fi + echo "This workflow ran for a pull request. We need build, stage-and-check and deploy to succeed" + if ${{ needs.build.result != 'success' || needs.stage-and-check.result != 'success' || needs.deploy.result != 'success' }}; 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.netlify-preview.result != 'skipped' }}; then exit 1; fi fi From 79c0629150c04be87fd18fb682a87f7f3f6f5550 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 17 Apr 2026 12:19:39 +0000 Subject: [PATCH 07/15] feat: add index.html generation with user_manual/en redirect for Netlify preview Agent-Logs-Url: https://github.com/nextcloud/documentation/sessions/dc4b0ad9-feda-46fe-ab00-99b1703bcdb0 Co-authored-by: skjnldsv <14975046+skjnldsv@users.noreply.github.com> --- .github/workflows/sphinxbuild.yml | 46 +++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/.github/workflows/sphinxbuild.yml b/.github/workflows/sphinxbuild.yml index 1d31866e66d..a976e91bdf9 100644 --- a/.github/workflows/sphinxbuild.yml +++ b/.github/workflows/sphinxbuild.yml @@ -518,6 +518,52 @@ jobs: if: github.event_name == 'pull_request' run: npm install -g netlify-cli + - name: Generate index.html for preview + if: github.event_name == 'pull_request' + run: | + # Build a root index listing all top-level version folders + cat > server/index.html <<'INDEXEOF' + + + + + + Nextcloud Documentation Preview + + +

Nextcloud Documentation Preview

+
    + INDEXEOF + + for version_dir in server/*/; do + [ -d "$version_dir" ] || continue + version="$(basename "$version_dir")" + echo "
  • ${version}
  • " >> server/index.html + done + + cat >> server/index.html <<'INDEXEOF' +
+ + + INDEXEOF + + # For user_manual, create a redirect from user_manual/index.html → user_manual/en/ + if [ -d server/user_manual ]; then + cat > server/user_manual/index.html <<'REDIRECTEOF' + + + + + + Redirecting… + + +

Redirecting to user_manual/en/

+ + + REDIRECTEOF + fi + - name: Deploy to Netlify id: netlify if: github.event_name == 'pull_request' From c61e9a6ff68d4a2cdf57b580ad1ca36d7114e74b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 17 Apr 2026 12:25:10 +0000 Subject: [PATCH 08/15] =?UTF-8?q?fix:=20remove=20redundant=20user=5Fmanual?= =?UTF-8?q?/index.html=20generation=20=E2=80=94=20already=20exists=20in=20?= =?UTF-8?q?gh-pages?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Agent-Logs-Url: https://github.com/nextcloud/documentation/sessions/a6ca17cd-be62-49ee-91e1-82877e91b614 Co-authored-by: skjnldsv <14975046+skjnldsv@users.noreply.github.com> --- .github/workflows/sphinxbuild.yml | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/.github/workflows/sphinxbuild.yml b/.github/workflows/sphinxbuild.yml index a976e91bdf9..dd00ff6d749 100644 --- a/.github/workflows/sphinxbuild.yml +++ b/.github/workflows/sphinxbuild.yml @@ -547,23 +547,6 @@ jobs: INDEXEOF - # For user_manual, create a redirect from user_manual/index.html → user_manual/en/ - if [ -d server/user_manual ]; then - cat > server/user_manual/index.html <<'REDIRECTEOF' - - - - - - Redirecting… - - -

Redirecting to user_manual/en/

- - - REDIRECTEOF - fi - - name: Deploy to Netlify id: netlify if: github.event_name == 'pull_request' From 6f4a7b2005c70168485fedf479d029f69078ce93 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 1 May 2026 16:53:36 +0000 Subject: [PATCH 09/15] fix: list only PR manuals in preview index.html, not all gh-pages versions Agent-Logs-Url: https://github.com/nextcloud/documentation/sessions/b646b2ff-3537-46cc-a405-f5bb8926e7d3 Co-authored-by: skjnldsv <14975046+skjnldsv@users.noreply.github.com> --- .github/workflows/sphinxbuild.yml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/.github/workflows/sphinxbuild.yml b/.github/workflows/sphinxbuild.yml index cb1496c4e46..82f4b89e159 100644 --- a/.github/workflows/sphinxbuild.yml +++ b/.github/workflows/sphinxbuild.yml @@ -574,7 +574,8 @@ jobs: - name: Generate index.html for preview if: github.event_name == 'pull_request' run: | - # Build a root index listing all top-level version folders + branch="${{ needs.stage-and-check.outputs.branch_name }}" + # Build a root index listing only the manuals built for this PR cat > server/index.html <<'INDEXEOF' @@ -588,10 +589,10 @@ jobs:
    INDEXEOF - for version_dir in server/*/; do - [ -d "$version_dir" ] || continue - version="$(basename "$version_dir")" - echo "
  • ${version}
  • " >> server/index.html + for manual_dir in "server/${branch}"/*/; do + [ -d "$manual_dir" ] || continue + manual="$(basename "$manual_dir")" + echo "
  • ${manual}
  • " >> server/index.html done cat >> server/index.html <<'INDEXEOF' From 5182ec19068335320dbc6196effa68d308346796 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 1 May 2026 16:55:09 +0000 Subject: [PATCH 10/15] feat: pin Netlify preview comment on PR after create/update Agent-Logs-Url: https://github.com/nextcloud/documentation/sessions/c2b4d982-2c08-48d7-9ad7-7a462e079cbe Co-authored-by: skjnldsv <14975046+skjnldsv@users.noreply.github.com> --- .github/workflows/sphinxbuild.yml | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/.github/workflows/sphinxbuild.yml b/.github/workflows/sphinxbuild.yml index 82f4b89e159..baedb639203 100644 --- a/.github/workflows/sphinxbuild.yml +++ b/.github/workflows/sphinxbuild.yml @@ -637,6 +637,7 @@ jobs: }); const existing = comments.find(c => c.body.includes(marker)); + let commentId; if (existing) { await github.rest.issues.updateComment({ owner: context.repo.owner, @@ -644,15 +645,23 @@ jobs: comment_id: existing.id, body, }); + commentId = existing.id; } else { - await github.rest.issues.createComment({ + const { data: created } = await github.rest.issues.createComment({ owner: context.repo.owner, repo: context.repo.repo, issue_number: context.issue.number, body, }); + commentId = created.id; } + await github.rest.issues.pinComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: commentId, + }); + # ============================================================================ # SUMMARY # ============================================================================ From 154bdd488d12550c1b05e41620a45eb0ec1b7a5f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 1 May 2026 17:05:22 +0000 Subject: [PATCH 11/15] fix: use github.request() to pin comment instead of missing pinComment method Agent-Logs-Url: https://github.com/nextcloud/documentation/sessions/9a18eeec-8e48-41ef-a446-41b121aea921 Co-authored-by: skjnldsv <14975046+skjnldsv@users.noreply.github.com> --- .github/workflows/sphinxbuild.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/sphinxbuild.yml b/.github/workflows/sphinxbuild.yml index baedb639203..00dd8f65d40 100644 --- a/.github/workflows/sphinxbuild.yml +++ b/.github/workflows/sphinxbuild.yml @@ -656,10 +656,11 @@ jobs: commentId = created.id; } - await github.rest.issues.pinComment({ + await github.request('PATCH /repos/{owner}/{repo}/issues/comments/{comment_id}', { owner: context.repo.owner, repo: context.repo.repo, comment_id: commentId, + pinned: true, }); # ============================================================================ From f3360db476778dbbe4e1bf78dc4c1c5e73b34610 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 1 May 2026 17:11:31 +0000 Subject: [PATCH 12/15] fix: only copy manuals (remove PDF moving/copying) in deploy workflow Agent-Logs-Url: https://github.com/nextcloud/documentation/sessions/18b676be-3c6e-4ee5-8096-6711e6577d13 Co-authored-by: skjnldsv <14975046+skjnldsv@users.noreply.github.com> --- .github/workflows/sphinxbuild.yml | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/.github/workflows/sphinxbuild.yml b/.github/workflows/sphinxbuild.yml index 00dd8f65d40..d09e4c5e397 100644 --- a/.github/workflows/sphinxbuild.yml +++ b/.github/workflows/sphinxbuild.yml @@ -337,16 +337,6 @@ jobs: fi done - # Move PDF files to the root of the branch folder for cleaner structure - echo "Looking for PDF files to move..." - find "stage/${branch}/" -maxdepth 2 -name "*.pdf" -type f - for pdf in "stage/${branch}"/*/*.pdf; do - if [ -f "$pdf" ]; then - echo "Moving PDF: $pdf" - mv "$pdf" "stage/${branch}/" - fi - done - # Clean up empty directories find stage -type d -empty -delete @@ -473,16 +463,6 @@ jobs: fi done - # Move pdf files to the root of the branch folder - echo "Looking for PDF files to copy..." - find stage/${branch}/ -name "*.pdf" -type f - for pdf in stage/${branch}/*.pdf; do - if [ -f "$pdf" ]; then - echo "Copying PDF: $pdf" - cp "$pdf" server/${branch}/ - fi - done - # If this is the highest stable branch, also deploy to its versioned folder if [ -n "${additional}" ]; then rm -rf server/${additional} From 3ee5a8cd5d90fa371f78b01fec966d36058a4085 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 1 May 2026 17:23:39 +0000 Subject: [PATCH 13/15] fix: remove unsupported pinned comment PATCH call that caused HttpError Agent-Logs-Url: https://github.com/nextcloud/documentation/sessions/bd55ee68-16b8-498e-beb0-fb8ed14f7b6b Co-authored-by: skjnldsv <14975046+skjnldsv@users.noreply.github.com> --- .github/workflows/sphinxbuild.yml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/.github/workflows/sphinxbuild.yml b/.github/workflows/sphinxbuild.yml index d09e4c5e397..468a1085d01 100644 --- a/.github/workflows/sphinxbuild.yml +++ b/.github/workflows/sphinxbuild.yml @@ -636,12 +636,6 @@ jobs: commentId = created.id; } - await github.request('PATCH /repos/{owner}/{repo}/issues/comments/{comment_id}', { - owner: context.repo.owner, - repo: context.repo.repo, - comment_id: commentId, - pinned: true, - }); # ============================================================================ # SUMMARY From 31d3860638fbbd7981e778019e066b2ec490554d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 1 May 2026 17:26:51 +0000 Subject: [PATCH 14/15] restore: pin comment using correct PUT /issues/comments/{id}/pin endpoint Agent-Logs-Url: https://github.com/nextcloud/documentation/sessions/5b0532ef-f50b-4b3c-8231-8265472bf35f Co-authored-by: skjnldsv <14975046+skjnldsv@users.noreply.github.com> --- .github/workflows/sphinxbuild.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/sphinxbuild.yml b/.github/workflows/sphinxbuild.yml index 468a1085d01..221e7d53703 100644 --- a/.github/workflows/sphinxbuild.yml +++ b/.github/workflows/sphinxbuild.yml @@ -636,6 +636,11 @@ jobs: commentId = created.id; } + await github.request('PUT /repos/{owner}/{repo}/issues/comments/{comment_id}/pin', { + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: commentId, + }); # ============================================================================ # SUMMARY From 71c6a1ba63a1c1b6a13b132a146a26e05550caea Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 1 May 2026 18:10:18 +0000 Subject: [PATCH 15/15] remove unsupported pin call (PR context + insufficient token permissions) Agent-Logs-Url: https://github.com/nextcloud/documentation/sessions/9b9ff82f-f385-4158-8d94-19585a65a684 Co-authored-by: skjnldsv <14975046+skjnldsv@users.noreply.github.com> --- .github/workflows/sphinxbuild.yml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/.github/workflows/sphinxbuild.yml b/.github/workflows/sphinxbuild.yml index 221e7d53703..868306babe1 100644 --- a/.github/workflows/sphinxbuild.yml +++ b/.github/workflows/sphinxbuild.yml @@ -636,12 +636,6 @@ jobs: commentId = created.id; } - await github.request('PUT /repos/{owner}/{repo}/issues/comments/{comment_id}/pin', { - owner: context.repo.owner, - repo: context.repo.repo, - comment_id: commentId, - }); - # ============================================================================ # SUMMARY # ============================================================================