This is an automated email from the ASF dual-hosted git repository.
kaxilnaik pushed a commit to branch v3-0-test
in repository https://gitbox.apache.org/repos/asf/airflow.git
The following commit(s) were added to refs/heads/v3-0-test by this push:
new 476ceb73937 [v3-0-test] Dev: Add Task SDK support to
`start-rc-process` (#54960) (#54963)
476ceb73937 is described below
commit 476ceb7393788f4b8888e99fcf15d44798a898fb
Author: github-actions[bot]
<41898282+github-actions[bot]@users.noreply.github.com>
AuthorDate: Wed Aug 27 01:06:33 2025 +0100
[v3-0-test] Dev: Add Task SDK support to `start-rc-process` (#54960)
(#54963)
Added support to automate building Task SDK too and `--remote-name`
parameter to allow passing a different remote (for example I use a different
remote than origin). Also added some validations to prevent errors!
(cherry picked from commit 0810b0df301f427034ee7b99a90aa4166b82f806)
Co-authored-by: Kaxil Naik <[email protected]>
---
dev/README_RELEASE_AIRFLOW.md | 74 +++---
.../output_release-management_start-rc-process.svg | 30 ++-
.../output_release-management_start-rc-process.txt | 2 +-
.../commands/release_candidate_command.py | 278 +++++++++++++++++++--
.../commands/release_management_commands_config.py | 2 +
5 files changed, 320 insertions(+), 66 deletions(-)
diff --git a/dev/README_RELEASE_AIRFLOW.md b/dev/README_RELEASE_AIRFLOW.md
index 2cf85f6454b..a9c85b5113d 100644
--- a/dev/README_RELEASE_AIRFLOW.md
+++ b/dev/README_RELEASE_AIRFLOW.md
@@ -230,10 +230,11 @@ The Release Candidate artifacts we vote upon should be
the exact ones we vote ag
export GPG_TTY=$(tty)
# Set Version
-export VERSION=2.1.2rc3
-export VERSION_SUFFIX=rc3
+export VERSION=3.0.5rc1
+export VERSION_SUFFIX=rc1
export VERSION_BRANCH=2-1
export VERSION_WITHOUT_RC=${VERSION/rc?/}
+export TASK_SDK_VERSION=1.0.5rc1
# Set AIRFLOW_REPO_ROOT to the path of your git repo
export AIRFLOW_REPO_ROOT=$(pwd)
@@ -323,7 +324,23 @@ pipx install -e ./dev/breeze
```shell script
git checkout main
git pull # Ensure that the script is up-to-date
- breeze release-management start-rc-process --version ${VERSION}
--previous-version <PREVIOUS_VERSION>
+ breeze release-management start-rc-process \
+ --version ${VERSION} \
+ --previous-version <PREVIOUS_VERSION> \
+ --task-sdk-version ${TASK_SDK_VERSION}
+ ```
+
+ **Testing the start-rc-process command:**
+ Before running the actual release command, you can safely test it using:
+
+ ```shell script
+ # Test with dry-run (shows what would be executed without doing it)
+ breeze release-management start-rc-process \
+ --version ${VERSION} \
+ --previous-version <PREVIOUS_VERSION> \
+ --task-sdk-version ${TASK_SDK_VERSION} \
+ --remote-name upstream \
+ --dry-run
```
- Create issue in github for testing the release using this subject:
@@ -706,8 +723,8 @@ this is a valid key already. To suppress the warning you
may edit the key's tru
by running `gpg --edit-key <key id> trust` and entering `5` to assign trust
level `ultimate`.
```
-Checking apache-airflow-2.0.2rc4.tar.gz.asc
-gpg: assuming signed data in 'apache-airflow-2.0.2rc4.tar.gz'
+Checking apache-airflow-3.0.5rc4.tar.gz.asc
+gpg: assuming signed data in 'apache-airflow-3.0.5rc4.tar.gz'
gpg: Signature made sob, 22 sie 2020, 20:28:28 CEST
gpg: using RSA key 12717556040EEF2EEAF1B9C275FCCD0A25FA0E4B
gpg: Good signature from "Kaxil Naik <[email protected]>" [unknown]
@@ -715,8 +732,8 @@ gpg: WARNING: This key is not certified with a trusted
signature!
gpg: There is no indication that the signature belongs to the owner.
Primary key fingerprint: 1271 7556 040E EF2E EAF1 B9C2 75FC CD0A 25FA 0E4B
-Checking apache_airflow-2.0.2rc4-py2.py3-none-any.whl.asc
-gpg: assuming signed data in 'apache_airflow-2.0.2rc4-py2.py3-none-any.whl'
+Checking apache_airflow-3.0.5rc4-py2.py3-none-any.whl.asc
+gpg: assuming signed data in 'apache_airflow-3.0.5rc4-py2.py3-none-any.whl'
gpg: Signature made sob, 22 sie 2020, 20:28:31 CEST
gpg: using RSA key 12717556040EEF2EEAF1B9C275FCCD0A25FA0E4B
gpg: Good signature from "Kaxil Naik <[email protected]>" [unknown]
@@ -724,8 +741,8 @@ gpg: WARNING: This key is not certified with a trusted
signature!
gpg: There is no indication that the signature belongs to the owner.
Primary key fingerprint: 1271 7556 040E EF2E EAF1 B9C2 75FC CD0A 25FA 0E4B
-Checking apache-airflow-2.0.2rc4-source.tar.gz.asc
-gpg: assuming signed data in 'apache-airflow-2.0.2rc4-source.tar.gz'
+Checking apache-airflow-3.0.5rc4-source.tar.gz.asc
+gpg: assuming signed data in 'apache-airflow-3.0.5rc4-source.tar.gz'
gpg: Signature made sob, 22 sie 2020, 20:28:25 CEST
gpg: using RSA key 12717556040EEF2EEAF1B9C275FCCD0A25FA0E4B
gpg: Good signature from "Kaxil Naik <[email protected]>" [unknown]
@@ -748,9 +765,9 @@ done
You should get output similar to:
```
-Checking apache-airflow-2.0.2rc4.tar.gz.sha512
-Checking apache_airflow-2.0.2rc4-py2.py3-none-any.whl.sha512
-Checking apache-airflow-2.0.2rc4-source.tar.gz.sha512
+Checking apache-airflow-3.0.5rc4.tar.gz.sha512
+Checking apache_airflow-3.0.5rc4-py2.py3-none-any.whl.sha512
+Checking apache-airflow-3.0.5rc4-source.tar.gz.sha512
```
@@ -815,7 +832,7 @@ Once the vote has been passed, you will need to send a
result vote to dev@airflo
Subject:
```
-[RESULT][VOTE] Release Airflow 2.0.2 from 2.0.2rc3
+[RESULT][VOTE] Release Airflow 3.0.5 from 3.0.5rc1 & Task SDK 1.0.5 from
1.0.5rc1
```
Message:
@@ -823,26 +840,27 @@ Message:
```
Hello,
-Apache Airflow 2.0.2 (based on RC3) has been accepted.
+The vote to release Apache Airflow version 3.0.5 based on 3.0.5rc3 & Task SDK
1.0.5 from 1.0.5rc3 is now closed.
+
+The vote PASSED with 6 binding "+1", 4 non-binding "+1" and 0 "-1" votes:
-4 "+1" binding votes received:
+"+1" Binding votes:
- Kaxil Naik
-- Bolke de Bruin
+- Jens Scheffler
+- Jarek Potiuk
- Ash Berlin-Taylor
-- Tao Feng
-
-
-4 "+1" non-binding votes received:
+- Hussein Awala
+- Amogh Desai
-- Deng Xiaodong
-- Stefan Seelmann
-- Joshua Patchus
-- Felix Uellendall
+"+1" non-Binding votes:
+- Wei Lee
+- Pavankumar Gopidesu
+- Ankit Chaurasia
+- Rahul Vats
-Vote thread:
-https://lists.apache.org/thread.html/736404ca3d2b2143b296d0910630b9bd0f8b56a0c54e3a05f4c8b5fe@%3Cdev.airflow.apache.org%3E
+Vote thread: https://lists.apache.org/thread/f72gglg5vdxnfmjqtjlhwgvn2tnh4gx4
-I'll continue with the release process, and the release announcement will
follow shortly.
+I will continue with the release process, and the release announcement will
follow shortly.
Cheers,
<your name>
@@ -857,7 +875,7 @@ https://dist.apache.org/repos/dist/release/airflow/
The best way of doing this is to svn cp between the two repos (this avoids
having to upload the binaries again, and gives a clearer history in the svn
commit logs):
```shell script
-export RC=2.0.2rc5
+export RC=3.0.5rc5
export VERSION=${RC/rc?/}
# cd to the airflow repo directory and set the environment variable below
export AIRFLOW_REPO_ROOT=$(pwd)
diff --git
a/dev/breeze/doc/images/output_release-management_start-rc-process.svg
b/dev/breeze/doc/images/output_release-management_start-rc-process.svg
index cd966701475..45c7c863b53 100644
--- a/dev/breeze/doc/images/output_release-management_start-rc-process.svg
+++ b/dev/breeze/doc/images/output_release-management_start-rc-process.svg
@@ -1,4 +1,4 @@
-<svg class="rich-terminal" viewBox="0 0 1482 440.4"
xmlns="http://www.w3.org/2000/svg">
+<svg class="rich-terminal" viewBox="0 0 1482 489.2"
xmlns="http://www.w3.org/2000/svg">
<!-- Generated with Rich https://www.textualize.io -->
<style>
@@ -45,7 +45,7 @@
<defs>
<clipPath id="breeze-release-management-start-rc-process-clip-terminal">
- <rect x="0" y="0" width="1463.0" height="389.4" />
+ <rect x="0" y="0" width="1463.0" height="438.2" />
</clipPath>
<clipPath id="breeze-release-management-start-rc-process-line-0">
<rect x="0" y="1.5" width="1464" height="24.65"/>
@@ -92,9 +92,15 @@
<clipPath id="breeze-release-management-start-rc-process-line-14">
<rect x="0" y="343.1" width="1464" height="24.65"/>
</clipPath>
+<clipPath id="breeze-release-management-start-rc-process-line-15">
+ <rect x="0" y="367.5" width="1464" height="24.65"/>
+ </clipPath>
+<clipPath id="breeze-release-management-start-rc-process-line-16">
+ <rect x="0" y="391.9" width="1464" height="24.65"/>
+ </clipPath>
</defs>
- <rect fill="#292929" stroke="rgba(255,255,255,0.35)" stroke-width="1"
x="1" y="1" width="1480" height="438.4" rx="8"/><text
class="breeze-release-management-start-rc-process-title" fill="#c5c8c6"
text-anchor="middle" x="740"
y="27">Command: release-management start-rc-process</text>
+ <rect fill="#292929" stroke="rgba(255,255,255,0.35)" stroke-width="1"
x="1" y="1" width="1480" height="487.2" rx="8"/><text
class="breeze-release-management-start-rc-process-title" fill="#c5c8c6"
text-anchor="middle" x="740"
y="27">Command: release-management start-rc-process</text>
<g transform="translate(26,22)">
<circle cx="0" cy="0" r="7" fill="#ff5f57"/>
<circle cx="22" cy="0" r="7" fill="#febc2e"/>
@@ -112,14 +118,16 @@
</text><text class="breeze-release-management-start-rc-process-r5" x="0"
y="142" textLength="24.4"
clip-path="url(#breeze-release-management-start-rc-process-line-5)">╭─</text><text
class="breeze-release-management-start-rc-process-r5" x="24.4" y="142"
textLength="292.8"
clip-path="url(#breeze-release-management-start-rc-process-line-5)"> Start RC process flags </text><text
class="breeze-release-management-start-rc-process-r5" x="317.2" y="142"
textLength="1122.4 [...]
</text><text class="breeze-release-management-start-rc-process-r5" x="0"
y="166.4" textLength="12.2"
clip-path="url(#breeze-release-management-start-rc-process-line-6)">│</text><text
class="breeze-release-management-start-rc-process-r6" x="24.4" y="166.4"
textLength="12.2"
clip-path="url(#breeze-release-management-start-rc-process-line-6)">*</text><text
class="breeze-release-management-start-rc-process-r4" x="61" y="166.4"
textLength="109.8" clip-path="url(#breeze-release-management-star [...]
</text><text class="breeze-release-management-start-rc-process-r5" x="0"
y="190.8" textLength="12.2"
clip-path="url(#breeze-release-management-start-rc-process-line-7)">│</text><text
class="breeze-release-management-start-rc-process-r6" x="24.4" y="190.8"
textLength="12.2"
clip-path="url(#breeze-release-management-start-rc-process-line-7)">*</text><text
class="breeze-release-management-start-rc-process-r4" x="61" y="190.8"
textLength="219.6" clip-path="url(#breeze-release-management-star [...]
-</text><text class="breeze-release-management-start-rc-process-r5" x="0"
y="215.2" textLength="12.2"
clip-path="url(#breeze-release-management-start-rc-process-line-8)">│</text><text
class="breeze-release-management-start-rc-process-r4" x="61" y="215.2"
textLength="170.8"
clip-path="url(#breeze-release-management-start-rc-process-line-8)">--github-token</text><text
class="breeze-release-management-start-rc-process-r1" x="329.4" y="215.2"
textLength="878.4" clip-path="url(#breeze-release- [...]
-</text><text class="breeze-release-management-start-rc-process-r5" x="0"
y="239.6" textLength="1464"
clip-path="url(#breeze-release-management-start-rc-process-line-9)">╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</text><text
class="breeze-release-management-start-rc-process-r1" x="1464" y="239.6"
textLength="12.2"
clip-path="url(#breeze-release-management-start-rc-process-line-9)">
-</text><text class="breeze-release-management-start-rc-process-r5" x="0"
y="264" textLength="24.4"
clip-path="url(#breeze-release-management-start-rc-process-line-10)">╭─</text><text
class="breeze-release-management-start-rc-process-r5" x="24.4" y="264"
textLength="195.2"
clip-path="url(#breeze-release-management-start-rc-process-line-10)"> Common options </text><text
class="breeze-release-management-start-rc-process-r5" x="219.6" y="264"
textLength="1220" clip-path="url(# [...]
-</text><text class="breeze-release-management-start-rc-process-r5" x="0"
y="288.4" textLength="12.2"
clip-path="url(#breeze-release-management-start-rc-process-line-11)">│</text><text
class="breeze-release-management-start-rc-process-r4" x="24.4" y="288.4"
textLength="97.6"
clip-path="url(#breeze-release-management-start-rc-process-line-11)">--answer</text><text
class="breeze-release-management-start-rc-process-r9" x="158.6" y="288.4"
textLength="24.4" clip-path="url(#breeze-release-mana [...]
-</text><text class="breeze-release-management-start-rc-process-r5" x="0"
y="312.8" textLength="12.2"
clip-path="url(#breeze-release-management-start-rc-process-line-12)">│</text><text
class="breeze-release-management-start-rc-process-r4" x="24.4" y="312.8"
textLength="109.8"
clip-path="url(#breeze-release-management-start-rc-process-line-12)">--dry-run</text><text
class="breeze-release-management-start-rc-process-r9" x="158.6" y="312.8"
textLength="24.4" clip-path="url(#breeze-release-ma [...]
-</text><text class="breeze-release-management-start-rc-process-r5" x="0"
y="337.2" textLength="12.2"
clip-path="url(#breeze-release-management-start-rc-process-line-13)">│</text><text
class="breeze-release-management-start-rc-process-r4" x="24.4" y="337.2"
textLength="109.8"
clip-path="url(#breeze-release-management-start-rc-process-line-13)">--verbose</text><text
class="breeze-release-management-start-rc-process-r9" x="158.6" y="337.2"
textLength="24.4" clip-path="url(#breeze-release-ma [...]
-</text><text class="breeze-release-management-start-rc-process-r5" x="0"
y="361.6" textLength="12.2"
clip-path="url(#breeze-release-management-start-rc-process-line-14)">│</text><text
class="breeze-release-management-start-rc-process-r4" x="24.4" y="361.6"
textLength="73.2"
clip-path="url(#breeze-release-management-start-rc-process-line-14)">--help</text><text
class="breeze-release-management-start-rc-process-r9" x="158.6" y="361.6"
textLength="24.4" clip-path="url(#breeze-release-manage [...]
-</text><text class="breeze-release-management-start-rc-process-r5" x="0"
y="386" textLength="1464"
clip-path="url(#breeze-release-management-start-rc-process-line-15)">╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</text><text
class="breeze-release-management-start-rc-process-r1" x="1464" y="386"
textLength="12.2"
clip-path="url(#breeze-release-management-start-rc-process-line-15)">
+</text><text class="breeze-release-management-start-rc-process-r5" x="0"
y="215.2" textLength="12.2"
clip-path="url(#breeze-release-management-start-rc-process-line-8)">│</text><text
class="breeze-release-management-start-rc-process-r6" x="24.4" y="215.2"
textLength="12.2"
clip-path="url(#breeze-release-management-start-rc-process-line-8)">*</text><text
class="breeze-release-management-start-rc-process-r4" x="61" y="215.2"
textLength="219.6" clip-path="url(#breeze-release-management-star [...]
+</text><text class="breeze-release-management-start-rc-process-r5" x="0"
y="239.6" textLength="12.2"
clip-path="url(#breeze-release-management-start-rc-process-line-9)">│</text><text
class="breeze-release-management-start-rc-process-r4" x="61" y="239.6"
textLength="170.8"
clip-path="url(#breeze-release-management-start-rc-process-line-9)">--github-token</text><text
class="breeze-release-management-start-rc-process-r1" x="329.4" y="239.6"
textLength="878.4" clip-path="url(#breeze-release- [...]
+</text><text class="breeze-release-management-start-rc-process-r5" x="0"
y="264" textLength="12.2"
clip-path="url(#breeze-release-management-start-rc-process-line-10)">│</text><text
class="breeze-release-management-start-rc-process-r4" x="61" y="264"
textLength="158.6"
clip-path="url(#breeze-release-management-start-rc-process-line-10)">--remote-name</text><text
class="breeze-release-management-start-rc-process-r1" x="329.4" y="264"
textLength="536.8" clip-path="url(#breeze-release-manag [...]
+</text><text class="breeze-release-management-start-rc-process-r5" x="0"
y="288.4" textLength="1464"
clip-path="url(#breeze-release-management-start-rc-process-line-11)">╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</text><text
class="breeze-release-management-start-rc-process-r1" x="1464" y="288.4"
textLength="12.2"
clip-path="url(#breeze-release-management-start-rc-process-line-11)">
+</text><text class="breeze-release-management-start-rc-process-r5" x="0"
y="312.8" textLength="24.4"
clip-path="url(#breeze-release-management-start-rc-process-line-12)">╭─</text><text
class="breeze-release-management-start-rc-process-r5" x="24.4" y="312.8"
textLength="195.2"
clip-path="url(#breeze-release-management-start-rc-process-line-12)"> Common options </text><text
class="breeze-release-management-start-rc-process-r5" x="219.6" y="312.8"
textLength="1220" clip-path= [...]
+</text><text class="breeze-release-management-start-rc-process-r5" x="0"
y="337.2" textLength="12.2"
clip-path="url(#breeze-release-management-start-rc-process-line-13)">│</text><text
class="breeze-release-management-start-rc-process-r4" x="24.4" y="337.2"
textLength="97.6"
clip-path="url(#breeze-release-management-start-rc-process-line-13)">--answer</text><text
class="breeze-release-management-start-rc-process-r9" x="158.6" y="337.2"
textLength="24.4" clip-path="url(#breeze-release-mana [...]
+</text><text class="breeze-release-management-start-rc-process-r5" x="0"
y="361.6" textLength="12.2"
clip-path="url(#breeze-release-management-start-rc-process-line-14)">│</text><text
class="breeze-release-management-start-rc-process-r4" x="24.4" y="361.6"
textLength="109.8"
clip-path="url(#breeze-release-management-start-rc-process-line-14)">--dry-run</text><text
class="breeze-release-management-start-rc-process-r9" x="158.6" y="361.6"
textLength="24.4" clip-path="url(#breeze-release-ma [...]
+</text><text class="breeze-release-management-start-rc-process-r5" x="0"
y="386" textLength="12.2"
clip-path="url(#breeze-release-management-start-rc-process-line-15)">│</text><text
class="breeze-release-management-start-rc-process-r4" x="24.4" y="386"
textLength="109.8"
clip-path="url(#breeze-release-management-start-rc-process-line-15)">--verbose</text><text
class="breeze-release-management-start-rc-process-r9" x="158.6" y="386"
textLength="24.4" clip-path="url(#breeze-release-manageme [...]
+</text><text class="breeze-release-management-start-rc-process-r5" x="0"
y="410.4" textLength="12.2"
clip-path="url(#breeze-release-management-start-rc-process-line-16)">│</text><text
class="breeze-release-management-start-rc-process-r4" x="24.4" y="410.4"
textLength="73.2"
clip-path="url(#breeze-release-management-start-rc-process-line-16)">--help</text><text
class="breeze-release-management-start-rc-process-r9" x="158.6" y="410.4"
textLength="24.4" clip-path="url(#breeze-release-manage [...]
+</text><text class="breeze-release-management-start-rc-process-r5" x="0"
y="434.8" textLength="1464"
clip-path="url(#breeze-release-management-start-rc-process-line-17)">╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</text><text
class="breeze-release-management-start-rc-process-r1" x="1464" y="434.8"
textLength="12.2"
clip-path="url(#breeze-release-management-start-rc-process-line-17)">
</text>
</g>
</g>
diff --git
a/dev/breeze/doc/images/output_release-management_start-rc-process.txt
b/dev/breeze/doc/images/output_release-management_start-rc-process.txt
index b3b458160d9..60bca552ac9 100644
--- a/dev/breeze/doc/images/output_release-management_start-rc-process.txt
+++ b/dev/breeze/doc/images/output_release-management_start-rc-process.txt
@@ -1 +1 @@
-e213b793839b778288f40a3af9e076f6
+8c938ba1642769000bab086687b60815
diff --git
a/dev/breeze/src/airflow_breeze/commands/release_candidate_command.py
b/dev/breeze/src/airflow_breeze/commands/release_candidate_command.py
index 87aa6dad66a..f71992b6c9d 100644
--- a/dev/breeze/src/airflow_breeze/commands/release_candidate_command.py
+++ b/dev/breeze/src/airflow_breeze/commands/release_candidate_command.py
@@ -32,7 +32,173 @@ from airflow_breeze.utils.run_utils import run_command
from airflow_breeze.utils.shared_options import get_dry_run
-def merge_pr(version_branch):
+def validate_remote_tracks_apache_airflow(remote_name):
+ """Validate that the specified remote tracks the apache/airflow
repository."""
+ console_print(f"[info]Validating remote '{remote_name}' tracks
apache/airflow...")
+
+ result = run_command(
+ ["git", "remote", "get-url", remote_name],
+ check=False,
+ capture_output=True,
+ text=True,
+ dry_run_override=False,
+ )
+
+ if result.returncode != 0:
+ console_print(f"[error]Remote '{remote_name}' does not exist!")
+ console_print("Available remotes:")
+ run_command(["git", "remote", "-v"])
+ exit(1)
+
+ remote_url = result.stdout.strip()
+
+ # Check if it's the apache/airflow repository
+ apache_patterns = [
+ "https://github.com/apache/airflow",
+ "https://github.com/apache/airflow.git",
+ "[email protected]:apache/airflow",
+ "[email protected]:apache/airflow.git",
+ "ssh://[email protected]/apache/airflow",
+ "ssh://[email protected]/apache/airflow.git",
+ ]
+
+ is_apache_repo = any(pattern in remote_url for pattern in apache_patterns)
+
+ if not is_apache_repo:
+ console_print(f"[error]Remote '{remote_name}' does not track
apache/airflow!")
+ console_print(f"Remote URL: {remote_url}")
+ console_print("Expected patterns: apache/airflow")
+ if not confirm_action("Do you want to continue anyway? This is NOT
recommended for releases."):
+ exit(1)
+ console_print(f"[success]Remote '{remote_name}' correctly tracks
apache/airflow")
+
+
+def validate_git_status():
+ """Validate that git working directory is clean."""
+ console_print("[info]Validating git status...")
+
+ # Check if working directory is clean
+ result = run_command(
+ ["git", "status", "--porcelain"],
+ check=True,
+ capture_output=True,
+ text=True,
+ )
+
+ if result.stdout.strip():
+ console_print("[error]Working directory is not clean!")
+ run_command(["git", "status"])
+ if not confirm_action("Do you want to continue with uncommitted
changes? This is NOT recommended."):
+ exit(1)
+ console_print("[success]Working directory is clean")
+
+
+def validate_version_branches_exist(version_branch, remote_name):
+ """Validate that the required version branches exist."""
+ console_print(f"[info]Validating version branches exist for
{version_branch}...")
+
+ # Check if test branch exists
+ test_branch = f"v{version_branch}-test"
+ stable_branch = f"v{version_branch}-stable"
+
+ # Fetch to get latest remote branches
+ run_command(["git", "fetch", remote_name], check=True)
+
+ # Check remote branches
+ result = run_command(
+ ["git", "branch", "-r"],
+ check=True,
+ capture_output=True,
+ text=True,
+ dry_run_override=False,
+ )
+ remote_branches = result.stdout
+
+ test_branch_exists = f"{remote_name}/{test_branch}" in remote_branches
+ stable_branch_exists = f"{remote_name}/{stable_branch}" in remote_branches
+
+ if not test_branch_exists:
+ console_print(f"[error]Test branch '{remote_name}/{test_branch}' does
not exist!")
+ console_print("Available remote branches:")
+ run_command(["git", "branch", "-r"])
+ exit(1)
+ console_print(f"[success]Test branch '{remote_name}/{test_branch}' exists")
+
+ if not stable_branch_exists:
+ console_print(f"[error]Stable branch '{remote_name}/{stable_branch}'
does not exist!")
+ console_print("Available remote branches:")
+ run_command(["git", "branch", "-r"])
+ exit(1)
+ console_print(f"[success]Stable branch '{remote_name}/{stable_branch}'
exists")
+
+
+def validate_tag_does_not_exist(version, remote_name):
+ """Validate that the release tag doesn't already exist."""
+ console_print(f"[info]Checking if tag '{version}' already exists...")
+
+ # Check if tag exists locally
+ local_result = run_command(
+ ["git", "tag", "-l", version],
+ check=True,
+ capture_output=True,
+ text=True,
+ )
+
+ # Check if tag exists on remote using ls-remote with --exit-code
+ remote_result = run_command(
+ ["git", "ls-remote", "--exit-code", "--tags", remote_name,
f"refs/tags/{version}"],
+ check=False,
+ )
+
+ tag_exists_locally = bool(local_result.stdout.strip())
+ tag_exists_remotely = remote_result.returncode == 0
+
+ if not tag_exists_locally and not tag_exists_remotely:
+ console_print(f"[success]Tag '{version}' does not exist yet")
+ return
+
+ location = []
+ if tag_exists_locally:
+ location.append("locally")
+ if tag_exists_remotely:
+ location.append("remotely")
+
+ console_print(f"[error]Tag '{version}' already exists {' and
'.join(location)}!")
+
+ if tag_exists_locally:
+ console_print(f"Use 'git tag -d {version}' to delete it locally if
needed")
+ if tag_exists_remotely:
+ console_print(f"Use 'git push {remote_name} --delete {version}' to
delete it remotely if needed")
+
+ if not confirm_action("Do you want to continue anyway? This may cause
issues."):
+ exit(1)
+
+
+def validate_on_correct_branch_for_tagging(version_branch):
+ """Validate that we're on the correct branch for tagging (stable
branch)."""
+ console_print("[info]Validating we're on the correct branch for
tagging...")
+
+ expected_branch = f"v{version_branch}-stable"
+
+ # Check current branch
+ result = run_command(
+ ["git", "branch", "--show-current"],
+ check=True,
+ capture_output=True,
+ text=True,
+ dry_run_override=False,
+ )
+ current_branch = result.stdout.strip()
+
+ if current_branch != expected_branch:
+ console_print(f"[error]Currently on branch '{current_branch}',
expected '{expected_branch}'!")
+ console_print("Tags should be created on the stable branch after
merging the sync PR.")
+ console_print("Make sure the PR merge step completed successfully.")
+ exit(1)
+ console_print(f"[success]On correct branch '{expected_branch}' for
tagging")
+
+
+def merge_pr(version_branch, remote_name):
if confirm_action("Do you want to merge the Sync PR?"):
run_command(
[
@@ -43,7 +209,7 @@ def merge_pr(version_branch):
check=True,
)
run_command(
- ["git", "reset", "--hard", f"origin/v{version_branch}-stable"],
+ ["git", "reset", "--hard",
f"{remote_name}/v{version_branch}-stable"],
check=True,
)
run_command(
@@ -52,18 +218,18 @@ def merge_pr(version_branch):
)
if confirm_action("Do you want to push the changes? Pushing the
changes closes the PR"):
run_command(
- ["git", "push", "origin", f"v{version_branch}-stable"],
+ ["git", "push", remote_name, f"v{version_branch}-stable"],
check=True,
)
-def git_tag(version):
+def git_tag(version, message):
if confirm_action(f"Tag {version}?"):
run_command(
- ["git", "tag", "-s", f"{version}", "-m", f"Apache Airflow
{version}"],
+ ["git", "tag", "-s", f"{version}", "-m", message],
check=True,
)
- console_print("[success]Tagged")
+ console_print(f"[success]Tagged {version}!")
def git_clean():
@@ -116,12 +282,27 @@ def create_artifacts_with_hatch(source_date_epoch: int):
AIRFLOW_DIST_PATH.mkdir(exist_ok=True)
env_copy = os.environ.copy()
env_copy["SOURCE_DATE_EPOCH"] = str(source_date_epoch)
+ # Build Airflow packages
run_command(
["hatch", "build", "-c", "-t", "custom", "-t", "sdist", "-t",
"wheel"], check=True, env=env_copy
)
- console_print("[success]Successfully prepared Airflow packages:")
+ # Build Task SDK packages
+ run_command(
+ [
+ "breeze",
+ "release-management",
+ "prepare-task-sdk-distributions",
+ "--distribution-format",
+ "both",
+ "--use-local-hatch",
+ ],
+ check=True,
+ )
+ console_print("[success]Successfully prepared Airflow and Task SDK
packages:")
for file in sorted(AIRFLOW_DIST_PATH.glob("apache_airflow*")):
console_print(print(file.name))
+ for file in sorted(AIRFLOW_DIST_PATH.glob("*task*")):
+ console_print(print(file.name))
console_print()
@@ -137,7 +318,17 @@ def create_artifacts_with_docker():
],
check=True,
)
- console_print("[success]Artifacts created")
+ run_command(
+ [
+ "breeze",
+ "release-management",
+ "prepare-task-sdk-distributions",
+ "--distribution-format",
+ "both",
+ ],
+ check=True,
+ )
+ console_print("[success]Airflow and Task SDK artifacts created")
def sign_the_release(repo_root):
@@ -147,10 +338,10 @@ def sign_the_release(repo_root):
console_print("[success]Release signed")
-def tag_and_push_constraints(version, version_branch):
+def tag_and_push_constraints(version, version_branch, remote_name):
if confirm_action("Do you want to tag and push constraints?"):
run_command(
- ["git", "checkout", f"origin/constraints-{version_branch}"],
+ ["git", "checkout", f"{remote_name}/constraints-{version_branch}"],
check=True,
)
run_command(
@@ -165,7 +356,7 @@ def tag_and_push_constraints(version, version_branch):
check=True,
)
run_command(
- ["git", "push", "origin", "tag", f"constraints-{version}"],
+ ["git", "push", remote_name, "tag", f"constraints-{version}"],
check=True,
)
console_print("[success]Constraints tagged and pushed")
@@ -187,13 +378,18 @@ def clone_asf_repo(version, repo_root):
console_print("[success]Cloned ASF repo successfully")
-def move_artifacts_to_svn(version, repo_root):
+def move_artifacts_to_svn(version, task_sdk_version, repo_root):
if confirm_action("Do you want to move artifacts to SVN?"):
os.chdir(f"{repo_root}/asf-dist/dev/airflow")
run_command(["svn", "mkdir", f"{version}"], check=True)
- run_command(f"mv {repo_root}/dist/* {version}/", check=True,
shell=True)
+ run_command(f"mv {repo_root}/dist/*{version}* {version}/", check=True,
shell=True)
+ run_command(
+ f"mv {repo_root}/dist/*{task_sdk_version}*
task-sdk/{task_sdk_version}/", check=True, shell=True
+ )
console_print("[success]Moved artifacts to SVN:")
run_command(["ls"])
+ run_command([f"ls {version}"])
+ run_command([f"ls task-sdk/{task_sdk_version}"])
def push_artifacts_to_asf_repo(version, repo_root):
@@ -237,6 +433,19 @@ def prepare_pypi_packages(version, version_suffix,
repo_root):
],
check=True,
)
+ # Task SDK
+ run_command(
+ [
+ "breeze",
+ "release-management",
+ "prepare-task-sdk-distributions",
+ "--version-suffix",
+ f"{version_suffix}",
+ "--distribution-format",
+ "both",
+ ],
+ check=True,
+ )
files_to_check = []
for files in Path(AIRFLOW_DIST_PATH).glob("apache_airflow*"):
if "-sources" not in files.name:
@@ -273,8 +482,8 @@ def push_packages_to_pypi(version):
)
-def push_release_candidate_tag_to_github(version):
- if confirm_action("Do you want to push release candidate tag to GitHub?"):
+def push_release_candidate_tag_to_github(version, remote_name):
+ if confirm_action("Do you want to push release candidate tags to GitHub?"):
console_print(
"""
This step should only be done now and not before, because it triggers
an automated
@@ -283,7 +492,7 @@ def push_release_candidate_tag_to_github(version):
"""
)
confirm_action(f"Confirm that {version} is pushed to PyPI(not PyPI
test). Is it pushed?", abort=True)
- run_command(["git", "push", "origin", "tag", f"{version}"], check=True)
+ run_command(["git", "push", remote_name, "tag", f"{version}"],
check=True)
console_print("[success]Release candidate tag pushed to GitHub")
@@ -345,13 +554,15 @@ def prepare_airflow_tarball(version: str):
)
@click.option("--version", required=True, help="The release candidate version
e.g. 2.4.3rc1")
@click.option("--previous-version", required=True, help="Previous version
released e.g. 2.4.2")
[email protected]("--task-sdk-version", required=True, help="The task SDK version
e.g. 1.0.6rc1.")
@click.option(
"--github-token", help="GitHub token to use in generating issue for
testing of release candidate"
)
[email protected]("--remote-name", default="origin", help="Git remote name to push
to (default: origin)")
@option_answer
@option_dry_run
@option_verbose
-def publish_release_candidate(version, previous_version, github_token):
+def publish_release_candidate(version, previous_version, task_sdk_version,
github_token, remote_name):
from packaging.version import Version
airflow_version = Version(version)
@@ -368,29 +579,43 @@ def publish_release_candidate(version, previous_version,
github_token):
version_suffix = airflow_version.pre[0] + str(airflow_version.pre[1])
version_branch = str(airflow_version.release[0]) + "-" +
str(airflow_version.release[1])
version_without_rc = airflow_version.base_version
+
+ task_sdk_version_obj = Version(task_sdk_version)
+ task_sdk_version_without_rc = task_sdk_version_obj.base_version
+
os.chdir(AIRFLOW_ROOT_PATH)
airflow_repo_root = os.getcwd()
+ validate_remote_tracks_apache_airflow(remote_name)
+ validate_git_status()
+ validate_version_branches_exist(version_branch, remote_name)
+ validate_tag_does_not_exist(version, remote_name)
+ validate_tag_does_not_exist(f"task-sdk/{task_sdk_version}", remote_name)
+
# List the above variables and ask for confirmation
console_print()
console_print(f"Previous version: {previous_version}")
- console_print(f"version: {version}")
+ console_print(f"Airflow version: {version}")
+ console_print(f"Task SDK version: {task_sdk_version}")
console_print(f"version_suffix: {version_suffix}")
console_print(f"version_branch: {version_branch}")
console_print(f"version_without_rc: {version_without_rc}")
+ console_print(f"task_sdk_version_without_rc:
{task_sdk_version_without_rc}")
console_print(f"airflow_repo_root: {airflow_repo_root}")
+ console_print(f"remote_name: {remote_name}")
console_print()
- console_print("Below are your git remotes. We will push to origin:")
+ console_print(f"Below are your git remotes. We will push to
{remote_name}:")
run_command(["git", "remote", "-v"])
console_print()
confirm_action("Verify that the above information is correct. Do you want
to continue?", abort=True)
- # Final confirmation
- confirm_action("Pushes will be made to origin. Do you want to continue?",
abort=True)
# Merge the sync PR
- merge_pr(version_branch)
+ merge_pr(version_branch, remote_name)
#
# # Tag & clean the repo
- git_tag(version)
+ # Validate we're on the correct branch before tagging
+ validate_on_correct_branch_for_tagging(version_branch)
+ git_tag(version, f"Apache Airflow {version}")
+ git_tag(f"task-sdk/{task_sdk_version}", f"Airflow Task SDK
{task_sdk_version}")
git_clean()
source_date_epoch = get_source_date_epoch(AIRFLOW_ROOT_PATH)
shutil.rmtree(AIRFLOW_DIST_PATH, ignore_errors=True)
@@ -407,11 +632,11 @@ def publish_release_candidate(version, previous_version,
github_token):
# Sign the release
sign_the_release(airflow_repo_root)
# Tag and push constraints
- tag_and_push_constraints(version, version_branch)
+ tag_and_push_constraints(version, version_branch, remote_name)
# Clone the asf repo
clone_asf_repo(version, airflow_repo_root)
# Move artifacts to SVN
- move_artifacts_to_svn(version, airflow_repo_root)
+ move_artifacts_to_svn(version, task_sdk_version, airflow_repo_root)
# Push the artifacts to the asf repo
push_artifacts_to_asf_repo(version, airflow_repo_root)
@@ -428,7 +653,8 @@ def publish_release_candidate(version, previous_version,
github_token):
push_packages_to_pypi(version)
# Push the release candidate tag to gitHub
- push_release_candidate_tag_to_github(version)
+ push_release_candidate_tag_to_github(version, remote_name)
+ push_release_candidate_tag_to_github(f"task-sdk/{task_sdk_version}",
remote_name)
# Create issue for testing
os.chdir(airflow_repo_root)
diff --git
a/dev/breeze/src/airflow_breeze/commands/release_management_commands_config.py
b/dev/breeze/src/airflow_breeze/commands/release_management_commands_config.py
index 62b41b30763..672c10e362b 100644
---
a/dev/breeze/src/airflow_breeze/commands/release_management_commands_config.py
+++
b/dev/breeze/src/airflow_breeze/commands/release_management_commands_config.py
@@ -421,7 +421,9 @@ RELEASE_MANAGEMENT_PARAMETERS: dict[str, list[dict[str, str
| list[str]]]] = {
"options": [
"--version",
"--previous-version",
+ "--task-sdk-version",
"--github-token",
+ "--remote-name",
],
}
],