From 9392937a2e2fa408eb9e6d449e0ad77adb1936eb Mon Sep 17 00:00:00 2001 From: Martijn Laarman Date: Thu, 9 Apr 2026 11:32:14 +0200 Subject: [PATCH 01/10] Split docs preview: reusable Docker workflow + local dotnet workflow - preview-build.yml: workflow_call only; single Docker path (remove docs-builder repo branches); unify validate/build step IDs for downstream steps. - docs-preview-local.yml: PR/push workflow with bootstrap + dotnet run for elastic/docs-builder (preserves fork guards and disabled deploy blocks). - preview-cleanup.yml: TODO for future migration to elastic/docs-actions. Made-with: Cursor --- .github/workflows/docs-preview-local.yml | 392 +++++++++++++++++++++++ .github/workflows/preview-build.yml | 85 +---- .github/workflows/preview-cleanup.yml | 2 + 3 files changed, 401 insertions(+), 78 deletions(-) create mode 100644 .github/workflows/docs-preview-local.yml diff --git a/.github/workflows/docs-preview-local.yml b/.github/workflows/docs-preview-local.yml new file mode 100644 index 0000000000..3dbaaeae67 --- /dev/null +++ b/.github/workflows/docs-preview-local.yml @@ -0,0 +1,392 @@ +name: Docs preview (local) + +# In-repo docs build using dotnet run (elastic/docs-builder only). Replaces the former +# github.repository branches in preview-build.yml; consumers still use preview-build.yml (Docker). + +on: + pull_request: + types: + - opened + - synchronize + - reopened + push: + branches: + - main + - master + - '\d+.\d+.\d+' + - '\d+.\d+' + - '\d+.x' + tags: + - 'v?\d+.\d+.\d+' + - 'v?\d+.\d+' + +permissions: + contents: read + id-token: write + pull-requests: read + +concurrency: + group: docs-preview-local-${{ github.event.pull_request.head.ref || github.ref }} + cancel-in-progress: ${{ startsWith(github.event_name, 'pull_request') }} + +jobs: + match: + if: github.event.repository.fork == false + runs-on: ubuntu-latest + permissions: + contents: none + deployments: none + pull-requests: none + id-token: none + outputs: + content-source-match: ${{ format('{0}{1}{2}', steps.pr-check.outputs.content-source-match, steps.push-check.outputs.content-source-match, steps.merge-group-check.outputs.content-source-match) }} + content-source-next: ${{ format('{0}{1}{2}', steps.pr-check.outputs.content-source-next, steps.push-check.outputs.content-source-next, steps.merge-group-check.outputs.content-source-next) }} + content-source-current: ${{ format('{0}{1}{2}', steps.pr-check.outputs.content-source-current, steps.push-check.outputs.content-source-current, steps.merge-group-check.outputs.content-source-current) }} + content-source-edge: ${{ format('{0}{1}{2}', steps.pr-check.outputs.content-source-edge, steps.push-check.outputs.content-source-edge, steps.merge-group-check.outputs.content-source-edge) }} + content-source-speculative: ${{ format('{0}{1}{2}', steps.pr-check.outputs.content-source-speculative, steps.push-check.outputs.content-source-speculative, steps.merge-group-check.outputs.content-source-speculative) }} + steps: + - name: Find merge-group base branch + if: contains(fromJSON('["merge_group"]'), github.event_name) + id: merge-group-base-branch + run: | + BASE_BRANCH=$(basename "${{ github.event.merge_group.base_ref }}") + echo "base_ref=${BASE_BRANCH}" >> $GITHUB_OUTPUT + - name: Match for merge-group events + id: merge-group-check + if: contains(fromJSON('["merge_group"]'), github.event_name) + uses: elastic/docs-builder/actions/assembler-match@main + with: + ref_name: ${{ steps.merge-group-base-branch.outputs.base_ref }} + repository: ${{ github.repository }} + + - name: Match for PR events + id: pr-check + if: contains(fromJSON('["pull_request", "pull_request_target"]'), github.event_name) + uses: elastic/docs-builder/actions/assembler-match@main + with: + ref_name: ${{ github.base_ref }} + repository: ${{ github.repository }} + - name: Match for PR events debug + if: contains(fromJSON('["pull_request", "pull_request_target"]'), github.event_name) + run: | + echo "ref=${{ github.base_ref }}" + echo "repo=${{ github.repository }}" + + - name: Match for push events + id: push-check + if: contains(fromJSON('["push"]'), github.event_name) + uses: elastic/docs-builder/actions/assembler-match@main + with: + ref_name: ${{ github.ref_name }} + repository: ${{ github.repository }} + - name: Match for push events debug + if: contains(fromJSON('["push"]'), github.event_name) + run: | + echo "ref=${{ github.ref_name }}" + echo "repo=${{ github.repository }}" + + - name: Debug outputs + run: | + echo "content-source-match: ${{ format('{0}{1}{2}', steps.pr-check.outputs.content-source-match, steps.push-check.outputs.content-source-match, steps.merge-group-check.outputs.content-source-match) }}" + echo "content-source-next: ${{ format('{0}{1}{2}', steps.pr-check.outputs.content-source-next, steps.push-check.outputs.content-source-next, steps.merge-group-check.outputs.content-source-next) }}" + echo "content-source-current: ${{ format('{0}{1}{2}', steps.pr-check.outputs.content-source-current, steps.push-check.outputs.content-source-current, steps.merge-group-check.outputs.content-source-current) }}" + echo "content-source-edge: ${{ format('{0}{1}{2}', steps.pr-check.outputs.content-source-edge, steps.push-check.outputs.content-source-edge, steps.merge-group-check.outputs.content-source-edge) }}" + echo "content-source-speculative: ${{ format('{0}{1}{2}', steps.pr-check.outputs.content-source-speculative, steps.push-check.outputs.content-source-speculative, steps.merge-group-check.outputs.content-source-speculative) }}" + + check: + runs-on: ubuntu-latest + needs: + - match + permissions: + contents: read + deployments: none + id-token: none + pull-requests: read + outputs: + any_modified: ${{ steps.check-files.outputs.any_modified }} + all_changed_files: ${{ steps.check-files.outputs.all_changed_files }} + added_files: ${{ steps.check-modified-file-detail.outputs.added_files }} + modified_files: ${{ steps.check-modified-file-detail.outputs.modified_files }} + deleted_files: ${{ steps.check-modified-file-detail.outputs.deleted_files }} + renamed_files: ${{ steps.check-modified-file-detail.outputs.renamed_files }} + steps: + - name: Checkout + if: contains(fromJSON('["push", "merge_group", "workflow_dispatch"]'), github.event_name) + uses: actions/checkout@v6 + with: + ref: ${{ github.event.pull_request.head.sha || github.ref }} + + - name: Get changed files + if: contains(fromJSON('["push", "merge_group", "pull_request", "pull_request_target"]'), github.event_name) + id: check-files + uses: tj-actions/changed-files@2f7c5bfce28377bc069a65ba478de0a74aa0ca32 # v46.0.1 + with: + files: '**' + files_ignore: | + .github/** + README.md + + - name: Get modified file detail + if: contains(fromJSON('["merge_group", "pull_request", "pull_request_target"]'), github.event_name) + id: check-modified-file-detail + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 + env: + IGNORE_PATTERNS: | + .github/** + README.md + with: + script: | + const ignorePatterns = process.env.IGNORE_PATTERNS; + const ignoreGlobber = await glob.create(ignorePatterns); + const ignoredPaths = new Set(await ignoreGlobber.glob()); + + const { owner, repo } = context.repo; + + let allFiles; + if (context.eventName === 'merge_group') { + const baseSha = context.payload.merge_group.base_sha; + const headSha = context.payload.merge_group.head_sha; + const comparison = await github.rest.repos.compareCommitsWithBasehead({ + owner, + repo, + basehead: `${baseSha}...${headSha}`, + }); + allFiles = comparison.data.files || []; + } else { + const pull_number = context.payload.pull_request.number; + allFiles = await github.paginate(github.rest.pulls.listFiles, { + owner, + repo, + pull_number, + }); + } + + const filteredFiles = allFiles.filter(file => !ignoredPaths.has(file.filename)); + + const added = []; + const modified = []; + const deleted = []; + const renamed = []; + + for (const file of filteredFiles) { + switch (file.status) { + case 'added': + added.push(file.filename); + break; + case 'modified': + modified.push(file.filename); + break; + case 'removed': + deleted.push(file.filename); + break; + case 'renamed': + renamed.push(`${file.previous_filename}:${file.filename}`); + break; + } + } + + core.setOutput('added_files', added.join(' ')); + core.setOutput('modified_files', modified.join(' ')); + core.setOutput('deleted_files', deleted.join(' ')); + core.setOutput('renamed_files', renamed.join(' ')); + + build: + if: github.event.repository.fork == false + runs-on: ubuntu-latest + permissions: + contents: read + deployments: none + id-token: write + pull-requests: none + outputs: + deployment_result: ${{ steps.deployment.outputs.result }} + path_prefix: ${{ steps.generate-path-prefix.outputs.result }} + env: + GITHUB_PR_REF_NAME: ${{ github.event.pull_request.head.ref }} + MATCH: ${{ needs.match.outputs.content-source-match }} + ADDED_FILES: ${{ needs.check.outputs.added_files }} + MODIFIED_FILES: ${{ needs.check.outputs.modified_files }} + DELETED_FILES: ${{ needs.check.outputs.deleted_files }} + RENAMED_FILES: ${{ needs.check.outputs.renamed_files }} + needs: + - check + - match + steps: + - name: Checkout + if: > + env.MATCH == 'true' + && needs.check.outputs.any_modified != 'false' + uses: actions/checkout@v6 + with: + ref: ${{ github.event.pull_request.head.sha || github.ref }} + persist-credentials: false + + - name: Create Deployment + # disabled: deployments are not enabled on this branch + if: > + false + && env.MATCH == 'true' + && needs.check.outputs.any_modified != 'false' + && ( + contains(fromJSON('["push", "workflow_dispatch"]'), github.event_name) + || startsWith(github.event_name, 'pull_request') + ) + uses: actions/github-script@v8 + id: deployment + env: + PR_NUMBER: ${{ github.event.pull_request.number }} + REF: ${{ startsWith(github.event_name, 'pull_request') && github.event.pull_request.head.sha || github.ref_name }} + with: + result-encoding: string + script: | + const { owner, repo } = context.repo; + const prNumber = process.env.PR_NUMBER; + const environment = 'docs-preview'; + const task = prNumber ? `docs-preview-${prNumber}` : undefined; + const deployment = await github.rest.repos.createDeployment({ + owner, + repo, + environment, + task, + ref: process.env.REF, + auto_merge: false, + transient_environment: true, + required_contexts: [], + }) + await github.rest.repos.createDeploymentStatus({ + deployment_id: deployment.data.id, + owner, + repo, + state: "in_progress", + log_url: `https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`, + }) + return deployment.data.id + + - name: Generate env.PATH_PREFIX + id: generate-path-prefix + # disabled: preview path prefix is not needed on this branch + if: > + false + && env.MATCH == 'true' + && steps.deployment.outputs.result + env: + PR_NUMBER: ${{ github.event.pull_request.number }} + GITHUB_REF_NAME: ${{ github.ref_name }} + run: | + case "${GITHUB_EVENT_NAME}" in + "merge_group" | "pull_request" | "pull_request_target") + path_prefix="/${GITHUB_REPOSITORY}/pull/${PR_NUMBER}" + ;; + "push" | "workflow_dispatch") + path_prefix="/${GITHUB_REPOSITORY}/tree/${GITHUB_REF_NAME}" + ;; + *) + echo "Unsupported event: '${GITHUB_EVENT_NAME}'"; + exit 1; + ;; + esac + echo "PATH_PREFIX=${path_prefix}" >> $GITHUB_ENV + echo "result=${path_prefix}" >> $GITHUB_OUTPUT + + - name: Bootstrap Action Workspace + if: > + env.MATCH == 'true' + && needs.check.outputs.any_modified != 'false' + && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) + uses: elastic/docs-builder/.github/actions/bootstrap@main + + - name: 'Validate redirect rules' + if: > + env.MATCH == 'true' + && needs.check.outputs.any_modified != 'false' + && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) + run: | + dotnet run --project src/tooling/docs-builder -- diff validate + + - name: Build documentation + id: internal-docs-build + if: > + env.MATCH == 'true' + && needs.check.outputs.any_modified != 'false' + && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) + run: | + dotnet run --project src/tooling/docs-builder -- --strict + + - name: 'Validate inbound links' + if: > + env.MATCH == 'true' + && !cancelled() + && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) + && steps.internal-docs-build.outputs.skip != 'true' + && needs.check.outputs.any_modified != 'false' + run: | + dotnet run --project src/tooling/docs-builder -- inbound-links validate-link-reference + + - name: 'Validate local path prefixes against those claimed by global navigation.yml' + id: internal-validate-path-prefixes + if: > + env.MATCH == 'true' + && !cancelled() + && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) + && steps.internal-docs-build.outputs.skip != 'true' + && steps.internal-docs-build.outcome != 'skipped' + && needs.check.outputs.any_modified != 'false' + run: | + dotnet run --project src/tooling/docs-builder -- assembler navigation validate-link-reference + + - uses: elastic/docs-builder/.github/actions/aws-auth@main + if: > + !cancelled() + && needs.check.outputs.any_modified != 'false' + && steps.internal-validate-path-prefixes.outcome == 'success' + + - name: Upload to S3 + id: s3-upload + # disabled: preview uploads are not enabled on this branch + if: > + false + && env.MATCH == 'true' + && !cancelled() + && steps.internal-docs-build.outputs.skip != 'true' + && steps.deployment.outputs.result + && steps.internal-docs-build.outcome == 'success' + env: + AWS_RETRY_MODE: standard + AWS_MAX_ATTEMPTS: 6 + run: | + aws s3 sync .artifacts/docs/html "s3://elastic-docs-v3-website-preview${PATH_PREFIX}" --delete --no-follow-symlinks + aws cloudfront create-invalidation \ + --distribution-id EKT7LT5PM8RKS \ + --paths "${PATH_PREFIX}" "${PATH_PREFIX}/*" + + - name: Update Link Index + if: > + env.MATCH == 'true' + && needs.check.outputs.any_modified != 'false' + && ( + contains(fromJSON('["push", "workflow_dispatch"]'), github.event_name) + && steps.internal-validate-path-prefixes.outcome == 'success' + ) + uses: elastic/docs-builder/actions/update-link-index@main + + - name: Update deployment status + uses: actions/github-script@v8 + # disabled: deployments are not enabled on this branch + if: > + false + && env.MATCH == 'true' + && always() + && steps.deployment.outputs.result + env: + PR_NUMBER: ${{ github.event.pull_request.number }} + LANDING_PAGE_PATH: ${{ env.PATH_PREFIX }} + with: + script: | + await github.rest.repos.createDeploymentStatus({ + owner: context.repo.owner, + repo: context.repo.repo, + deployment_id: ${{ steps.deployment.outputs.result }}, + state: "${{ steps.internal-docs-build.outputs.skip == 'true' && 'inactive' || (steps.s3-upload.outcome == 'success' && 'success' || 'failure') }}", + environment_url: `https://docs-v3-preview.elastic.dev${process.env.LANDING_PAGE_PATH}`, + log_url: `https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`, + }) diff --git a/.github/workflows/preview-build.yml b/.github/workflows/preview-build.yml index cc40ed97e9..8d8bd1b033 100644 --- a/.github/workflows/preview-build.yml +++ b/.github/workflows/preview-build.yml @@ -1,22 +1,8 @@ name: preview-build +# Reusable workflow for repositories that still call this workflow. elastic/docs-builder runs +# docs-preview-local.yml on pull_request/push instead (dotnet run from source). on: - pull_request: - types: - - opened - - synchronize - - reopened - push: - branches: - - main - - master - # TODO remove these need to be added to individual repositories - - '\d+.\d+.\d+' - - '\d+.\d+' - - '\d+.x' - tags: - - 'v?\d+.\d+.\d+' - - 'v?\d+.\d+' workflow_call: inputs: strict: @@ -341,46 +327,15 @@ jobs: echo "PATH_PREFIX=${path_prefix}" >> $GITHUB_ENV echo "result=${path_prefix}" >> $GITHUB_OUTPUT - - name: Bootstrap Action Workspace - if: > - env.MATCH == 'true' - && github.repository == 'elastic/docs-builder' - && needs.check.outputs.any_modified != 'false' - && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) - uses: elastic/docs-builder/.github/actions/bootstrap@main - - - name: 'Validate redirect rules' - if: > - env.MATCH == 'true' - && github.repository == 'elastic/docs-builder' - && needs.check.outputs.any_modified != 'false' - && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) - run: | - dotnet run --project src/tooling/docs-builder -- diff validate - - name: 'Validate redirect rules' if: > env.MATCH == 'true' - && github.repository != 'elastic/docs-builder' && needs.check.outputs.any_modified != 'false' uses: elastic/docs-builder/actions/diff-validate@main - # we run our artifact directly, please use the prebuild - # elastic/docs-builder@main GitHub Action for all other repositories! - - name: Build documentation - id: internal-docs-build - if: > - env.MATCH == 'true' - && github.repository == 'elastic/docs-builder' - && needs.check.outputs.any_modified != 'false' - && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) - run: | - dotnet run --project src/tooling/docs-builder -- --strict - - name: Build documentation if: > env.MATCH == 'true' - && github.repository != 'elastic/docs-builder' && needs.check.outputs.any_modified != 'false' uses: elastic/docs-builder@main id: docs-build @@ -393,51 +348,25 @@ jobs: if: > env.MATCH == 'true' && !cancelled() - && github.repository != 'elastic/docs-builder' && steps.docs-build.outputs.skip != 'true' && needs.check.outputs.any_modified != 'false' uses: elastic/docs-builder/actions/validate-inbound-local@main - - - name: 'Validate inbound links' - if: > - env.MATCH == 'true' - && !cancelled() - && github.repository == 'elastic/docs-builder' - && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) - && steps.internal-docs-build.outputs.skip != 'true' - && needs.check.outputs.any_modified != 'false' - run: | - dotnet run --project src/tooling/docs-builder -- inbound-links validate-link-reference - name: 'Validate local path prefixes against those claimed by global navigation.yml' id: validate-path-prefixes if: > env.MATCH == 'true' && !cancelled() - && github.repository != 'elastic/docs-builder' && steps.docs-build.outputs.skip != 'true' && steps.docs-build.outcome != 'skipped' && needs.check.outputs.any_modified != 'false' uses: elastic/docs-builder/actions/validate-path-prefixes-local@main - - - name: 'Validate local path prefixes against those claimed by global navigation.yml' - id: internal-validate-path-prefixes - if: > - env.MATCH == 'true' - && !cancelled() - && github.repository == 'elastic/docs-builder' - && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) - && steps.internal-docs-build.outputs.skip != 'true' - && steps.internal-docs-build.outcome != 'skipped' - && needs.check.outputs.any_modified != 'false' - run: | - dotnet run --project src/tooling/docs-builder -- assembler navigation validate-link-reference - uses: elastic/docs-builder/.github/actions/aws-auth@main if: > - !cancelled() + !cancelled() && needs.check.outputs.any_modified != 'false' - && (steps.validate-path-prefixes.outcome == 'success' || steps.internal-validate-path-prefixes.outcome == 'success') + && steps.validate-path-prefixes.outcome == 'success' - name: Upload to S3 id: s3-upload # disabled: preview uploads are not enabled on this branch @@ -445,9 +374,9 @@ jobs: false && env.MATCH == 'true' && !cancelled() - && (steps.docs-build.outputs.skip != 'true' || steps.internal-docs-build.outputs.skip != 'true') + && steps.docs-build.outputs.skip != 'true' && steps.deployment.outputs.result - && (steps.docs-build.outcome == 'success' || steps.internal-docs-build.outcome == 'success') + && steps.docs-build.outcome == 'success' env: # https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-retries.html#cli-usage-retries-configure AWS_RETRY_MODE: standard @@ -464,7 +393,7 @@ jobs: && needs.check.outputs.any_modified != 'false' && ( contains(fromJSON('["push", "workflow_dispatch"]'), github.event_name) - && (steps.validate-path-prefixes.outcome == 'success' || steps.internal-validate-path-prefixes.outcome == 'success') + && steps.validate-path-prefixes.outcome == 'success' ) uses: elastic/docs-builder/actions/update-link-index@main diff --git a/.github/workflows/preview-cleanup.yml b/.github/workflows/preview-cleanup.yml index b246698c70..cd3cf0bd72 100644 --- a/.github/workflows/preview-cleanup.yml +++ b/.github/workflows/preview-cleanup.yml @@ -1,5 +1,7 @@ name: preview-cleanup +# TODO: Migrate to elastic/docs-actions/.github/workflows/docs-preview-cleanup.yml (docs-eng-team#474). + on: workflow_call: ~ pull_request_target: From 460bd4804001a5dd600e3acb2bd7303cc482e232 Mon Sep 17 00:00:00 2001 From: Martijn Laarman Date: Thu, 9 Apr 2026 11:44:26 +0200 Subject: [PATCH 02/10] fix(ci): use glob patterns for push branch/tag filters in docs-preview-local GitHub Actions matches branches/tags with fnmatch-style globs, not regex. Replace regex-style entries with *.*.*, *.*, *.x for branches and *.*.*, *.* for tags. Made-with: Cursor --- .github/workflows/docs-preview-local.yml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/.github/workflows/docs-preview-local.yml b/.github/workflows/docs-preview-local.yml index 3dbaaeae67..4421c8a321 100644 --- a/.github/workflows/docs-preview-local.yml +++ b/.github/workflows/docs-preview-local.yml @@ -13,12 +13,13 @@ on: branches: - main - master - - '\d+.\d+.\d+' - - '\d+.\d+' - - '\d+.x' + # Glob patterns (GitHub Actions uses fnmatch-style globs for branches/tags, not regex). + - '*.*.*' + - '*.*' + - '*.x' tags: - - 'v?\d+.\d+.\d+' - - 'v?\d+.\d+' + - '*.*.*' + - '*.*' permissions: contents: read From 59e7e9c793f9baca08bf5ad993e7b0bc90bd234a Mon Sep 17 00:00:00 2001 From: Martijn Laarman Date: Thu, 9 Apr 2026 11:48:38 +0200 Subject: [PATCH 03/10] chore(ci): use [0-9] character-class globs for docs-preview-local push filters Made-with: Cursor --- .github/workflows/docs-preview-local.yml | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/.github/workflows/docs-preview-local.yml b/.github/workflows/docs-preview-local.yml index 4421c8a321..2ed5d612d3 100644 --- a/.github/workflows/docs-preview-local.yml +++ b/.github/workflows/docs-preview-local.yml @@ -13,13 +13,15 @@ on: branches: - main - master - # Glob patterns (GitHub Actions uses fnmatch-style globs for branches/tags, not regex). - - '*.*.*' - - '*.*' - - '*.x' + # fnmatch-style globs (not regex). [0-9] matches one digit; * matches within a label. + - '[0-9]*.[0-9]*.[0-9]*' + - '[0-9]*.[0-9]*' + - '[0-9]*.x' tags: - - '*.*.*' - - '*.*' + - 'v[0-9]*.[0-9]*.[0-9]*' + - 'v[0-9]*.[0-9]*' + - '[0-9]*.[0-9]*.[0-9]*' + - '[0-9]*.[0-9]*' permissions: contents: read From ec7524fbcb78ff93020c71843c3ea47069db6739 Mon Sep 17 00:00:00 2001 From: Martijn Laarman Date: Thu, 9 Apr 2026 11:56:05 +0200 Subject: [PATCH 04/10] fix(ci): drop pull_request_target checks from docs-preview-local (not a trigger) Made-with: Cursor --- .github/workflows/docs-preview-local.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/docs-preview-local.yml b/.github/workflows/docs-preview-local.yml index 2ed5d612d3..7c3c708018 100644 --- a/.github/workflows/docs-preview-local.yml +++ b/.github/workflows/docs-preview-local.yml @@ -64,13 +64,13 @@ jobs: - name: Match for PR events id: pr-check - if: contains(fromJSON('["pull_request", "pull_request_target"]'), github.event_name) + if: contains(fromJSON('["pull_request"]'), github.event_name) uses: elastic/docs-builder/actions/assembler-match@main with: ref_name: ${{ github.base_ref }} repository: ${{ github.repository }} - name: Match for PR events debug - if: contains(fromJSON('["pull_request", "pull_request_target"]'), github.event_name) + if: contains(fromJSON('["pull_request"]'), github.event_name) run: | echo "ref=${{ github.base_ref }}" echo "repo=${{ github.repository }}" @@ -120,7 +120,7 @@ jobs: ref: ${{ github.event.pull_request.head.sha || github.ref }} - name: Get changed files - if: contains(fromJSON('["push", "merge_group", "pull_request", "pull_request_target"]'), github.event_name) + if: contains(fromJSON('["push", "merge_group", "pull_request"]'), github.event_name) id: check-files uses: tj-actions/changed-files@2f7c5bfce28377bc069a65ba478de0a74aa0ca32 # v46.0.1 with: @@ -130,7 +130,7 @@ jobs: README.md - name: Get modified file detail - if: contains(fromJSON('["merge_group", "pull_request", "pull_request_target"]'), github.event_name) + if: contains(fromJSON('["merge_group", "pull_request"]'), github.event_name) id: check-modified-file-detail uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: @@ -277,7 +277,7 @@ jobs: GITHUB_REF_NAME: ${{ github.ref_name }} run: | case "${GITHUB_EVENT_NAME}" in - "merge_group" | "pull_request" | "pull_request_target") + "merge_group" | "pull_request") path_prefix="/${GITHUB_REPOSITORY}/pull/${PR_NUMBER}" ;; "push" | "workflow_dispatch") From 795e68af709f655a730c4d4ec028b16c38a978e4 Mon Sep 17 00:00:00 2001 From: Martijn Laarman Date: Thu, 9 Apr 2026 11:58:55 +0200 Subject: [PATCH 05/10] fix(ci): align docs-preview-local with pull_request/push triggers only Remove merge_group and workflow_dispatch steps/branches; match job outputs use pr + push only; simplify PR file-list script to pull_request API path. Made-with: Cursor --- .github/workflows/docs-preview-local.yml | 87 +++++++++--------------- 1 file changed, 31 insertions(+), 56 deletions(-) diff --git a/.github/workflows/docs-preview-local.yml b/.github/workflows/docs-preview-local.yml index 7c3c708018..7a74f2808d 100644 --- a/.github/workflows/docs-preview-local.yml +++ b/.github/workflows/docs-preview-local.yml @@ -2,6 +2,7 @@ name: Docs preview (local) # In-repo docs build using dotnet run (elastic/docs-builder only). Replaces the former # github.repository branches in preview-build.yml; consumers still use preview-build.yml (Docker). +# Triggers are only pull_request and push — conditions below match those events only. on: pull_request: @@ -30,7 +31,7 @@ permissions: concurrency: group: docs-preview-local-${{ github.event.pull_request.head.ref || github.ref }} - cancel-in-progress: ${{ startsWith(github.event_name, 'pull_request') }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} jobs: match: @@ -42,59 +43,45 @@ jobs: pull-requests: none id-token: none outputs: - content-source-match: ${{ format('{0}{1}{2}', steps.pr-check.outputs.content-source-match, steps.push-check.outputs.content-source-match, steps.merge-group-check.outputs.content-source-match) }} - content-source-next: ${{ format('{0}{1}{2}', steps.pr-check.outputs.content-source-next, steps.push-check.outputs.content-source-next, steps.merge-group-check.outputs.content-source-next) }} - content-source-current: ${{ format('{0}{1}{2}', steps.pr-check.outputs.content-source-current, steps.push-check.outputs.content-source-current, steps.merge-group-check.outputs.content-source-current) }} - content-source-edge: ${{ format('{0}{1}{2}', steps.pr-check.outputs.content-source-edge, steps.push-check.outputs.content-source-edge, steps.merge-group-check.outputs.content-source-edge) }} - content-source-speculative: ${{ format('{0}{1}{2}', steps.pr-check.outputs.content-source-speculative, steps.push-check.outputs.content-source-speculative, steps.merge-group-check.outputs.content-source-speculative) }} + content-source-match: ${{ format('{0}{1}', steps.pr-check.outputs.content-source-match, steps.push-check.outputs.content-source-match) }} + content-source-next: ${{ format('{0}{1}', steps.pr-check.outputs.content-source-next, steps.push-check.outputs.content-source-next) }} + content-source-current: ${{ format('{0}{1}', steps.pr-check.outputs.content-source-current, steps.push-check.outputs.content-source-current) }} + content-source-edge: ${{ format('{0}{1}', steps.pr-check.outputs.content-source-edge, steps.push-check.outputs.content-source-edge) }} + content-source-speculative: ${{ format('{0}{1}', steps.pr-check.outputs.content-source-speculative, steps.push-check.outputs.content-source-speculative) }} steps: - - name: Find merge-group base branch - if: contains(fromJSON('["merge_group"]'), github.event_name) - id: merge-group-base-branch - run: | - BASE_BRANCH=$(basename "${{ github.event.merge_group.base_ref }}") - echo "base_ref=${BASE_BRANCH}" >> $GITHUB_OUTPUT - - name: Match for merge-group events - id: merge-group-check - if: contains(fromJSON('["merge_group"]'), github.event_name) - uses: elastic/docs-builder/actions/assembler-match@main - with: - ref_name: ${{ steps.merge-group-base-branch.outputs.base_ref }} - repository: ${{ github.repository }} - - name: Match for PR events id: pr-check - if: contains(fromJSON('["pull_request"]'), github.event_name) + if: github.event_name == 'pull_request' uses: elastic/docs-builder/actions/assembler-match@main with: ref_name: ${{ github.base_ref }} repository: ${{ github.repository }} - name: Match for PR events debug - if: contains(fromJSON('["pull_request"]'), github.event_name) + if: github.event_name == 'pull_request' run: | echo "ref=${{ github.base_ref }}" echo "repo=${{ github.repository }}" - name: Match for push events id: push-check - if: contains(fromJSON('["push"]'), github.event_name) + if: github.event_name == 'push' uses: elastic/docs-builder/actions/assembler-match@main with: ref_name: ${{ github.ref_name }} repository: ${{ github.repository }} - name: Match for push events debug - if: contains(fromJSON('["push"]'), github.event_name) + if: github.event_name == 'push' run: | echo "ref=${{ github.ref_name }}" echo "repo=${{ github.repository }}" - name: Debug outputs run: | - echo "content-source-match: ${{ format('{0}{1}{2}', steps.pr-check.outputs.content-source-match, steps.push-check.outputs.content-source-match, steps.merge-group-check.outputs.content-source-match) }}" - echo "content-source-next: ${{ format('{0}{1}{2}', steps.pr-check.outputs.content-source-next, steps.push-check.outputs.content-source-next, steps.merge-group-check.outputs.content-source-next) }}" - echo "content-source-current: ${{ format('{0}{1}{2}', steps.pr-check.outputs.content-source-current, steps.push-check.outputs.content-source-current, steps.merge-group-check.outputs.content-source-current) }}" - echo "content-source-edge: ${{ format('{0}{1}{2}', steps.pr-check.outputs.content-source-edge, steps.push-check.outputs.content-source-edge, steps.merge-group-check.outputs.content-source-edge) }}" - echo "content-source-speculative: ${{ format('{0}{1}{2}', steps.pr-check.outputs.content-source-speculative, steps.push-check.outputs.content-source-speculative, steps.merge-group-check.outputs.content-source-speculative) }}" + echo "content-source-match: ${{ format('{0}{1}', steps.pr-check.outputs.content-source-match, steps.push-check.outputs.content-source-match) }}" + echo "content-source-next: ${{ format('{0}{1}', steps.pr-check.outputs.content-source-next, steps.push-check.outputs.content-source-next) }}" + echo "content-source-current: ${{ format('{0}{1}', steps.pr-check.outputs.content-source-current, steps.push-check.outputs.content-source-current) }}" + echo "content-source-edge: ${{ format('{0}{1}', steps.pr-check.outputs.content-source-edge, steps.push-check.outputs.content-source-edge) }}" + echo "content-source-speculative: ${{ format('{0}{1}', steps.pr-check.outputs.content-source-speculative, steps.push-check.outputs.content-source-speculative) }}" check: runs-on: ubuntu-latest @@ -114,13 +101,13 @@ jobs: renamed_files: ${{ steps.check-modified-file-detail.outputs.renamed_files }} steps: - name: Checkout - if: contains(fromJSON('["push", "merge_group", "workflow_dispatch"]'), github.event_name) + if: github.event_name == 'push' uses: actions/checkout@v6 with: ref: ${{ github.event.pull_request.head.sha || github.ref }} - name: Get changed files - if: contains(fromJSON('["push", "merge_group", "pull_request"]'), github.event_name) + if: contains(fromJSON('["push", "pull_request"]'), github.event_name) id: check-files uses: tj-actions/changed-files@2f7c5bfce28377bc069a65ba478de0a74aa0ca32 # v46.0.1 with: @@ -130,7 +117,7 @@ jobs: README.md - name: Get modified file detail - if: contains(fromJSON('["merge_group", "pull_request"]'), github.event_name) + if: github.event_name == 'pull_request' id: check-modified-file-detail uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: @@ -145,24 +132,12 @@ jobs: const { owner, repo } = context.repo; - let allFiles; - if (context.eventName === 'merge_group') { - const baseSha = context.payload.merge_group.base_sha; - const headSha = context.payload.merge_group.head_sha; - const comparison = await github.rest.repos.compareCommitsWithBasehead({ - owner, - repo, - basehead: `${baseSha}...${headSha}`, - }); - allFiles = comparison.data.files || []; - } else { - const pull_number = context.payload.pull_request.number; - allFiles = await github.paginate(github.rest.pulls.listFiles, { - owner, - repo, - pull_number, - }); - } + const pull_number = context.payload.pull_request.number; + const allFiles = await github.paginate(github.rest.pulls.listFiles, { + owner, + repo, + pull_number, + }); const filteredFiles = allFiles.filter(file => !ignoredPaths.has(file.filename)); @@ -231,14 +206,14 @@ jobs: && env.MATCH == 'true' && needs.check.outputs.any_modified != 'false' && ( - contains(fromJSON('["push", "workflow_dispatch"]'), github.event_name) - || startsWith(github.event_name, 'pull_request') + github.event_name == 'push' + || github.event_name == 'pull_request' ) uses: actions/github-script@v8 id: deployment env: PR_NUMBER: ${{ github.event.pull_request.number }} - REF: ${{ startsWith(github.event_name, 'pull_request') && github.event.pull_request.head.sha || github.ref_name }} + REF: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.ref_name }} with: result-encoding: string script: | @@ -277,10 +252,10 @@ jobs: GITHUB_REF_NAME: ${{ github.ref_name }} run: | case "${GITHUB_EVENT_NAME}" in - "merge_group" | "pull_request") + pull_request) path_prefix="/${GITHUB_REPOSITORY}/pull/${PR_NUMBER}" ;; - "push" | "workflow_dispatch") + push) path_prefix="/${GITHUB_REPOSITORY}/tree/${GITHUB_REF_NAME}" ;; *) @@ -367,7 +342,7 @@ jobs: env.MATCH == 'true' && needs.check.outputs.any_modified != 'false' && ( - contains(fromJSON('["push", "workflow_dispatch"]'), github.event_name) + github.event_name == 'push' && steps.internal-validate-path-prefixes.outcome == 'success' ) uses: elastic/docs-builder/actions/update-link-index@main From 298ccc735c7560320adb2698da3925ebfbbcc6ef Mon Sep 17 00:00:00 2001 From: Martijn Laarman Date: Wed, 15 Apr 2026 21:30:48 +0200 Subject: [PATCH 06/10] Update .github/workflows/docs-preview-local.yml Co-authored-by: Jan Calanog --- .github/workflows/docs-preview-local.yml | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/.github/workflows/docs-preview-local.yml b/.github/workflows/docs-preview-local.yml index 7a74f2808d..55720d8115 100644 --- a/.github/workflows/docs-preview-local.yml +++ b/.github/workflows/docs-preview-local.yml @@ -13,16 +13,6 @@ on: push: branches: - main - - master - # fnmatch-style globs (not regex). [0-9] matches one digit; * matches within a label. - - '[0-9]*.[0-9]*.[0-9]*' - - '[0-9]*.[0-9]*' - - '[0-9]*.x' - tags: - - 'v[0-9]*.[0-9]*.[0-9]*' - - 'v[0-9]*.[0-9]*' - - '[0-9]*.[0-9]*.[0-9]*' - - '[0-9]*.[0-9]*' permissions: contents: read From 186069247e43a2c47bf4b9807e84e5ae405bc4aa Mon Sep 17 00:00:00 2001 From: Martijn Laarman Date: Tue, 21 Apr 2026 13:12:20 +0200 Subject: [PATCH 07/10] feat(ci): docs-preview-cleanup-local; preview-cleanup workflow_call only - Add docs-preview-cleanup-local.yml on PR close, reusing elastic/docs-actions docs-preview-cleanup (docs-builder/preview/cleanup@v1). - Drop pull_request_target from preview-cleanup.yml so this repo does not run duplicate cleanups; keep workflow_call for straggler callers per #474. Made-with: Cursor --- .../workflows/docs-preview-cleanup-local.yml | 20 +++++++++++++++++++ .github/workflows/preview-cleanup.yml | 6 +++--- 2 files changed, 23 insertions(+), 3 deletions(-) create mode 100644 .github/workflows/docs-preview-cleanup-local.yml diff --git a/.github/workflows/docs-preview-cleanup-local.yml b/.github/workflows/docs-preview-cleanup-local.yml new file mode 100644 index 0000000000..a537a04f38 --- /dev/null +++ b/.github/workflows/docs-preview-cleanup-local.yml @@ -0,0 +1,20 @@ +name: Docs preview cleanup (local) + +# Pairs with docs-preview-local.yml. Delegates to elastic/docs-actions instead of maintaining +# duplicate cleanup logic here (docs-eng-team#474). preview-cleanup.yml remains workflow_call-only +# for out-of-repo callers. + +on: + pull_request_target: + types: [closed] + +permissions: + contents: none + deployments: write + id-token: write + +jobs: + destroy: + if: github.event.repository.fork == false + uses: elastic/docs-actions/.github/workflows/docs-preview-cleanup.yml@main + secrets: inherit diff --git a/.github/workflows/preview-cleanup.yml b/.github/workflows/preview-cleanup.yml index cd3cf0bd72..c586c65d0b 100644 --- a/.github/workflows/preview-cleanup.yml +++ b/.github/workflows/preview-cleanup.yml @@ -1,11 +1,11 @@ name: preview-cleanup -# TODO: Migrate to elastic/docs-actions/.github/workflows/docs-preview-cleanup.yml (docs-eng-team#474). +# Reusable workflow for repositories that still call this workflow. elastic/docs-builder uses +# docs-preview-cleanup-local.yml on pull_request_target: closed (delegates to docs-actions). +# TODO: Remove this file once all callers migrate (docs-eng-team#474). on: workflow_call: ~ - pull_request_target: - types: [closed] permissions: contents: none From f1cd7d05eba152dce0905b5ab3d7b59416b98c80 Mon Sep 17 00:00:00 2001 From: Martijn Laarman Date: Tue, 21 Apr 2026 13:19:28 +0200 Subject: [PATCH 08/10] chore(ci): remove preview-build/preview-cleanup; inline docs-preview-cleanup-local - Delete preview-build.yml (reusable workflow removed from this repo). - Replace preview-cleanup.yml with docs-preview-cleanup-local.yml content: inline GitHub deployment + S3 cleanup via aws-auth (no docs-actions). - Drop docs-actions reusable call from docs-preview-cleanup-local. - Adjust docs-preview-local.yml header comment. Made-with: Cursor --- .../workflows/docs-preview-cleanup-local.yml | 48 +- .github/workflows/docs-preview-local.yml | 2 +- .github/workflows/preview-build.yml | 601 ------------------ .github/workflows/preview-cleanup.yml | 56 -- 4 files changed, 43 insertions(+), 664 deletions(-) delete mode 100644 .github/workflows/preview-build.yml delete mode 100644 .github/workflows/preview-cleanup.yml diff --git a/.github/workflows/docs-preview-cleanup-local.yml b/.github/workflows/docs-preview-cleanup-local.yml index a537a04f38..7702ed1de0 100644 --- a/.github/workflows/docs-preview-cleanup-local.yml +++ b/.github/workflows/docs-preview-cleanup-local.yml @@ -1,8 +1,7 @@ name: Docs preview cleanup (local) -# Pairs with docs-preview-local.yml. Delegates to elastic/docs-actions instead of maintaining -# duplicate cleanup logic here (docs-eng-team#474). preview-cleanup.yml remains workflow_call-only -# for out-of-repo callers. +# Runs when a PR is closed. Pairs with docs-preview-local.yml (push/PR build). +# Inlined cleanup: GitHub deployment records + S3 preview prefix (no docs-actions call). on: pull_request_target: @@ -15,6 +14,43 @@ permissions: jobs: destroy: - if: github.event.repository.fork == false - uses: elastic/docs-actions/.github/workflows/docs-preview-cleanup.yml@main - secrets: inherit + if: github.event.repository.fork == false # Skip on fork repo; upstream PRs still run here. + runs-on: ubuntu-latest + steps: + - name: Delete GitHub environment + uses: actions/github-script@v8 + id: delete-deployment + with: + script: | + const { owner, repo } = context.repo; + const deployments = await github.rest.repos.listDeployments({ + owner, + repo, + environment: 'docs-preview', + task: `docs-preview-${context.issue.number}`, + }); + core.setOutput('is-empty', deployments.data.length === 0) + for (const deployment of deployments.data) { + await github.rest.repos.createDeploymentStatus({ + owner, + repo, + deployment_id: deployment.id, + state: 'inactive', + description: 'Marking deployment as inactive' + }); + await github.rest.repos.deleteDeployment({ + owner, + repo, + deployment_id: deployment.id + }); + } + + - uses: elastic/docs-builder/.github/actions/aws-auth@main + if: steps.delete-deployment.outputs.is-empty == 'false' + + - name: Delete s3 objects + if: steps.delete-deployment.outputs.is-empty == 'false' + env: + PR_NUMBER: ${{ github.event.pull_request.number }} + run: | + aws s3 rm "s3://elastic-docs-v3-website-preview/${GITHUB_REPOSITORY}/pull/${PR_NUMBER}" --recursive diff --git a/.github/workflows/docs-preview-local.yml b/.github/workflows/docs-preview-local.yml index 55720d8115..8047b242e5 100644 --- a/.github/workflows/docs-preview-local.yml +++ b/.github/workflows/docs-preview-local.yml @@ -1,7 +1,7 @@ name: Docs preview (local) # In-repo docs build using dotnet run (elastic/docs-builder only). Replaces the former -# github.repository branches in preview-build.yml; consumers still use preview-build.yml (Docker). +# Previously lived in preview-build.yml (removed); this repo builds docs from source here. # Triggers are only pull_request and push — conditions below match those events only. on: diff --git a/.github/workflows/preview-build.yml b/.github/workflows/preview-build.yml deleted file mode 100644 index 8d8bd1b033..0000000000 --- a/.github/workflows/preview-build.yml +++ /dev/null @@ -1,601 +0,0 @@ -name: preview-build - -# Reusable workflow for repositories that still call this workflow. elastic/docs-builder runs -# docs-preview-local.yml on pull_request/push instead (dotnet run from source). -on: - workflow_call: - inputs: - strict: - description: 'Treat warnings as errors' - type: string - default: 'true' - metadata-only: - description: 'Only generate documentation metadata files' - type: string - required: false - default: 'false' - continue-on-error: - description: 'Do not fail to publish if build fails' - type: string - required: false - default: 'false' - path-pattern: - description: 'Path pattern to filter files. Only if changed files match the pattern, the workflow will continue.' - type: string - default: '**' - required: false - path-pattern-ignore: - description: 'Path pattern to ignore files.' - type: string - default: '' - required: false - free-disk-space: - description: 'Free disk space before running the build' - type: string - default: 'false' - required: false - disable-comments: - description: 'Disable comments' - type: boolean - default: false - required: false - enable-cumulative-comment: - description: 'Enable info comment' - type: boolean - default: false - required: false - enable-vale-linting: - description: 'Enable vale linting' - type: boolean - default: false - required: false - include-paths: - description: 'Paths to include for Vale linting (multi-line). Only files matching these paths will be linted.' - type: string - default: '' - required: false - -permissions: - contents: read - id-token: write - pull-requests: read - -concurrency: - group: ${{ github.workflow }}-${{ github.event.pull_request.head.ref || github.ref }} - cancel-in-progress: ${{ startsWith(github.event_name, 'pull_request') }} - -jobs: - match: - if: github.event.repository.fork == false # Skip running the job on the fork itself (It still runs on PRs on the upstream from forks) - runs-on: ubuntu-latest - permissions: - contents: none - deployments: none - pull-requests: none - id-token: none - outputs: - content-source-match: ${{ format('{0}{1}{2}', steps.pr-check.outputs.content-source-match, steps.push-check.outputs.content-source-match, steps.merge-group-check.outputs.content-source-match) }} - content-source-next: ${{ format('{0}{1}{2}', steps.pr-check.outputs.content-source-next, steps.push-check.outputs.content-source-next, steps.merge-group-check.outputs.content-source-next) }} - content-source-current: ${{ format('{0}{1}{2}', steps.pr-check.outputs.content-source-current, steps.push-check.outputs.content-source-current, steps.merge-group-check.outputs.content-source-current) }} - content-source-edge: ${{ format('{0}{1}{2}', steps.pr-check.outputs.content-source-edge, steps.push-check.outputs.content-source-edge, steps.merge-group-check.outputs.content-source-edge) }} - content-source-speculative: ${{ format('{0}{1}{2}', steps.pr-check.outputs.content-source-speculative, steps.push-check.outputs.content-source-speculative, steps.merge-group-check.outputs.content-source-speculative) }} - steps: - - name: Find merge-group base branch - if: contains(fromJSON('["merge_group"]'), github.event_name) - id: merge-group-base-branch - run: | - BASE_BRANCH=$(basename "${{ github.event.merge_group.base_ref }}") - echo "base_ref=${BASE_BRANCH}" >> $GITHUB_OUTPUT - - name: Match for merge-group events - id: merge-group-check - if: contains(fromJSON('["merge_group"]'), github.event_name) - uses: elastic/docs-builder/actions/assembler-match@main - with: - ref_name: ${{ steps.merge-group-base-branch.outputs.base_ref }} - repository: ${{ github.repository }} - - - name: Match for PR events - id: pr-check - if: contains(fromJSON('["pull_request", "pull_request_target"]'), github.event_name) - uses: elastic/docs-builder/actions/assembler-match@main - with: - ref_name: ${{ github.base_ref }} - repository: ${{ github.repository }} - - name: Match for PR events debug - if: contains(fromJSON('["pull_request", "pull_request_target"]'), github.event_name) - run: | - echo "ref=${{ github.base_ref }}" - echo "repo=${{ github.repository }}" - - - name: Match for push events - id: push-check - if: contains(fromJSON('["push"]'), github.event_name) - uses: elastic/docs-builder/actions/assembler-match@main - with: - ref_name: ${{ github.ref_name }} - repository: ${{ github.repository }} - - name: Match for push events debug - if: contains(fromJSON('["push"]'), github.event_name) - run: | - echo "ref=${{ github.ref_name }}" - echo "repo=${{ github.repository }}" - - - name: Debug outputs - run: | - echo "content-source-match: ${{ format('{0}{1}{2}', steps.pr-check.outputs.content-source-match, steps.push-check.outputs.content-source-match, steps.merge-group-check.outputs.content-source-match) }}" - echo "content-source-next: ${{ format('{0}{1}{2}', steps.pr-check.outputs.content-source-next, steps.push-check.outputs.content-source-next, steps.merge-group-check.outputs.content-source-next) }}" - echo "content-source-current: ${{ format('{0}{1}{2}', steps.pr-check.outputs.content-source-current, steps.push-check.outputs.content-source-current, steps.merge-group-check.outputs.content-source-current) }}" - echo "content-source-edge: ${{ format('{0}{1}{2}', steps.pr-check.outputs.content-source-edge, steps.push-check.outputs.content-source-edge, steps.merge-group-check.outputs.content-source-edge) }}" - echo "content-source-speculative: ${{ format('{0}{1}{2}', steps.pr-check.outputs.content-source-speculative, steps.push-check.outputs.content-source-speculative, steps.merge-group-check.outputs.content-source-speculative) }}" - - check: - runs-on: ubuntu-latest - needs: - - match - permissions: - contents: read - deployments: none - id-token: none - pull-requests: read - outputs: - any_modified: ${{ steps.check-files.outputs.any_modified }} - all_changed_files: ${{ steps.check-files.outputs.all_changed_files }} - added_files: ${{ steps.check-modified-file-detail.outputs.added_files }} - modified_files: ${{ steps.check-modified-file-detail.outputs.modified_files }} - deleted_files: ${{ steps.check-modified-file-detail.outputs.deleted_files }} - renamed_files: ${{ steps.check-modified-file-detail.outputs.renamed_files }} - steps: - - name: Checkout - if: contains(fromJSON('["push", "merge_group", "workflow_dispatch"]'), github.event_name) - uses: actions/checkout@v6 - with: - ref: ${{ github.event.pull_request.head.sha || github.ref }} - - - name: Get changed files - if: contains(fromJSON('["push", "merge_group", "pull_request", "pull_request_target"]'), github.event_name) - id: check-files - uses: tj-actions/changed-files@2f7c5bfce28377bc069a65ba478de0a74aa0ca32 # v46.0.1 - with: - files: ${{ inputs.path-pattern != '' && inputs.path-pattern || '**' }} - files_ignore: | - ${{ inputs.path-pattern-ignore != '' && inputs.path-pattern-ignore || '' }} - .github/** - README.md - - - name: Get modified file detail - if: contains(fromJSON('["merge_group", "pull_request", "pull_request_target"]'), github.event_name) - id: check-modified-file-detail - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 - env: - PATH_PATTERN: "${{ inputs.path-pattern != '' && inputs.path-pattern || '**' }}" - IGNORE_PATTERNS: | - ${{ inputs.path-pattern-ignore }} - .github/** - README.md - with: - script: | - const pathPattern = process.env.PATH_PATTERN; - const ignorePatterns = process.env.IGNORE_PATTERNS; - const ignoreGlobber = await glob.create(ignorePatterns); - const ignoredPaths = new Set(await ignoreGlobber.glob()); - - const { owner, repo } = context.repo; - - let allFiles; - if (context.eventName === 'merge_group') { - const baseSha = context.payload.merge_group.base_sha; - const headSha = context.payload.merge_group.head_sha; - const comparison = await github.rest.repos.compareCommitsWithBasehead({ - owner, - repo, - basehead: `${baseSha}...${headSha}`, - }); - allFiles = comparison.data.files || []; - } else { - const pull_number = context.payload.pull_request.number; - allFiles = await github.paginate(github.rest.pulls.listFiles, { - owner, - repo, - pull_number, - }); - } - - const filteredFiles = allFiles.filter(file => !ignoredPaths.has(file.filename)); - - const added = []; - const modified = []; - const deleted = []; - const renamed = []; - - for (const file of filteredFiles) { - switch (file.status) { - case 'added': - added.push(file.filename); - break; - case 'modified': - modified.push(file.filename); - break; - case 'removed': - deleted.push(file.filename); - break; - case 'renamed': - renamed.push(`${file.previous_filename}:${file.filename}`); - break; - } - } - - core.setOutput('added_files', added.join(' ')); - core.setOutput('modified_files', modified.join(' ')); - core.setOutput('deleted_files', deleted.join(' ')); - core.setOutput('renamed_files', renamed.join(' ')); - - build: - if: github.event.repository.fork == false # Skip running the job on the fork itself (It still runs on PRs on the upstream from forks) - runs-on: ubuntu-latest - permissions: - contents: read - deployments: none - id-token: write - pull-requests: none - outputs: - deployment_result: ${{ steps.deployment.outputs.result }} - path_prefix: ${{ steps.generate-path-prefix.outputs.result }} - env: - GITHUB_PR_REF_NAME: ${{ github.event.pull_request.head.ref }} - MATCH: ${{ needs.match.outputs.content-source-match }} - ADDED_FILES: ${{ needs.check.outputs.added_files }} - MODIFIED_FILES: ${{ needs.check.outputs.modified_files }} - DELETED_FILES: ${{ needs.check.outputs.deleted_files }} - RENAMED_FILES: ${{ needs.check.outputs.renamed_files }} - needs: - - check - - match - steps: - - name: Checkout - if: > - env.MATCH == 'true' - && needs.check.outputs.any_modified != 'false' - uses: actions/checkout@v6 - with: - ref: ${{ github.event.pull_request.head.sha || github.ref }} - persist-credentials: false - - - name: Create Deployment - # disabled: deployments are not enabled on this branch - if: > - false - && env.MATCH == 'true' - && needs.check.outputs.any_modified != 'false' - && ( - contains(fromJSON('["push", "workflow_dispatch"]'), github.event_name) - || startsWith(github.event_name, 'pull_request') - ) - uses: actions/github-script@v8 - id: deployment - env: - PR_NUMBER: ${{ github.event.pull_request.number }} - REF: ${{ startsWith(github.event_name, 'pull_request') && github.event.pull_request.head.sha || github.ref_name }} - with: - result-encoding: string - script: | - const { owner, repo } = context.repo; - const prNumber = process.env.PR_NUMBER; - const environment = 'docs-preview'; - const task = prNumber ? `docs-preview-${prNumber}` : undefined; - const deployment = await github.rest.repos.createDeployment({ - owner, - repo, - environment, - task, - ref: process.env.REF, - auto_merge: false, - transient_environment: true, - required_contexts: [], - }) - await github.rest.repos.createDeploymentStatus({ - deployment_id: deployment.data.id, - owner, - repo, - state: "in_progress", - log_url: `https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`, - }) - return deployment.data.id - - - name: Generate env.PATH_PREFIX - id: generate-path-prefix - # disabled: preview path prefix is not needed on this branch - if: > - false - && env.MATCH == 'true' - && steps.deployment.outputs.result - env: - PR_NUMBER: ${{ github.event.pull_request.number }} - GITHUB_REF_NAME: ${{ github.ref_name }} - run: | - case "${GITHUB_EVENT_NAME}" in - "merge_group" | "pull_request" | "pull_request_target") - path_prefix="/${GITHUB_REPOSITORY}/pull/${PR_NUMBER}" - ;; - "push" | "workflow_dispatch") - path_prefix="/${GITHUB_REPOSITORY}/tree/${GITHUB_REF_NAME}" - ;; - *) - echo "Unsupported event: '${GITHUB_EVENT_NAME}'"; - exit 1; - ;; - esac - echo "PATH_PREFIX=${path_prefix}" >> $GITHUB_ENV - echo "result=${path_prefix}" >> $GITHUB_OUTPUT - - - name: 'Validate redirect rules' - if: > - env.MATCH == 'true' - && needs.check.outputs.any_modified != 'false' - uses: elastic/docs-builder/actions/diff-validate@main - - - name: Build documentation - if: > - env.MATCH == 'true' - && needs.check.outputs.any_modified != 'false' - uses: elastic/docs-builder@main - id: docs-build - continue-on-error: ${{ fromJSON(inputs.continue-on-error != '' && inputs.continue-on-error || 'false') }} - with: - strict: ${{ fromJSON(inputs.strict != '' && inputs.strict || 'true') }} - metadata-only: ${{ fromJSON(inputs.metadata-only != '' && inputs.metadata-only || 'false') }} - - - name: 'Validate inbound links' - if: > - env.MATCH == 'true' - && !cancelled() - && steps.docs-build.outputs.skip != 'true' - && needs.check.outputs.any_modified != 'false' - uses: elastic/docs-builder/actions/validate-inbound-local@main - - - name: 'Validate local path prefixes against those claimed by global navigation.yml' - id: validate-path-prefixes - if: > - env.MATCH == 'true' - && !cancelled() - && steps.docs-build.outputs.skip != 'true' - && steps.docs-build.outcome != 'skipped' - && needs.check.outputs.any_modified != 'false' - uses: elastic/docs-builder/actions/validate-path-prefixes-local@main - - - uses: elastic/docs-builder/.github/actions/aws-auth@main - if: > - !cancelled() - && needs.check.outputs.any_modified != 'false' - && steps.validate-path-prefixes.outcome == 'success' - - name: Upload to S3 - id: s3-upload - # disabled: preview uploads are not enabled on this branch - if: > - false - && env.MATCH == 'true' - && !cancelled() - && steps.docs-build.outputs.skip != 'true' - && steps.deployment.outputs.result - && steps.docs-build.outcome == 'success' - env: - # https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-retries.html#cli-usage-retries-configure - AWS_RETRY_MODE: standard - AWS_MAX_ATTEMPTS: 6 - run: | - aws s3 sync .artifacts/docs/html "s3://elastic-docs-v3-website-preview${PATH_PREFIX}" --delete --no-follow-symlinks - aws cloudfront create-invalidation \ - --distribution-id EKT7LT5PM8RKS \ - --paths "${PATH_PREFIX}" "${PATH_PREFIX}/*" - - - name: Update Link Index - if: > - env.MATCH == 'true' - && needs.check.outputs.any_modified != 'false' - && ( - contains(fromJSON('["push", "workflow_dispatch"]'), github.event_name) - && steps.validate-path-prefixes.outcome == 'success' - ) - uses: elastic/docs-builder/actions/update-link-index@main - - - name: Update deployment status - uses: actions/github-script@v8 - # disabled: deployments are not enabled on this branch - if: > - false - && env.MATCH == 'true' - && always() - && steps.deployment.outputs.result - env: - PR_NUMBER: ${{ github.event.pull_request.number }} - LANDING_PAGE_PATH: ${{ steps.docs-build.outputs.landing-page-path || env.PATH_PREFIX }} - with: - script: | - await github.rest.repos.createDeploymentStatus({ - owner: context.repo.owner, - repo: context.repo.repo, - deployment_id: ${{ steps.deployment.outputs.result }}, - state: "${{ steps.docs-build.outputs.skip == 'true' && 'inactive' || (steps.s3-upload.outcome == 'success' && 'success' || 'failure') }}", - environment_url: `https://docs-v3-preview.elastic.dev${process.env.LANDING_PAGE_PATH}`, - log_url: `https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`, - }) - comment: - # disabled: preview comments are not enabled on this branch - if: > - false - && startsWith(github.event_name, 'pull_request') - && inputs.disable-comments != true - && needs.build.outputs.deployment_result - && needs.check.outputs.any_modified == 'true' - runs-on: ubuntu-latest - needs: - - check - - build - permissions: - contents: none - deployments: none - id-token: none - pull-requests: write - steps: - - name: Comment on PR - continue-on-error: true - uses: actions/github-script@v8 - env: - ALL_CHANGED_FILES: ${{ needs.check.outputs.all_changed_files }} - PATH_PREFIX: ${{ needs.build.outputs.path_prefix }} - with: - # language=javascript - script: | - const title = '## 🔍 Preview links for changed docs' - const changedMdFiles = process.env.ALL_CHANGED_FILES - .split(/\s+/) - .filter(i => i.endsWith('.md')) - .filter(i => !i.includes('/_snippets/')); - - if (changedMdFiles.length === 0) { - return; - } - - const toMarkdownLink = (file) => { - const path = file - .replace(/^docs\//, '') - .replace(/\/index.md$/, '') - .replace(/\.md$/, ''); - return `[${file}](https://docs-v3-preview.elastic.dev${process.env.PATH_PREFIX}/${path})`; - } - - const links = changedMdFiles.map(toMarkdownLink) - - const body = [ - title, - ...links.slice(0, 10).map(i => `- ${i}`), - ] - - if (links.length > 10) { - body.push('
'); - body.push('More links â€Ļ'); - body.push(''); - for (const link of links.slice(10, 100)) { - body.push(`- ${link}`); - } - body.push(''); - body.push('
'); - } - - if (links.length > 100) { - body.push(''); - body.push(`In total, ${links.length} files changed.`); - } - - const owner = context.repo.owner; - const repo = context.repo.repo; - const issue_number = context.payload.pull_request.number; - - // Post or update a single bot comment - const { data: comments } = await github.rest.issues.listComments({ - owner, repo, issue_number - }); - const existing = comments.find(c => - c.user.type === 'Bot' && - c.body.startsWith(title) - ); - if (existing) { - await github.rest.issues.updateComment({ - owner, repo, - comment_id: existing.id, - body: body.join('\n'), - }); - } else { - await github.rest.issues.createComment({ - owner, repo, - issue_number, - body:body.join('\n'), - }); - } - - name: Comment on docs changes about versioning requirements - if: inputs.enable-cumulative-comment == true - uses: actions/github-script@v8 - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - # language=javascript - script: | - const pr = context.payload.pull_request; - const prNum = pr.number; - const owner = context.repo.owner; - const repo = context.repo.repo; - - - const { data: comments } = await github.rest.issues.listComments({ - owner, repo, issue_number: prNum - }); - - const title = '## â„šī¸ Important: Docs version tagging'; - - const existingComment = comments.find(c => - c.user.type === 'Bot' && - c.body.startsWith(title) - ); - - if (existingComment) return; - - const body = `${title} - - 👋 Thanks for updating the docs! Just a friendly reminder that our docs are now **cumulative**. This means all 9.x versions are documented on the same page and published off of the main branch, instead of creating separate pages for each minor version. - - We use [applies_to tags](https://elastic.github.io/docs-builder/syntax/applies) to mark version-specific features and changes. - -
- Expand for a quick overview - - ### When to use applies_to tags: - ✅ At the page level to indicate which products/deployments the content applies to (mandatory) - ✅ When features change state (e.g. preview, ga) in a specific version - ✅ When availability differs across deployments and environments - - ### What NOT to do: - ❌ Don't remove or replace information that applies to an older version - ❌ Don't add new information that applies to a specific version without an applies_to tag - ❌ Don't forget that applies_to tags can be used at the page, section, and inline level -
- - ### 🤔 Need help? - - Check out the [cumulative docs guidelines](https://www.elastic.co/docs/contribute-docs/how-to/cumulative-docs/) - - Reach out in the [#docs](https://elastic.slack.com/archives/C0JF80CJZ) Slack channel`; - - await github.rest.issues.createComment({ - owner, repo, - issue_number: prNum, - body - }); - - vale: - # disabled: vale linting is not enabled on this branch - if: > - false - && startsWith(github.event_name, 'pull_request') - && inputs.enable-vale-linting == true - && needs.check.outputs.any_modified == 'true' - runs-on: ubuntu-latest - needs: check - permissions: - contents: read - pull-requests: write - deployments: none - id-token: none - steps: - - name: Checkout - uses: actions/checkout@v6 - with: - fetch-depth: 0 - filter: blob:none - fetch-tags: false - ref: ${{ github.event.pull_request.head.sha || github.ref }} - persist-credentials: false - - name: Run Vale Linter - uses: elastic/vale-rules/lint@main - with: - files: ${{ needs.check.outputs.all_changed_files }} - include-paths: ${{ inputs.include-paths }} - - name: Post Vale Results - uses: elastic/vale-rules/report@main - with: - download_artifacts: false diff --git a/.github/workflows/preview-cleanup.yml b/.github/workflows/preview-cleanup.yml deleted file mode 100644 index c586c65d0b..0000000000 --- a/.github/workflows/preview-cleanup.yml +++ /dev/null @@ -1,56 +0,0 @@ -name: preview-cleanup - -# Reusable workflow for repositories that still call this workflow. elastic/docs-builder uses -# docs-preview-cleanup-local.yml on pull_request_target: closed (delegates to docs-actions). -# TODO: Remove this file once all callers migrate (docs-eng-team#474). - -on: - workflow_call: ~ - -permissions: - contents: none - deployments: write - id-token: write - -jobs: - destroy: - if: github.event.repository.fork == false # Skip running the job on the fork itself (It still runs on PRs on the upstream from forks) - runs-on: ubuntu-latest - steps: - - name: Delete GitHub environment - uses: actions/github-script@v8 - id: delete-deployment - with: - script: | - const { owner, repo } = context.repo; - const deployments = await github.rest.repos.listDeployments({ - owner, - repo, - environment: 'docs-preview', - task: `docs-preview-${context.issue.number}`, - }); - core.setOutput('is-empty', deployments.data.length === 0) - for (const deployment of deployments.data) { - await github.rest.repos.createDeploymentStatus({ - owner, - repo, - deployment_id: deployment.id, - state: 'inactive', - description: 'Marking deployment as inactive' - }); - await github.rest.repos.deleteDeployment({ - owner, - repo, - deployment_id: deployment.id - }); - } - - - uses: elastic/docs-builder/.github/actions/aws-auth@main - if: steps.delete-deployment.outputs.is-empty == 'false' - - - name: Delete s3 objects - if: steps.delete-deployment.outputs.is-empty == 'false' - env: - PR_NUMBER: ${{ github.event.pull_request.number }} - run: | - aws s3 rm "s3://elastic-docs-v3-website-preview/${GITHUB_REPOSITORY}/pull/${PR_NUMBER}" --recursive From ee09d230f11990a58d1a3f5e6ee9851d6ad4eaac Mon Sep 17 00:00:00 2001 From: Martijn Laarman Date: Tue, 21 Apr 2026 13:54:06 +0200 Subject: [PATCH 09/10] fix(ci): scope docs-preview-local concurrency by PR number on pull_request Avoids grouping distinct PRs that share the same head.ref (forks / same branch name). Made-with: Cursor --- .github/workflows/docs-preview-local.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/docs-preview-local.yml b/.github/workflows/docs-preview-local.yml index 8047b242e5..007576786b 100644 --- a/.github/workflows/docs-preview-local.yml +++ b/.github/workflows/docs-preview-local.yml @@ -19,8 +19,9 @@ permissions: id-token: write pull-requests: read +# PR number avoids grouping disparate PRs that share the same head.ref (e.g. forks). concurrency: - group: docs-preview-local-${{ github.event.pull_request.head.ref || github.ref }} + group: docs-preview-local-${{ github.event_name == 'pull_request' && format('pr{0}-{1}', github.event.pull_request.number, github.event.pull_request.head.ref) || github.ref }} cancel-in-progress: ${{ github.event_name == 'pull_request' }} jobs: From fc7928462d3e995944371c6f9cd891a523002cf1 Mon Sep 17 00:00:00 2001 From: Martijn Laarman Date: Wed, 22 Apr 2026 20:48:23 +0200 Subject: [PATCH 10/10] fix(preview): match IGNORE_PATTERNS on paths without filesystem glob Made-with: Cursor --- .github/workflows/docs-preview-local.yml | 31 +++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/.github/workflows/docs-preview-local.yml b/.github/workflows/docs-preview-local.yml index 007576786b..7830f04578 100644 --- a/.github/workflows/docs-preview-local.yml +++ b/.github/workflows/docs-preview-local.yml @@ -118,8 +118,33 @@ jobs: with: script: | const ignorePatterns = process.env.IGNORE_PATTERNS; - const ignoreGlobber = await glob.create(ignorePatterns); - const ignoredPaths = new Set(await ignoreGlobber.glob()); + const ignoreLines = ignorePatterns + .split(/\r?\n/) + .map((line) => line.trim()) + .filter(Boolean); + + const pathMatchesIgnorePattern = (path, pattern) => { + if (pattern.endsWith('/**')) { + const prefix = pattern.slice(0, -2); + const dir = prefix.endsWith('/') ? prefix.slice(0, -1) : prefix; + return path === dir || path.startsWith(`${dir}/`); + } + if (pattern.includes('*')) { + const re = new RegExp( + '^' + + pattern + .replace(/[.+^${}()|[\]\\]/g, '\\$&') + .replace(/\*\*/g, '.*') + .replace(/\*/g, '[^/]*') + + '$' + ); + return re.test(path); + } + return path === pattern; + }; + + const pathMatchesAnyIgnore = (path) => + ignoreLines.some((p) => pathMatchesIgnorePattern(path, p)); const { owner, repo } = context.repo; @@ -130,7 +155,7 @@ jobs: pull_number, }); - const filteredFiles = allFiles.filter(file => !ignoredPaths.has(file.filename)); + const filteredFiles = allFiles.filter((file) => !pathMatchesAnyIgnore(file.filename)); const added = []; const modified = [];