This is an automated email from the ASF dual-hosted git repository.
vatsrahul1001 pushed a commit to branch v3-4-test
in repository https://gitbox.apache.org/repos/asf/airflow.git
The following commit(s) were added to refs/heads/v3-4-test by this push:
new 864f1077ef2 [v3-4-test] Commit pre-release constraints onto the shared
constraints-X-Y branch (#75329) (#75592)
864f1077ef2 is described below
commit 864f1077ef26b9ef7a307064cd3940189bef9771
Author: github-actions[bot]
<41898282+github-actions[bot]@users.noreply.github.com>
AuthorDate: Sun Oct 25 06:19:54 2026 +0000
[v3-4-test] Commit pre-release constraints onto the shared constraints-X-Y
branch (#75329) (#75592)
* Commit pre-release constraints onto the shared constraints-X-Y branch
A per-candidate constraints-<VERSION> branch shares its name with the
constraints-<VERSION> tag, which makes the ref ambiguous for git and for
anything resolving constraints by name. Keeping a single constraints-X-Y
branch history for betas, rcs and finals removes the ambiguity, and a
leftover per-candidate branch is now reported instead of silently kept.
Generated-by: Claude Opus 5
* Rebase-and-retry the shared-branch push; fix remote in stale-branch hint
Pushing pre-release constraints onto the shared constraints-X-Y branch is a
plain push, but that branch is also written by
update-constraints-on-push.yml
on every vX-Y-test uv.lock change -- which happens during the same beta/rc
window this workflow runs in. A concurrent write makes the push a
non-fast-forward and fails the job, forcing a manual re-run. Rebase onto the
latest and retry (up to 5x) so a routine rolling-constraints commit no
longer
breaks the release run.
The stale-branch error hint printed 'git push upstream --delete', but the
workflow uses 'origin' throughout; align the hint to 'origin' so the pasted
command targets the right remote in the RM checkout.
---------
(cherry picked from commit fd52d8703fb3eca7bd9bdc08005e7b532097c49b)
Co-authored-by: Jarek Potiuk <[email protected]>
Co-authored-by: Rahul Vats <[email protected]>
---
.github/workflows/release-constraints.yml | 46 +++++++++++++++++++------------
dev/README_RELEASE_AIRFLOW.md | 19 +++++++------
2 files changed, 38 insertions(+), 27 deletions(-)
diff --git a/.github/workflows/release-constraints.yml
b/.github/workflows/release-constraints.yml
index c2e61fc9090..fe74f5527f7 100644
--- a/.github/workflows/release-constraints.yml
+++ b/.github/workflows/release-constraints.yml
@@ -118,15 +118,14 @@ jobs:
constraints_branch="constraints-${major}-${minor}"
echo "constraints-branch=${constraints_branch}" >> "${GITHUB_OUTPUT}"
# A pre-release - beta or rc - pins the providers at the versions
PyPI holds (rc provider
- # waves included) and lands on a branch and tag of its own, so it
never moves the shared
- # constraints-X-Y branch. A final pins released providers and
commits onto the shared branch.
+ # waves included); a final pins released providers. Both commit onto
the shared
+ # constraints-X-Y branch and are tagged constraints-<VERSION>.
if [[ "${VERSION}" =~ (b|rc)[0-9]+$ ]]; then
echo "allow-pre-releases=true" >> "${GITHUB_OUTPUT}"
- echo "target-branch=constraints-${VERSION}" >> "${GITHUB_OUTPUT}"
else
echo "allow-pre-releases=false" >> "${GITHUB_OUTPUT}"
- echo "target-branch=${constraints_branch}" >> "${GITHUB_OUTPUT}"
fi
+ echo "target-branch=${constraints_branch}" >> "${GITHUB_OUTPUT}"
- name: "Parameters summary"
shell: bash
env:
@@ -231,30 +230,29 @@ jobs:
with:
pattern: constraints-*
path: ./files
- # A candidate's branch and tag belong to that candidate alone, so
re-running for the same
- # rc replaces them rather than adding to them: the branch would
otherwise already hold the
- # previous run's constraints (making the push a non-fast-forward) and
the tag already exist.
- # A final never gets this - it commits onto the shared constraints-X-Y
branch, whose history
- # every other build reads.
- - name: "Delete the previous ${{ needs.build-info.outputs.target-branch
}} branch and tag"
+ # Re-running for the same pre-release adds a commit on constraints-X-Y
and moves its tag, so
+ # the previous run's tag is deleted first. A leftover
constraints-<VERSION> branch (from when
+ # pre-releases had a branch of their own) would make that tag name
ambiguous, so it fails fast.
+ - name: "Check for a stale constraints-${{ inputs.version }} branch and
replace the tag"
if: needs.build-info.outputs.allow-pre-releases == 'true'
working-directory: "constraints"
shell: bash
run: |
- if git ls-remote --exit-code origin "refs/heads/${TARGET_BRANCH}" >
/dev/null; then
- echo "Deleting the existing '${TARGET_BRANCH}' branch."
- git push origin --delete "refs/heads/${TARGET_BRANCH}"
+ if git ls-remote --exit-code origin
"refs/heads/constraints-${VERSION}" > /dev/null; then
+ echo "ERROR! Branch 'constraints-${VERSION}' must not exist -" \
+ "it would make the 'constraints-${VERSION}' tag ambiguous."
+ echo "Delete it first, e.g.: git push origin --delete
'refs/heads/constraints-${VERSION}'"
+ exit 1
fi
if git ls-remote --exit-code origin
"refs/tags/constraints-${VERSION}" > /dev/null; then
echo "Deleting the existing 'constraints-${VERSION}' tag."
git push origin --delete "refs/tags/constraints-${VERSION}"
fi
git tag --delete "constraints-${VERSION}" > /dev/null 2>&1 || true
- git branch --delete --force "${TARGET_BRANCH}" > /dev/null 2>&1 ||
true
- name: "Switch to ${{ needs.build-info.outputs.target-branch }}"
working-directory: "constraints"
shell: bash
- run: git switch -c "${TARGET_BRANCH}" 2>/dev/null || git switch
"${TARGET_BRANCH}"
+ run: git switch "${TARGET_BRANCH}"
- name: "Diff in constraints for Python: ${{
needs.build-info.outputs.python-versions-list-as-string }}"
run: ./scripts/ci/constraints/ci_diff_constraints.sh
- name: "Commit changed constraint files"
@@ -262,9 +260,21 @@ jobs:
- name: "Push ${{ needs.build-info.outputs.target-branch }}"
working-directory: "constraints"
shell: bash
- # Qualify as refs/heads/ -- for a release candidate the branch and the
tag share a name
- # (both constraints-<VERSION>), so a bare refspec is ambiguous
("matches more than one").
- run: git push origin "refs/heads/${TARGET_BRANCH}"
+ # constraints-X-Y is also written by update-constraints-on-push.yml on
every vX-Y-test
+ # uv.lock change, which can land during the beta/rc window this
workflow runs in. A
+ # concurrent write makes this push a non-fast-forward, so rebase onto
the latest and retry.
+ # Qualify as refs/heads/ so the push never resolves to a same-named
tag.
+ run: |
+ for attempt in $(seq 1 5); do
+ if git push origin "refs/heads/${TARGET_BRANCH}"; then
+ exit 0
+ fi
+ echo "Push of '${TARGET_BRANCH}' rejected (it moved underneath
us);" \
+ "rebasing onto the latest and retrying (attempt ${attempt}/5)."
+ git pull --rebase origin "${TARGET_BRANCH}"
+ done
+ echo "ERROR! Could not push '${TARGET_BRANCH}' after 5 attempts."
+ exit 1
- name: "Tag constraints-${{ inputs.version }}"
working-directory: "constraints"
shell: bash
diff --git a/dev/README_RELEASE_AIRFLOW.md b/dev/README_RELEASE_AIRFLOW.md
index 5c81a5f8461..be218b3f6eb 100644
--- a/dev/README_RELEASE_AIRFLOW.md
+++ b/dev/README_RELEASE_AIRFLOW.md
@@ -182,8 +182,8 @@ no vote.
It validates that `vX-Y-test` exists, checks out its tip, tags the beta
there, builds and signs
the artifacts, resolves constraints through the `release-constraints`
workflow from `vX-Y-test`
- (exactly like an `rc`: provider rc waves are allowed and the result lands
on its own
- `constraints-X.Y.0bN` branch and tag, leaving the shared `constraints-X-Y`
untouched), uploads to
+ (exactly like an `rc`: provider rc waves are allowed, the result is
committed onto the shared
+ `constraints-X-Y` branch and tagged `constraints-X.Y.0bN`), uploads to
SVN dev and PyPI, and pushes the tags. Everything downstream of the tag is
the same as an `rc`;
only the branch/sync/vote handling differs.
@@ -722,9 +722,9 @@ still works but is no longer recommended.
allowance is scoped to `apache-airflow-providers-*`; no other package can
resolve to a
pre-release, so a beta of some third-party library cannot slip into what
the candidate ships.
- The result lands on a branch of its own (`constraints-${VERSION_RC}`) and
is tagged
- `constraints-${VERSION_RC}`. The shared `constraints-X-Y` branch is left
where it was — only
- the final release moves it.
+ The result is committed onto the shared `constraints-X-Y` branch and tagged
+ `constraints-${VERSION_RC}`. There is no `constraints-${VERSION_RC}` branch
— a branch with the
+ same name as the tag would make the name ambiguous, so the workflow fails
if it finds one.
**Testing the start-rc-process command:**
Before running the actual release command, you can safely test it using:
@@ -1557,7 +1557,7 @@ disagree with the version:
| `--version` | Provider pins | Lands on | Tagged |
|---|---|---|---|
-| `3.3.1rc1` | newest in PyPI, `rcN` included | `constraints-3.3.1rc1`,
branched off `constraints-3-3` | `constraints-3.3.1rc1` |
+| `3.3.1rc1` | newest in PyPI, `rcN` included | `constraints-3-3` (commit) |
`constraints-3.3.1rc1` |
| `3.3.1` | newest final in PyPI | `constraints-3-3` (commit) |
`constraints-3.3.1` |
The providers are pinned rather than resolved: every
`apache-airflow-providers-*` is named with
@@ -1568,9 +1568,10 @@ candidate in the wave keeps its release. A final ignores
candidates entirely, wh
it impossible for a released constraints file to carry an `rc` pin. Nothing
else in the dependency
graph can resolve to a pre-release, since no other requirement mentions one.
-Re-running the workflow for the same candidate deletes and re-creates that
candidate's branch and
-tag, so redoing a candidate's constraints just works. A final is never treated
this way - it commits
-onto the shared `constraints-X-Y` branch, whose history everything downstream
reads.
+Re-running the workflow for the same candidate adds another commit onto
`constraints-X-Y` and
+re-creates the candidate's tag on it, so redoing a candidate's constraints
just works. If a
+leftover `constraints-${VERSION}` branch exists (from when candidates had a
branch of their own),
+the workflow stops and asks for it to be deleted first.
4. Make sure to update Airflow version in ``v3-*-test`` branch after
cherry-picking to X.Y.1 in