This is an automated email from the ASF dual-hosted git repository.

potiuk pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow.git


The following commit(s) were added to refs/heads/main by this push:
     new c66d54075b9 Fix scheduled CI upgrade job: extract versions from 
uv.lock + Slack alerts (#65211)
c66d54075b9 is described below

commit c66d54075b93b9e538d12bc4f536ec96c0e5fafb
Author: Jarek Potiuk <[email protected]>
AuthorDate: Tue Apr 14 15:27:38 2026 +0200

    Fix scheduled CI upgrade job: extract versions from uv.lock + Slack alerts 
(#65211)
    
    Removes all hard-coded uv/prek version strings from GitHub Actions
    workflows and composite actions and derives them at runtime from
    'uv.lock' via a small sed snippet. Also adds Slack success and failure
    notifications to the scheduled CI upgrade workflow.
    
    Why: the scheduled '[v3-2-test] Scheduled CI upgrade check' job was
    failing because 'breeze ci upgrade' kept rewriting version strings in
    '.github/workflows/*.yml' files, which the default GITHUB_TOKEN (a
    GitHub App token) is not permitted to push. Reading versions from
    'uv.lock' (which gets refreshed by 'uv lock --upgrade' as part of the
    upgrade run anyway) means the job no longer needs to touch any file
    under '.github/workflows/' and its PR can be created using only the
    standard GITHUB_TOKEN — no PAT or GitHub App required.
    
    Notifications to '#internal-airflow-ci-cd' now fire on both success
    (with PR link and Undraft -> Review -> Merge-once-CI-passes steps) and
    failure (with a link to the failed run). The branch prefix
    (e.g. '[main]' vs '[v3-2-test]') distinguishes notifications between
    the schedules.
---
 .github/actions/breeze/action.yml                  | 16 ++++---
 .github/actions/install-prek/action.yml            | 31 +++++++++-----
 .github/workflows/basic-tests.yml                  | 43 +++++++++++++------
 .github/workflows/ci-amd-arm.yml                   |  1 -
 .github/workflows/release_dockerhub_image.yml      |  3 --
 .../scheduled-verify-release-calendar.yml          | 13 ++++--
 .github/workflows/update-constraints-on-push.yml   |  2 +-
 .github/workflows/upgrade-check.yml                | 49 ++++++++++++++++++----
 scripts/ci/prek/upgrade_important_versions.py      |  6 ---
 9 files changed, 112 insertions(+), 52 deletions(-)

diff --git a/.github/actions/breeze/action.yml 
b/.github/actions/breeze/action.yml
index cc6005f3955..f9a4dc5a683 100644
--- a/.github/actions/breeze/action.yml
+++ b/.github/actions/breeze/action.yml
@@ -22,9 +22,6 @@ inputs:
   python-version:
     description: 'Python version to use'
     default: "3.10"
-  uv-version:
-    description: 'uv version to use'
-    default: "0.11.3"  # Keep this comment to allow automatic replacement of 
uv version
 outputs:
   host-python-version:
     description: Python version used in host
@@ -38,9 +35,16 @@ runs:
         python-version: ${{ inputs.python-version }}
     - name: "Install uv"
       shell: bash
-      run: pip install "uv==${UV_VERSION}"
-      env:
-        UV_VERSION: ${{ inputs.uv-version }}
+      # Extract uv version from uv.lock. The format is stable: the line
+      # immediately after `name = "uv"` is `version = "<X.Y.Z>"`.
+      run: |
+        UV_VERSION=$(sed -n '/^name = "uv"$/{n;s/^version = "\(.*\)"$/\1/p;}' 
uv.lock)
+        if [[ -z "${UV_VERSION}" ]]; then
+          echo "Failed to extract uv version from uv.lock" >&2
+          exit 1
+        fi
+        echo "Installing uv==${UV_VERSION}"
+        pip install "uv==${UV_VERSION}"
     # NOTE! Installing Breeze without using cache is FASTER than when using 
cache - uv is so fast and has
     # so low overhead, that just running upload cache/restore cache is slower 
than installing it from scratch
     - name: "Install Breeze"
diff --git a/.github/actions/install-prek/action.yml 
b/.github/actions/install-prek/action.yml
index 7e43c3245f4..998a1b186d3 100644
--- a/.github/actions/install-prek/action.yml
+++ b/.github/actions/install-prek/action.yml
@@ -22,12 +22,6 @@ inputs:
   python-version:
     description: 'Python version to use'
     default: "3.10"
-  uv-version:
-    description: 'uv version to use'
-    default: "0.11.3"  # Keep this comment to allow automatic replacement of 
uv version
-  prek-version:
-    description: 'prek version to use'
-    default: "0.3.8"  # Keep this comment to allow automatic replacement of 
prek version
   save-cache:
     description: "Whether to save prek cache"
     required: true
@@ -37,16 +31,31 @@ inputs:
 runs:
   using: "composite"
   steps:
+    - name: "Extract uv and prek versions from uv.lock"
+      id: versions
+      shell: bash
+      # The uv.lock format is stable: the line immediately after `name = 
"<pkg>"`
+      # is `version = "<X.Y.Z>"` for that package.
+      run: |
+        UV_VERSION=$(sed -n '/^name = "uv"$/{n;s/^version = "\(.*\)"$/\1/p;}' 
uv.lock)
+        PREK_VERSION=$(sed -n '/^name = "prek"$/{n;s/^version = 
"\(.*\)"$/\1/p;}' uv.lock)
+        if [[ -z "${UV_VERSION}" || -z "${PREK_VERSION}" ]]; then
+          echo "Failed to extract uv (${UV_VERSION}) or prek (${PREK_VERSION}) 
version from uv.lock" >&2
+          exit 1
+        fi
+        echo "Extracted uv==${UV_VERSION}, prek==${PREK_VERSION}"
+        echo "uv-version=${UV_VERSION}" >> "${GITHUB_OUTPUT}"
+        echo "prek-version=${PREK_VERSION}" >> "${GITHUB_OUTPUT}"
     - name: "Install uv"
       shell: bash
       run: pip install "uv==${UV_VERSION}"
       env:
-        UV_VERSION: ${{ inputs.uv-version }}
+        UV_VERSION: ${{ steps.versions.outputs.uv-version }}
     - name: Install prek
       shell: bash
       env:
-        PREK_VERSION: ${{inputs.prek-version}}
-        UV_VERSION: ${{ inputs.uv-version }}
+        UV_VERSION: ${{ steps.versions.outputs.uv-version }}
+        PREK_VERSION: ${{ steps.versions.outputs.prek-version }}
       run: |
         uv tool install prek==${PREK_VERSION} --with uv==${UV_VERSION}
       working-directory: ${{ github.workspace }}
@@ -64,7 +73,7 @@ runs:
       uses: 
apache/infrastructure-actions/stash/restore@1c35b5ccf8fba5d4c3fdf25a045ca91aa0cbc468
       with:
         # yamllint disable rule:line-length
-        key: cache-prek-v9-${{ inputs.platform }}-python${{ 
inputs.python-version }}-uv${{ inputs.uv-version }}-${{ 
hashFiles('**/.pre-commit-config.yaml') }}
+        key: cache-prek-v9-${{ inputs.platform }}-python${{ 
inputs.python-version }}-uv${{ steps.versions.outputs.uv-version }}-${{ 
hashFiles('**/.pre-commit-config.yaml') }}
         path: /tmp/
       id: restore-prek-cache
     - name: "Restore .cache from the tar file"
@@ -113,7 +122,7 @@ runs:
       uses: 
apache/infrastructure-actions/stash/save@1c35b5ccf8fba5d4c3fdf25a045ca91aa0cbc468
       with:
         # yamllint disable rule:line-length
-        key: cache-prek-v9-${{ inputs.platform }}-python${{ 
inputs.python-version }}-uv${{ inputs.uv-version }}-${{ 
hashFiles('**/.pre-commit-config.yaml') }}
+        key: cache-prek-v9-${{ inputs.platform }}-python${{ 
inputs.python-version }}-uv${{ steps.versions.outputs.uv-version }}-${{ 
hashFiles('**/.pre-commit-config.yaml') }}
         path: /tmp/cache-prek.tar.gz
         if-no-files-found: 'error'
         retention-days: '2'
diff --git a/.github/workflows/basic-tests.yml 
b/.github/workflows/basic-tests.yml
index 5f449ab2227..5bc1fc8905b 100644
--- a/.github/workflows/basic-tests.yml
+++ b/.github/workflows/basic-tests.yml
@@ -72,10 +72,6 @@ on:  # yamllint disable-line rule:truthy
         description: "Whether to use uv in the image"
         required: true
         type: string
-      uv-version:
-        description: 'uv version to use'
-        default: "0.11.3"  # Keep this comment to allow automatic replacement 
of uv version
-        type: string
       platform:
         description: 'Platform for the build - linux/amd64 or linux/arm64'
         required: true
@@ -143,9 +139,16 @@ jobs:
           fetch-depth: 1
           persist-credentials: false
       - name: "Install uv"
-        run: pip install "uv==${UV_VERSION}"
-        env:
-          UV_VERSION: ${{ inputs.uv-version }}
+        # Extract uv version from uv.lock. The format is stable: the line
+        # immediately after `name = "uv"` is `version = "<X.Y.Z>"`.
+        run: |
+          UV_VERSION=$(sed -n '/^name = "uv"$/{n;s/^version = 
"\(.*\)"$/\1/p;}' uv.lock)
+          if [[ -z "${UV_VERSION}" ]]; then
+            echo "Failed to extract uv version from uv.lock" >&2
+            exit 1
+          fi
+          echo "Installing uv==${UV_VERSION}"
+          pip install "uv==${UV_VERSION}"
       - name: "Run shared ${{ matrix.shared-distribution }} tests"
         run: uv run --group dev pytest --color=yes -n auto
         working-directory: shared/${{ matrix.shared-distribution }}
@@ -161,9 +164,16 @@ jobs:
           fetch-depth: 1
           persist-credentials: false
       - name: "Install uv"
-        run: pip install "uv==${UV_VERSION}"
-        env:
-          UV_VERSION: ${{ inputs.uv-version }}
+        # Extract uv version from uv.lock. The format is stable: the line
+        # immediately after `name = "uv"` is `version = "<X.Y.Z>"`.
+        run: |
+          UV_VERSION=$(sed -n '/^name = "uv"$/{n;s/^version = 
"\(.*\)"$/\1/p;}' uv.lock)
+          if [[ -z "${UV_VERSION}" ]]; then
+            echo "Failed to extract uv version from uv.lock" >&2
+            exit 1
+          fi
+          echo "Installing uv==${UV_VERSION}"
+          pip install "uv==${UV_VERSION}"
       - name: "Run scripts tests"
         run: uv run --project . pytest --color=yes -n auto
         working-directory: ./scripts/
@@ -368,9 +378,16 @@ jobs:
         with:
           persist-credentials: false
       - name: "Install uv"
-        run: pip install "uv==${UV_VERSION}"
-        env:
-          UV_VERSION: ${{ inputs.uv-version }}
+        # Extract uv version from uv.lock. The format is stable: the line
+        # immediately after `name = "uv"` is `version = "<X.Y.Z>"`.
+        run: |
+          UV_VERSION=$(sed -n '/^name = "uv"$/{n;s/^version = 
"\(.*\)"$/\1/p;}' uv.lock)
+          if [[ -z "${UV_VERSION}" ]]; then
+            echo "Failed to extract uv version from uv.lock" >&2
+            exit 1
+          fi
+          echo "Installing uv==${UV_VERSION}"
+          pip install "uv==${UV_VERSION}"
       - name: "Set up Airflow home directory"
         run: |
           echo "Setting AIRFLOW_HOME to $AIRFLOW_HOME"
diff --git a/.github/workflows/ci-amd-arm.yml b/.github/workflows/ci-amd-arm.yml
index 035b4d53d74..da98b28b4d0 100644
--- a/.github/workflows/ci-amd-arm.yml
+++ b/.github/workflows/ci-amd-arm.yml
@@ -40,7 +40,6 @@ env:
   GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
   GITHUB_USERNAME: ${{ github.actor }}
   SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}
-  UV_VERSION: "0.11.3"  # Keep this comment to allow automatic replacement of 
uv version
   VERBOSE: "true"
 
 concurrency:
diff --git a/.github/workflows/release_dockerhub_image.yml 
b/.github/workflows/release_dockerhub_image.yml
index 3614f4c7eef..f3efcb67c2f 100644
--- a/.github/workflows/release_dockerhub_image.yml
+++ b/.github/workflows/release_dockerhub_image.yml
@@ -58,7 +58,6 @@ jobs:
       AIRFLOW_VERSION: ${{ github.event.inputs.airflowVersion }}
       AMD_ONLY: ${{ github.event.inputs.amdOnly }}
       LIMIT_PYTHON_VERSIONS: ${{ github.event.inputs.limitPythonVersions }}
-      UV_VERSION: "0.11.3"  # Keep this comment to allow automatic replacement 
of uv version
     if: contains(fromJSON('[
       "ashb",
       "bugraoz93",
@@ -93,8 +92,6 @@ jobs:
           persist-credentials: false
       - name: "Install Breeze"
         uses: ./.github/actions/breeze
-        with:
-          uv-version: ${{ env.UV_VERSION }}
       - name: Selective checks
         id: selective-checks
         env:
diff --git a/.github/workflows/scheduled-verify-release-calendar.yml 
b/.github/workflows/scheduled-verify-release-calendar.yml
index 75b5b8742dd..199ccfb72c1 100644
--- a/.github/workflows/scheduled-verify-release-calendar.yml
+++ b/.github/workflows/scheduled-verify-release-calendar.yml
@@ -24,8 +24,6 @@ on:  # yamllint disable-line rule:truthy
   workflow_dispatch:
 permissions:
   contents: read
-env:
-  UV_VERSION: "0.11.3"  # Keep this comment to allow automatic replacement of 
uv version
 jobs:
   verify-release-calendar:
     name: "Verify release calendar"
@@ -37,7 +35,16 @@ jobs:
         with:
           persist-credentials: false
       - name: "Install uv"
-        run: pip install "uv==${UV_VERSION}"
+        # Extract uv version from uv.lock. The format is stable: the line
+        # immediately after `name = "uv"` is `version = "<X.Y.Z>"`.
+        run: |
+          UV_VERSION=$(sed -n '/^name = "uv"$/{n;s/^version = 
"\(.*\)"$/\1/p;}' uv.lock)
+          if [[ -z "${UV_VERSION}" ]]; then
+            echo "Failed to extract uv version from uv.lock" >&2
+            exit 1
+          fi
+          echo "Installing uv==${UV_VERSION}"
+          pip install "uv==${UV_VERSION}"
       - name: "Verify release calendar"
         run: uv run dev/verify_release_calendar.py
       # yamllint disable rule:line-length
diff --git a/.github/workflows/update-constraints-on-push.yml 
b/.github/workflows/update-constraints-on-push.yml
index 182ad7374e4..18042838cd1 100644
--- a/.github/workflows/update-constraints-on-push.yml
+++ b/.github/workflows/update-constraints-on-push.yml
@@ -22,6 +22,7 @@ on:  # yamllint disable-line rule:truthy
     branches:
       - main
       - v[0-9]+-[0-9]+-test
+      - v[0-9]+-[0-9]+-stable
     paths:
       - 'uv.lock'
 permissions:
@@ -34,7 +35,6 @@ env:
   GITHUB_REPOSITORY: ${{ github.repository }}
   GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
   GITHUB_USERNAME: ${{ github.actor }}
-  UV_VERSION: "0.10.10"  # Keep this comment to allow automatic replacement of 
uv version
   VERBOSE: "true"
 jobs:
   build-info:
diff --git a/.github/workflows/upgrade-check.yml 
b/.github/workflows/upgrade-check.yml
index 55a7383665e..d4f23d57822 100644
--- a/.github/workflows/upgrade-check.yml
+++ b/.github/workflows/upgrade-check.yml
@@ -100,8 +100,8 @@ jobs:
             --jq '.[0].url' 2>/dev/null || true)
           echo "pr-url=${PR_URL}" >> "${GITHUB_OUTPUT}"
       - name: >-
-          [${{ inputs.target-branch }}] Notify Slack
-        if: steps.find-pr.outputs.pr-url != ''
+          [${{ inputs.target-branch }}] Notify Slack on success
+        if: success() && steps.find-pr.outputs.pr-url != ''
         uses: >-
           slackapi/slack-github-action@af78098f536edbc4de71162a307590698245be95
         with:
@@ -110,16 +110,49 @@ jobs:
           payload: |
             channel: "internal-airflow-ci-cd"
             text: >-
-              🔧 [${{ inputs.target-branch }}] CI upgrade PR
-              ready for review. Please undraft, review and
-              merge: ${{ steps.find-pr.outputs.pr-url }}
+              🔧 [${{ inputs.target-branch }}] Scheduled CI upgrade PR
+              ready: ${{ steps.find-pr.outputs.pr-url }}
             blocks:
               - type: section
                 text:
                   type: mrkdwn
                   text: >-
-                    🔧 *[${{ inputs.target-branch }}] CI upgrade
-                    PR ready for review*
+                    🔧 *[${{ inputs.target-branch }}] Scheduled CI upgrade
+                    PR ready*
+
+                    A new CI upgrade PR has been created as a draft on the
+                    `${{ inputs.target-branch }}` branch. Please:
+
+                      1. *Undraft* the PR to trigger CI
+                      2. *Review* the changes
+                      3. *Merge* it once CI passes
 
-                    Please undraft, review and merge:
                     <${{ steps.find-pr.outputs.pr-url }}|View PR>
+      - name: >-
+          [${{ inputs.target-branch }}] Notify Slack on failure
+        if: failure()
+        uses: >-
+          slackapi/slack-github-action@af78098f536edbc4de71162a307590698245be95
+        with:
+          method: chat.postMessage
+          token: ${{ env.SLACK_BOT_TOKEN }}
+          # yamllint disable rule:line-length
+          payload: |
+            channel: "internal-airflow-ci-cd"
+            text: >-
+              ⚠️ [${{ inputs.target-branch }}] Scheduled CI upgrade FAILED.
+              See: ${{ github.server_url }}/${{ github.repository 
}}/actions/runs/${{ github.run_id }}
+            blocks:
+              - type: section
+                text:
+                  type: mrkdwn
+                  text: >-
+                    ⚠️ *[${{ inputs.target-branch }}] Scheduled CI upgrade
+                    FAILED*
+
+                    The `breeze ci upgrade` job on the
+                    `${{ inputs.target-branch }}` branch did not complete
+                    successfully. Please investigate the failed run and
+                    re-run the workflow if needed.
+
+                    <${{ github.server_url }}/${{ github.repository 
}}/actions/runs/${{ github.run_id }}|View failed run>
diff --git a/scripts/ci/prek/upgrade_important_versions.py 
b/scripts/ci/prek/upgrade_important_versions.py
index 63975364b80..472abd81b89 100755
--- a/scripts/ci/prek/upgrade_important_versions.py
+++ b/scripts/ci/prek/upgrade_important_versions.py
@@ -67,7 +67,6 @@ FILES_TO_UPDATE: list[tuple[Path, bool]] = [
     (AIRFLOW_ROOT_PATH / "scripts" / "docker" / "common.sh", False),
     (AIRFLOW_ROOT_PATH / "scripts" / "tools" / "setup_breeze", False),
     (AIRFLOW_ROOT_PATH / "pyproject.toml", False),
-    (AIRFLOW_ROOT_PATH / ".github" / "workflows" / 
"airflow-distributions-tests.yml", False),
     (AIRFLOW_ROOT_PATH / "dev" / "breeze" / "pyproject.toml", False),
     (AIRFLOW_ROOT_PATH / "dev" / "breeze" / "src" / "airflow_breeze" / 
"global_constants.py", False),
     (
@@ -80,11 +79,6 @@ FILES_TO_UPDATE: list[tuple[Path, bool]] = [
         / "release_management_commands.py",
         False,
     ),
-    (AIRFLOW_ROOT_PATH / ".github" / "workflows" / 
"release_dockerhub_image.yml", False),
-    (AIRFLOW_ROOT_PATH / ".github" / "actions" / "install-prek" / 
"action.yml", False),
-    (AIRFLOW_ROOT_PATH / ".github" / "actions" / "breeze" / "action.yml", 
False),
-    (AIRFLOW_ROOT_PATH / ".github" / "workflows" / "basic-tests.yml", False),
-    (AIRFLOW_ROOT_PATH / ".github" / "workflows" / "ci-amd-arm.yml", False),
     (AIRFLOW_ROOT_PATH / "dev" / "breeze" / "doc" / "ci" / "02_images.md", 
True),
     (AIRFLOW_ROOT_PATH / "docker-stack-docs" / "build-arg-ref.rst", True),
     (AIRFLOW_ROOT_PATH / "devel-common" / "pyproject.toml", True),

Reply via email to