From aa7fdc1584070348f0998f3ebf620caa962175e3 Mon Sep 17 00:00:00 2001 From: Weronika Olejniczak Date: Thu, 16 Jul 2026 12:11:04 +0200 Subject: [PATCH 1/5] chore: add nightly schedule --- .github/workflows/release.yml | 31 +++ .../workflows/update_kibana_dependencies.yml | 19 +- .../update_kibana_dependencies__open_pr.yml | 184 ++++++++++++++++-- ...e_kibana_dependencies__prepare_changes.yml | 47 ++++- 4 files changed, 261 insertions(+), 20 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5fbfc321b98..fadc352a612 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -47,6 +47,14 @@ on: description: "[Kibana integration only] Source EUI PR number that triggered the run" required: false type: string + kibana_nightly: + description: "[Kibana integration only] Whether this is a scheduled nightly run" + default: false + type: boolean +concurrency: + group: ${{ inputs.kibana_nightly && 'kibana-nightly' || format('release-{0}', github.run_id) }} + cancel-in-progress: false + queue: max permissions: id-token: write # Required for OIDC contents: read @@ -169,14 +177,37 @@ jobs: PR_TITLE: ${{ inputs.kibana_pr_title }} PR_BODY: ${{ inputs.kibana_pr_body }} SOURCE_PR_NUMBER: ${{ inputs.kibana_source_pr_number }} + NIGHTLY: ${{ inputs.kibana_nightly }} + EUI_REF: ${{ inputs.release_ref }} # language=bash run: | set -euo pipefail dependencies="$(jq -c . .release/published_packages.json)" + eui_version='' + eui_last_release='' + + if [[ "$NIGHTLY" == "true" ]]; then + eui_version="$( + jq -r '.[] | select(.name == "@elastic/eui") | .version' \ + .release/published_packages.json + )" + if [[ -z "$eui_version" ]]; then + echo "::error::The snapshot did not publish @elastic/eui" + exit 1 + fi + + last_release="$(git describe --tags --abbrev=0 --match 'v[0-9]*' --exclude '*-*' "$EUI_REF")" + eui_last_release="$last_release" + fi + args=( -f "dependencies=$dependencies" -f "pr_title=$PR_TITLE" -f "pr_body=$PR_BODY" + -f "nightly=$NIGHTLY" + -f "eui_ref=$EUI_REF" + -f "eui_last_release=$eui_last_release" + -f "eui_version=$eui_version" ) if [[ -n "${SOURCE_PR_NUMBER:-}" ]]; then args+=( -f "source_pr_number=$SOURCE_PR_NUMBER" ) diff --git a/.github/workflows/update_kibana_dependencies.yml b/.github/workflows/update_kibana_dependencies.yml index 9befa9d147a..2b68c318029 100644 --- a/.github/workflows/update_kibana_dependencies.yml +++ b/.github/workflows/update_kibana_dependencies.yml @@ -1,6 +1,13 @@ name: Update Kibana dependencies on: + schedule: + # 03:05 UTC, Monday-Friday + # "High load times include the start of every hour. If the load is sufficiently high enough, + # some queued jobs may be dropped. To decrease the chance of delay, schedule your workflow + # to run at a different time of the hour." + # Source: https://docs.github.com/en/actions/reference/workflows-and-actions/events-that-trigger-workflows#schedule + - cron: '5 3 * * 1-5' pull_request_target: types: [labeled] workflow_dispatch: @@ -26,7 +33,9 @@ jobs: trigger_release: name: Trigger snapshot release runs-on: ubuntu-latest - if: ${{ github.event_name != 'pull_request_target' || github.event.label.name == 'ci:regression-integration-test-kibana' }} + if: >- + (github.event_name != 'pull_request_target' || github.event.label.name == 'ci:regression-integration-test-kibana') && + (github.event_name != 'schedule' || github.repository == 'elastic/eui') permissions: actions: write contents: read @@ -35,7 +44,7 @@ jobs: env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} EVENT_NAME: ${{ github.event_name }} - RELEASE_REF: ${{ github.event_name == 'pull_request_target' && github.event.pull_request.head.sha || inputs.release_ref }} + RELEASE_REF: ${{ github.event_name == 'pull_request_target' && github.event.pull_request.head.sha || inputs.release_ref || github.sha }} PR_NUMBER: ${{ github.event.pull_request.number }} PR_URL: ${{ github.event.pull_request.html_url }} INPUT_PR_TITLE: ${{ inputs.pr_title }} @@ -57,6 +66,11 @@ jobs: "-f" "kibana_pr_body=This PR was created programmatically to run regression integration tests of EUI changes from $PR_URL" "-f" "kibana_source_pr_number=$PR_NUMBER" ) + elif [[ "$EVENT_NAME" == "schedule" ]]; then + workflow_args+=( + "-f" "kibana_pr_title=[DO NOT MERGE][EUI] Nightly Build" + "-f" "kibana_nightly=true" + ) else workflow_args+=( "-f" "kibana_pr_title=$INPUT_PR_TITLE" @@ -64,4 +78,5 @@ jobs: ) fi + # This job has no checkout, so gh cannot infer the repository. gh workflow run release.yml --repo "$GITHUB_REPOSITORY" "${workflow_args[@]}" diff --git a/.github/workflows/update_kibana_dependencies__open_pr.yml b/.github/workflows/update_kibana_dependencies__open_pr.yml index d14826b7b96..caa33e717ac 100644 --- a/.github/workflows/update_kibana_dependencies__open_pr.yml +++ b/.github/workflows/update_kibana_dependencies__open_pr.yml @@ -24,6 +24,24 @@ on: source_pr_number: description: Source PR number that triggered this workflow type: number + nightly: + description: Whether this is the scheduled nightly run + type: boolean + default: false + eui_ref: + description: EUI ref used to publish the snapshot + type: string + required: true + eui_last_release: + description: Latest stable EUI release reachable from the snapshot + type: string + eui_version: + description: Published @elastic/eui snapshot version + type: string +concurrency: + group: ${{ inputs.nightly && 'kibana-nightly' || format('open-kibana-pr-{0}', github.run_id) }} + cancel-in-progress: false + queue: max permissions: contents: read id-token: write @@ -36,6 +54,12 @@ jobs: outputs: pr_url: ${{ steps.open_pr.outputs.pr_url }} steps: + - name: Checkout EUI snapshot + if: ${{ inputs.nightly }} + uses: actions/checkout@v6 + with: + ref: ${{ inputs.eui_ref }} + persist-credentials: false - name: Fetch GitHub token id: ephemeral_token uses: elastic/ci-gh-actions/fetch-github-token@v1.5.3 @@ -50,6 +74,10 @@ jobs: PR_TITLE: ${{ inputs.pr_title }} PR_BODY: ${{ inputs.pr_body }} PR_DRAFT: ${{ inputs.pr_draft }} + NIGHTLY: ${{ inputs.nightly }} + EUI_REF: ${{ inputs.eui_ref }} + EUI_LAST_RELEASE: ${{ inputs.eui_last_release }} + EUI_VERSION: ${{ inputs.eui_version }} # language=bash run: | set -euo pipefail @@ -60,6 +88,61 @@ jobs: base_repo='elastic/kibana' head_repo='elastic/eui-kibana' + if [[ "$NIGHTLY" == "true" && ( -z "$EUI_REF" || -z "$EUI_LAST_RELEASE" || -z "$EUI_VERSION" ) ]]; then + echo "::error::Nightly runs require eui_ref, eui_last_release, and eui_version" + exit 1 + fi + + if [[ "$NIGHTLY" == "true" ]]; then + compare_url="https://github.com/elastic/eui/compare/$EUI_LAST_RELEASE...$EUI_REF" + changelog_entries='' + while IFS= read -r changelog_file; do + changelog_number="$(basename "$changelog_file" .md)" + changelog_url="https://github.com/elastic/eui/blob/$EUI_REF/$changelog_file" + if [[ -n "$changelog_entries" ]]; then + changelog_entries+=$'\n\n' + fi + changelog_entries+="### [#$changelog_number]($changelog_url)"$'\n\n' + changelog_entries+="$(< "$changelog_file")" + done < <( + find packages/eui/changelogs/upcoming -maxdepth 1 -type f \ + -name '*.md' ! -name '_template.md' | sort + ) + + PR_BODY="$( + printf '%s\n' \ + 'This draft PR validates the latest EUI snapshot from `main` in Kibana.' \ + '' \ + "- **npm snapshot:** [\`@elastic/eui@$EUI_VERSION\`](https://www.npmjs.com/package/@elastic/eui/v/$EUI_VERSION)" \ + "- **Changes since $EUI_LAST_RELEASE:** [$EUI_LAST_RELEASE...${EUI_REF:0:12}]($compare_url)" \ + "- **EUI commit:** [\`${EUI_REF:0:12}\`](https://github.com/elastic/eui/commit/$EUI_REF)" \ + '' \ + '
' \ + 'Upcoming @elastic/eui changelog entries' \ + '' \ + "${changelog_entries:-_No upcoming changelog entries._}" \ + '' \ + '
' + )" + + # Reuse the open PR for the fixed nightly branch. + existing_pr_url="$(gh api "repos/$base_repo/pulls?state=open&head=elastic:$PR_HEAD" --jq '.[0].html_url // ""')" + if [[ -n "$existing_pr_url" ]]; then + gh pr edit "$existing_pr_url" --title "$PR_TITLE" --body "${PR_BODY:-}" + if [[ "$(gh pr view "$existing_pr_url" --json isDraft --jq .isDraft)" != "true" ]]; then + gh pr ready "$existing_pr_url" --undo + fi + + echo "pr_url=$existing_pr_url" >> "$GITHUB_OUTPUT" + { + echo "### Pull request updated" + echo "" + echo "$existing_pr_url" + } >> "$GITHUB_STEP_SUMMARY" + exit 0 + fi + fi + base_repo_id="$(gh api "repos/$base_repo" --jq .node_id)" head_repo_id="$(gh api "repos/$head_repo" --jq .node_id)" @@ -159,6 +242,7 @@ jobs: needs: [open_pr, post_open_pr] outputs: pending: ${{ steps.check_status.outputs.pending }} + buildkite_url: ${{ steps.check_status.outputs.buildkite_url }} steps: # Token cannot be reused in this job because it's short-lived - name: Fetch GitHub token @@ -174,12 +258,26 @@ jobs: PR_URL: ${{ needs.open_pr.outputs.pr_url }} # language=bash run: | - status_json="$(gh pr checks "$PR_URL" --required --json name,link,bucket)" - all_num="$(echo $status_json | jq length)" - pending_json="$(echo $status_json | jq 'map(select(.bucket == "pending"))')" - pending_num="$(echo $pending_json | jq length)" - fail_json="$(echo $status_json | jq 'map(select(.bucket | test("fail|cancel")))')" - fail_num="$(echo $fail_json | jq length)" + all_status_json="$(gh pr checks "$PR_URL" --json name,link,bucket || true)" + if jq -e 'type == "array"' > /dev/null <<< "$all_status_json"; then + buildkite_url="$( + jq -r '[.[] | select((.link // "") | contains("buildkite.com"))][0].link // empty' \ + <<< "$all_status_json" + )" + echo "buildkite_url=$buildkite_url" >> "$GITHUB_OUTPUT" + else + echo "::warning::Could not retrieve the Buildkite URL" + fi + + status_json="$(gh pr checks "$PR_URL" --required --json name,link,bucket || true)" + if ! jq -e 'type == "array" and length > 0' > /dev/null <<< "$status_json"; then + echo "::error::No required CI checks were returned" + exit 1 + fi + + all_num="$(jq length <<< "$status_json")" + pending_num="$(jq '[.[] | select(.bucket == "pending")] | length' <<< "$status_json")" + fail_num="$(jq '[.[] | select(.bucket | test("fail|cancel"))] | length' <<< "$status_json")" if [[ "$fail_num" -gt 0 ]]; then echo "Some CI checks failed. Please check the logs for details." @@ -202,6 +300,8 @@ jobs: environment: delayed-2h needs: [open_pr, check_ci_status_1] if: ${{ needs.check_ci_status_1.outputs.pending == 'true' }} + outputs: + buildkite_url: ${{ steps.check_status.outputs.buildkite_url }} steps: # Token cannot be reused in this job because it's short-lived - name: Fetch GitHub token @@ -211,17 +311,32 @@ jobs: vault-instance: ci-prod vault-role: token-policy-6becee3e5a43 - name: Check status + id: check_status env: GH_TOKEN: ${{ steps.ephemeral_token.outputs.token }} PR_URL: ${{ needs.open_pr.outputs.pr_url }} # language=bash run: | - status_json="$(gh pr checks "$PR_URL" --required --json name,link,bucket)" - all_num="$(echo $status_json | jq length)" - pending_json="$(echo $status_json | jq 'map(select(.bucket == "pending"))')" - pending_num="$(echo $pending_json | jq length)" - fail_json="$(echo $status_json | jq 'map(select(.bucket | test("fail|cancel")))')" - fail_num="$(echo $fail_json | jq length)" + all_status_json="$(gh pr checks "$PR_URL" --json name,link,bucket || true)" + if jq -e 'type == "array"' > /dev/null <<< "$all_status_json"; then + buildkite_url="$( + jq -r '[.[] | select((.link // "") | contains("buildkite.com"))][0].link // empty' \ + <<< "$all_status_json" + )" + echo "buildkite_url=$buildkite_url" >> "$GITHUB_OUTPUT" + else + echo "::warning::Could not retrieve the Buildkite URL" + fi + + status_json="$(gh pr checks "$PR_URL" --required --json name,link,bucket || true)" + if ! jq -e 'type == "array" and length > 0' > /dev/null <<< "$status_json"; then + echo "::error::No required CI checks were returned" + exit 1 + fi + + all_num="$(jq length <<< "$status_json")" + pending_num="$(jq '[.[] | select(.bucket == "pending")] | length' <<< "$status_json")" + fail_num="$(jq '[.[] | select(.bucket | test("fail|cancel"))] | length' <<< "$status_json")" if [[ "$fail_num" -gt 0 ]]; then echo "Some CI checks failed. Please check the logs for details." @@ -243,7 +358,7 @@ jobs: needs: [open_pr, check_ci_status_1, check_ci_status_2] if: | always() && - inputs.source_pr_number && + (inputs.source_pr_number || inputs.nightly) && ( (needs.check_ci_status_1.result == 'success' && needs.check_ci_status_2.result == 'skipped') || (needs.check_ci_status_1.result == 'failure' && needs.check_ci_status_2.result == 'skipped') || @@ -251,6 +366,7 @@ jobs: ) steps: - name: Checkout repository + if: ${{ inputs.source_pr_number }} uses: actions/checkout@v6 - name: Compose comment body id: compose_comment_body @@ -259,6 +375,10 @@ jobs: PR_HEAD: ${{ inputs.pr_head }} CHECK_1_RESULT: ${{ needs.check_ci_status_1.result }} CHECK_2_RESULT: ${{ needs.check_ci_status_2.result }} + NIGHTLY: ${{ inputs.nightly }} + EUI_REF: ${{ inputs.eui_ref }} + EUI_LAST_RELEASE: ${{ inputs.eui_last_release }} + BUILDKITE_URL: ${{ needs.check_ci_status_2.result != 'skipped' && needs.check_ci_status_2.outputs.buildkite_url || needs.check_ci_status_1.outputs.buildkite_url }} # language=bash run: | if [[ "$CHECK_2_RESULT" == "skipped" && "$CHECK_1_RESULT" != "success" ]] || [[ "$CHECK_2_RESULT" != "success" && "$CHECK_2_RESULT" != "skipped" ]]; then @@ -283,7 +403,31 @@ jobs: fi echo 'COMMENT_DELIMITER' } >> "$GITHUB_OUTPUT" + + if [[ "$NIGHTLY" == "true" ]]; then + if [[ -n "$details" ]]; then + slack_status=':red_circle: EUI nightly did not pass Kibana CI' + else + slack_status=':large_green_circle: EUI nightly passed Kibana CI' + fi + + compare_url="https://github.com/elastic/eui/compare/$EUI_LAST_RELEASE...$EUI_REF" + slack_text="$slack_status"$'\n'"<$KIBANA_PR_URL|Kibana PR>" + if [[ -n "$BUILDKITE_URL" ]]; then + slack_text+=" · <$BUILDKITE_URL|Buildkite>" + else + slack_text+=' · Buildkite link unavailable' + fi + slack_text+=" · <$compare_url|EUI main diff>" + + { + echo 'slack_text<> "$GITHUB_OUTPUT" + fi - name: Add a comment + if: ${{ inputs.source_pr_number }} uses: ./.github/actions/pr_bot_comment with: gh_token: ${{ github.token }} @@ -291,3 +435,17 @@ jobs: marker: 'kibana-regression-integration-test' comment_author: 'github-actions[bot]' comment_body: ${{ steps.compose_comment_body.outputs.comment_body }} + - name: Notify Slack + if: ${{ inputs.nightly }} + env: + SLACK_WEBHOOK_URL: ${{ secrets.KIBANA_NIGHTLY_SLACK_WEBHOOK }} + SLACK_TEXT: ${{ steps.compose_comment_body.outputs.slack_text }} + # language=bash + run: | + set -euo pipefail + if [[ -z "${SLACK_WEBHOOK_URL:-}" ]]; then + echo "::error::KIBANA_NIGHTLY_SLACK_WEBHOOK is not set" + exit 1 + fi + payload="$(jq -n --arg text "$SLACK_TEXT" '{ text: $text }')" + curl -fsSL -X POST -H 'Content-type: application/json' --data "$payload" "$SLACK_WEBHOOK_URL" diff --git a/.github/workflows/update_kibana_dependencies__prepare_changes.yml b/.github/workflows/update_kibana_dependencies__prepare_changes.yml index 9b88ab9098d..851f28ba08d 100644 --- a/.github/workflows/update_kibana_dependencies__prepare_changes.yml +++ b/.github/workflows/update_kibana_dependencies__prepare_changes.yml @@ -25,11 +25,28 @@ on: type: string pr_body: description: PR body - required: true type: string source_pr_number: description: Source PR number that triggered this workflow type: number + nightly: + description: Whether this is the scheduled nightly run + type: boolean + default: false + eui_ref: + description: EUI ref used to publish the snapshot + type: string + required: true + eui_last_release: + description: Latest stable EUI release reachable from the snapshot + type: string + eui_version: + description: Published @elastic/eui snapshot version + type: string +concurrency: + group: ${{ inputs.nightly && 'kibana-nightly' || format('prepare-kibana-{0}', github.run_id) }} + cancel-in-progress: false + queue: max permissions: id-token: write # Required for OIDC (Vault ephemeral token) contents: read @@ -85,9 +102,16 @@ jobs: git remote add fork https://github.com/elastic/eui-kibana.git - name: Create new branch id: git_branch + env: + NIGHTLY: ${{ inputs.nightly }} # language=bash run: | - branch_name="update-dependencies/$(date +%s)" + # Nightly runs reuse a fixed branch; all other runs remain unique. + if [[ "$NIGHTLY" == "true" ]]; then + branch_name="nightly/update-dependencies" + else + branch_name="update-dependencies/$(date +%s)" + fi git checkout -b "$branch_name" echo "BRANCH_NAME=$branch_name" >> "$GITHUB_OUTPUT" echo "::debug::Created branch '$branch_name'" @@ -119,8 +143,14 @@ jobs: env: GH_TOKEN: ${{ steps.fetch_ephemeral_token.outputs.token }} BRANCH_NAME: ${{ steps.git_branch.outputs.BRANCH_NAME }} + NIGHTLY: ${{ inputs.nightly }} # language=bash - run: git push -u fork "$BRANCH_NAME" + run: | + if [[ "$NIGHTLY" == "true" ]]; then + git push -u --force fork "$BRANCH_NAME" + else + git push -u fork "$BRANCH_NAME" + fi - name: Trigger workflow to open PR env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -128,6 +158,10 @@ jobs: PR_BODY: ${{ inputs.pr_body }} BRANCH_NAME: ${{ steps.git_branch.outputs.BRANCH_NAME }} SOURCE_PR_NUMBER: ${{ inputs.source_pr_number }} + NIGHTLY: ${{ inputs.nightly }} + EUI_REF: ${{ inputs.eui_ref }} + EUI_LAST_RELEASE: ${{ inputs.eui_last_release }} + EUI_VERSION: ${{ inputs.eui_version }} # language=bash run: | set -euo pipefail @@ -137,9 +171,12 @@ jobs: -f "pr_title=$PR_TITLE" -f "pr_body=$PR_BODY" -f "pr_head=$BRANCH_NAME" + -f "nightly=$NIGHTLY" + -f "eui_ref=$EUI_REF" + -f "eui_last_release=$EUI_LAST_RELEASE" + -f "eui_version=$EUI_VERSION" ) - # Only pass source_pr_number when set - it's a number-typed input, so an - # empty value (nightly/manual runs) would fail validation. + # An empty value is invalid for a number-typed workflow input. if [[ -n "${SOURCE_PR_NUMBER:-}" ]]; then args+=( -f "source_pr_number=$SOURCE_PR_NUMBER" ) fi From 7dc8e21323f6fd71af26f127d381a0133534c583 Mon Sep 17 00:00:00 2001 From: Weronika Olejniczak Date: Fri, 17 Jul 2026 10:18:28 +0200 Subject: [PATCH 2/5] chore: simplify changelog entries and include all public pkgs --- .../update_kibana_dependencies__open_pr.yml | 18 +-- scripts/upcoming_changelogs.sh | 109 ++++++++++++++++++ 2 files changed, 112 insertions(+), 15 deletions(-) create mode 100755 scripts/upcoming_changelogs.sh diff --git a/.github/workflows/update_kibana_dependencies__open_pr.yml b/.github/workflows/update_kibana_dependencies__open_pr.yml index caa33e717ac..34c310ad312 100644 --- a/.github/workflows/update_kibana_dependencies__open_pr.yml +++ b/.github/workflows/update_kibana_dependencies__open_pr.yml @@ -95,19 +95,7 @@ jobs: if [[ "$NIGHTLY" == "true" ]]; then compare_url="https://github.com/elastic/eui/compare/$EUI_LAST_RELEASE...$EUI_REF" - changelog_entries='' - while IFS= read -r changelog_file; do - changelog_number="$(basename "$changelog_file" .md)" - changelog_url="https://github.com/elastic/eui/blob/$EUI_REF/$changelog_file" - if [[ -n "$changelog_entries" ]]; then - changelog_entries+=$'\n\n' - fi - changelog_entries+="### [#$changelog_number]($changelog_url)"$'\n\n' - changelog_entries+="$(< "$changelog_file")" - done < <( - find packages/eui/changelogs/upcoming -maxdepth 1 -type f \ - -name '*.md' ! -name '_template.md' | sort - ) + changelog_entries="$(scripts/upcoming_changelogs.sh --ref "$EUI_REF")" PR_BODY="$( printf '%s\n' \ @@ -118,9 +106,9 @@ jobs: "- **EUI commit:** [\`${EUI_REF:0:12}\`](https://github.com/elastic/eui/commit/$EUI_REF)" \ '' \ '
' \ - 'Upcoming @elastic/eui changelog entries' \ + 'Upcoming changelog entries' \ '' \ - "${changelog_entries:-_No upcoming changelog entries._}" \ + "$changelog_entries" \ '' \ '
' )" diff --git a/scripts/upcoming_changelogs.sh b/scripts/upcoming_changelogs.sh new file mode 100755 index 00000000000..2a31c913399 --- /dev/null +++ b/scripts/upcoming_changelogs.sh @@ -0,0 +1,109 @@ +#!/usr/bin/env bash + +# Prints upcoming changelog entries for all public packages as Markdown. + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" +REF='HEAD' + +usage() { + echo "Usage: $0 [--ref ]" +} + +while [[ $# -gt 0 ]]; do + case "$1" in + --ref) + REF="${2:-}" + shift 2 + ;; + --help|-h) + usage + exit 0 + ;; + *) + echo "Unknown argument: $1" >&2 + usage >&2 + exit 1 + ;; + esac +done + +if [[ -z "$REF" ]]; then + usage >&2 + exit 1 +fi + +PACKAGES_DIR="$ROOT_DIR/packages" + +if [[ ! -d "$PACKAGES_DIR" ]]; then + echo "Packages directory not found: $PACKAGES_DIR" >&2 + exit 1 +fi + +# Prepare temporary storage for sorting changelog entries by PR number +entries_file="$(mktemp "${TMPDIR:-/tmp}/eui-upcoming-changelogs-entries.XXXXXX")" +trap 'rm -f "$entries_file"' EXIT + +# Find public packages, show `@elastic/eui` first +workspaces="$( + cd "$ROOT_DIR" + yarn workspaces list --json --no-private | + jq -r '[if .name == "@elastic/eui" then 0 else 1 end, .name, .location] | @tsv' | + LC_ALL=C sort -k1,1n -k2,2 +)" + +has_output=false +tab=$'\t' + +# Build a Markdown section for each package with upcoming changes +while IFS="$tab" read -r _ package_name package_location; do + [[ -n "$package_name" && -n "$package_location" ]] || continue + package_dir="$ROOT_DIR/$package_location" + [[ -d "$package_dir/changelogs/upcoming" ]] || continue + + : > "$entries_file" + + # Collect the package's changelog entries, excluding the template. + for changelog_file in "$package_dir/changelogs/upcoming"/*.md; do + [[ -f "$changelog_file" ]] || continue + changelog_number="$(basename "$changelog_file" .md)" + [[ "$changelog_number" != '_template' ]] || continue + + if [[ ! "$changelog_number" =~ ^[0-9]+$ ]]; then + echo "Invalid upcoming changelog filename: $changelog_file" >&2 + exit 1 + fi + + printf '%s\t%s\n' "$changelog_number" "$changelog_file" >> "$entries_file" + done + + [[ -s "$entries_file" ]] || continue + + # Print the package heading followed by entries in PR order + if [[ "$has_output" == true ]]; then + printf '\n\n' + fi + printf '## `%s`' "$package_name" + + while IFS="$tab" read -r changelog_number changelog_file; do + changelog_path="${changelog_file#"$ROOT_DIR/"}" + changelog_url="https://github.com/elastic/eui/blob/$REF/$changelog_path" + changelog_content="$(< "$changelog_file")" + + printf '\n\n### [#%s](%s)\n\n%s' \ + "$changelog_number" \ + "$changelog_url" \ + "$changelog_content" + done < <(LC_ALL=C sort -n -k1,1 "$entries_file") + + has_output=true +done <<< "$workspaces" + +# Show a clear message when no package has upcoming changes +if [[ "$has_output" == false ]]; then + printf '_No upcoming changelog entries._' +fi + +printf '\n' From 0d96e40c7f0099443c575aaa66e04112b5459a2a Mon Sep 17 00:00:00 2001 From: Weronika Olejniczak Date: Fri, 17 Jul 2026 10:30:09 +0200 Subject: [PATCH 3/5] chore: remove all the argument parsing --- .../update_kibana_dependencies__open_pr.yml | 2 +- scripts/upcoming_changelogs.sh | 36 +------------------ 2 files changed, 2 insertions(+), 36 deletions(-) diff --git a/.github/workflows/update_kibana_dependencies__open_pr.yml b/.github/workflows/update_kibana_dependencies__open_pr.yml index 34c310ad312..3dfe625d4fa 100644 --- a/.github/workflows/update_kibana_dependencies__open_pr.yml +++ b/.github/workflows/update_kibana_dependencies__open_pr.yml @@ -95,7 +95,7 @@ jobs: if [[ "$NIGHTLY" == "true" ]]; then compare_url="https://github.com/elastic/eui/compare/$EUI_LAST_RELEASE...$EUI_REF" - changelog_entries="$(scripts/upcoming_changelogs.sh --ref "$EUI_REF")" + changelog_entries="$(scripts/upcoming_changelogs.sh "$EUI_REF")" PR_BODY="$( printf '%s\n' \ diff --git a/scripts/upcoming_changelogs.sh b/scripts/upcoming_changelogs.sh index 2a31c913399..cc00b1b8667 100755 --- a/scripts/upcoming_changelogs.sh +++ b/scripts/upcoming_changelogs.sh @@ -6,41 +6,7 @@ set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" -REF='HEAD' - -usage() { - echo "Usage: $0 [--ref ]" -} - -while [[ $# -gt 0 ]]; do - case "$1" in - --ref) - REF="${2:-}" - shift 2 - ;; - --help|-h) - usage - exit 0 - ;; - *) - echo "Unknown argument: $1" >&2 - usage >&2 - exit 1 - ;; - esac -done - -if [[ -z "$REF" ]]; then - usage >&2 - exit 1 -fi - -PACKAGES_DIR="$ROOT_DIR/packages" - -if [[ ! -d "$PACKAGES_DIR" ]]; then - echo "Packages directory not found: $PACKAGES_DIR" >&2 - exit 1 -fi +REF="${1:-HEAD}" # Prepare temporary storage for sorting changelog entries by PR number entries_file="$(mktemp "${TMPDIR:-/tmp}/eui-upcoming-changelogs-entries.XXXXXX")" From 76b7bfee9b1113a93867602dd36630b1189e05fc Mon Sep 17 00:00:00 2001 From: Weronika Olejniczak Date: Fri, 17 Jul 2026 10:31:12 +0200 Subject: [PATCH 4/5] chore: remove unused branch_name output --- .../update_kibana_dependencies__prepare_changes.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/update_kibana_dependencies__prepare_changes.yml b/.github/workflows/update_kibana_dependencies__prepare_changes.yml index 851f28ba08d..08931eb1dea 100644 --- a/.github/workflows/update_kibana_dependencies__prepare_changes.yml +++ b/.github/workflows/update_kibana_dependencies__prepare_changes.yml @@ -56,16 +56,13 @@ jobs: update: name: Prepare changes runs-on: ubuntu-latest - outputs: - branch_name: ${{ steps.git_branch.outputs.BRANCH_NAME }} steps: - name: Verify dependencies input env: DEPENDENCIES: ${{ inputs.dependencies }} # language=bash run: | - num_deps=$(jq 'map(select(has("name") and has("version"))) | length' <<< "$DEPENDENCIES") || exit_code=$? - if [[ "$exit_code" -ne 0 ]]; then + if ! num_deps="$(jq 'map(select(has("name") and has("version"))) | length' <<< "$DEPENDENCIES")"; then echo "::error::Invalid dependencies input" exit 2 elif [[ "$num_deps" -eq 0 ]]; then From a9f32b081cb9b966c9a6be9a4f96a714dcbb1db2 Mon Sep 17 00:00:00 2001 From: Weronika Olejniczak Date: Fri, 17 Jul 2026 10:36:18 +0200 Subject: [PATCH 5/5] chore: move PR status check to reusable bash script --- .../update_kibana_dependencies__open_pr.yml | 79 ++----------------- scripts/check_pr_status.sh | 62 +++++++++++++++ 2 files changed, 68 insertions(+), 73 deletions(-) create mode 100755 scripts/check_pr_status.sh diff --git a/.github/workflows/update_kibana_dependencies__open_pr.yml b/.github/workflows/update_kibana_dependencies__open_pr.yml index 3dfe625d4fa..db17f5d01d6 100644 --- a/.github/workflows/update_kibana_dependencies__open_pr.yml +++ b/.github/workflows/update_kibana_dependencies__open_pr.yml @@ -232,6 +232,8 @@ jobs: pending: ${{ steps.check_status.outputs.pending }} buildkite_url: ${{ steps.check_status.outputs.buildkite_url }} steps: + - name: Checkout repository + uses: actions/checkout@v6 # Token cannot be reused in this job because it's short-lived - name: Fetch GitHub token id: ephemeral_token @@ -245,43 +247,7 @@ jobs: GH_TOKEN: ${{ steps.ephemeral_token.outputs.token }} PR_URL: ${{ needs.open_pr.outputs.pr_url }} # language=bash - run: | - all_status_json="$(gh pr checks "$PR_URL" --json name,link,bucket || true)" - if jq -e 'type == "array"' > /dev/null <<< "$all_status_json"; then - buildkite_url="$( - jq -r '[.[] | select((.link // "") | contains("buildkite.com"))][0].link // empty' \ - <<< "$all_status_json" - )" - echo "buildkite_url=$buildkite_url" >> "$GITHUB_OUTPUT" - else - echo "::warning::Could not retrieve the Buildkite URL" - fi - - status_json="$(gh pr checks "$PR_URL" --required --json name,link,bucket || true)" - if ! jq -e 'type == "array" and length > 0' > /dev/null <<< "$status_json"; then - echo "::error::No required CI checks were returned" - exit 1 - fi - - all_num="$(jq length <<< "$status_json")" - pending_num="$(jq '[.[] | select(.bucket == "pending")] | length' <<< "$status_json")" - fail_num="$(jq '[.[] | select(.bucket | test("fail|cancel"))] | length' <<< "$status_json")" - - if [[ "$fail_num" -gt 0 ]]; then - echo "Some CI checks failed. Please check the logs for details." - echo "Some CI checks failed. Please check the logs for details." >> $GITHUB_STEP_SUMMARY - exit 1 - fi - - if [[ "$pending_num" -gt 0 ]]; then - echo "pending=true" >> "$GITHUB_OUTPUT" - echo "$pending_num/$all_num CI checks are pending. Retrying later..." - echo "$pending_num/$all_num CI checks are pending. Retrying later..." >> $GITHUB_STEP_SUMMARY - exit 0 - fi - - echo "CI checks passed!" - echo "CI checks passed! :tada:" >> $GITHUB_STEP_SUMMARY + run: scripts/check_pr_status.sh "$PR_URL" retry check_ci_status_2: name: Check CI status - attempt 2/2 runs-on: ubuntu-slim @@ -291,6 +257,8 @@ jobs: outputs: buildkite_url: ${{ steps.check_status.outputs.buildkite_url }} steps: + - name: Checkout repository + uses: actions/checkout@v6 # Token cannot be reused in this job because it's short-lived - name: Fetch GitHub token id: ephemeral_token @@ -304,42 +272,7 @@ jobs: GH_TOKEN: ${{ steps.ephemeral_token.outputs.token }} PR_URL: ${{ needs.open_pr.outputs.pr_url }} # language=bash - run: | - all_status_json="$(gh pr checks "$PR_URL" --json name,link,bucket || true)" - if jq -e 'type == "array"' > /dev/null <<< "$all_status_json"; then - buildkite_url="$( - jq -r '[.[] | select((.link // "") | contains("buildkite.com"))][0].link // empty' \ - <<< "$all_status_json" - )" - echo "buildkite_url=$buildkite_url" >> "$GITHUB_OUTPUT" - else - echo "::warning::Could not retrieve the Buildkite URL" - fi - - status_json="$(gh pr checks "$PR_URL" --required --json name,link,bucket || true)" - if ! jq -e 'type == "array" and length > 0' > /dev/null <<< "$status_json"; then - echo "::error::No required CI checks were returned" - exit 1 - fi - - all_num="$(jq length <<< "$status_json")" - pending_num="$(jq '[.[] | select(.bucket == "pending")] | length' <<< "$status_json")" - fail_num="$(jq '[.[] | select(.bucket | test("fail|cancel"))] | length' <<< "$status_json")" - - if [[ "$fail_num" -gt 0 ]]; then - echo "Some CI checks failed. Please check the logs for details." - echo "Some CI checks failed. Please check the logs for details." >> $GITHUB_STEP_SUMMARY - exit 1 - fi - - if [[ "$pending_num" -gt 0 ]]; then - echo "$pending_num/$all_num CI checks are still pending. This is unexpected." - echo "$pending_num/$all_num CI checks are still pending. This is unexpected." >> $GITHUB_STEP_SUMMARY - exit 1 - fi - - echo "CI checks passed!" - echo "CI checks passed! :tada:" >> $GITHUB_STEP_SUMMARY + run: scripts/check_pr_status.sh "$PR_URL" fail report_status: name: Report CI status runs-on: ubuntu-slim diff --git a/scripts/check_pr_status.sh b/scripts/check_pr_status.sh new file mode 100755 index 00000000000..d8b4da1f222 --- /dev/null +++ b/scripts/check_pr_status.sh @@ -0,0 +1,62 @@ +#!/usr/bin/env bash + +# Checks required PR statuses and exposes their state to a GitHub Actions job. + +set -euo pipefail + +PR_URL="${1:-}" +PENDING_BEHAVIOR="${2:-retry}" + +if [[ -z "$PR_URL" || ( "$PENDING_BEHAVIOR" != 'retry' && "$PENDING_BEHAVIOR" != 'fail' ) ]]; then + echo "Usage: $0 [retry|fail]" >&2 + exit 1 +fi + +# Find the Buildkite check so notifications can link to its build +all_status_json="$(gh pr checks "$PR_URL" --json name,link,bucket || true)" +if jq -e 'type == "array"' > /dev/null <<< "$all_status_json"; then + buildkite_url="$( + jq -r '[.[] | select((.link // "") | contains("buildkite.com"))][0].link // empty' \ + <<< "$all_status_json" + )" + echo "buildkite_url=$buildkite_url" >> "$GITHUB_OUTPUT" +else + echo "::warning::Could not retrieve the Buildkite URL" +fi + +# Count required checks by state to decide whether CI passed +status_json="$(gh pr checks "$PR_URL" --required --json name,link,bucket || true)" +if ! jq -e 'type == "array" and length > 0' > /dev/null <<< "$status_json"; then + echo "::error::No required CI checks were returned" + exit 1 +fi + +all_num="$(jq length <<< "$status_json")" +pending_num="$(jq '[.[] | select(.bucket == "pending")] | length' <<< "$status_json")" +fail_num="$(jq '[.[] | select(.bucket | test("fail|cancel"))] | length' <<< "$status_json")" + +# Report failures immediately or let the first attempt retry pending checks +if [[ "$fail_num" -gt 0 ]]; then + message='Some CI checks failed. Please check the logs for details.' + echo "$message" + echo "$message" >> "$GITHUB_STEP_SUMMARY" + exit 1 +fi + +if [[ "$pending_num" -gt 0 ]]; then + if [[ "$PENDING_BEHAVIOR" == 'retry' ]]; then + echo "pending=true" >> "$GITHUB_OUTPUT" + message="$pending_num/$all_num CI checks are pending. Retrying later..." + echo "$message" + echo "$message" >> "$GITHUB_STEP_SUMMARY" + exit 0 + fi + + message="$pending_num/$all_num CI checks are still pending. This is unexpected." + echo "$message" + echo "$message" >> "$GITHUB_STEP_SUMMARY" + exit 1 +fi + +echo 'CI checks passed!' +echo 'CI checks passed! :tada:' >> "$GITHUB_STEP_SUMMARY"