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

ephraimanierobi pushed a commit to branch v3-1-test
in repository https://gitbox.apache.org/repos/asf/airflow.git

commit b9aed715fada9b41572c0741260fb0b852d8f68d
Author: github-actions[bot] 
<41898282+github-actions[bot]@users.noreply.github.com>
AuthorDate: Sat Nov 1 19:15:42 2025 +0100

    [v3-1-test] Dev: Add Task SDK support to `start-release` command (#57658) 
(#57666)
    
    * Add Task SDK support to start-release command
    
    Enhance the breeze release-management start-release command to handle
    Task SDK releases alongside Airflow releases, matching the functionality
    of the start-rc-process command.
    
    Changes:
    - Add optional --task-sdk-release-candidate parameter to start-release
    - Update SVN operations to handle both Airflow and Task SDK artifacts
    - Support uploading both packages to PyPI separately
    - Create and push tags for both Airflow and Task SDK releases
    - Improve documentation with environment variables for all version 
parameters
    - Add concrete examples for publish-docs commands
    
    This allows release managers to release Airflow and Task SDK together
    in a single workflow or release Airflow independently when Task SDK
    updates are not needed.
    
    * Update vote email template to include Task SDK
    
    Update the vote email template to include Task SDK release information,
    based on the format used in recent releases.
    
    Changes:
    - Add TASK_SDK_VERSION_WITHOUT_RC environment variable
    - Update subject line to include both Airflow and Task SDK versions
    - Add separate sections for Task SDK artifacts
    - Include Task SDK in PyPI installation instructions
    - Add docs preview and release notes links
    - Add simplified uv-based installation example
    (cherry picked from commit 1c845c2838fb8db0969b62588a362778cc9a1f30)
    
    Co-authored-by: Kaxil Naik <[email protected]>
---
 dev/README_RELEASE_AIRFLOW.md                      |  96 +++++++------
 .../output_release-management_start-release.svg    |  28 ++--
 .../output_release-management_start-release.txt    |   2 +-
 .../src/airflow_breeze/commands/release_command.py | 151 +++++++++++++++++----
 .../commands/release_management_commands_config.py |   5 +-
 5 files changed, 201 insertions(+), 81 deletions(-)

diff --git a/dev/README_RELEASE_AIRFLOW.md b/dev/README_RELEASE_AIRFLOW.md
index 4aed6b4daa5..f8aaa54d122 100644
--- a/dev/README_RELEASE_AIRFLOW.md
+++ b/dev/README_RELEASE_AIRFLOW.md
@@ -277,6 +277,8 @@ export VERSION_SUFFIX=rc1
 export VERSION_BRANCH=2-1
 export VERSION_WITHOUT_RC=${VERSION/rc?/}
 export TASK_SDK_VERSION=1.0.5rc1
+export TASK_SDK_VERSION_WITHOUT_RC=${TASK_SDK_VERSION/rc?/}
+export PREVIOUS_VERSION=3.0.4
 
 # Set AIRFLOW_REPO_ROOT to the path of your git repo
 export AIRFLOW_REPO_ROOT=$(pwd)
@@ -362,7 +364,7 @@ uv tool install -e ./dev/breeze
     git pull # Ensure that the script is up-to-date
     breeze release-management start-rc-process \
         --version ${VERSION} \
-        --previous-version <PREVIOUS_VERSION> \
+        --previous-version ${PREVIOUS_VERSION} \
         --task-sdk-version ${TASK_SDK_VERSION}
    ```
 
@@ -373,7 +375,7 @@ uv tool install -e ./dev/breeze
    # Test with dry-run (shows what would be executed without doing it)
    breeze release-management start-rc-process \
        --version ${VERSION} \
-       --previous-version <PREVIOUS_VERSION> \
+       --previous-version ${PREVIOUS_VERSION} \
        --task-sdk-version ${TASK_SDK_VERSION} \
        --remote-name upstream \
        --dry-run
@@ -390,7 +392,7 @@ uv tool install -e ./dev/breeze
 - Generate the body of the issue using the below command:
 
   ```shell script
-    breeze release-management generate-issue-content-core --previous-release 
<PREVIOUS_VERSION> --current-release ${VERSION}
+    breeze release-management generate-issue-content-core --previous-release 
${PREVIOUS_VERSION} --current-release ${VERSION}
     ```
 
 ## Publish release candidate documentation (staging)
@@ -414,7 +416,10 @@ The command does the following:
 3. Triggers S3 to GitHub Sync
 
 ```shell script
-  breeze workflow-run publish-docs --ref <tag> --site-env <staging/live/auto> 
apache-airflow docker-stack task-sdk
+breeze workflow-run publish-docs --ref <tag> --site-env <staging/live/auto> 
apache-airflow docker-stack task-sdk
+
+# Example for RC
+breeze workflow-run publish-docs --ref ${VERSION} --site-env staging 
apache-airflow docker-stack task-sdk
 ```
 
 The `--ref` parameter should be the tag of the release candidate you are 
publishing.
@@ -501,7 +506,7 @@ Subject:
 
 ```shell script
 cat <<EOF
-[VOTE] Release Airflow ${VERSION_WITHOUT_RC} from ${VERSION}
+[VOTE] Release Airflow ${VERSION_WITHOUT_RC} from ${VERSION} & Task SDK 
${TASK_SDK_VERSION_WITHOUT_RC} from ${TASK_SDK_VERSION}
 EOF
 ```
 
@@ -511,24 +516,35 @@ Body:
 cat <<EOF
 Hey fellow Airflowers,
 
-I have cut Airflow ${VERSION}. This email is calling a vote on the release,
-which will last at least 72 hours, from Friday, October 8, 2021 at 4:00 pm UTC
+The release candidates for Apache Airflow ${VERSION} and Task SDK 
${TASK_SDK_VERSION} are now available for testing!
+
+This email is calling for a vote on the release, which will last at least 72 
hours, from Friday, October 8, 2021 at 4:00 pm UTC
 until Monday, October 11, 2021 at 4:00 pm UTC, and until 3 binding +1 votes 
have been received.
 
 
https://www.timeanddate.com/worldclock/fixedtime.html?msg=8&iso=20211011T1600&p1=1440
 
 Status of testing of the release is kept in TODO:URL_OF_THE_ISSUE_HERE
 
-Consider this my (binding) +1.
+Consider this my +1 binding vote.
 
 Airflow ${VERSION} is available at:
-https://dist.apache.org/repos/dist/dev/airflow/$VERSION/
+https://dist.apache.org/repos/dist/dev/airflow/${VERSION}/
+
+"apache-airflow" Meta package:
+- *apache-airflow-${VERSION_WITHOUT_RC}-source.tar.gz* is a source release 
that comes with INSTALL instructions.
+- *apache-airflow-${VERSION_WITHOUT_RC}.tar.gz* is the binary Python "sdist" 
release.
+- *apache_airflow-${VERSION_WITHOUT_RC}-py3-none-any.whl* is the binary Python 
wheel "binary" release.
+
+"apache-airflow-core" package:
+- *apache_airflow_core-${VERSION_WITHOUT_RC}.tar.gz* is the binary Python 
"sdist" release.
+- *apache_airflow_core-${VERSION_WITHOUT_RC}-py3-none-any.whl* is the binary 
Python wheel "binary" release.
+
+Task SDK ${TASK_SDK_VERSION} is available at:
+https://dist.apache.org/repos/dist/dev/airflow/task-sdk/${TASK_SDK_VERSION}/
 
-*apache-airflow-${VERSION_WITHOUT_RC}-source.tar.gz* is a source release that 
comes with INSTALL instructions.
-*apache-airflow-${VERSION_WITHOUT_RC}.tar.gz* is the binary Python "sdist" 
release fore airflow meta distribution.
-*apache_airflow-${VERSION_WITHOUT_RC}-py3-none-any.whl* is the binary Python 
wheel "binary" release for airflow meta distribution.
-*apache-airflow_core-${VERSION_WITHOUT_RC}.tar.gz* is the binary Python 
"sdist" release for airflow core distribution.
-*apache_airflow_core-${VERSION_WITHOUT_RC}-py3-none-any.whl* is the binary 
Python wheel "binary" release for airflow core distribution.
+"apache-airflow-task-sdk" package:
+- *apache_airflow_task_sdk-${TASK_SDK_VERSION_WITHOUT_RC}.tar.gz* is the 
binary Python "sdist" release.
+- *apache_airflow_task_sdk-${TASK_SDK_VERSION_WITHOUT_RC}-py3-none-any.whl* is 
the binary Python wheel "binary" release.
 
 Public keys are available at:
 https://dist.apache.org/repos/dist/release/airflow/KEYS
@@ -549,36 +565,26 @@ The test procedure for contributors and members of the 
community who would like
 
https://github.com/apache/airflow/blob/main/dev/README_RELEASE_AIRFLOW.md#verify-the-release-candidate-by-contributors
 
 Please note that the version number excludes the 'rcX' string, so it's now
-simply ${VERSION_WITHOUT_RC}. This will allow us to rename the artifact 
without modifying
+simply ${VERSION_WITHOUT_RC} for Airflow package and 
${TASK_SDK_VERSION_WITHOUT_RC} for Task SDK. This will allow us to rename the 
artifact without modifying
 the artifact checksums when we actually release.
 
-Release Notes: 
https://github.com/apache/airflow/blob/${VERSION}/RELEASE_NOTES.rst
+Docs (for preview):
+https://airflow.staged.apache.org/docs/apache-airflow/${VERSION_WITHOUT_RC}
 
-For information on what goes into a release please see: 
https://github.com/apache/airflow/blob/main/dev/WHAT_GOES_INTO_THE_NEXT_RELEASE.md
+Release Notes:
+- https://github.com/apache/airflow/blob/${VERSION}/RELEASE_NOTES.rst
+- 
https://airflow.staged.apache.org/docs/apache-airflow/${VERSION_WITHOUT_RC}/release_notes.html
 (Rendered HTML)
 
-Changes since PREVIOUS_VERSION_OR_RC:
-*Bugs*:
-[AIRFLOW-3732] Fix issue when trying to edit connection in RBAC UI
-[AIRFLOW-2866] Fix missing CSRF token head when using RBAC UI (#3804)
-...
+Testing Instructions using PyPI:
+You can build a virtualenv that installs this and other required packages 
(e.g. task sdk), like this:
 
+uv venv
+uv pip install -U \\
+  apache-airflow==${VERSION} \\
+  apache-airflow-core==${VERSION} \\
+  apache-airflow-task-sdk==${TASK_SDK_VERSION}
 
-*Improvements*:
-[AIRFLOW-3302] Small CSS fixes (#4140)
-[Airflow-2766] Respect shared datetime across tabs
-...
-
-
-*New features*:
-[AIRFLOW-2874] Enables FAB's theme support (#3719)
-[AIRFLOW-3336] Add new TriggerRule for 0 upstream failures (#4182)
-...
-
-
-*Doc-only Change*:
-[AIRFLOW-XXX] Fix BashOperator Docstring (#4052)
-[AIRFLOW-3018] Fix Minor issues in Documentation
-...
+Constraints files are at 
https://github.com/apache/airflow/tree/constraints-${VERSION}
 
 Cheers,
 <your name>
@@ -947,12 +953,19 @@ The best way of doing this is to svn cp between the two 
repos (this avoids havin
 ```shell script
 export RC=3.0.5rc5
 export VERSION=${RC/rc?/}
+export TASK_SDK_RC=1.0.5rc1
+export PREVIOUS_RELEASE=3.0.4
 # cd to the airflow repo directory and set the environment variable below
 export AIRFLOW_REPO_ROOT=$(pwd)
 # start the release process by running the below command
-breeze release-management start-release --release-candidate ${RC} 
--previous-release <PREVIOUS RELEASE>
+breeze release-management start-release \
+    --release-candidate ${RC} \
+    --previous-release ${PREVIOUS_RELEASE} \
+    --task-sdk-release-candidate ${TASK_SDK_RC}
 ```
 
+Note: The `--task-sdk-release-candidate` parameter is optional. If you are 
releasing Airflow without a corresponding Task SDK release, you can omit this 
parameter.
+
 ```Dockerfile
 ARG AIRFLOW_EXTRAS=".....,<provider>,...."
 ```
@@ -1016,10 +1029,11 @@ The command does the following:
 3. Triggers S3 to GitHub Sync
 
 ```shell script
-  breeze workflow-run publish-docs --ref <tag> --site-env <staging/live/auto>
+# Example for final release
+breeze workflow-run publish-docs --ref ${VERSION_WITHOUT_RC} --site-env live 
apache-airflow docker-stack task-sdk
 ```
 
-The `--ref` parameter should be the tag of the final candidate you are 
publishing.
+The `--ref` parameter should be the tag of the final version you are 
publishing.
 
 The `--site-env` parameter should be set to `staging` for pre-release versions 
or `live` for final releases. the default option is `auto`
 if the tag is rc it publishes to `staging` bucket, otherwise it publishes to 
`live` bucket.
diff --git a/dev/breeze/doc/images/output_release-management_start-release.svg 
b/dev/breeze/doc/images/output_release-management_start-release.svg
index 91411fe496c..41227a891b6 100644
--- a/dev/breeze/doc/images/output_release-management_start-release.svg
+++ b/dev/breeze/doc/images/output_release-management_start-release.svg
@@ -1,4 +1,4 @@
-<svg class="rich-terminal" viewBox="0 0 1482 416.0" 
xmlns="http://www.w3.org/2000/svg";>
+<svg class="rich-terminal" viewBox="0 0 1482 440.4" 
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-release-clip-terminal">
-      <rect x="0" y="0" width="1463.0" height="365.0" />
+      <rect x="0" y="0" width="1463.0" height="389.4" />
     </clipPath>
     <clipPath id="breeze-release-management-start-release-line-0">
     <rect x="0" y="1.5" width="1464" height="24.65"/>
@@ -89,9 +89,12 @@
 <clipPath id="breeze-release-management-start-release-line-13">
     <rect x="0" y="318.7" width="1464" height="24.65"/>
             </clipPath>
+<clipPath id="breeze-release-management-start-release-line-14">
+    <rect x="0" y="343.1" 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="414" rx="8"/><text 
class="breeze-release-management-start-release-title" fill="#c5c8c6" 
text-anchor="middle" x="740" 
y="27">Command:&#160;release-management&#160;start-release</text>
+    <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-release-title" fill="#c5c8c6" 
text-anchor="middle" x="740" 
y="27">Command:&#160;release-management&#160;start-release</text>
             <g transform="translate(26,22)">
             <circle cx="0" cy="0" r="7" fill="#ff5f57"/>
             <circle cx="22" cy="0" r="7" fill="#febc2e"/>
@@ -107,15 +110,16 @@
 </text><text class="breeze-release-management-start-release-r1" x="12.2" 
y="93.2" textLength="1305.4" 
clip-path="url(#breeze-release-management-start-release-line-3)">Start&#160;the&#160;process&#160;of&#160;releasing&#160;an&#160;Airflow&#160;version.&#160;This&#160;command&#160;will&#160;guide&#160;you&#160;through&#160;the&#160;release&#160;process.</text><text
 class="breeze-release-management-start-release-r1" x="1464" y="93.2" 
textLength="12.2" clip-path="url(#breeze-release-managem [...]
 </text><text class="breeze-release-management-start-release-r1" x="1464" 
y="117.6" textLength="12.2" 
clip-path="url(#breeze-release-management-start-release-line-4)">
 </text><text class="breeze-release-management-start-release-r5" x="0" y="142" 
textLength="24.4" 
clip-path="url(#breeze-release-management-start-release-line-5)">╭─</text><text 
class="breeze-release-management-start-release-r5" x="24.4" y="142" 
textLength="256.2" 
clip-path="url(#breeze-release-management-start-release-line-5)">&#160;Start&#160;release&#160;flags&#160;</text><text
 class="breeze-release-management-start-release-r5" x="280.6" y="142" 
textLength="1159" clip-path="url(#breeze- [...]
-</text><text class="breeze-release-management-start-release-r5" x="0" 
y="166.4" textLength="12.2" 
clip-path="url(#breeze-release-management-start-release-line-6)">│</text><text 
class="breeze-release-management-start-release-r6" x="24.4" y="166.4" 
textLength="12.2" 
clip-path="url(#breeze-release-management-start-release-line-6)">*</text><text 
class="breeze-release-management-start-release-r4" x="61" y="166.4" 
textLength="231.8" 
clip-path="url(#breeze-release-management-start-release-line- [...]
-</text><text class="breeze-release-management-start-release-r5" x="0" 
y="190.8" textLength="12.2" 
clip-path="url(#breeze-release-management-start-release-line-7)">│</text><text 
class="breeze-release-management-start-release-r6" x="24.4" y="190.8" 
textLength="12.2" 
clip-path="url(#breeze-release-management-start-release-line-7)">*</text><text 
class="breeze-release-management-start-release-r4" x="61" y="190.8" 
textLength="219.6" 
clip-path="url(#breeze-release-management-start-release-line- [...]
-</text><text class="breeze-release-management-start-release-r5" x="0" 
y="215.2" textLength="1464" 
clip-path="url(#breeze-release-management-start-release-line-8)">╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</text><text
 class="breeze-release-management-start-release-r1" x="1464" y="215.2" 
textLength="12.2" 
clip-path="url(#breeze-release-management-start-release-line-8)">
-</text><text class="breeze-release-management-start-release-r5" x="0" 
y="239.6" textLength="24.4" 
clip-path="url(#breeze-release-management-start-release-line-9)">╭─</text><text 
class="breeze-release-management-start-release-r5" x="24.4" y="239.6" 
textLength="195.2" 
clip-path="url(#breeze-release-management-start-release-line-9)">&#160;Common&#160;options&#160;</text><text
 class="breeze-release-management-start-release-r5" x="219.6" y="239.6" 
textLength="1220" clip-path="url(#breeze-rele [...]
-</text><text class="breeze-release-management-start-release-r5" x="0" y="264" 
textLength="12.2" 
clip-path="url(#breeze-release-management-start-release-line-10)">│</text><text 
class="breeze-release-management-start-release-r4" x="24.4" y="264" 
textLength="97.6" 
clip-path="url(#breeze-release-management-start-release-line-10)">--answer</text><text
 class="breeze-release-management-start-release-r9" x="158.6" y="264" 
textLength="24.4" clip-path="url(#breeze-release-management-start-release- [...]
-</text><text class="breeze-release-management-start-release-r5" x="0" 
y="288.4" textLength="12.2" 
clip-path="url(#breeze-release-management-start-release-line-11)">│</text><text 
class="breeze-release-management-start-release-r4" x="24.4" y="288.4" 
textLength="109.8" 
clip-path="url(#breeze-release-management-start-release-line-11)">--dry-run</text><text
 class="breeze-release-management-start-release-r9" x="158.6" y="288.4" 
textLength="24.4" clip-path="url(#breeze-release-management-start- [...]
-</text><text class="breeze-release-management-start-release-r5" x="0" 
y="312.8" textLength="12.2" 
clip-path="url(#breeze-release-management-start-release-line-12)">│</text><text 
class="breeze-release-management-start-release-r4" x="24.4" y="312.8" 
textLength="109.8" 
clip-path="url(#breeze-release-management-start-release-line-12)">--verbose</text><text
 class="breeze-release-management-start-release-r9" x="158.6" y="312.8" 
textLength="24.4" clip-path="url(#breeze-release-management-start- [...]
-</text><text class="breeze-release-management-start-release-r5" x="0" 
y="337.2" textLength="12.2" 
clip-path="url(#breeze-release-management-start-release-line-13)">│</text><text 
class="breeze-release-management-start-release-r4" x="24.4" y="337.2" 
textLength="73.2" 
clip-path="url(#breeze-release-management-start-release-line-13)">--help</text><text
 class="breeze-release-management-start-release-r9" x="158.6" y="337.2" 
textLength="24.4" clip-path="url(#breeze-release-management-start-rele [...]
-</text><text class="breeze-release-management-start-release-r5" x="0" 
y="361.6" textLength="1464" 
clip-path="url(#breeze-release-management-start-release-line-14)">╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</text><text
 class="breeze-release-management-start-release-r1" x="1464" y="361.6" 
textLength="12.2" 
clip-path="url(#breeze-release-management-start-release-line-14)">
+</text><text class="breeze-release-management-start-release-r5" x="0" 
y="166.4" textLength="12.2" 
clip-path="url(#breeze-release-management-start-release-line-6)">│</text><text 
class="breeze-release-management-start-release-r6" x="24.4" y="166.4" 
textLength="12.2" 
clip-path="url(#breeze-release-management-start-release-line-6)">*</text><text 
class="breeze-release-management-start-release-r4" x="61" y="166.4" 
textLength="231.8" 
clip-path="url(#breeze-release-management-start-release-line- [...]
+</text><text class="breeze-release-management-start-release-r5" x="0" 
y="190.8" textLength="12.2" 
clip-path="url(#breeze-release-management-start-release-line-7)">│</text><text 
class="breeze-release-management-start-release-r6" x="24.4" y="190.8" 
textLength="12.2" 
clip-path="url(#breeze-release-management-start-release-line-7)">*</text><text 
class="breeze-release-management-start-release-r4" x="61" y="190.8" 
textLength="219.6" 
clip-path="url(#breeze-release-management-start-release-line- [...]
+</text><text class="breeze-release-management-start-release-r5" x="0" 
y="215.2" textLength="12.2" 
clip-path="url(#breeze-release-management-start-release-line-8)">│</text><text 
class="breeze-release-management-start-release-r4" x="61" y="215.2" 
textLength="341.6" 
clip-path="url(#breeze-release-management-start-release-line-8)">--task-sdk-release-candidate</text><text
 class="breeze-release-management-start-release-r1" x="451.4" y="215.2" 
textLength="488" clip-path="url(#breeze-release-man [...]
+</text><text class="breeze-release-management-start-release-r5" x="0" 
y="239.6" textLength="1464" 
clip-path="url(#breeze-release-management-start-release-line-9)">╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</text><text
 class="breeze-release-management-start-release-r1" x="1464" y="239.6" 
textLength="12.2" 
clip-path="url(#breeze-release-management-start-release-line-9)">
+</text><text class="breeze-release-management-start-release-r5" x="0" y="264" 
textLength="24.4" 
clip-path="url(#breeze-release-management-start-release-line-10)">╭─</text><text
 class="breeze-release-management-start-release-r5" x="24.4" y="264" 
textLength="195.2" 
clip-path="url(#breeze-release-management-start-release-line-10)">&#160;Common&#160;options&#160;</text><text
 class="breeze-release-management-start-release-r5" x="219.6" y="264" 
textLength="1220" clip-path="url(#breeze-release- [...]
+</text><text class="breeze-release-management-start-release-r5" x="0" 
y="288.4" textLength="12.2" 
clip-path="url(#breeze-release-management-start-release-line-11)">│</text><text 
class="breeze-release-management-start-release-r4" x="24.4" y="288.4" 
textLength="97.6" 
clip-path="url(#breeze-release-management-start-release-line-11)">--answer</text><text
 class="breeze-release-management-start-release-r9" x="158.6" y="288.4" 
textLength="24.4" clip-path="url(#breeze-release-management-start-re [...]
+</text><text class="breeze-release-management-start-release-r5" x="0" 
y="312.8" textLength="12.2" 
clip-path="url(#breeze-release-management-start-release-line-12)">│</text><text 
class="breeze-release-management-start-release-r4" x="24.4" y="312.8" 
textLength="109.8" 
clip-path="url(#breeze-release-management-start-release-line-12)">--dry-run</text><text
 class="breeze-release-management-start-release-r9" x="158.6" y="312.8" 
textLength="24.4" clip-path="url(#breeze-release-management-start- [...]
+</text><text class="breeze-release-management-start-release-r5" x="0" 
y="337.2" textLength="12.2" 
clip-path="url(#breeze-release-management-start-release-line-13)">│</text><text 
class="breeze-release-management-start-release-r4" x="24.4" y="337.2" 
textLength="109.8" 
clip-path="url(#breeze-release-management-start-release-line-13)">--verbose</text><text
 class="breeze-release-management-start-release-r9" x="158.6" y="337.2" 
textLength="24.4" clip-path="url(#breeze-release-management-start- [...]
+</text><text class="breeze-release-management-start-release-r5" x="0" 
y="361.6" textLength="12.2" 
clip-path="url(#breeze-release-management-start-release-line-14)">│</text><text 
class="breeze-release-management-start-release-r4" x="24.4" y="361.6" 
textLength="73.2" 
clip-path="url(#breeze-release-management-start-release-line-14)">--help</text><text
 class="breeze-release-management-start-release-r9" x="158.6" y="361.6" 
textLength="24.4" clip-path="url(#breeze-release-management-start-rele [...]
+</text><text class="breeze-release-management-start-release-r5" x="0" y="386" 
textLength="1464" 
clip-path="url(#breeze-release-management-start-release-line-15)">╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</text><text
 class="breeze-release-management-start-release-r1" x="1464" y="386" 
textLength="12.2" 
clip-path="url(#breeze-release-management-start-release-line-15)">
 </text>
     </g>
     </g>
diff --git a/dev/breeze/doc/images/output_release-management_start-release.txt 
b/dev/breeze/doc/images/output_release-management_start-release.txt
index 98156901ab4..5920f348902 100644
--- a/dev/breeze/doc/images/output_release-management_start-release.txt
+++ b/dev/breeze/doc/images/output_release-management_start-release.txt
@@ -1 +1 @@
-d84c6e5a6fcab35c07287bc4d2d65841
+a860ddc98626bc82dc298e2bd0081f97
diff --git a/dev/breeze/src/airflow_breeze/commands/release_command.py 
b/dev/breeze/src/airflow_breeze/commands/release_command.py
index 4fe371c2e7a..2ac3bd2547a 100644
--- a/dev/breeze/src/airflow_breeze/commands/release_command.py
+++ b/dev/breeze/src/airflow_breeze/commands/release_command.py
@@ -44,14 +44,18 @@ def clone_asf_repo(working_dir):
         )
 
 
-def create_version_dir(version):
-    if confirm_action(f"Create SVN version directory for {version}?"):
+def create_version_dir(version, task_sdk_version=None):
+    if confirm_action(f"Create SVN version directory for Airflow {version}?"):
         run_command(["svn", "mkdir", f"{version}"], check=True)
-        console_print(f"{version} directory created")
+        console_print(f"Airflow {version} directory created")
 
+    if task_sdk_version and confirm_action(f"Create SVN version directory for 
Task SDK {task_sdk_version}?"):
+        run_command(["svn", "mkdir", f"task-sdk/{task_sdk_version}"], 
check=True)
+        console_print(f"Task SDK {task_sdk_version} directory created")
 
-def copy_artifacts_to_svn(rc, svn_dev_repo):
-    if confirm_action(f"Copy artifacts to SVN for {rc}?"):
+
+def copy_artifacts_to_svn(rc, task_sdk_rc, svn_dev_repo, svn_release_repo):
+    if confirm_action(f"Copy Airflow artifacts to SVN for {rc}?"):
         bash_command = f"""
         for f in {svn_dev_repo}/{rc}/*; do
             svn cp "$f" "$(basename "$f")/"
@@ -66,16 +70,51 @@ def copy_artifacts_to_svn(rc, svn_dev_repo):
             ],
             check=True,
         )
-        console_print("Artifacts copied to SVN:")
+        console_print("Airflow artifacts copied to SVN:")
+        run_command(["ls"])
+
+    if task_sdk_rc and confirm_action(f"Copy Task SDK artifacts to SVN for 
{task_sdk_rc}?"):
+        # Save current directory
+        current_dir = os.getcwd()
+        # Change to task-sdk release directory
+        task_sdk_version = task_sdk_rc[:-3]
+        os.chdir(f"{svn_release_repo}/task-sdk/{task_sdk_version}")
+
+        bash_command = f"""
+        for f in {svn_dev_repo}/task-sdk/{task_sdk_rc}/*; do
+            svn cp "$f" "$(basename "$f")/"
+        done
+        """
+
+        run_command(
+            [
+                "bash",
+                "-c",
+                bash_command,
+            ],
+            check=True,
+        )
+        console_print("Task SDK artifacts copied to SVN:")
         run_command(["ls"])
 
+        # Go back to previous directory
+        os.chdir(current_dir)
 
-def commit_release(version, rc, svn_release_version_dir):
-    if confirm_action(f"Commit release {version} to SVN?"):
+
+def commit_release(version, task_sdk_version, rc, task_sdk_rc, 
svn_release_repo):
+    commit_message = f"Release Airflow {version} from {rc}"
+    if task_sdk_version and task_sdk_rc:
+        commit_message += f" & Task SDK {task_sdk_version} from {task_sdk_rc}"
+
+    if confirm_action("Commit release to SVN?"):
+        # Need to commit from parent directory to include both airflow and 
task-sdk if applicable
+        current_dir = os.getcwd()
+        os.chdir(svn_release_repo)
         run_command(
-            ["svn", "commit", "-m", f"Release Airflow {version} from {rc}"],
+            ["svn", "commit", "-m", commit_message],
             check=True,
         )
+        os.chdir(current_dir)
 
 
 def remove_old_release(previous_release):
@@ -97,19 +136,46 @@ def verify_pypi_package(version):
         run_command(["twine", "check", "*.whl", f"*{version}.tar.gz"], 
check=True)
 
 
-def upload_to_pypi(version):
-    if confirm_action("Upload to PyPI?"):
+def upload_to_pypi(version, task_sdk_version=None):
+    if confirm_action("Upload Airflow packages to PyPI?"):
         run_command(
-            ["twine", "upload", "-r", "pypi", "*.whl", f"*{version}.tar.gz"],
+            [
+                "twine",
+                "upload",
+                "-r",
+                "pypi",
+                "apache_airflow-*.whl",
+                f"apache_airflow-{version}.tar.gz",
+                f"apache_airflow_core-{version}.tar.gz",
+                "apache_airflow_core-*.whl",
+            ],
             check=True,
         )
-        console_print("Packages pushed to production PyPI")
+        console_print("Airflow packages pushed to production PyPI")
         console_print(
             "Verify that the package looks good by downloading it and 
installing it into a virtual "
             "environment. The package download link is available at: "
             "https://pypi.python.org/pypi/apache-airflow";
         )
 
+    if task_sdk_version and confirm_action("Upload Task SDK packages to 
PyPI?"):
+        run_command(
+            [
+                "twine",
+                "upload",
+                "-r",
+                "pypi",
+                "apache_airflow_task_sdk-*.whl",
+                f"apache_airflow_task_sdk-{task_sdk_version}.tar.gz",
+            ],
+            check=True,
+        )
+        console_print("Task SDK packages pushed to production PyPI")
+        console_print(
+            "Verify that the Task SDK package is available at: "
+            "https://pypi.python.org/pypi/apache-airflow-task-sdk";
+        )
+
 
 def retag_constraints(release_candidate, version):
     if confirm_action(f"Retag constraints for {release_candidate} as 
{version}?"):
@@ -157,8 +223,8 @@ def tag_and_push_latest_constraint(version):
         )
 
 
-def push_tag_for_final_version(version, release_candidate):
-    if confirm_action(f"Push tag for final version {version}?"):
+def push_tag_for_final_version(version, release_candidate, 
task_sdk_version=None, task_sdk_rc=None):
+    if confirm_action(f"Push Airflow tag for final version {version}?"):
         console_print(
             """
         This step should only be done now and not before, because it triggers 
an automated
@@ -175,6 +241,28 @@ def push_tag_for_final_version(version, release_candidate):
         )
         run_command(["git", "push", "origin", "tag", f"{version}"], check=True)
 
+    if (
+        task_sdk_version
+        and task_sdk_rc
+        and confirm_action(f"Push Task SDK tag for final version 
{task_sdk_version}?")
+    ):
+        confirm_action(
+            f"Confirm that Task SDK {task_sdk_version} is pushed to PyPI. Is 
it pushed?", abort=True
+        )
+        run_command(["git", "checkout", f"task-sdk/{task_sdk_rc}"], check=True)
+        run_command(
+            [
+                "git",
+                "tag",
+                "-s",
+                f"task-sdk/{task_sdk_version}",
+                "-m",
+                f"Airflow Task SDK {task_sdk_version}",
+            ],
+            check=True,
+        )
+        run_command(["git", "push", "origin", "tag", 
f"task-sdk/{task_sdk_version}"], check=True)
+
 
 @release_management.command(
     name="start-release",
@@ -182,23 +270,34 @@ def push_tag_for_final_version(version, 
release_candidate):
     help="Start the process of releasing an Airflow version. "
     "This command will guide you through the release process. ",
 )
[email protected]("--release-candidate", required=True)
[email protected]("--previous-release", required=True)
[email protected]("--release-candidate", required=True, help="Airflow release 
candidate e.g. 3.0.5rc1")
[email protected]("--previous-release", required=True, help="Previous Airflow 
release e.g. 3.0.4")
[email protected]("--task-sdk-release-candidate", required=False, help="Task SDK 
release candidate e.g. 1.0.5rc1")
 @option_answer
 @option_dry_run
 @option_verbose
-def airflow_release(release_candidate, previous_release):
+def airflow_release(release_candidate, previous_release, 
task_sdk_release_candidate):
     if "rc" not in release_candidate:
         exit("Release candidate must contain 'rc'")
     if "rc" in previous_release:
         exit("Previous release must not contain 'rc'")
+
     version = release_candidate[:-3]
+    task_sdk_version = None
+    if task_sdk_release_candidate:
+        if "rc" not in task_sdk_release_candidate:
+            exit("Task SDK release candidate must contain 'rc'")
+        task_sdk_version = task_sdk_release_candidate[:-3]
+
     os.chdir(AIRFLOW_ROOT_PATH)
     airflow_repo_root = os.getcwd()
     console_print()
-    console_print("Release candidate:", release_candidate)
-    console_print("Release Version:", version)
-    console_print("Previous release:", previous_release)
+    console_print("Airflow Release candidate:", release_candidate)
+    console_print("Airflow Release Version:", version)
+    console_print("Previous Airflow release:", previous_release)
+    if task_sdk_release_candidate:
+        console_print("Task SDK Release candidate:", 
task_sdk_release_candidate)
+        console_print("Task SDK Release Version:", task_sdk_version)
     console_print("Airflow repo root:", airflow_repo_root)
     console_print()
     console_print("Below are your git remotes. We will push to origin:")
@@ -224,7 +323,7 @@ def airflow_release(release_candidate, previous_release):
     os.chdir(svn_release_repo)
 
     # Create the version directory
-    create_version_dir(version)
+    create_version_dir(version, task_sdk_version)
     svn_release_version_dir = f"{svn_release_repo}/{version}"
     console_print("SVN Release version dir:", svn_release_version_dir)
 
@@ -235,10 +334,10 @@ def airflow_release(release_candidate, previous_release):
         confirm_action("Version directory does not exist. Do you want to 
Continue?", abort=True)
 
     # Copy artifacts to the version directory
-    copy_artifacts_to_svn(release_candidate, svn_dev_repo)
+    copy_artifacts_to_svn(release_candidate, task_sdk_release_candidate, 
svn_dev_repo, svn_release_repo)
 
     # Commit the release to svn
-    commit_release(version, release_candidate, svn_release_version_dir)
+    commit_release(version, task_sdk_version, release_candidate, 
task_sdk_release_candidate, svn_release_repo)
 
     confirm_action(
         "Verify that the artifacts appear in 
https://dist.apache.org/repos/dist/release/airflow/";, abort=True
@@ -255,7 +354,7 @@ def airflow_release(release_candidate, previous_release):
     verify_pypi_package(version)
 
     # Upload to pypi
-    upload_to_pypi(version)
+    upload_to_pypi(version, task_sdk_version)
 
     # Change Directory to airflow
     os.chdir(airflow_repo_root)
@@ -265,6 +364,6 @@ def airflow_release(release_candidate, previous_release):
     tag_and_push_latest_constraint(version)
 
     # Push tag for final version
-    push_tag_for_final_version(version, release_candidate)
+    push_tag_for_final_version(version, release_candidate, task_sdk_version, 
task_sdk_release_candidate)
 
     console_print("Done!")
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 2e843a04af6..9c7b086c1dd 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
@@ -436,7 +436,10 @@ RELEASE_MANAGEMENT_PARAMETERS: dict[str, list[dict[str, 
str | list[str]]]] = {
         }
     ],
     "breeze release-management start-release": [
-        {"name": "Start release flags", "options": ["--release-candidate", 
"--previous-release"]}
+        {
+            "name": "Start release flags",
+            "options": ["--release-candidate", "--previous-release", 
"--task-sdk-release-candidate"],
+        }
     ],
     "breeze release-management update-constraints": [
         {


Reply via email to