This is an automated email from the ASF dual-hosted git repository.
Lee-W 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 41613f2b977 Add airflowctl tasks clear command (#69320)
41613f2b977 is described below
commit 41613f2b9777e7f39238d1f8a8a7b7b2ce1bf737
Author: PoAn Yang <[email protected]>
AuthorDate: Thu Jul 23 14:35:18 2026 +0900
Add airflowctl tasks clear command (#69320)
Signed-off-by: PoAn Yang <[email protected]>
---
.../src/airflow/cli/commands/task_command.py | 1 +
.../unit/cli/commands/test_command_deprecations.py | 1 +
.../airflowctl_tests/test_airflowctl_commands.py | 1 +
airflow-ctl/docs/images/command_hashes.txt | 4 +-
airflow-ctl/docs/images/output_main.svg | 136 ++++++++++-----------
airflow-ctl/docs/images/output_tasks.svg | 70 ++++++-----
airflow-ctl/src/airflowctl/api/client.py | 7 ++
airflow-ctl/src/airflowctl/api/operations.py | 18 +++
airflow-ctl/src/airflowctl/ctl/cli_config.py | 66 +++++++---
airflow-ctl/src/airflowctl/ctl/help_texts.yaml | 3 +
.../tests/airflow_ctl/api/test_operations.py | 44 +++++++
.../tests/airflow_ctl/ctl/test_cli_config.py | 49 ++++++++
12 files changed, 279 insertions(+), 121 deletions(-)
diff --git a/airflow-core/src/airflow/cli/commands/task_command.py
b/airflow-core/src/airflow/cli/commands/task_command.py
index e2a1ab28e04..b8cae95ae80 100644
--- a/airflow-core/src/airflow/cli/commands/task_command.py
+++ b/airflow-core/src/airflow/cli/commands/task_command.py
@@ -493,6 +493,7 @@ def task_render(args, dag: DAG | None = None) -> None:
)
+@deprecated_for_airflowctl("airflowctl tasks clear")
@cli_utils.action_cli(check_db=False)
@providers_configuration_loaded
def task_clear(args) -> None:
diff --git a/airflow-core/tests/unit/cli/commands/test_command_deprecations.py
b/airflow-core/tests/unit/cli/commands/test_command_deprecations.py
index 65982d65adb..59d26a12864 100644
--- a/airflow-core/tests/unit/cli/commands/test_command_deprecations.py
+++ b/airflow-core/tests/unit/cli/commands/test_command_deprecations.py
@@ -76,6 +76,7 @@ MIGRATED_CLI_COMMANDS = [
(config_command.get_value, "airflowctl config get"),
(config_command.show_config, "airflowctl config list"),
(task_command.task_states_for_dag_run, "airflowctl tasks
states-for-dag-run"),
+ (task_command.task_clear, "airflowctl tasks clear"),
]
diff --git
a/airflow-ctl-tests/tests/airflowctl_tests/test_airflowctl_commands.py
b/airflow-ctl-tests/tests/airflowctl_tests/test_airflowctl_commands.py
index 87d9851cbf0..d2440f05d24 100644
--- a/airflow-ctl-tests/tests/airflowctl_tests/test_airflowctl_commands.py
+++ b/airflow-ctl-tests/tests/airflowctl_tests/test_airflowctl_commands.py
@@ -99,6 +99,7 @@ TEST_COMMANDS = [
# Tasks commands
'tasks states-for-dag-run example_bash_operator "manual__{date_param}"',
'tasks states-for-dag-run example_bash_operator --logical-date
"{date_param}"',
+ 'tasks clear example_bash_operator --dag-run-id "manual__{date_param}"
--task-ids runme_0 -o json',
# Task Instances commands
'taskinstances list example_bash_operator "manual__{date_param}"',
# XCom commands - need a Dag run with completed tasks
diff --git a/airflow-ctl/docs/images/command_hashes.txt
b/airflow-ctl/docs/images/command_hashes.txt
index 10e59c5959f..74b5ede7ce3 100644
--- a/airflow-ctl/docs/images/command_hashes.txt
+++ b/airflow-ctl/docs/images/command_hashes.txt
@@ -1,4 +1,4 @@
-main:0460d9c03248bee26207b20b05aa36b9
+main:2c358f5fc894541cab11854dcd67658a
assets:6419e20452692f577c4c6f570b74be0c
auth:d79e9c7d00c432bdbcbc2a86e2e32053
backfill:74c8737b0a62a86ed3605fa9e6165874
@@ -10,7 +10,7 @@ jobs:a5b644c5da8889443bb40ee10b599270
pools:19efe105b9515ab1926ebcaf0e028d71
providers:34502fe09dc0b8b0a13e7e46efdffda6
taskinstances:7e323968c0b585287c2a4ab4339aee1f
-tasks:089b19625c893d189b5fc02b3abd547e
+tasks:e2ed0ab7f67ebacf4be87b5f396ff782
variables:f8fc76d3d398b2780f4e97f7cd816646
version:31f4efdf8de0dbaaa4fac71ff7efecc3
plugins:4864fd8f356704bd2b3cd1aec3567e35
diff --git a/airflow-ctl/docs/images/output_main.svg
b/airflow-ctl/docs/images/output_main.svg
index 9c8cec5269b..2a39e7e0ce2 100644
--- a/airflow-ctl/docs/images/output_main.svg
+++ b/airflow-ctl/docs/images/output_main.svg
@@ -19,114 +19,114 @@
font-weight: 700;
}
- .terminal-3519450412-matrix {
+ .terminal-151424782-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-3519450412-title {
+ .terminal-151424782-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-3519450412-r1 { fill: #ff8700 }
-.terminal-3519450412-r2 { fill: #c5c8c6 }
-.terminal-3519450412-r3 { fill: #808080 }
-.terminal-3519450412-r4 { fill: #68a0b3 }
+ .terminal-151424782-r1 { fill: #ff8700 }
+.terminal-151424782-r2 { fill: #c5c8c6 }
+.terminal-151424782-r3 { fill: #808080 }
+.terminal-151424782-r4 { fill: #68a0b3 }
</style>
<defs>
- <clipPath id="terminal-3519450412-clip-terminal">
+ <clipPath id="terminal-151424782-clip-terminal">
<rect x="0" y="0" width="914.0" height="731.0" />
</clipPath>
- <clipPath id="terminal-3519450412-line-0">
+ <clipPath id="terminal-151424782-line-0">
<rect x="0" y="1.5" width="915" height="24.65"/>
</clipPath>
-<clipPath id="terminal-3519450412-line-1">
+<clipPath id="terminal-151424782-line-1">
<rect x="0" y="25.9" width="915" height="24.65"/>
</clipPath>
-<clipPath id="terminal-3519450412-line-2">
+<clipPath id="terminal-151424782-line-2">
<rect x="0" y="50.3" width="915" height="24.65"/>
</clipPath>
-<clipPath id="terminal-3519450412-line-3">
+<clipPath id="terminal-151424782-line-3">
<rect x="0" y="74.7" width="915" height="24.65"/>
</clipPath>
-<clipPath id="terminal-3519450412-line-4">
+<clipPath id="terminal-151424782-line-4">
<rect x="0" y="99.1" width="915" height="24.65"/>
</clipPath>
-<clipPath id="terminal-3519450412-line-5">
+<clipPath id="terminal-151424782-line-5">
<rect x="0" y="123.5" width="915" height="24.65"/>
</clipPath>
-<clipPath id="terminal-3519450412-line-6">
+<clipPath id="terminal-151424782-line-6">
<rect x="0" y="147.9" width="915" height="24.65"/>
</clipPath>
-<clipPath id="terminal-3519450412-line-7">
+<clipPath id="terminal-151424782-line-7">
<rect x="0" y="172.3" width="915" height="24.65"/>
</clipPath>
-<clipPath id="terminal-3519450412-line-8">
+<clipPath id="terminal-151424782-line-8">
<rect x="0" y="196.7" width="915" height="24.65"/>
</clipPath>
-<clipPath id="terminal-3519450412-line-9">
+<clipPath id="terminal-151424782-line-9">
<rect x="0" y="221.1" width="915" height="24.65"/>
</clipPath>
-<clipPath id="terminal-3519450412-line-10">
+<clipPath id="terminal-151424782-line-10">
<rect x="0" y="245.5" width="915" height="24.65"/>
</clipPath>
-<clipPath id="terminal-3519450412-line-11">
+<clipPath id="terminal-151424782-line-11">
<rect x="0" y="269.9" width="915" height="24.65"/>
</clipPath>
-<clipPath id="terminal-3519450412-line-12">
+<clipPath id="terminal-151424782-line-12">
<rect x="0" y="294.3" width="915" height="24.65"/>
</clipPath>
-<clipPath id="terminal-3519450412-line-13">
+<clipPath id="terminal-151424782-line-13">
<rect x="0" y="318.7" width="915" height="24.65"/>
</clipPath>
-<clipPath id="terminal-3519450412-line-14">
+<clipPath id="terminal-151424782-line-14">
<rect x="0" y="343.1" width="915" height="24.65"/>
</clipPath>
-<clipPath id="terminal-3519450412-line-15">
+<clipPath id="terminal-151424782-line-15">
<rect x="0" y="367.5" width="915" height="24.65"/>
</clipPath>
-<clipPath id="terminal-3519450412-line-16">
+<clipPath id="terminal-151424782-line-16">
<rect x="0" y="391.9" width="915" height="24.65"/>
</clipPath>
-<clipPath id="terminal-3519450412-line-17">
+<clipPath id="terminal-151424782-line-17">
<rect x="0" y="416.3" width="915" height="24.65"/>
</clipPath>
-<clipPath id="terminal-3519450412-line-18">
+<clipPath id="terminal-151424782-line-18">
<rect x="0" y="440.7" width="915" height="24.65"/>
</clipPath>
-<clipPath id="terminal-3519450412-line-19">
+<clipPath id="terminal-151424782-line-19">
<rect x="0" y="465.1" width="915" height="24.65"/>
</clipPath>
-<clipPath id="terminal-3519450412-line-20">
+<clipPath id="terminal-151424782-line-20">
<rect x="0" y="489.5" width="915" height="24.65"/>
</clipPath>
-<clipPath id="terminal-3519450412-line-21">
+<clipPath id="terminal-151424782-line-21">
<rect x="0" y="513.9" width="915" height="24.65"/>
</clipPath>
-<clipPath id="terminal-3519450412-line-22">
+<clipPath id="terminal-151424782-line-22">
<rect x="0" y="538.3" width="915" height="24.65"/>
</clipPath>
-<clipPath id="terminal-3519450412-line-23">
+<clipPath id="terminal-151424782-line-23">
<rect x="0" y="562.7" width="915" height="24.65"/>
</clipPath>
-<clipPath id="terminal-3519450412-line-24">
+<clipPath id="terminal-151424782-line-24">
<rect x="0" y="587.1" width="915" height="24.65"/>
</clipPath>
-<clipPath id="terminal-3519450412-line-25">
+<clipPath id="terminal-151424782-line-25">
<rect x="0" y="611.5" width="915" height="24.65"/>
</clipPath>
-<clipPath id="terminal-3519450412-line-26">
+<clipPath id="terminal-151424782-line-26">
<rect x="0" y="635.9" width="915" height="24.65"/>
</clipPath>
-<clipPath id="terminal-3519450412-line-27">
+<clipPath id="terminal-151424782-line-27">
<rect x="0" y="660.3" width="915" height="24.65"/>
</clipPath>
-<clipPath id="terminal-3519450412-line-28">
+<clipPath id="terminal-151424782-line-28">
<rect x="0" y="684.7" width="915" height="24.65"/>
</clipPath>
</defs>
@@ -138,39 +138,39 @@
<circle cx="44" cy="0" r="7" fill="#28c840"/>
</g>
- <g transform="translate(9, 41)"
clip-path="url(#terminal-3519450412-clip-terminal)">
+ <g transform="translate(9, 41)"
clip-path="url(#terminal-151424782-clip-terminal)">
- <g class="terminal-3519450412-matrix">
- <text class="terminal-3519450412-r1" x="0" y="20" textLength="73.2"
clip-path="url(#terminal-3519450412-line-0)">Usage:</text><text
class="terminal-3519450412-r3" x="85.4" y="20" textLength="122"
clip-path="url(#terminal-3519450412-line-0)">airflowctl</text><text
class="terminal-3519450412-r2" x="207.4" y="20" textLength="24.4"
clip-path="url(#terminal-3519450412-line-0)"> [</text><text
class="terminal-3519450412-r4" x="231.8" y="20" textLength="24.4"
clip-path="url(#terminal-35 [...]
-</text><text class="terminal-3519450412-r2" x="915" y="44.4" textLength="12.2"
clip-path="url(#terminal-3519450412-line-1)">
-</text><text class="terminal-3519450412-r1" x="0" y="68.8" textLength="256.2"
clip-path="url(#terminal-3519450412-line-2)">Positional Arguments:</text><text
class="terminal-3519450412-r2" x="915" y="68.8" textLength="12.2"
clip-path="url(#terminal-3519450412-line-2)">
-</text><text class="terminal-3519450412-r4" x="24.4" y="93.2"
textLength="195.2"
clip-path="url(#terminal-3519450412-line-3)">GROUP_OR_COMMAND</text><text
class="terminal-3519450412-r2" x="915" y="93.2" textLength="12.2"
clip-path="url(#terminal-3519450412-line-3)">
-</text><text class="terminal-3519450412-r2" x="915" y="117.6"
textLength="12.2" clip-path="url(#terminal-3519450412-line-4)">
-</text><text class="terminal-3519450412-r4" x="0" y="142" textLength="122"
clip-path="url(#terminal-3519450412-line-5)">    Groups</text><text
class="terminal-3519450412-r2" x="915" y="142" textLength="12.2"
clip-path="url(#terminal-3519450412-line-5)">
-</text><text class="terminal-3519450412-r4" x="73.2" y="166.4"
textLength="73.2"
clip-path="url(#terminal-3519450412-line-6)">assets</text><text
class="terminal-3519450412-r2" x="244" y="166.4" textLength="305"
clip-path="url(#terminal-3519450412-line-6)">Perform Assets operations</text><text
class="terminal-3519450412-r2" x="915" y="166.4" textLength="12.2"
clip-path="url(#terminal-3519450412-line-6)">
-</text><text class="terminal-3519450412-r4" x="73.2" y="190.8"
textLength="48.8" clip-path="url(#terminal-3519450412-line-7)">auth</text><text
class="terminal-3519450412-r2" x="244" y="190.8" textLength="646.6"
clip-path="url(#terminal-3519450412-line-7)">Manage authentication for CLI. Either pass token from</text><text
class="terminal-3519450412-r2" x="915" y="190.8" textLength="12.2"
clip-path="url(#terminal-3519450412-line-7)">
-</text><text class="terminal-3519450412-r2" x="244" y="215.2"
textLength="622.2"
clip-path="url(#terminal-3519450412-line-8)">environment variable/parameter or pass username and</text><text
class="terminal-3519450412-r2" x="915" y="215.2" textLength="12.2"
clip-path="url(#terminal-3519450412-line-8)">
-</text><text class="terminal-3519450412-r2" x="244" y="239.6"
textLength="109.8"
clip-path="url(#terminal-3519450412-line-9)">password.</text><text
class="terminal-3519450412-r2" x="915" y="239.6" textLength="12.2"
clip-path="url(#terminal-3519450412-line-9)">
-</text><text class="terminal-3519450412-r4" x="73.2" y="264" textLength="97.6"
clip-path="url(#terminal-3519450412-line-10)">backfill</text><text
class="terminal-3519450412-r2" x="244" y="264" textLength="329.4"
clip-path="url(#terminal-3519450412-line-10)">Perform Backfill operations</text><text
class="terminal-3519450412-r2" x="915" y="264" textLength="12.2"
clip-path="url(#terminal-3519450412-line-10)">
-</text><text class="terminal-3519450412-r4" x="73.2" y="288.4"
textLength="73.2"
clip-path="url(#terminal-3519450412-line-11)">config</text><text
class="terminal-3519450412-r2" x="244" y="288.4" textLength="305"
clip-path="url(#terminal-3519450412-line-11)">Perform Config operations</text><text
class="terminal-3519450412-r2" x="915" y="288.4" textLength="12.2"
clip-path="url(#terminal-3519450412-line-11)">
-</text><text class="terminal-3519450412-r4" x="73.2" y="312.8"
textLength="134.2"
clip-path="url(#terminal-3519450412-line-12)">connections</text><text
class="terminal-3519450412-r2" x="244" y="312.8" textLength="366"
clip-path="url(#terminal-3519450412-line-12)">Perform Connections operations</text><text
class="terminal-3519450412-r2" x="915" y="312.8" textLength="12.2"
clip-path="url(#terminal-3519450412-line-12)">
-</text><text class="terminal-3519450412-r4" x="73.2" y="337.2"
textLength="73.2"
clip-path="url(#terminal-3519450412-line-13)">dagrun</text><text
class="terminal-3519450412-r2" x="244" y="337.2" textLength="305"
clip-path="url(#terminal-3519450412-line-13)">Perform DagRun operations</text><text
class="terminal-3519450412-r2" x="915" y="337.2" textLength="12.2"
clip-path="url(#terminal-3519450412-line-13)">
-</text><text class="terminal-3519450412-r4" x="73.2" y="361.6"
textLength="48.8"
clip-path="url(#terminal-3519450412-line-14)">dags</text><text
class="terminal-3519450412-r2" x="244" y="361.6" textLength="280.6"
clip-path="url(#terminal-3519450412-line-14)">Perform Dags operations</text><text
class="terminal-3519450412-r2" x="915" y="361.6" textLength="12.2"
clip-path="url(#terminal-3519450412-line-14)">
-</text><text class="terminal-3519450412-r4" x="73.2" y="386" textLength="48.8"
clip-path="url(#terminal-3519450412-line-15)">jobs</text><text
class="terminal-3519450412-r2" x="244" y="386" textLength="280.6"
clip-path="url(#terminal-3519450412-line-15)">Perform Jobs operations</text><text
class="terminal-3519450412-r2" x="915" y="386" textLength="12.2"
clip-path="url(#terminal-3519450412-line-15)">
-</text><text class="terminal-3519450412-r4" x="73.2" y="410.4"
textLength="85.4"
clip-path="url(#terminal-3519450412-line-16)">plugins</text><text
class="terminal-3519450412-r2" x="244" y="410.4" textLength="317.2"
clip-path="url(#terminal-3519450412-line-16)">Perform Plugins operations</text><text
class="terminal-3519450412-r2" x="915" y="410.4" textLength="12.2"
clip-path="url(#terminal-3519450412-line-16)">
-</text><text class="terminal-3519450412-r4" x="73.2" y="434.8" textLength="61"
clip-path="url(#terminal-3519450412-line-17)">pools</text><text
class="terminal-3519450412-r2" x="244" y="434.8" textLength="292.8"
clip-path="url(#terminal-3519450412-line-17)">Perform Pools operations</text><text
class="terminal-3519450412-r2" x="915" y="434.8" textLength="12.2"
clip-path="url(#terminal-3519450412-line-17)">
-</text><text class="terminal-3519450412-r4" x="73.2" y="459.2"
textLength="109.8"
clip-path="url(#terminal-3519450412-line-18)">providers</text><text
class="terminal-3519450412-r2" x="244" y="459.2" textLength="341.6"
clip-path="url(#terminal-3519450412-line-18)">Perform Providers operations</text><text
class="terminal-3519450412-r2" x="915" y="459.2" textLength="12.2"
clip-path="url(#terminal-3519450412-line-18)">
-</text><text class="terminal-3519450412-r4" x="73.2" y="483.6"
textLength="158.6"
clip-path="url(#terminal-3519450412-line-19)">taskinstances</text><text
class="terminal-3519450412-r2" x="915" y="483.6" textLength="12.2"
clip-path="url(#terminal-3519450412-line-19)">
-</text><text class="terminal-3519450412-r2" x="244" y="508" textLength="390.4"
clip-path="url(#terminal-3519450412-line-20)">Perform TaskInstances operations</text><text
class="terminal-3519450412-r2" x="915" y="508" textLength="12.2"
clip-path="url(#terminal-3519450412-line-20)">
-</text><text class="terminal-3519450412-r4" x="73.2" y="532.4" textLength="61"
clip-path="url(#terminal-3519450412-line-21)">tasks</text><text
class="terminal-3519450412-r2" x="244" y="532.4" textLength="244"
clip-path="url(#terminal-3519450412-line-21)">Manage Airflow tasks</text><text
class="terminal-3519450412-r2" x="915" y="532.4" textLength="12.2"
clip-path="url(#terminal-3519450412-line-21)">
-</text><text class="terminal-3519450412-r4" x="73.2" y="556.8"
textLength="109.8"
clip-path="url(#terminal-3519450412-line-22)">variables</text><text
class="terminal-3519450412-r2" x="244" y="556.8" textLength="341.6"
clip-path="url(#terminal-3519450412-line-22)">Perform Variables operations</text><text
class="terminal-3519450412-r2" x="915" y="556.8" textLength="12.2"
clip-path="url(#terminal-3519450412-line-22)">
-</text><text class="terminal-3519450412-r4" x="73.2" y="581.2"
textLength="48.8"
clip-path="url(#terminal-3519450412-line-23)">xcom</text><text
class="terminal-3519450412-r2" x="244" y="581.2" textLength="280.6"
clip-path="url(#terminal-3519450412-line-23)">Perform XCom operations</text><text
class="terminal-3519450412-r2" x="915" y="581.2" textLength="12.2"
clip-path="url(#terminal-3519450412-line-23)">
-</text><text class="terminal-3519450412-r2" x="915" y="605.6"
textLength="12.2" clip-path="url(#terminal-3519450412-line-24)">
-</text><text class="terminal-3519450412-r4" x="0" y="630" textLength="158.6"
clip-path="url(#terminal-3519450412-line-25)">    Commands:</text><text
class="terminal-3519450412-r2" x="915" y="630" textLength="12.2"
clip-path="url(#terminal-3519450412-line-25)">
-</text><text class="terminal-3519450412-r4" x="73.2" y="654.4"
textLength="85.4"
clip-path="url(#terminal-3519450412-line-26)">version</text><text
class="terminal-3519450412-r2" x="244" y="654.4" textLength="292.8"
clip-path="url(#terminal-3519450412-line-26)">Show version information</text><text
class="terminal-3519450412-r2" x="915" y="654.4" textLength="12.2"
clip-path="url(#terminal-3519450412-line-26)">
-</text><text class="terminal-3519450412-r2" x="915" y="678.8"
textLength="12.2" clip-path="url(#terminal-3519450412-line-27)">
-</text><text class="terminal-3519450412-r1" x="0" y="703.2" textLength="97.6"
clip-path="url(#terminal-3519450412-line-28)">Options:</text><text
class="terminal-3519450412-r2" x="915" y="703.2" textLength="12.2"
clip-path="url(#terminal-3519450412-line-28)">
-</text><text class="terminal-3519450412-r4" x="24.4" y="727.6"
textLength="24.4" clip-path="url(#terminal-3519450412-line-29)">-h</text><text
class="terminal-3519450412-r2" x="48.8" y="727.6" textLength="24.4"
clip-path="url(#terminal-3519450412-line-29)">, </text><text
class="terminal-3519450412-r4" x="73.2" y="727.6" textLength="73.2"
clip-path="url(#terminal-3519450412-line-29)">--help</text><text
class="terminal-3519450412-r2" x="244" y="727.6" textLength="378.2"
clip-path="url( [...]
+ <g class="terminal-151424782-matrix">
+ <text class="terminal-151424782-r1" x="0" y="20" textLength="73.2"
clip-path="url(#terminal-151424782-line-0)">Usage:</text><text
class="terminal-151424782-r3" x="85.4" y="20" textLength="122"
clip-path="url(#terminal-151424782-line-0)">airflowctl</text><text
class="terminal-151424782-r2" x="207.4" y="20" textLength="24.4"
clip-path="url(#terminal-151424782-line-0)"> [</text><text
class="terminal-151424782-r4" x="231.8" y="20" textLength="24.4"
clip-path="url(#terminal-151424782 [...]
+</text><text class="terminal-151424782-r2" x="915" y="44.4" textLength="12.2"
clip-path="url(#terminal-151424782-line-1)">
+</text><text class="terminal-151424782-r1" x="0" y="68.8" textLength="256.2"
clip-path="url(#terminal-151424782-line-2)">Positional Arguments:</text><text
class="terminal-151424782-r2" x="915" y="68.8" textLength="12.2"
clip-path="url(#terminal-151424782-line-2)">
+</text><text class="terminal-151424782-r4" x="24.4" y="93.2"
textLength="195.2"
clip-path="url(#terminal-151424782-line-3)">GROUP_OR_COMMAND</text><text
class="terminal-151424782-r2" x="915" y="93.2" textLength="12.2"
clip-path="url(#terminal-151424782-line-3)">
+</text><text class="terminal-151424782-r2" x="915" y="117.6" textLength="12.2"
clip-path="url(#terminal-151424782-line-4)">
+</text><text class="terminal-151424782-r4" x="0" y="142" textLength="122"
clip-path="url(#terminal-151424782-line-5)">    Groups</text><text
class="terminal-151424782-r2" x="915" y="142" textLength="12.2"
clip-path="url(#terminal-151424782-line-5)">
+</text><text class="terminal-151424782-r4" x="73.2" y="166.4"
textLength="73.2"
clip-path="url(#terminal-151424782-line-6)">assets</text><text
class="terminal-151424782-r2" x="244" y="166.4" textLength="305"
clip-path="url(#terminal-151424782-line-6)">Perform Assets operations</text><text
class="terminal-151424782-r2" x="915" y="166.4" textLength="12.2"
clip-path="url(#terminal-151424782-line-6)">
+</text><text class="terminal-151424782-r4" x="73.2" y="190.8"
textLength="48.8" clip-path="url(#terminal-151424782-line-7)">auth</text><text
class="terminal-151424782-r2" x="244" y="190.8" textLength="646.6"
clip-path="url(#terminal-151424782-line-7)">Manage authentication for CLI. Either pass token from</text><text
class="terminal-151424782-r2" x="915" y="190.8" textLength="12.2"
clip-path="url(#terminal-151424782-line-7)">
+</text><text class="terminal-151424782-r2" x="244" y="215.2"
textLength="622.2"
clip-path="url(#terminal-151424782-line-8)">environment variable/parameter or pass username and</text><text
class="terminal-151424782-r2" x="915" y="215.2" textLength="12.2"
clip-path="url(#terminal-151424782-line-8)">
+</text><text class="terminal-151424782-r2" x="244" y="239.6"
textLength="109.8"
clip-path="url(#terminal-151424782-line-9)">password.</text><text
class="terminal-151424782-r2" x="915" y="239.6" textLength="12.2"
clip-path="url(#terminal-151424782-line-9)">
+</text><text class="terminal-151424782-r4" x="73.2" y="264" textLength="97.6"
clip-path="url(#terminal-151424782-line-10)">backfill</text><text
class="terminal-151424782-r2" x="244" y="264" textLength="329.4"
clip-path="url(#terminal-151424782-line-10)">Perform Backfill operations</text><text
class="terminal-151424782-r2" x="915" y="264" textLength="12.2"
clip-path="url(#terminal-151424782-line-10)">
+</text><text class="terminal-151424782-r4" x="73.2" y="288.4"
textLength="73.2"
clip-path="url(#terminal-151424782-line-11)">config</text><text
class="terminal-151424782-r2" x="244" y="288.4" textLength="305"
clip-path="url(#terminal-151424782-line-11)">Perform Config operations</text><text
class="terminal-151424782-r2" x="915" y="288.4" textLength="12.2"
clip-path="url(#terminal-151424782-line-11)">
+</text><text class="terminal-151424782-r4" x="73.2" y="312.8"
textLength="134.2"
clip-path="url(#terminal-151424782-line-12)">connections</text><text
class="terminal-151424782-r2" x="244" y="312.8" textLength="366"
clip-path="url(#terminal-151424782-line-12)">Perform Connections operations</text><text
class="terminal-151424782-r2" x="915" y="312.8" textLength="12.2"
clip-path="url(#terminal-151424782-line-12)">
+</text><text class="terminal-151424782-r4" x="73.2" y="337.2"
textLength="73.2"
clip-path="url(#terminal-151424782-line-13)">dagrun</text><text
class="terminal-151424782-r2" x="244" y="337.2" textLength="305"
clip-path="url(#terminal-151424782-line-13)">Perform DagRun operations</text><text
class="terminal-151424782-r2" x="915" y="337.2" textLength="12.2"
clip-path="url(#terminal-151424782-line-13)">
+</text><text class="terminal-151424782-r4" x="73.2" y="361.6"
textLength="48.8" clip-path="url(#terminal-151424782-line-14)">dags</text><text
class="terminal-151424782-r2" x="244" y="361.6" textLength="280.6"
clip-path="url(#terminal-151424782-line-14)">Perform Dags operations</text><text
class="terminal-151424782-r2" x="915" y="361.6" textLength="12.2"
clip-path="url(#terminal-151424782-line-14)">
+</text><text class="terminal-151424782-r4" x="73.2" y="386" textLength="48.8"
clip-path="url(#terminal-151424782-line-15)">jobs</text><text
class="terminal-151424782-r2" x="244" y="386" textLength="280.6"
clip-path="url(#terminal-151424782-line-15)">Perform Jobs operations</text><text
class="terminal-151424782-r2" x="915" y="386" textLength="12.2"
clip-path="url(#terminal-151424782-line-15)">
+</text><text class="terminal-151424782-r4" x="73.2" y="410.4"
textLength="85.4"
clip-path="url(#terminal-151424782-line-16)">plugins</text><text
class="terminal-151424782-r2" x="244" y="410.4" textLength="317.2"
clip-path="url(#terminal-151424782-line-16)">Perform Plugins operations</text><text
class="terminal-151424782-r2" x="915" y="410.4" textLength="12.2"
clip-path="url(#terminal-151424782-line-16)">
+</text><text class="terminal-151424782-r4" x="73.2" y="434.8" textLength="61"
clip-path="url(#terminal-151424782-line-17)">pools</text><text
class="terminal-151424782-r2" x="244" y="434.8" textLength="292.8"
clip-path="url(#terminal-151424782-line-17)">Perform Pools operations</text><text
class="terminal-151424782-r2" x="915" y="434.8" textLength="12.2"
clip-path="url(#terminal-151424782-line-17)">
+</text><text class="terminal-151424782-r4" x="73.2" y="459.2"
textLength="109.8"
clip-path="url(#terminal-151424782-line-18)">providers</text><text
class="terminal-151424782-r2" x="244" y="459.2" textLength="341.6"
clip-path="url(#terminal-151424782-line-18)">Perform Providers operations</text><text
class="terminal-151424782-r2" x="915" y="459.2" textLength="12.2"
clip-path="url(#terminal-151424782-line-18)">
+</text><text class="terminal-151424782-r4" x="73.2" y="483.6"
textLength="158.6"
clip-path="url(#terminal-151424782-line-19)">taskinstances</text><text
class="terminal-151424782-r2" x="915" y="483.6" textLength="12.2"
clip-path="url(#terminal-151424782-line-19)">
+</text><text class="terminal-151424782-r2" x="244" y="508" textLength="390.4"
clip-path="url(#terminal-151424782-line-20)">Perform TaskInstances operations</text><text
class="terminal-151424782-r2" x="915" y="508" textLength="12.2"
clip-path="url(#terminal-151424782-line-20)">
+</text><text class="terminal-151424782-r4" x="73.2" y="532.4" textLength="61"
clip-path="url(#terminal-151424782-line-21)">tasks</text><text
class="terminal-151424782-r2" x="244" y="532.4" textLength="292.8"
clip-path="url(#terminal-151424782-line-21)">Perform Tasks operations</text><text
class="terminal-151424782-r2" x="915" y="532.4" textLength="12.2"
clip-path="url(#terminal-151424782-line-21)">
+</text><text class="terminal-151424782-r4" x="73.2" y="556.8"
textLength="109.8"
clip-path="url(#terminal-151424782-line-22)">variables</text><text
class="terminal-151424782-r2" x="244" y="556.8" textLength="341.6"
clip-path="url(#terminal-151424782-line-22)">Perform Variables operations</text><text
class="terminal-151424782-r2" x="915" y="556.8" textLength="12.2"
clip-path="url(#terminal-151424782-line-22)">
+</text><text class="terminal-151424782-r4" x="73.2" y="581.2"
textLength="48.8" clip-path="url(#terminal-151424782-line-23)">xcom</text><text
class="terminal-151424782-r2" x="244" y="581.2" textLength="280.6"
clip-path="url(#terminal-151424782-line-23)">Perform XCom operations</text><text
class="terminal-151424782-r2" x="915" y="581.2" textLength="12.2"
clip-path="url(#terminal-151424782-line-23)">
+</text><text class="terminal-151424782-r2" x="915" y="605.6" textLength="12.2"
clip-path="url(#terminal-151424782-line-24)">
+</text><text class="terminal-151424782-r4" x="0" y="630" textLength="158.6"
clip-path="url(#terminal-151424782-line-25)">    Commands:</text><text
class="terminal-151424782-r2" x="915" y="630" textLength="12.2"
clip-path="url(#terminal-151424782-line-25)">
+</text><text class="terminal-151424782-r4" x="73.2" y="654.4"
textLength="85.4"
clip-path="url(#terminal-151424782-line-26)">version</text><text
class="terminal-151424782-r2" x="244" y="654.4" textLength="292.8"
clip-path="url(#terminal-151424782-line-26)">Show version information</text><text
class="terminal-151424782-r2" x="915" y="654.4" textLength="12.2"
clip-path="url(#terminal-151424782-line-26)">
+</text><text class="terminal-151424782-r2" x="915" y="678.8" textLength="12.2"
clip-path="url(#terminal-151424782-line-27)">
+</text><text class="terminal-151424782-r1" x="0" y="703.2" textLength="97.6"
clip-path="url(#terminal-151424782-line-28)">Options:</text><text
class="terminal-151424782-r2" x="915" y="703.2" textLength="12.2"
clip-path="url(#terminal-151424782-line-28)">
+</text><text class="terminal-151424782-r4" x="24.4" y="727.6"
textLength="24.4" clip-path="url(#terminal-151424782-line-29)">-h</text><text
class="terminal-151424782-r2" x="48.8" y="727.6" textLength="24.4"
clip-path="url(#terminal-151424782-line-29)">, </text><text
class="terminal-151424782-r4" x="73.2" y="727.6" textLength="73.2"
clip-path="url(#terminal-151424782-line-29)">--help</text><text
class="terminal-151424782-r2" x="244" y="727.6" textLength="378.2"
clip-path="url(#termin [...]
</text>
</g>
</g>
diff --git a/airflow-ctl/docs/images/output_tasks.svg
b/airflow-ctl/docs/images/output_tasks.svg
index d9349c11752..f3fa3c4d945 100644
--- a/airflow-ctl/docs/images/output_tasks.svg
+++ b/airflow-ctl/docs/images/output_tasks.svg
@@ -1,4 +1,4 @@
-<svg class="rich-terminal" viewBox="0 0 933 318.4"
xmlns="http://www.w3.org/2000/svg">
+<svg class="rich-terminal" viewBox="0 0 933 342.79999999999995"
xmlns="http://www.w3.org/2000/svg">
<!-- Generated with Rich https://www.textualize.io -->
<style>
@@ -19,82 +19,86 @@
font-weight: 700;
}
- .terminal-3141987423-matrix {
+ .terminal-177420400-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-3141987423-title {
+ .terminal-177420400-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-3141987423-r1 { fill: #ff8700 }
-.terminal-3141987423-r2 { fill: #c5c8c6 }
-.terminal-3141987423-r3 { fill: #808080 }
-.terminal-3141987423-r4 { fill: #68a0b3 }
+ .terminal-177420400-r1 { fill: #ff8700 }
+.terminal-177420400-r2 { fill: #c5c8c6 }
+.terminal-177420400-r3 { fill: #808080 }
+.terminal-177420400-r4 { fill: #68a0b3 }
</style>
<defs>
- <clipPath id="terminal-3141987423-clip-terminal">
- <rect x="0" y="0" width="914.0" height="267.4" />
+ <clipPath id="terminal-177420400-clip-terminal">
+ <rect x="0" y="0" width="914.0" height="291.79999999999995" />
</clipPath>
- <clipPath id="terminal-3141987423-line-0">
+ <clipPath id="terminal-177420400-line-0">
<rect x="0" y="1.5" width="915" height="24.65"/>
</clipPath>
-<clipPath id="terminal-3141987423-line-1">
+<clipPath id="terminal-177420400-line-1">
<rect x="0" y="25.9" width="915" height="24.65"/>
</clipPath>
-<clipPath id="terminal-3141987423-line-2">
+<clipPath id="terminal-177420400-line-2">
<rect x="0" y="50.3" width="915" height="24.65"/>
</clipPath>
-<clipPath id="terminal-3141987423-line-3">
+<clipPath id="terminal-177420400-line-3">
<rect x="0" y="74.7" width="915" height="24.65"/>
</clipPath>
-<clipPath id="terminal-3141987423-line-4">
+<clipPath id="terminal-177420400-line-4">
<rect x="0" y="99.1" width="915" height="24.65"/>
</clipPath>
-<clipPath id="terminal-3141987423-line-5">
+<clipPath id="terminal-177420400-line-5">
<rect x="0" y="123.5" width="915" height="24.65"/>
</clipPath>
-<clipPath id="terminal-3141987423-line-6">
+<clipPath id="terminal-177420400-line-6">
<rect x="0" y="147.9" width="915" height="24.65"/>
</clipPath>
-<clipPath id="terminal-3141987423-line-7">
+<clipPath id="terminal-177420400-line-7">
<rect x="0" y="172.3" width="915" height="24.65"/>
</clipPath>
-<clipPath id="terminal-3141987423-line-8">
+<clipPath id="terminal-177420400-line-8">
<rect x="0" y="196.7" width="915" height="24.65"/>
</clipPath>
-<clipPath id="terminal-3141987423-line-9">
+<clipPath id="terminal-177420400-line-9">
<rect x="0" y="221.1" width="915" height="24.65"/>
</clipPath>
+<clipPath id="terminal-177420400-line-10">
+ <rect x="0" y="245.5" width="915" height="24.65"/>
+ </clipPath>
</defs>
- <rect fill="#292929" stroke="rgba(255,255,255,0.35)" stroke-width="1"
x="1" y="1" width="931" height="316.4" rx="8"/>
+ <rect fill="#292929" stroke="rgba(255,255,255,0.35)" stroke-width="1"
x="1" y="1" width="931" height="340.8" rx="8"/>
<g transform="translate(26,22)">
<circle cx="0" cy="0" r="7" fill="#ff5f57"/>
<circle cx="22" cy="0" r="7" fill="#febc2e"/>
<circle cx="44" cy="0" r="7" fill="#28c840"/>
</g>
- <g transform="translate(9, 41)"
clip-path="url(#terminal-3141987423-clip-terminal)">
+ <g transform="translate(9, 41)"
clip-path="url(#terminal-177420400-clip-terminal)">
- <g class="terminal-3141987423-matrix">
- <text class="terminal-3141987423-r1" x="0" y="20" textLength="73.2"
clip-path="url(#terminal-3141987423-line-0)">Usage:</text><text
class="terminal-3141987423-r3" x="85.4" y="20" textLength="195.2"
clip-path="url(#terminal-3141987423-line-0)">airflowctl tasks</text><text
class="terminal-3141987423-r2" x="280.6" y="20" textLength="24.4"
clip-path="url(#terminal-3141987423-line-0)"> [</text><text
class="terminal-3141987423-r4" x="305" y="20" textLength="24.4"
clip-path="url(# [...]
-</text><text class="terminal-3141987423-r2" x="915" y="44.4" textLength="12.2"
clip-path="url(#terminal-3141987423-line-1)">
-</text><text class="terminal-3141987423-r2" x="0" y="68.8" textLength="244"
clip-path="url(#terminal-3141987423-line-2)">Manage Airflow tasks</text><text
class="terminal-3141987423-r2" x="915" y="68.8" textLength="12.2"
clip-path="url(#terminal-3141987423-line-2)">
-</text><text class="terminal-3141987423-r2" x="915" y="93.2" textLength="12.2"
clip-path="url(#terminal-3141987423-line-3)">
-</text><text class="terminal-3141987423-r1" x="0" y="117.6" textLength="256.2"
clip-path="url(#terminal-3141987423-line-4)">Positional Arguments:</text><text
class="terminal-3141987423-r2" x="915" y="117.6" textLength="12.2"
clip-path="url(#terminal-3141987423-line-4)">
-</text><text class="terminal-3141987423-r4" x="24.4" y="142" textLength="85.4"
clip-path="url(#terminal-3141987423-line-5)">COMMAND</text><text
class="terminal-3141987423-r2" x="915" y="142" textLength="12.2"
clip-path="url(#terminal-3141987423-line-5)">
-</text><text class="terminal-3141987423-r4" x="48.8" y="166.4"
textLength="219.6"
clip-path="url(#terminal-3141987423-line-6)">states-for-dag-run</text><text
class="terminal-3141987423-r2" x="915" y="166.4" textLength="12.2"
clip-path="url(#terminal-3141987423-line-6)">
-</text><text class="terminal-3141987423-r2" x="268.4" y="190.8"
textLength="597.8"
clip-path="url(#terminal-3141987423-line-7)">Get the status of all task instances in a Dag run</text><text
class="terminal-3141987423-r2" x="915" y="190.8" textLength="12.2"
clip-path="url(#terminal-3141987423-line-7)">
-</text><text class="terminal-3141987423-r2" x="915" y="215.2"
textLength="12.2" clip-path="url(#terminal-3141987423-line-8)">
-</text><text class="terminal-3141987423-r1" x="0" y="239.6" textLength="97.6"
clip-path="url(#terminal-3141987423-line-9)">Options:</text><text
class="terminal-3141987423-r2" x="915" y="239.6" textLength="12.2"
clip-path="url(#terminal-3141987423-line-9)">
-</text><text class="terminal-3141987423-r4" x="24.4" y="264" textLength="24.4"
clip-path="url(#terminal-3141987423-line-10)">-h</text><text
class="terminal-3141987423-r2" x="48.8" y="264" textLength="24.4"
clip-path="url(#terminal-3141987423-line-10)">, </text><text
class="terminal-3141987423-r4" x="73.2" y="264" textLength="73.2"
clip-path="url(#terminal-3141987423-line-10)">--help</text><text
class="terminal-3141987423-r2" x="268.4" y="264" textLength="378.2"
clip-path="url(#termi [...]
+ <g class="terminal-177420400-matrix">
+ <text class="terminal-177420400-r1" x="0" y="20" textLength="73.2"
clip-path="url(#terminal-177420400-line-0)">Usage:</text><text
class="terminal-177420400-r3" x="85.4" y="20" textLength="195.2"
clip-path="url(#terminal-177420400-line-0)">airflowctl tasks</text><text
class="terminal-177420400-r2" x="280.6" y="20" textLength="24.4"
clip-path="url(#terminal-177420400-line-0)"> [</text><text
class="terminal-177420400-r4" x="305" y="20" textLength="24.4"
clip-path="url(#termina [...]
+</text><text class="terminal-177420400-r2" x="915" y="44.4" textLength="12.2"
clip-path="url(#terminal-177420400-line-1)">
+</text><text class="terminal-177420400-r2" x="0" y="68.8" textLength="292.8"
clip-path="url(#terminal-177420400-line-2)">Perform Tasks operations</text><text
class="terminal-177420400-r2" x="915" y="68.8" textLength="12.2"
clip-path="url(#terminal-177420400-line-2)">
+</text><text class="terminal-177420400-r2" x="915" y="93.2" textLength="12.2"
clip-path="url(#terminal-177420400-line-3)">
+</text><text class="terminal-177420400-r1" x="0" y="117.6" textLength="256.2"
clip-path="url(#terminal-177420400-line-4)">Positional Arguments:</text><text
class="terminal-177420400-r2" x="915" y="117.6" textLength="12.2"
clip-path="url(#terminal-177420400-line-4)">
+</text><text class="terminal-177420400-r4" x="24.4" y="142" textLength="85.4"
clip-path="url(#terminal-177420400-line-5)">COMMAND</text><text
class="terminal-177420400-r2" x="915" y="142" textLength="12.2"
clip-path="url(#terminal-177420400-line-5)">
+</text><text class="terminal-177420400-r4" x="48.8" y="166.4" textLength="61"
clip-path="url(#terminal-177420400-line-6)">clear</text><text
class="terminal-177420400-r2" x="268.4" y="166.4" textLength="475.8"
clip-path="url(#terminal-177420400-line-6)">Clear task instances of a Dag by its ID</text><text
class="terminal-177420400-r2" x="915" y="166.4" textLength="12.2"
clip-path="url(#terminal-177420400-line-6)">
+</text><text class="terminal-177420400-r4" x="48.8" y="190.8"
textLength="219.6"
clip-path="url(#terminal-177420400-line-7)">states-for-dag-run</text><text
class="terminal-177420400-r2" x="915" y="190.8" textLength="12.2"
clip-path="url(#terminal-177420400-line-7)">
+</text><text class="terminal-177420400-r2" x="268.4" y="215.2"
textLength="597.8"
clip-path="url(#terminal-177420400-line-8)">Get the status of all task instances in a Dag run</text><text
class="terminal-177420400-r2" x="915" y="215.2" textLength="12.2"
clip-path="url(#terminal-177420400-line-8)">
+</text><text class="terminal-177420400-r2" x="915" y="239.6" textLength="12.2"
clip-path="url(#terminal-177420400-line-9)">
+</text><text class="terminal-177420400-r1" x="0" y="264" textLength="97.6"
clip-path="url(#terminal-177420400-line-10)">Options:</text><text
class="terminal-177420400-r2" x="915" y="264" textLength="12.2"
clip-path="url(#terminal-177420400-line-10)">
+</text><text class="terminal-177420400-r4" x="24.4" y="288.4"
textLength="24.4" clip-path="url(#terminal-177420400-line-11)">-h</text><text
class="terminal-177420400-r2" x="48.8" y="288.4" textLength="24.4"
clip-path="url(#terminal-177420400-line-11)">, </text><text
class="terminal-177420400-r4" x="73.2" y="288.4" textLength="73.2"
clip-path="url(#terminal-177420400-line-11)">--help</text><text
class="terminal-177420400-r2" x="268.4" y="288.4" textLength="378.2"
clip-path="url(#term [...]
</text>
</g>
</g>
diff --git a/airflow-ctl/src/airflowctl/api/client.py
b/airflow-ctl/src/airflowctl/api/client.py
index 7baf87684dd..65f90302868 100644
--- a/airflow-ctl/src/airflowctl/api/client.py
+++ b/airflow-ctl/src/airflowctl/api/client.py
@@ -58,6 +58,7 @@ from airflowctl.api.operations import (
ProvidersOperations,
ServerResponseError,
TaskInstancesOperations,
+ TasksOperations,
VariablesOperations,
VersionOperations,
XComOperations,
@@ -457,6 +458,12 @@ class Client(httpx.Client):
"""Operations related to task instances."""
return TaskInstancesOperations(self)
+ @lru_cache() # type: ignore[prop-decorator]
+ @property
+ def tasks(self):
+ """Operations related to tasks."""
+ return TasksOperations(self)
+
@lru_cache() # type: ignore[prop-decorator]
@property
def variables(self):
diff --git a/airflow-ctl/src/airflowctl/api/operations.py
b/airflow-ctl/src/airflowctl/api/operations.py
index 44bfa30125f..5684dfac840 100644
--- a/airflow-ctl/src/airflowctl/api/operations.py
+++ b/airflow-ctl/src/airflowctl/api/operations.py
@@ -39,6 +39,7 @@ from airflowctl.api.datamodels.generated import (
BulkBodyPoolBody,
BulkBodyVariableBody,
BulkResponse,
+ ClearTaskInstancesBody,
Config,
ConnectionBody,
ConnectionCollectionResponse,
@@ -787,6 +788,23 @@ class TaskInstancesOperations(BaseOperations):
)
+class TasksOperations(BaseOperations):
+ """Tasks operations."""
+
+ def clear(
+ self, dag_id: str, clear_task_instances: ClearTaskInstancesBody
+ ) -> TaskInstanceCollectionResponse | ServerResponseError:
+ """Clear task instances of a Dag; with dry_run (the default) only
previews the affected task instances."""
+ try:
+ self.response = self.client.post(
+ f"dags/{dag_id}/clearTaskInstances",
+ json=clear_task_instances.model_dump(mode="json",
exclude_none=True),
+ )
+ return
TaskInstanceCollectionResponse.model_validate_json(self.response.content)
+ except ServerResponseError as e:
+ raise e
+
+
class VariablesOperations(BaseOperations):
"""Variable operations."""
diff --git a/airflow-ctl/src/airflowctl/ctl/cli_config.py
b/airflow-ctl/src/airflowctl/ctl/cli_config.py
index 47270d9c29c..fbaff969cca 100755
--- a/airflow-ctl/src/airflowctl/ctl/cli_config.py
+++ b/airflow-ctl/src/airflowctl/ctl/cli_config.py
@@ -426,7 +426,21 @@ class CommandFactory:
# Exclude parameters that are not needed for CLI from datamodels
self.excluded_parameters = ["schema_"]
# This list is used to determine if the command/operation needs to
output data
- self.output_command_list = ["list", "get", "create", "delete",
"update", "trigger", "add", "edit"]
+ self.output_command_list = [
+ "list",
+ "get",
+ "create",
+ "delete",
+ "update",
+ "trigger",
+ "add",
+ "edit",
+ "clear",
+ ]
+ # Datamodels whose generated bool flags follow the datamodel field
defaults instead of
+ # defaulting to False, so the CLI keeps the API semantics (e.g. a bare
``tasks clear``
+ # must keep ``dry_run=True`` and preview instead of clearing).
+ self.field_bool_default_datamodels = ["ClearTaskInstancesBody"]
self.exclude_operation_names = ["LoginOperations",
"VersionOperations", "BaseOperations"]
self.exclude_method_names = [
"error",
@@ -593,6 +607,14 @@ class CommandFactory:
help=arg_help,
)
+ def _get_bool_arg_default(self, parameter_type: str, field_default: Any)
-> bool | None:
+ """Get default for a generated bool flag: the datamodel field default
for datamodels in ``field_bool_default_datamodels``, otherwise False."""
+ if parameter_type in self.field_bool_default_datamodels and (
+ field_default is None or isinstance(field_default, bool)
+ ):
+ return field_default
+ return False
+
def _create_arg_for_non_primitive_type(
self,
parameter_type: str,
@@ -608,30 +630,24 @@ class CommandFactory:
continue
self.datamodels_extended_map[parameter_type].append(field)
if type(field_type.annotation) is type:
- commands.append(
- self._create_arg(
- arg_flags=("--" +
self._sanitize_arg_parameter_key(field),),
-
arg_type=self._python_type_from_string(field_type.annotation),
- arg_action=argparse.BooleanOptionalAction if
field_type.annotation is bool else None, # type: ignore
- arg_help=f"{field} for {parameter_key} operation",
- arg_default=False if field_type.annotation is bool
else None,
- )
- )
+ annotation = field_type.annotation
else:
try:
annotation = field_type.annotation.__args__[0]
except AttributeError:
annotation = field_type.annotation
- commands.append(
- self._create_arg(
- arg_flags=("--" +
self._sanitize_arg_parameter_key(field),),
- arg_type=self._python_type_from_string(annotation),
- arg_action=argparse.BooleanOptionalAction if
annotation is bool else None, # type: ignore
- arg_help=f"{field} for {parameter_key} operation",
- arg_default=False if annotation is bool else None,
- )
+ commands.append(
+ self._create_arg(
+
arg_flags=(f"--{self._sanitize_arg_parameter_key(field)}",),
+ arg_type=self._python_type_from_string(annotation),
+ arg_action=argparse.BooleanOptionalAction if annotation is
bool else None, # type: ignore
+ arg_help=f"{field} for {parameter_key} operation",
+ arg_default=self._get_bool_arg_default(parameter_type,
field_type.default)
+ if annotation is bool
+ else None,
)
+ )
return commands
def _create_args_map_from_operation(self):
@@ -711,6 +727,20 @@ class CommandFactory:
):
params["logical_date"] =
datetime.datetime.now(datetime.timezone.utc)
+ # Handle ClearTaskInstancesBody: --task-ids arrives as a single string
but the API expects
+ # a list of task_id or [task_id, map_index]; accept comma-separated
ids or a JSON list
+ if datamodel.__name__ == "ClearTaskInstancesBody" and
isinstance(params.get("task_ids"), str):
+ raw_task_ids = params["task_ids"]
+ if raw_task_ids.lstrip().startswith("["):
+ try:
+ params["task_ids"] = json.loads(raw_task_ids)
+ except json.JSONDecodeError as e:
+ raise SystemExit(f"Invalid JSON list for --task-ids
{raw_task_ids!r}: {e}")
+ else:
+ params["task_ids"] = [
+ task_id.strip() for task_id in raw_task_ids.split(",") if
task_id.strip()
+ ]
+
return params
def _create_func_map_from_operation(self):
diff --git a/airflow-ctl/src/airflowctl/ctl/help_texts.yaml
b/airflow-ctl/src/airflowctl/ctl/help_texts.yaml
index 1c9b23d3f94..9c2c591417b 100644
--- a/airflow-ctl/src/airflowctl/ctl/help_texts.yaml
+++ b/airflow-ctl/src/airflowctl/ctl/help_texts.yaml
@@ -88,6 +88,9 @@ providers:
taskinstances:
list: "List all task instances for a given Dag run"
+tasks:
+ clear: "Clear task instances of a Dag by its ID"
+
variables:
get: "Retrieve a variable by its key"
list: "List all variables"
diff --git a/airflow-ctl/tests/airflow_ctl/api/test_operations.py
b/airflow-ctl/tests/airflow_ctl/api/test_operations.py
index f988a6f329a..aaacdd68696 100644
--- a/airflow-ctl/tests/airflow_ctl/api/test_operations.py
+++ b/airflow-ctl/tests/airflow_ctl/api/test_operations.py
@@ -48,6 +48,7 @@ from airflowctl.api.datamodels.generated import (
BulkCreateActionPoolBody,
BulkCreateActionVariableBody,
BulkResponse,
+ ClearTaskInstancesBody,
Config,
ConfigOption,
ConfigSection,
@@ -1594,6 +1595,49 @@ class TestTaskInstancesOperations:
assert response == self.task_instance_collection_response
+class TestTasksOperations:
+ dag_id = "dag_id"
+ clear_task_instances = ClearTaskInstancesBody(
+ dry_run=True,
+ task_ids=["task_1"],
+ dag_run_id="dag_run_id",
+ )
+ task_instance_response = TaskInstanceResponse(
+ id=uuid.UUID("0195c8b0-8bb5-7fc1-9ac9-04c286c47b7f"),
+ task_id="task_1",
+ dag_id=dag_id,
+ dag_run_id="dag_run_id",
+ map_index=-1,
+ run_after=datetime.datetime(2025, 1, 1, 0, 0, 0),
+ try_number=1,
+ max_tries=2,
+ task_display_name="task_1",
+ dag_display_name="dag_display_name",
+ pool="default_pool",
+ pool_slots=1,
+ executor_config="{}",
+ )
+ task_instance_collection_response = TaskInstanceCollectionResponse(
+ task_instances=[task_instance_response],
+ total_entries=1,
+ )
+
+ def test_clear(self):
+ expected_body = self.clear_task_instances.model_dump(mode="json",
exclude_none=True)
+
+ def handle_request(request: httpx.Request) -> httpx.Response:
+ assert request.url.path ==
f"/api/v2/dags/{self.dag_id}/clearTaskInstances"
+ assert request.headers.get("content-type",
"").startswith("application/json")
+ assert json.loads(request.content.decode()) == expected_body
+ return httpx.Response(
+ 200,
json=json.loads(self.task_instance_collection_response.model_dump_json())
+ )
+
+ client = make_api_client(transport=httpx.MockTransport(handle_request))
+ response = client.tasks.clear(self.dag_id, self.clear_task_instances)
+ assert response == self.task_instance_collection_response
+
+
class TestVariablesOperations:
key = "key"
value = "val"
diff --git a/airflow-ctl/tests/airflow_ctl/ctl/test_cli_config.py
b/airflow-ctl/tests/airflow_ctl/ctl/test_cli_config.py
index b843b3b7568..d568cacc098 100644
--- a/airflow-ctl/tests/airflow_ctl/ctl/test_cli_config.py
+++ b/airflow-ctl/tests/airflow_ctl/ctl/test_cli_config.py
@@ -25,6 +25,7 @@ from textwrap import dedent
import httpx
import pytest
+from airflowctl.api.datamodels.generated import ClearTaskInstancesBody
from airflowctl.api.operations import ServerResponseError
from airflowctl.ctl.cli_config import (
ARG_AUTH_TOKEN,
@@ -779,12 +780,60 @@ class TestCliConfigMethods:
# Should return params unchanged for other datamodels
assert result == params, "Params should be unchanged for
non-TriggerDAGRunPostBody datamodels"
+ def test_tasks_clear_args_follow_datamodel_defaults(self):
+ """Bool flags of ``tasks clear`` keep the ClearTaskInstancesBody
defaults so a bare invocation only dry-runs."""
+ command_factory = CommandFactory()
+ tasks_group = next(
+ group_command for group_command in command_factory.group_commands
if group_command.name == "tasks"
+ )
+ clear_command = next(
+ sub_command for sub_command in tasks_group.subcommands if
sub_command.name == "clear"
+ )
+ args_by_flag = {arg.flags[0]: arg for arg in clear_command.args}
+
+ assert "dag_id" in args_by_flag, "required path parameter should be
positional"
+ assert args_by_flag["--dry-run"].kwargs["action"] ==
BooleanOptionalAction
+ assert args_by_flag["--dry-run"].kwargs["default"] is True
+ assert args_by_flag["--only-failed"].kwargs["default"] is True
+ assert args_by_flag["--reset-dag-runs"].kwargs["default"] is True
+ assert args_by_flag["--only-running"].kwargs["default"] is False
+ assert args_by_flag["--run-on-latest-version"].kwargs["default"] is
None
+ assert args_by_flag["--task-ids"].kwargs["type"] is str
+ assert "--output" in args_by_flag
+
+ @pytest.mark.parametrize(
+ ("raw_task_ids", "expected_task_ids"),
+ [
+ ("task_1", ["task_1"]),
+ ("task_1,task_2", ["task_1", "task_2"]),
+ (" task_1 , task_2 ,", ["task_1", "task_2"]),
+ ('["task_1", ["mapped_task", 0]]', ["task_1", ["mapped_task", 0]]),
+ (None, None),
+ ],
+ )
+ def test_apply_datamodel_defaults_clear_task_instances_task_ids(self,
raw_task_ids, expected_task_ids):
+ """Test _apply_datamodel_defaults parses --task-ids strings for
ClearTaskInstancesBody."""
+ command_factory = CommandFactory()
+ result = command_factory._apply_datamodel_defaults(
+ ClearTaskInstancesBody, {"task_ids": raw_task_ids, "dry_run": True}
+ )
+
+ assert result["task_ids"] == expected_task_ids
+ assert result["dry_run"] is True
+
+ def
test_apply_datamodel_defaults_clear_task_instances_invalid_json_task_ids(self):
+ """Test _apply_datamodel_defaults rejects malformed JSON lists passed
to --task-ids."""
+ command_factory = CommandFactory()
+ with pytest.raises(SystemExit, match="Invalid JSON list for
--task-ids"):
+ command_factory._apply_datamodel_defaults(ClearTaskInstancesBody,
{"task_ids": '["oops'})
+
@pytest.mark.parametrize(
("group_name", "subcommand_name", "expected_help"),
[
("assets", "get", "Retrieve an asset by its ID"),
("connections", "get", "Retrieve a connection by its ID"),
("taskinstances", "list", "List all task instances for a given Dag
run"),
+ ("tasks", "clear", "Clear task instances of a Dag by its ID"),
],
)
def test_help_texts_used_for_auto_generated_commands(self, group_name,
subcommand_name, expected_help):