Skip to content

Add more logging to CLI invocations on CI and ensure CLI invocations have timeouts on CI #4525

Add more logging to CLI invocations on CI and ensure CLI invocations have timeouts on CI

Add more logging to CLI invocations on CI and ensure CLI invocations have timeouts on CI #4525

Workflow file for this run

name: preview-build
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:
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
permissions:
contents: read
deployments: write
id-token: write
pull-requests: write
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: ${{ steps.event-check.outputs.content-source-match != '' && steps.event-check.outputs.content-source-match || steps.match.outputs.content-source-match }}
content-source-next: ${{ steps.event-check.outputs.content-source-next != '' && steps.event-check.outputs.content-source-next || steps.match.outputs.content-source-next }}
content-source-current: ${{ steps.event-check.outputs.content-source-current != '' && steps.event-check.outputs.content-source-current || steps.match.outputs.content-source-current }}
content-source-speculative: ${{ steps.event-check.outputs.content-source-speculative != '' && steps.event-check.outputs.content-source-speculative || steps.match.outputs.content-source-speculative }}
steps:
- name: Not a push event
id: event-check
if: contains(fromJSON('["merge_group", "pull_request", "pull_request_target"]'), github.event_name)
# we always want to run for pull requests, but we do not want to indicate its either content source
run: |
echo "content-source-match=true" >> $GITHUB_OUTPUT
echo "content-source-next=false" >> $GITHUB_OUTPUT
echo "content-source-current=false" >> $GITHUB_OUTPUT
echo "content-source-speculative=false" >> $GITHUB_OUTPUT
- name: Match for push events
id: match
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: Debug
run: |
echo "Non sensitive data, echo'ing here temporarily to validate this job before connecting it further into the build job"
echo "content-source-match=${{ steps.event-check.outputs.content-source-match != '' && steps.event-check.outputs.content-source-match || steps.match.outputs.content-source-match }}"
echo "content-source-next=${{ steps.event-check.outputs.content-source-next != '' && steps.event-check.outputs.content-source-next || steps.match.outputs.content-source-next }}"
echo "content-source-current=${{ steps.event-check.outputs.content-source-current != '' && steps.event-check.outputs.content-source-current || steps.match.outputs.content-source-current }}"
echo "content-source-speculative=${{ steps.event-check.outputs.content-source-speculative != '' && steps.event-check.outputs.content-source-speculative || steps.match.outputs.content-source-speculative }}"
echo "ref=${{ github.ref_name }}"
echo "repo=${{ github.repository }}"
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@v5
with:
ref: ${{ github.event.pull_request.head.sha || github.ref }}
- name: Get changed files
if: contains(fromJSON('["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;
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));
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: write
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 == 'true'
|| contains(fromJSON('["push", "workflow_dispatch"]'), github.event_name)
)
uses: actions/checkout@v5
with:
ref: ${{ github.event.pull_request.head.sha || github.ref }}
persist-credentials: false
- name: Create Deployment
if: >
env.MATCH == 'true'
&& (
contains(fromJSON('["push", "workflow_dispatch"]'), github.event_name)
|| (needs.check.outputs.any_modified == 'true' && 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
if: >
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'
&& github.repository == 'elastic/docs-builder'
&& steps.deployment.outputs.result
uses: elastic/docs-builder/.github/actions/bootstrap@main
- name: 'Validate redirect rules'
if: >
env.MATCH == 'true'
&& github.repository == 'elastic/docs-builder'
&& (
steps.deployment.outputs.result
|| (
needs.check.outputs.any_modified == 'true'
&& github.event_name == 'merge_group'
)
)
run: |
dotnet run --project src/tooling/docs-builder -- diff validate
- name: 'Validate redirect rules'
if: >
env.MATCH == 'true'
&& (
github.repository != 'elastic/docs-builder'
&& (
steps.deployment.outputs.result
|| (
needs.check.outputs.any_modified == 'true'
&& github.event_name == 'merge_group'
)
)
)
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
if: >
env.MATCH == 'true'
&& github.repository == 'elastic/docs-builder'
&& steps.deployment.outputs.result
run: |
dotnet run --project src/tooling/docs-builder -- --strict --path-prefix "${PATH_PREFIX}"
- name: Build documentation
if: >
env.MATCH == 'true'
&& (
github.repository != 'elastic/docs-builder'
&& (
steps.deployment.outputs.result
|| (
needs.check.outputs.any_modified == 'true'
&& github.event_name == 'merge_group'
)
)
)
uses: elastic/docs-builder@main
id: docs-build
continue-on-error: ${{ fromJSON(inputs.continue-on-error != '' && inputs.continue-on-error || 'false') }}
with:
prefix: ${{ env.PATH_PREFIX }}
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()
&& github.repository != 'elastic/docs-builder'
&& steps.docs-build.outputs.skip != 'true'
&& (
steps.deployment.outputs.result
|| (
needs.check.outputs.any_modified == 'true'
&& github.event_name == 'merge_group'
)
)
)
uses: elastic/docs-builder/actions/validate-inbound-local@main
- name: 'Validate inbound links'
if: >
env.MATCH == 'true'
&& (
!cancelled()
&& github.repository == 'elastic/docs-builder'
&& steps.docs-build.outputs.skip != 'true'
&& (
steps.deployment.outputs.result
|| (
needs.check.outputs.any_modified == 'true'
&& github.event_name == 'merge_group'
)
)
)
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'
if: >
env.MATCH == 'true'
&& (
!cancelled()
&& github.repository != 'elastic/docs-builder'
&& steps.docs-build.outputs.skip != 'true'
&& (
steps.deployment.outputs.result
|| (
needs.check.outputs.any_modified == 'true'
&& github.event_name == 'merge_group'
)
)
)
uses: elastic/docs-builder/actions/validate-path-prefixes-local@main
- name: 'Validate local path prefixes against those claimed by global navigation.yml'
if: >
env.MATCH == 'true'
&& (
!cancelled()
&& github.repository == 'elastic/docs-builder'
&& steps.docs-build.outputs.skip != 'true'
&& (
steps.deployment.outputs.result
|| (
needs.check.outputs.any_modified == 'true'
&& github.event_name == 'merge_group'
)
)
)
run: |
dotnet run --project src/tooling/docs-assembler -- navigation validate-link-reference
- uses: elastic/docs-builder/.github/actions/aws-auth@main
if: >
!cancelled()
&& steps.docs-build.outputs.skip != 'true'
&& steps.deployment.outputs.result
- name: Upload to S3
id: s3-upload
if: >
env.MATCH == 'true'
&& !cancelled()
&& steps.docs-build.outputs.skip != 'true'
&& steps.deployment.outputs.result
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'
&& (
contains(fromJSON('["push", "workflow_dispatch"]'), github.event_name)
&& (
needs.match.outputs.content-source-current == 'true'
|| needs.match.outputs.content-source-next == 'true'
|| needs.match.outputs.content-source-speculative == 'true'
)
&& steps.s3-upload.outcome == 'success'
)
uses: elastic/docs-builder/actions/update-link-index@main
- name: Update deployment status
uses: actions/github-script@v8
if: >
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:
if: >
startsWith(github.event_name, 'pull_request')
&& inputs.disable-comments != true
&& needs.build.outputs.deployment_result
&& needs.check.outputs.any_modified
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 toLink = (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(toLink)
const body = [
title,
...links.slice(0, 10).map(i => `- ${i}`),
]
if (links.length > 10) {
body.push('<details>');
body.push('<summary>More links …</summary>');
body.push('');
for (const link of links.slice(10, 100)) {
body.push(`- ${link}`);
}
body.push('');
body.push('</details>');
}
if (links.length > 100) {
body.push('');
body.push(`<sub>In total, ${links.length} files changed.</sub>`);
}
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.
<details>
<summary>Expand for a quick overview</summary>
### 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
</details>
### πŸ€” Need help?
- Check out the [cumulative docs guidelines](https://elastic.github.io/docs-builder/contribute/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
});