From d25c92528a15b3b975d25ca122a32693df94bc2f Mon Sep 17 00:00:00 2001 From: anakin87 Date: Tue, 4 Aug 2026 16:15:16 +0200 Subject: [PATCH 1/3] draft --- .github/utils/prepare_release_notification.sh | 19 ++- .github/utils/trigger_platform_update.sh | 101 +++++++++++++++ .../utils/update_haystack_dc_custom_nodes.py | 61 --------- .github/workflows/release.yml | 117 ++++++------------ 4 files changed, 152 insertions(+), 146 deletions(-) create mode 100755 .github/utils/trigger_platform_update.sh delete mode 100755 .github/utils/update_haystack_dc_custom_nodes.py diff --git a/.github/utils/prepare_release_notification.sh b/.github/utils/prepare_release_notification.sh index e4b562361d2..e1f56bb86e6 100755 --- a/.github/utils/prepare_release_notification.sh +++ b/.github/utils/prepare_release_notification.sh @@ -4,7 +4,8 @@ # Requires: VERSION, RUN_URL, HAS_FAILURE, GH_TOKEN, GITHUB_REPOSITORY # Optional: IS_RC, IS_FIRST_RC, MAJOR_MINOR, GITHUB_URL, PYPI_URL, DOCKER_URL, # BUMP_VERSION_PR_URL, DC_PIPELINE_TEMPLATES_PR_URL, DC_CUSTOM_NODES_PR_URL, -# HAYSTACK_RUNTIME_PR_URL, GITHUB_WORKSPACE +# HAYSTACK_RUNTIME_PR_URL, DC_PIPELINE_TEMPLATES_RESULT, DC_CUSTOM_NODES_RESULT, +# HAYSTACK_RUNTIME_RESULT, GITHUB_WORKSPACE # Output: slack_payload.json # # This script is used in the release.yml workflow to prepare the notification payload @@ -59,12 +60,20 @@ if [[ "${IS_FIRST_RC:-}" == "true" && -n "${BUMP_VERSION_PR_URL:-}" ]]; then TXT+=$'\n'"- <${BUMP_VERSION_PR_URL}|Bump unstable version and create unstable docs>" fi -# For RCs, include Platform test PRs +# For RCs, include Platform test PRs (or a warning for repos whose bump job failed) if [[ "${IS_RC:-}" == "true" ]]; then PLATFORM_PRS="" - [[ -n "${DC_PIPELINE_TEMPLATES_PR_URL:-}" ]] && PLATFORM_PRS+=$'\n'"- <${DC_PIPELINE_TEMPLATES_PR_URL}|dc-pipeline-templates>" - [[ -n "${DC_CUSTOM_NODES_PR_URL:-}" ]] && PLATFORM_PRS+=$'\n'"- <${DC_CUSTOM_NODES_PR_URL}|deepset-cloud-custom-nodes>" - [[ -n "${HAYSTACK_RUNTIME_PR_URL:-}" ]] && PLATFORM_PRS+=$'\n'"- <${HAYSTACK_RUNTIME_PR_URL}|haystack-runtime>" + platform_line() { + local result="$1" url="$2" label="$3" + if [[ "${result}" == "failure" ]]; then + PLATFORM_PRS+=$'\n'"- ${label}: :warning: bump failed, see <${RUN_URL}|workflow logs>" + elif [[ -n "${url}" ]]; then + PLATFORM_PRS+=$'\n'"- <${url}|${label}>" + fi + } + platform_line "${DC_PIPELINE_TEMPLATES_RESULT:-}" "${DC_PIPELINE_TEMPLATES_PR_URL:-}" "dc-pipeline-templates" + platform_line "${DC_CUSTOM_NODES_RESULT:-}" "${DC_CUSTOM_NODES_PR_URL:-}" "deepset-cloud-custom-nodes" + platform_line "${HAYSTACK_RUNTIME_RESULT:-}" "${HAYSTACK_RUNTIME_PR_URL:-}" "haystack-runtime" if [[ -n "${PLATFORM_PRS}" ]]; then TXT+=$'\n\n'":factory: *Test PRs opened on Platform:*${PLATFORM_PRS}" fi diff --git a/.github/utils/trigger_platform_update.sh b/.github/utils/trigger_platform_update.sh new file mode 100755 index 00000000000..9335482ef32 --- /dev/null +++ b/.github/utils/trigger_platform_update.sh @@ -0,0 +1,101 @@ +#!/bin/bash +# trigger_platform_update.sh - Trigger a platform repo's Haystack bump workflow and wait for the PR +# +# Usage: ./trigger_platform_update.sh +# Requires: GH_TOKEN environment variable with actions:write on +# Output: Writes pr_url to $GITHUB_OUTPUT if set, otherwise to stdout. +# pr_url is empty when the version was already pinned and the workflow skipped the PR. +# +# Example: +# ./trigger_platform_update.sh deepset-ai/haystack-runtime update-package-version.yaml 2.99.0-rc1 +# +# This script is used in the release.yml workflow to dispatch a platform repo's update workflow, +# which bumps the Haystack pin in that repo and opens a PR from branch "bump/hs". + + +# With the default values, we wait up to 30 seconds for the dispatched run to appear +# and up to 5 minutes for it to complete +FIND_MAX_ATTEMPTS="${FIND_MAX_ATTEMPTS:-6}" +FIND_SLEEP_SECONDS="${FIND_SLEEP_SECONDS:-5}" +MAX_ATTEMPTS="${MAX_ATTEMPTS:-30}" +SLEEP_SECONDS="${SLEEP_SECONDS:-10}" + +set -euo pipefail + +if [[ -z "${GH_TOKEN:-}" ]]; then + echo "❌ GH_TOKEN must be set" + exit 1 +fi + +REPO="$1" +WORKFLOW_FILE="$2" +VERSION="$3" + +# --- Dispatch the update workflow --- + +TRIGGER_TIME=$(date -u +%Y-%m-%dT%H:%M:%SZ) +gh workflow run "${WORKFLOW_FILE}" -R "${REPO}" -f haystack_version="${VERSION}" +echo "✅ Dispatched ${WORKFLOW_FILE} on ${REPO} with haystack_version=${VERSION}" + +# --- Find the dispatched run (workflow_dispatch returns no run id) --- + +echo "⏳ Waiting for the run to appear" +RUN_ID="" +for ((i=1; i<=FIND_MAX_ATTEMPTS; i++)); do + sleep "${FIND_SLEEP_SECONDS}" + RUN_ID=$(gh run list -R "${REPO}" --workflow="${WORKFLOW_FILE}" \ + --created ">=${TRIGGER_TIME}" --json databaseId \ + --jq '.[0].databaseId // empty' 2>/dev/null || true) + [[ -n "${RUN_ID}" ]] && break + echo " Attempt $i/${FIND_MAX_ATTEMPTS}: not started yet..." +done + +if [[ -z "${RUN_ID}" ]]; then + echo "❌ Dispatched run never appeared on ${REPO}" + exit 1 +fi +echo "✅ Found run: https://github.com/${REPO}/actions/runs/${RUN_ID}" + +# --- Wait for the run to complete --- + +echo "⏳ Waiting for the run to complete" +STATUS="" +CONCLUSION="" +for ((i=1; i<=MAX_ATTEMPTS; i++)); do + result=$(gh run view "${RUN_ID}" -R "${REPO}" --json status,conclusion 2>/dev/null || echo "") + if [[ -n "${result}" ]]; then + STATUS=$(echo "${result}" | jq -r '.status') + CONCLUSION=$(echo "${result}" | jq -r '.conclusion') + [[ "${STATUS}" == "completed" ]] && break + fi + echo " Attempt $i/${MAX_ATTEMPTS}: ${STATUS:-unknown}..." + sleep "${SLEEP_SECONDS}" +done + +if [[ "${STATUS}" != "completed" ]]; then + echo "❌ Run did not complete within $((MAX_ATTEMPTS * SLEEP_SECONDS / 60)) minutes" + exit 1 +fi +if [[ "${CONCLUSION}" != "success" ]]; then + echo "❌ Run failed: ${CONCLUSION}" + exit 1 +fi +echo "✅ Run completed successfully" + +# --- Look up the PR --- + +# The branch name is version-specific, so an open PR on it is the right one +# even when a re-run updated an existing PR instead of creating it +PR_URL=$(gh pr list -R "${REPO}" --head "bump/hs${VERSION}" --state open \ + --json url --jq '.[0].url // empty') + +if [[ -n "${PR_URL}" ]]; then + echo "✅ Found PR: ${PR_URL}" +else + echo "ℹ️ No PR opened: ${VERSION} is already pinned on ${REPO}" +fi + +# --- Output to GITHUB_OUTPUT (or stdout for local testing) --- + +OUTPUT_FILE="${GITHUB_OUTPUT:-/dev/stdout}" +echo "pr_url=${PR_URL}" >> "${OUTPUT_FILE}" diff --git a/.github/utils/update_haystack_dc_custom_nodes.py b/.github/utils/update_haystack_dc_custom_nodes.py deleted file mode 100755 index a372d0ac15f..00000000000 --- a/.github/utils/update_haystack_dc_custom_nodes.py +++ /dev/null @@ -1,61 +0,0 @@ -#!/usr/bin/env python3 -""" -Update the haystack-ai version in the deepset-cloud-custom-nodes uv.lock file. - -Fetches sdist/wheel hashes from PyPI and updates the haystack-ai package entry, -preserving the existing lock file formatting. -""" - -import argparse -import json -import sys -import urllib.request - -import tomlkit - -if __name__ == "__main__": - parser = argparse.ArgumentParser() - parser.add_argument("version", help="Version to update to (e.g. 2.26.1-rc1)") - parser.add_argument("lock_file", help="Path to uv.lock") - args = parser.parse_args() - - # PEP 440 normalized version for filenames - new_version = args.version.replace("-", "") - - # Fetch hashes from PyPI - pypi_data = json.load(urllib.request.urlopen(f"https://pypi.org/pypi/haystack-ai/{args.version}/json")) - sdist_sha = wheel_sha = None - for u in pypi_data["urls"]: - if u["packagetype"] == "sdist": - sdist_sha = u["digests"]["sha256"] - elif u["packagetype"] == "bdist_wheel": - wheel_sha = u["digests"]["sha256"] - if not sdist_sha or not wheel_sha: - sys.exit("Could not find sdist or wheel hashes on PyPI") - - with open(args.lock_file) as f: - data = tomlkit.load(f) - - found = False - for pkg in data["package"]: - if pkg["name"] == "haystack-ai": - old_version = pkg["version"] - - pkg["version"] = new_version - - pkg["sdist"]["url"] = pkg["sdist"]["url"].replace(old_version, new_version) - pkg["sdist"]["hash"] = f"sha256:{sdist_sha}" - - wheel = pkg["wheels"][0] - wheel["url"] = wheel["url"].replace(old_version, new_version) - wheel["hash"] = f"sha256:{wheel_sha}" - - found = True - print(f"Updated haystack-ai from {old_version} to {new_version}") - break - - if not found: - sys.exit("haystack-ai package not found in uv.lock") - - with open(args.lock_file, "w") as f: - tomlkit.dump(data, f) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 33610ad2e0a..4ab8f92afad 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -144,124 +144,76 @@ jobs: echo "docker_url=https://hub.docker.com/r/deepset/haystack/tags?name=base-v${{ env.VERSION }}" } >> "$GITHUB_OUTPUT" + # The three bump jobs dispatch a workflow owned by each platform repo, which + # updates its own Haystack pin and opens a PR (branch "bump/hs"). bump-dc-pipeline-templates: needs: ["parse-validate-version", "check-artifacts"] if: always() && needs.check-artifacts.result == 'success' && needs.parse-validate-version.outputs.is_rc == 'true' runs-on: ubuntu-slim outputs: - pr_url: ${{ steps.create-pr.outputs.pull-request-url }} + pr_url: ${{ steps.trigger.outputs.pr_url }} env: VERSION: ${{ needs.parse-validate-version.outputs.version }} steps: - - name: Checkout dc-pipeline-templates + - name: Checkout haystack (for utils) uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: - repository: deepset-ai/dc-pipeline-templates - token: ${{ secrets.HAYSTACK_BOT_TOKEN }} - - - name: Update haystack pin - run: sed -i "s/haystack-ai>=.*/haystack-ai>=${VERSION}/" requirements-test.txt + sparse-checkout: .github/utils/trigger_platform_update.sh + sparse-checkout-cone-mode: false - - name: Create Pull Request - id: create-pr - uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 # v8.1.1 - with: - token: ${{ secrets.HAYSTACK_BOT_TOKEN }} - commit-message: "Bump haystack to ${{ env.VERSION }}" - branch: bump-haystack-${{ env.VERSION }} - base: main - title: "chore: bump haystack to ${{ env.VERSION }}" - add-paths: requirements-test.txt - body: | - Bump haystack pin to `${{ env.VERSION }}` for platform testing. + - name: Trigger update workflow and wait for PR + id: trigger + env: + GH_TOKEN: ${{ secrets.HAYSTACK_BOT_TOKEN }} + run: | + .github/utils/trigger_platform_update.sh \ + deepset-ai/dc-pipeline-templates update-haystack-version.yaml "$VERSION" bump-deepset-cloud-custom-nodes: needs: ["parse-validate-version", "check-artifacts"] if: always() && needs.check-artifacts.result == 'success' && needs.parse-validate-version.outputs.is_rc == 'true' runs-on: ubuntu-slim outputs: - pr_url: ${{ steps.create-pr.outputs.pull-request-url }} + pr_url: ${{ steps.trigger.outputs.pr_url }} env: VERSION: ${{ needs.parse-validate-version.outputs.version }} steps: - name: Checkout haystack (for utils) uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: - path: haystack - sparse-checkout: .github/utils/update_haystack_dc_custom_nodes.py + sparse-checkout: .github/utils/trigger_platform_update.sh sparse-checkout-cone-mode: false - - name: Checkout deepset-cloud-custom-nodes - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - repository: deepset-ai/deepset-cloud-custom-nodes - token: ${{ secrets.HAYSTACK_BOT_TOKEN }} - path: deepset-cloud-custom-nodes - - - name: Set up Python - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 - with: - python-version: "3.13" - - - name: Install tomlkit + - name: Trigger update workflow and wait for PR + id: trigger + env: + GH_TOKEN: ${{ secrets.HAYSTACK_BOT_TOKEN }} run: | - python -m pip install --upgrade pip - pip install tomlkit --uploaded-prior-to=P1D - - - name: Update haystack-ai in uv.lock - run: python haystack/.github/utils/update_haystack_dc_custom_nodes.py "${{ env.VERSION }}" deepset-cloud-custom-nodes/uv.lock - - - name: Create Pull Request - id: create-pr - uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 # v8.1.1 - with: - token: ${{ secrets.HAYSTACK_BOT_TOKEN }} - path: deepset-cloud-custom-nodes - commit-message: "Bump haystack to ${{ env.VERSION }}" - branch: bump-haystack-${{ env.VERSION }} - base: main - title: "chore: bump haystack to ${{ env.VERSION }}" - add-paths: uv.lock - body: | - Bump haystack pin to `${{ env.VERSION }}` for platform testing. + .github/utils/trigger_platform_update.sh \ + deepset-ai/deepset-cloud-custom-nodes update-haystack-version.yaml "$VERSION" bump-haystack-runtime: needs: ["parse-validate-version", "check-artifacts"] if: always() && needs.check-artifacts.result == 'success' && needs.parse-validate-version.outputs.is_rc == 'true' runs-on: ubuntu-slim outputs: - pr_url: ${{ steps.wait-pr.outputs.pr_url }} + pr_url: ${{ steps.trigger.outputs.pr_url }} env: VERSION: ${{ needs.parse-validate-version.outputs.version }} steps: - - name: Trigger "Update package version" workflow - env: - GH_TOKEN: ${{ secrets.HAYSTACK_BOT_TOKEN }} - run: | - gh workflow run update-package-version.yaml \ - -R deepset-ai/haystack-runtime \ - -f haystack_version="${{ env.VERSION }}" + - name: Checkout haystack (for utils) + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + sparse-checkout: .github/utils/trigger_platform_update.sh + sparse-checkout-cone-mode: false - - name: Wait for PR - id: wait-pr + - name: Trigger update workflow and wait for PR + id: trigger env: GH_TOKEN: ${{ secrets.HAYSTACK_BOT_TOKEN }} run: | - TRIGGER_TIME=$(date -u +%Y-%m-%dT%H:%M:%SZ) - for i in $(seq 1 30); do - PR_URL=$(gh pr list -R deepset-ai/haystack-runtime \ - --head "bump/hs${{ env.VERSION }}" \ - --json url,createdAt \ - --jq ".[] | select(.createdAt >= \"$TRIGGER_TIME\") | .url") - if [[ -n "$PR_URL" ]]; then - echo "pr_url=$PR_URL" >> "$GITHUB_OUTPUT" - echo "Found PR: $PR_URL" - exit 0 - fi - echo "Attempt $i: PR not found yet, waiting 10s..." - sleep 10 - done - echo "PR not found after 5 minutes" + .github/utils/trigger_platform_update.sh \ + deepset-ai/haystack-runtime update-package-version.yaml "$VERSION" notify: needs: @@ -283,7 +235,9 @@ jobs: VERSION: ${{ inputs.version }} GH_TOKEN: ${{ github.token }} RUN_URL: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }} - HAS_FAILURE: ${{ contains(needs.*.result, 'failure') }} + # Bump jobs are excluded: they run after the artifacts are out, so their + # failure must not report the release itself as failed (reported per repo instead) + HAS_FAILURE: ${{ needs.parse-validate-version.result == 'failure' || needs.branch-off.result == 'failure' || needs.create-release-tag.result == 'failure' || needs.check-artifacts.result == 'failure' }} IS_RC: ${{ needs.parse-validate-version.outputs.is_rc }} IS_FIRST_RC: ${{ needs.parse-validate-version.outputs.is_first_rc }} MAJOR_MINOR: ${{ needs.parse-validate-version.outputs.major_minor }} @@ -294,6 +248,9 @@ jobs: DC_PIPELINE_TEMPLATES_PR_URL: ${{ needs.bump-dc-pipeline-templates.outputs.pr_url }} DC_CUSTOM_NODES_PR_URL: ${{ needs.bump-deepset-cloud-custom-nodes.outputs.pr_url }} HAYSTACK_RUNTIME_PR_URL: ${{ needs.bump-haystack-runtime.outputs.pr_url }} + DC_PIPELINE_TEMPLATES_RESULT: ${{ needs.bump-dc-pipeline-templates.result }} + DC_CUSTOM_NODES_RESULT: ${{ needs.bump-deepset-cloud-custom-nodes.result }} + HAYSTACK_RUNTIME_RESULT: ${{ needs.bump-haystack-runtime.result }} run: .github/utils/prepare_release_notification.sh - name: Send release notification to Slack From 2e92483e49e67573834ed33cf354054b543943ee Mon Sep 17 00:00:00 2001 From: anakin87 Date: Tue, 4 Aug 2026 16:31:44 +0200 Subject: [PATCH 2/3] simplify --- .github/utils/prepare_release_notification.sh | 6 +- .github/utils/trigger_platform_update.sh | 101 ---------------- .github/utils/wait_for_platform_pr.sh | 46 ++++++++ .github/workflows/release.yml | 108 +++++++----------- 4 files changed, 90 insertions(+), 171 deletions(-) delete mode 100755 .github/utils/trigger_platform_update.sh create mode 100755 .github/utils/wait_for_platform_pr.sh diff --git a/.github/utils/prepare_release_notification.sh b/.github/utils/prepare_release_notification.sh index e1f56bb86e6..a9620bf2233 100755 --- a/.github/utils/prepare_release_notification.sh +++ b/.github/utils/prepare_release_notification.sh @@ -65,10 +65,10 @@ if [[ "${IS_RC:-}" == "true" ]]; then PLATFORM_PRS="" platform_line() { local result="$1" url="$2" label="$3" - if [[ "${result}" == "failure" ]]; then - PLATFORM_PRS+=$'\n'"- ${label}: :warning: bump failed, see <${RUN_URL}|workflow logs>" - elif [[ -n "${url}" ]]; then + if [[ -n "${url}" ]]; then PLATFORM_PRS+=$'\n'"- <${url}|${label}>" + elif [[ "${result}" != "success" ]]; then + PLATFORM_PRS+=$'\n'"- ${label}: :warning: PR not found, something might have failed, see <${RUN_URL}|workflow logs>" fi } platform_line "${DC_PIPELINE_TEMPLATES_RESULT:-}" "${DC_PIPELINE_TEMPLATES_PR_URL:-}" "dc-pipeline-templates" diff --git a/.github/utils/trigger_platform_update.sh b/.github/utils/trigger_platform_update.sh deleted file mode 100755 index 9335482ef32..00000000000 --- a/.github/utils/trigger_platform_update.sh +++ /dev/null @@ -1,101 +0,0 @@ -#!/bin/bash -# trigger_platform_update.sh - Trigger a platform repo's Haystack bump workflow and wait for the PR -# -# Usage: ./trigger_platform_update.sh -# Requires: GH_TOKEN environment variable with actions:write on -# Output: Writes pr_url to $GITHUB_OUTPUT if set, otherwise to stdout. -# pr_url is empty when the version was already pinned and the workflow skipped the PR. -# -# Example: -# ./trigger_platform_update.sh deepset-ai/haystack-runtime update-package-version.yaml 2.99.0-rc1 -# -# This script is used in the release.yml workflow to dispatch a platform repo's update workflow, -# which bumps the Haystack pin in that repo and opens a PR from branch "bump/hs". - - -# With the default values, we wait up to 30 seconds for the dispatched run to appear -# and up to 5 minutes for it to complete -FIND_MAX_ATTEMPTS="${FIND_MAX_ATTEMPTS:-6}" -FIND_SLEEP_SECONDS="${FIND_SLEEP_SECONDS:-5}" -MAX_ATTEMPTS="${MAX_ATTEMPTS:-30}" -SLEEP_SECONDS="${SLEEP_SECONDS:-10}" - -set -euo pipefail - -if [[ -z "${GH_TOKEN:-}" ]]; then - echo "❌ GH_TOKEN must be set" - exit 1 -fi - -REPO="$1" -WORKFLOW_FILE="$2" -VERSION="$3" - -# --- Dispatch the update workflow --- - -TRIGGER_TIME=$(date -u +%Y-%m-%dT%H:%M:%SZ) -gh workflow run "${WORKFLOW_FILE}" -R "${REPO}" -f haystack_version="${VERSION}" -echo "✅ Dispatched ${WORKFLOW_FILE} on ${REPO} with haystack_version=${VERSION}" - -# --- Find the dispatched run (workflow_dispatch returns no run id) --- - -echo "⏳ Waiting for the run to appear" -RUN_ID="" -for ((i=1; i<=FIND_MAX_ATTEMPTS; i++)); do - sleep "${FIND_SLEEP_SECONDS}" - RUN_ID=$(gh run list -R "${REPO}" --workflow="${WORKFLOW_FILE}" \ - --created ">=${TRIGGER_TIME}" --json databaseId \ - --jq '.[0].databaseId // empty' 2>/dev/null || true) - [[ -n "${RUN_ID}" ]] && break - echo " Attempt $i/${FIND_MAX_ATTEMPTS}: not started yet..." -done - -if [[ -z "${RUN_ID}" ]]; then - echo "❌ Dispatched run never appeared on ${REPO}" - exit 1 -fi -echo "✅ Found run: https://github.com/${REPO}/actions/runs/${RUN_ID}" - -# --- Wait for the run to complete --- - -echo "⏳ Waiting for the run to complete" -STATUS="" -CONCLUSION="" -for ((i=1; i<=MAX_ATTEMPTS; i++)); do - result=$(gh run view "${RUN_ID}" -R "${REPO}" --json status,conclusion 2>/dev/null || echo "") - if [[ -n "${result}" ]]; then - STATUS=$(echo "${result}" | jq -r '.status') - CONCLUSION=$(echo "${result}" | jq -r '.conclusion') - [[ "${STATUS}" == "completed" ]] && break - fi - echo " Attempt $i/${MAX_ATTEMPTS}: ${STATUS:-unknown}..." - sleep "${SLEEP_SECONDS}" -done - -if [[ "${STATUS}" != "completed" ]]; then - echo "❌ Run did not complete within $((MAX_ATTEMPTS * SLEEP_SECONDS / 60)) minutes" - exit 1 -fi -if [[ "${CONCLUSION}" != "success" ]]; then - echo "❌ Run failed: ${CONCLUSION}" - exit 1 -fi -echo "✅ Run completed successfully" - -# --- Look up the PR --- - -# The branch name is version-specific, so an open PR on it is the right one -# even when a re-run updated an existing PR instead of creating it -PR_URL=$(gh pr list -R "${REPO}" --head "bump/hs${VERSION}" --state open \ - --json url --jq '.[0].url // empty') - -if [[ -n "${PR_URL}" ]]; then - echo "✅ Found PR: ${PR_URL}" -else - echo "ℹ️ No PR opened: ${VERSION} is already pinned on ${REPO}" -fi - -# --- Output to GITHUB_OUTPUT (or stdout for local testing) --- - -OUTPUT_FILE="${GITHUB_OUTPUT:-/dev/stdout}" -echo "pr_url=${PR_URL}" >> "${OUTPUT_FILE}" diff --git a/.github/utils/wait_for_platform_pr.sh b/.github/utils/wait_for_platform_pr.sh new file mode 100755 index 00000000000..24100bffb88 --- /dev/null +++ b/.github/utils/wait_for_platform_pr.sh @@ -0,0 +1,46 @@ +#!/bin/bash +# wait_for_platform_pr.sh - Wait for the PR opened by a platform repo's Haystack bump workflow +# +# Usage: ./wait_for_platform_pr.sh +# Requires: GH_TOKEN environment variable +# Output: Writes pr_url to $GITHUB_OUTPUT if set, otherwise to stdout +# +# Example: +# ./wait_for_platform_pr.sh deepset-ai/haystack-runtime 2.99.0-rc1 +# +# This script is used in the release.yml workflow after dispatching a platform repo's update +# workflow, which opens a PR from branch "bump/hs". + + +# With the default values, we wait for 5 minutes +MAX_ATTEMPTS="${MAX_ATTEMPTS:-30}" +SLEEP_SECONDS="${SLEEP_SECONDS:-10}" + +set -euo pipefail + +if [[ -z "${GH_TOKEN:-}" ]]; then + echo "❌ GH_TOKEN must be set" + exit 1 +fi + +REPO="$1" +VERSION="$2" +BRANCH="bump/hs${VERSION}" + +echo "⏳ Waiting for a PR from branch ${BRANCH} on ${REPO}" + +for ((i=1; i<=MAX_ATTEMPTS; i++)); do + PR_URL=$(gh pr list -R "${REPO}" --head "${BRANCH}" --state open \ + --json url --jq '.[0].url // empty' 2>/dev/null || true) + if [[ -n "${PR_URL}" ]]; then + echo "✅ Found PR: ${PR_URL}" + OUTPUT_FILE="${GITHUB_OUTPUT:-/dev/stdout}" + echo "pr_url=${PR_URL}" >> "${OUTPUT_FILE}" + exit 0 + fi + echo " Attempt $i/${MAX_ATTEMPTS}: PR not found yet..." + sleep "${SLEEP_SECONDS}" +done + +echo "❌ PR not found. Something might have failed: check https://github.com/${REPO}/actions" +exit 1 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4ab8f92afad..6c5ba05b231 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -144,76 +144,53 @@ jobs: echo "docker_url=https://hub.docker.com/r/deepset/haystack/tags?name=base-v${{ env.VERSION }}" } >> "$GITHUB_OUTPUT" - # The three bump jobs dispatch a workflow owned by each platform repo, which - # updates its own Haystack pin and opens a PR (branch "bump/hs"). - bump-dc-pipeline-templates: + bump-platform-repos: needs: ["parse-validate-version", "check-artifacts"] if: always() && needs.check-artifacts.result == 'success' && needs.parse-validate-version.outputs.is_rc == 'true' runs-on: ubuntu-slim outputs: - pr_url: ${{ steps.trigger.outputs.pr_url }} + dc_pipeline_templates_pr_url: ${{ steps.dc-pipeline-templates.outputs.pr_url }} + dc_pipeline_templates_result: ${{ steps.dc-pipeline-templates.outcome }} + dc_custom_nodes_pr_url: ${{ steps.deepset-cloud-custom-nodes.outputs.pr_url }} + dc_custom_nodes_result: ${{ steps.deepset-cloud-custom-nodes.outcome }} + haystack_runtime_pr_url: ${{ steps.haystack-runtime.outputs.pr_url }} + haystack_runtime_result: ${{ steps.haystack-runtime.outcome }} env: + GH_TOKEN: ${{ secrets.HAYSTACK_BOT_TOKEN }} VERSION: ${{ needs.parse-validate-version.outputs.version }} steps: - name: Checkout haystack (for utils) uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: - sparse-checkout: .github/utils/trigger_platform_update.sh + sparse-checkout: .github/utils/wait_for_platform_pr.sh sparse-checkout-cone-mode: false - - name: Trigger update workflow and wait for PR - id: trigger - env: - GH_TOKEN: ${{ secrets.HAYSTACK_BOT_TOKEN }} + - name: Trigger platform update workflows run: | - .github/utils/trigger_platform_update.sh \ - deepset-ai/dc-pipeline-templates update-haystack-version.yaml "$VERSION" - - bump-deepset-cloud-custom-nodes: - needs: ["parse-validate-version", "check-artifacts"] - if: always() && needs.check-artifacts.result == 'success' && needs.parse-validate-version.outputs.is_rc == 'true' - runs-on: ubuntu-slim - outputs: - pr_url: ${{ steps.trigger.outputs.pr_url }} - env: - VERSION: ${{ needs.parse-validate-version.outputs.version }} - steps: - - name: Checkout haystack (for utils) - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - sparse-checkout: .github/utils/trigger_platform_update.sh - sparse-checkout-cone-mode: false - - - name: Trigger update workflow and wait for PR - id: trigger - env: - GH_TOKEN: ${{ secrets.HAYSTACK_BOT_TOKEN }} - run: | - .github/utils/trigger_platform_update.sh \ - deepset-ai/deepset-cloud-custom-nodes update-haystack-version.yaml "$VERSION" - - bump-haystack-runtime: - needs: ["parse-validate-version", "check-artifacts"] - if: always() && needs.check-artifacts.result == 'success' && needs.parse-validate-version.outputs.is_rc == 'true' - runs-on: ubuntu-slim - outputs: - pr_url: ${{ steps.trigger.outputs.pr_url }} - env: - VERSION: ${{ needs.parse-validate-version.outputs.version }} - steps: - - name: Checkout haystack (for utils) - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - sparse-checkout: .github/utils/trigger_platform_update.sh - sparse-checkout-cone-mode: false - - - name: Trigger update workflow and wait for PR - id: trigger - env: - GH_TOKEN: ${{ secrets.HAYSTACK_BOT_TOKEN }} + gh workflow run update-haystack-version.yaml -R deepset-ai/dc-pipeline-templates -f haystack_version="$VERSION" + gh workflow run update-haystack-version.yaml -R deepset-ai/deepset-cloud-custom-nodes -f haystack_version="$VERSION" + gh workflow run update-package-version.yaml -R deepset-ai/haystack-runtime -f haystack_version="$VERSION" + + - name: Wait for dc-pipeline-templates PR + id: dc-pipeline-templates + continue-on-error: true + run: .github/utils/wait_for_platform_pr.sh deepset-ai/dc-pipeline-templates "$VERSION" + + - name: Wait for deepset-cloud-custom-nodes PR + id: deepset-cloud-custom-nodes + continue-on-error: true + run: .github/utils/wait_for_platform_pr.sh deepset-ai/deepset-cloud-custom-nodes "$VERSION" + + - name: Wait for haystack-runtime PR + id: haystack-runtime + continue-on-error: true + run: .github/utils/wait_for_platform_pr.sh deepset-ai/haystack-runtime "$VERSION" + + - name: Fail if any platform PR is missing + if: steps.dc-pipeline-templates.outcome == 'failure' || steps.deepset-cloud-custom-nodes.outcome == 'failure' || steps.haystack-runtime.outcome == 'failure' run: | - .github/utils/trigger_platform_update.sh \ - deepset-ai/haystack-runtime update-package-version.yaml "$VERSION" + echo "::error::One or more platform PRs were not found. Something might have failed; check the logs above." + exit 1 notify: needs: @@ -221,9 +198,7 @@ jobs: - "branch-off" - "create-release-tag" - "check-artifacts" - - "bump-dc-pipeline-templates" - - "bump-deepset-cloud-custom-nodes" - - "bump-haystack-runtime" + - "bump-platform-repos" if: always() runs-on: ubuntu-slim steps: @@ -235,8 +210,7 @@ jobs: VERSION: ${{ inputs.version }} GH_TOKEN: ${{ github.token }} RUN_URL: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }} - # Bump jobs are excluded: they run after the artifacts are out, so their - # failure must not report the release itself as failed (reported per repo instead) + # HAS_FAILURE = the release failed. Failing PRs on Platform repos are reported on Slack but do not mean the release failed. HAS_FAILURE: ${{ needs.parse-validate-version.result == 'failure' || needs.branch-off.result == 'failure' || needs.create-release-tag.result == 'failure' || needs.check-artifacts.result == 'failure' }} IS_RC: ${{ needs.parse-validate-version.outputs.is_rc }} IS_FIRST_RC: ${{ needs.parse-validate-version.outputs.is_first_rc }} @@ -245,12 +219,12 @@ jobs: PYPI_URL: ${{ needs.check-artifacts.outputs.pypi_url }} DOCKER_URL: ${{ needs.check-artifacts.outputs.docker_url }} BUMP_VERSION_PR_URL: ${{ needs.branch-off.outputs.bump_version_pr_url }} - DC_PIPELINE_TEMPLATES_PR_URL: ${{ needs.bump-dc-pipeline-templates.outputs.pr_url }} - DC_CUSTOM_NODES_PR_URL: ${{ needs.bump-deepset-cloud-custom-nodes.outputs.pr_url }} - HAYSTACK_RUNTIME_PR_URL: ${{ needs.bump-haystack-runtime.outputs.pr_url }} - DC_PIPELINE_TEMPLATES_RESULT: ${{ needs.bump-dc-pipeline-templates.result }} - DC_CUSTOM_NODES_RESULT: ${{ needs.bump-deepset-cloud-custom-nodes.result }} - HAYSTACK_RUNTIME_RESULT: ${{ needs.bump-haystack-runtime.result }} + DC_PIPELINE_TEMPLATES_PR_URL: ${{ needs.bump-platform-repos.outputs.dc_pipeline_templates_pr_url }} + DC_CUSTOM_NODES_PR_URL: ${{ needs.bump-platform-repos.outputs.dc_custom_nodes_pr_url }} + HAYSTACK_RUNTIME_PR_URL: ${{ needs.bump-platform-repos.outputs.haystack_runtime_pr_url }} + DC_PIPELINE_TEMPLATES_RESULT: ${{ needs.bump-platform-repos.outputs.dc_pipeline_templates_result }} + DC_CUSTOM_NODES_RESULT: ${{ needs.bump-platform-repos.outputs.dc_custom_nodes_result }} + HAYSTACK_RUNTIME_RESULT: ${{ needs.bump-platform-repos.outputs.haystack_runtime_result }} run: .github/utils/prepare_release_notification.sh - name: Send release notification to Slack From d8c3b07964889495d17c0ab383c83d4ea840bebb Mon Sep 17 00:00:00 2001 From: Julian Risch Date: Tue, 4 Aug 2026 18:30:14 +0200 Subject: [PATCH 3/3] ci: scope GH_TOKEN to steps that need it in bump-platform-repos Move GH_TOKEN from the job-level env to just the four steps that call gh, so the checkout and final failure-check steps in this job no longer have the bot token in their environment. Co-Authored-By: Claude Sonnet 5 --- .github/workflows/release.yml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6c5ba05b231..d140d87d5d3 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -156,7 +156,6 @@ jobs: haystack_runtime_pr_url: ${{ steps.haystack-runtime.outputs.pr_url }} haystack_runtime_result: ${{ steps.haystack-runtime.outcome }} env: - GH_TOKEN: ${{ secrets.HAYSTACK_BOT_TOKEN }} VERSION: ${{ needs.parse-validate-version.outputs.version }} steps: - name: Checkout haystack (for utils) @@ -166,6 +165,8 @@ jobs: sparse-checkout-cone-mode: false - name: Trigger platform update workflows + env: + GH_TOKEN: ${{ secrets.HAYSTACK_BOT_TOKEN }} run: | gh workflow run update-haystack-version.yaml -R deepset-ai/dc-pipeline-templates -f haystack_version="$VERSION" gh workflow run update-haystack-version.yaml -R deepset-ai/deepset-cloud-custom-nodes -f haystack_version="$VERSION" @@ -174,16 +175,22 @@ jobs: - name: Wait for dc-pipeline-templates PR id: dc-pipeline-templates continue-on-error: true + env: + GH_TOKEN: ${{ secrets.HAYSTACK_BOT_TOKEN }} run: .github/utils/wait_for_platform_pr.sh deepset-ai/dc-pipeline-templates "$VERSION" - name: Wait for deepset-cloud-custom-nodes PR id: deepset-cloud-custom-nodes continue-on-error: true + env: + GH_TOKEN: ${{ secrets.HAYSTACK_BOT_TOKEN }} run: .github/utils/wait_for_platform_pr.sh deepset-ai/deepset-cloud-custom-nodes "$VERSION" - name: Wait for haystack-runtime PR id: haystack-runtime continue-on-error: true + env: + GH_TOKEN: ${{ secrets.HAYSTACK_BOT_TOKEN }} run: .github/utils/wait_for_platform_pr.sh deepset-ai/haystack-runtime "$VERSION" - name: Fail if any platform PR is missing