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

bugraoz93 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 16ad4794f5a Add dags next execution command #66172 (#66188)
16ad4794f5a is described below

commit 16ad4794f5a6c70480de7b137561f7f1bcdc2735
Author: Haseeb Malik <[email protected]>
AuthorDate: Sat May 23 16:05:49 2026 -0400

    Add dags next execution command #66172 (#66188)
    
    * Add airflowctl dags next-execution command #66172
    
    * Add generated OpenAPI spec and UI types
    
    * Revert "Add generated OpenAPI spec and UI types"
    
    This reverts commit 6748ed8c45a8cb73ca8c31e711557012db0a30fc.
    
    * Update help text Dag definition
    
    ---------
    
    Co-authored-by: bugraoz93 <[email protected]>
---
 .../airflowctl_tests/test_airflowctl_commands.py   |   1 +
 airflow-ctl/docs/images/command_hashes.txt         |   2 +-
 airflow-ctl/docs/images/output_dags.svg            | 126 +++++++++++----------
 airflow-ctl/src/airflowctl/ctl/cli_config.py       |   9 ++
 .../src/airflowctl/ctl/commands/dag_command.py     |  31 +++++
 .../airflow_ctl/ctl/commands/test_dag_command.py   |  72 ++++++++++++
 6 files changed, 179 insertions(+), 62 deletions(-)

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 b8b49ad3ae7..e1cfc665804 100644
--- a/airflow-ctl-tests/tests/airflowctl_tests/test_airflowctl_commands.py
+++ b/airflow-ctl-tests/tests/airflowctl_tests/test_airflowctl_commands.py
@@ -86,6 +86,7 @@ TEST_COMMANDS = [
     "dags trigger example_bash_operator --logical-date={date_param} 
--run-after={date_param}",
     # Test trigger without logical-date (should default to now)
     "dags trigger example_bash_operator",
+    "dags next-execution example_bash_operator",
     "dags pause example_bash_operator",
     "dags unpause example_bash_operator",
     # Dag Run commands
diff --git a/airflow-ctl/docs/images/command_hashes.txt 
b/airflow-ctl/docs/images/command_hashes.txt
index 8e590f3b820..53c93e7546d 100644
--- a/airflow-ctl/docs/images/command_hashes.txt
+++ b/airflow-ctl/docs/images/command_hashes.txt
@@ -4,7 +4,7 @@ auth:d79e9c7d00c432bdbcbc2a86e2e32053
 backfill:74c8737b0a62a86ed3605fa9e6165874
 config:a3d936cb15fe3b547bf6c82cf93d923f
 connections:942f9f88cb908c28bf5c19159fc5065b
-dags:e2a18f90b1bd150be981cef6fef91858
+dags:6b38e6bcd491bc1941e7814b77e63bde
 dagrun:c32e0011aa9a845456c778786717208e
 jobs:a5b644c5da8889443bb40ee10b599270
 pools:19efe105b9515ab1926ebcaf0e028d71
diff --git a/airflow-ctl/docs/images/output_dags.svg 
b/airflow-ctl/docs/images/output_dags.svg
index f38d12d60e3..7f95b26bdbf 100644
--- a/airflow-ctl/docs/images/output_dags.svg
+++ b/airflow-ctl/docs/images/output_dags.svg
@@ -1,4 +1,4 @@
-<svg class="rich-terminal" viewBox="0 0 933 660.0" 
xmlns="http://www.w3.org/2000/svg";>
+<svg class="rich-terminal" viewBox="0 0 933 684.4" 
xmlns="http://www.w3.org/2000/svg";>
     <!-- Generated with Rich https://www.textualize.io -->
     <style>
 
@@ -19,138 +19,142 @@
         font-weight: 700;
     }
 
-    .terminal-3628713872-matrix {
+    .terminal-323819653-matrix {
         font-family: Fira Code, monospace;
         font-size: 20px;
         line-height: 24.4px;
         font-variant-east-asian: full-width;
     }
 
-    .terminal-3628713872-title {
+    .terminal-323819653-title {
         font-size: 18px;
         font-weight: bold;
         font-family: arial;
     }
 
-    .terminal-3628713872-r1 { fill: #ff8700 }
-.terminal-3628713872-r2 { fill: #c5c8c6 }
-.terminal-3628713872-r3 { fill: #808080 }
-.terminal-3628713872-r4 { fill: #68a0b3 }
+    .terminal-323819653-r1 { fill: #ff8700 }
+.terminal-323819653-r2 { fill: #c5c8c6 }
+.terminal-323819653-r3 { fill: #808080 }
+.terminal-323819653-r4 { fill: #68a0b3 }
     </style>
 
     <defs>
-    <clipPath id="terminal-3628713872-clip-terminal">
-      <rect x="0" y="0" width="914.0" height="609.0" />
+    <clipPath id="terminal-323819653-clip-terminal">
+      <rect x="0" y="0" width="914.0" height="633.4" />
     </clipPath>
-    <clipPath id="terminal-3628713872-line-0">
+    <clipPath id="terminal-323819653-line-0">
     <rect x="0" y="1.5" width="915" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-3628713872-line-1">
+<clipPath id="terminal-323819653-line-1">
     <rect x="0" y="25.9" width="915" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-3628713872-line-2">
+<clipPath id="terminal-323819653-line-2">
     <rect x="0" y="50.3" width="915" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-3628713872-line-3">
+<clipPath id="terminal-323819653-line-3">
     <rect x="0" y="74.7" width="915" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-3628713872-line-4">
+<clipPath id="terminal-323819653-line-4">
     <rect x="0" y="99.1" width="915" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-3628713872-line-5">
+<clipPath id="terminal-323819653-line-5">
     <rect x="0" y="123.5" width="915" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-3628713872-line-6">
+<clipPath id="terminal-323819653-line-6">
     <rect x="0" y="147.9" width="915" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-3628713872-line-7">
+<clipPath id="terminal-323819653-line-7">
     <rect x="0" y="172.3" width="915" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-3628713872-line-8">
+<clipPath id="terminal-323819653-line-8">
     <rect x="0" y="196.7" width="915" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-3628713872-line-9">
+<clipPath id="terminal-323819653-line-9">
     <rect x="0" y="221.1" width="915" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-3628713872-line-10">
+<clipPath id="terminal-323819653-line-10">
     <rect x="0" y="245.5" width="915" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-3628713872-line-11">
+<clipPath id="terminal-323819653-line-11">
     <rect x="0" y="269.9" width="915" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-3628713872-line-12">
+<clipPath id="terminal-323819653-line-12">
     <rect x="0" y="294.3" width="915" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-3628713872-line-13">
+<clipPath id="terminal-323819653-line-13">
     <rect x="0" y="318.7" width="915" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-3628713872-line-14">
+<clipPath id="terminal-323819653-line-14">
     <rect x="0" y="343.1" width="915" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-3628713872-line-15">
+<clipPath id="terminal-323819653-line-15">
     <rect x="0" y="367.5" width="915" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-3628713872-line-16">
+<clipPath id="terminal-323819653-line-16">
     <rect x="0" y="391.9" width="915" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-3628713872-line-17">
+<clipPath id="terminal-323819653-line-17">
     <rect x="0" y="416.3" width="915" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-3628713872-line-18">
+<clipPath id="terminal-323819653-line-18">
     <rect x="0" y="440.7" width="915" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-3628713872-line-19">
+<clipPath id="terminal-323819653-line-19">
     <rect x="0" y="465.1" width="915" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-3628713872-line-20">
+<clipPath id="terminal-323819653-line-20">
     <rect x="0" y="489.5" width="915" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-3628713872-line-21">
+<clipPath id="terminal-323819653-line-21">
     <rect x="0" y="513.9" width="915" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-3628713872-line-22">
+<clipPath id="terminal-323819653-line-22">
     <rect x="0" y="538.3" width="915" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-3628713872-line-23">
+<clipPath id="terminal-323819653-line-23">
     <rect x="0" y="562.7" width="915" height="24.65"/>
             </clipPath>
+<clipPath id="terminal-323819653-line-24">
+    <rect x="0" y="587.1" 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="658" rx="8"/>
+    <rect fill="#292929" stroke="rgba(255,255,255,0.35)" stroke-width="1" 
x="1" y="1" width="931" height="682.4" 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-3628713872-clip-terminal)">
+    <g transform="translate(9, 41)" 
clip-path="url(#terminal-323819653-clip-terminal)">
     
-    <g class="terminal-3628713872-matrix">
-    <text class="terminal-3628713872-r1" x="0" y="20" textLength="73.2" 
clip-path="url(#terminal-3628713872-line-0)">Usage:</text><text 
class="terminal-3628713872-r3" x="85.4" y="20" textLength="183" 
clip-path="url(#terminal-3628713872-line-0)">airflowctl&#160;dags</text><text 
class="terminal-3628713872-r2" x="268.4" y="20" textLength="24.4" 
clip-path="url(#terminal-3628713872-line-0)">&#160;[</text><text 
class="terminal-3628713872-r4" x="292.8" y="20" textLength="24.4" 
clip-path="url(#t [...]
-</text><text class="terminal-3628713872-r2" x="915" y="44.4" textLength="12.2" 
clip-path="url(#terminal-3628713872-line-1)">
-</text><text class="terminal-3628713872-r2" x="0" y="68.8" textLength="280.6" 
clip-path="url(#terminal-3628713872-line-2)">Perform&#160;Dags&#160;operations</text><text
 class="terminal-3628713872-r2" x="915" y="68.8" textLength="12.2" 
clip-path="url(#terminal-3628713872-line-2)">
-</text><text class="terminal-3628713872-r2" x="915" y="93.2" textLength="12.2" 
clip-path="url(#terminal-3628713872-line-3)">
-</text><text class="terminal-3628713872-r1" x="0" y="117.6" textLength="256.2" 
clip-path="url(#terminal-3628713872-line-4)">Positional&#160;Arguments:</text><text
 class="terminal-3628713872-r2" x="915" y="117.6" textLength="12.2" 
clip-path="url(#terminal-3628713872-line-4)">
-</text><text class="terminal-3628713872-r4" x="24.4" y="142" textLength="85.4" 
clip-path="url(#terminal-3628713872-line-5)">COMMAND</text><text 
class="terminal-3628713872-r2" x="915" y="142" textLength="12.2" 
clip-path="url(#terminal-3628713872-line-5)">
-</text><text class="terminal-3628713872-r4" x="48.8" y="166.4" 
textLength="73.2" 
clip-path="url(#terminal-3628713872-line-6)">delete</text><text 
class="terminal-3628713872-r2" x="268.4" y="166.4" textLength="268.4" 
clip-path="url(#terminal-3628713872-line-6)">Delete&#160;a&#160;Dag&#160;by&#160;its&#160;ID</text><text
 class="terminal-3628713872-r2" x="915" y="166.4" textLength="12.2" 
clip-path="url(#terminal-3628713872-line-6)">
-</text><text class="terminal-3628713872-r4" x="48.8" y="190.8" 
textLength="36.6" clip-path="url(#terminal-3628713872-line-7)">get</text><text 
class="terminal-3628713872-r2" x="268.4" y="190.8" textLength="292.8" 
clip-path="url(#terminal-3628713872-line-7)">Retrieve&#160;a&#160;Dag&#160;by&#160;its&#160;ID</text><text
 class="terminal-3628713872-r2" x="915" y="190.8" textLength="12.2" 
clip-path="url(#terminal-3628713872-line-7)">
-</text><text class="terminal-3628713872-r4" x="48.8" y="215.2" 
textLength="134.2" 
clip-path="url(#terminal-3628713872-line-8)">get-details</text><text 
class="terminal-3628713872-r2" x="268.4" y="215.2" textLength="475.8" 
clip-path="url(#terminal-3628713872-line-8)">Retrieve&#160;detailed&#160;information&#160;for&#160;a&#160;Dag</text><text
 class="terminal-3628713872-r2" x="915" y="215.2" textLength="12.2" 
clip-path="url(#terminal-3628713872-line-8)">
-</text><text class="terminal-3628713872-r4" x="48.8" y="239.6" 
textLength="195.2" 
clip-path="url(#terminal-3628713872-line-9)">get-import-error</text><text 
class="terminal-3628713872-r2" x="268.4" y="239.6" textLength="451.4" 
clip-path="url(#terminal-3628713872-line-9)">Retrieve&#160;a&#160;Dag&#160;import&#160;error&#160;by&#160;its&#160;ID</text><text
 class="terminal-3628713872-r2" x="915" y="239.6" textLength="12.2" 
clip-path="url(#terminal-3628713872-line-9)">
-</text><text class="terminal-3628713872-r4" x="48.8" y="264" 
textLength="109.8" 
clip-path="url(#terminal-3628713872-line-10)">get-stats</text><text 
class="terminal-3628713872-r2" x="268.4" y="264" textLength="536.8" 
clip-path="url(#terminal-3628713872-line-10)">Retrieve&#160;run&#160;statistics&#160;for&#160;one&#160;or&#160;more&#160;Dags</text><text
 class="terminal-3628713872-r2" x="915" y="264" textLength="12.2" 
clip-path="url(#terminal-3628713872-line-10)">
-</text><text class="terminal-3628713872-r4" x="48.8" y="288.4" 
textLength="97.6" 
clip-path="url(#terminal-3628713872-line-11)">get-tags</text><text 
class="terminal-3628713872-r2" x="268.4" y="288.4" textLength="366" 
clip-path="url(#terminal-3628713872-line-11)">List&#160;all&#160;tags&#160;used&#160;across&#160;Dags</text><text
 class="terminal-3628713872-r2" x="915" y="288.4" textLength="12.2" 
clip-path="url(#terminal-3628713872-line-11)">
-</text><text class="terminal-3628713872-r4" x="48.8" y="312.8" 
textLength="134.2" 
clip-path="url(#terminal-3628713872-line-12)">get-version</text><text 
class="terminal-3628713872-r2" x="268.4" y="312.8" textLength="439.2" 
clip-path="url(#terminal-3628713872-line-12)">Retrieve&#160;a&#160;specific&#160;version&#160;of&#160;a&#160;Dag</text><text
 class="terminal-3628713872-r2" x="915" y="312.8" textLength="12.2" 
clip-path="url(#terminal-3628713872-line-12)">
-</text><text class="terminal-3628713872-r4" x="48.8" y="337.2" 
textLength="48.8" 
clip-path="url(#terminal-3628713872-line-13)">list</text><text 
class="terminal-3628713872-r2" x="268.4" y="337.2" textLength="158.6" 
clip-path="url(#terminal-3628713872-line-13)">List&#160;all&#160;Dags</text><text
 class="terminal-3628713872-r2" x="915" y="337.2" textLength="12.2" 
clip-path="url(#terminal-3628713872-line-13)">
-</text><text class="terminal-3628713872-r4" x="48.8" y="361.6" 
textLength="219.6" 
clip-path="url(#terminal-3628713872-line-14)">list-import-errors</text><text 
class="terminal-3628713872-r2" x="915" y="361.6" textLength="12.2" 
clip-path="url(#terminal-3628713872-line-14)">
-</text><text class="terminal-3628713872-r2" x="268.4" y="386" 
textLength="317.2" 
clip-path="url(#terminal-3628713872-line-15)">List&#160;all&#160;Dag&#160;import&#160;errors</text><text
 class="terminal-3628713872-r2" x="915" y="386" textLength="12.2" 
clip-path="url(#terminal-3628713872-line-15)">
-</text><text class="terminal-3628713872-r4" x="48.8" y="410.4" 
textLength="146.4" 
clip-path="url(#terminal-3628713872-line-16)">list-version</text><text 
class="terminal-3628713872-r2" x="268.4" y="410.4" textLength="317.2" 
clip-path="url(#terminal-3628713872-line-16)">List&#160;all&#160;versions&#160;of&#160;a&#160;Dag</text><text
 class="terminal-3628713872-r2" x="915" y="410.4" textLength="12.2" 
clip-path="url(#terminal-3628713872-line-16)">
-</text><text class="terminal-3628713872-r4" x="48.8" y="434.8" 
textLength="146.4" 
clip-path="url(#terminal-3628713872-line-17)">list-warning</text><text 
class="terminal-3628713872-r2" x="268.4" y="434.8" textLength="256.2" 
clip-path="url(#terminal-3628713872-line-17)">List&#160;all&#160;Dag&#160;warnings</text><text
 class="terminal-3628713872-r2" x="915" y="434.8" textLength="12.2" 
clip-path="url(#terminal-3628713872-line-17)">
-</text><text class="terminal-3628713872-r4" x="48.8" y="459.2" textLength="61" 
clip-path="url(#terminal-3628713872-line-18)">pause</text><text 
class="terminal-3628713872-r2" x="268.4" y="459.2" textLength="134.2" 
clip-path="url(#terminal-3628713872-line-18)">Pause&#160;a&#160;Dag</text><text 
class="terminal-3628713872-r2" x="915" y="459.2" textLength="12.2" 
clip-path="url(#terminal-3628713872-line-18)">
-</text><text class="terminal-3628713872-r4" x="48.8" y="483.6" 
textLength="85.4" 
clip-path="url(#terminal-3628713872-line-19)">trigger</text><text 
class="terminal-3628713872-r2" x="268.4" y="483.6" textLength="256.2" 
clip-path="url(#terminal-3628713872-line-19)">Trigger&#160;a&#160;new&#160;Dag&#160;run</text><text
 class="terminal-3628713872-r2" x="915" y="483.6" textLength="12.2" 
clip-path="url(#terminal-3628713872-line-19)">
-</text><text class="terminal-3628713872-r4" x="48.8" y="508" textLength="85.4" 
clip-path="url(#terminal-3628713872-line-20)">unpause</text><text 
class="terminal-3628713872-r2" x="268.4" y="508" textLength="158.6" 
clip-path="url(#terminal-3628713872-line-20)">Unpause&#160;a&#160;Dag</text><text
 class="terminal-3628713872-r2" x="915" y="508" textLength="12.2" 
clip-path="url(#terminal-3628713872-line-20)">
-</text><text class="terminal-3628713872-r4" x="48.8" y="532.4" 
textLength="73.2" 
clip-path="url(#terminal-3628713872-line-21)">update</text><text 
class="terminal-3628713872-r2" x="268.4" y="532.4" textLength="317.2" 
clip-path="url(#terminal-3628713872-line-21)">Update&#160;properties&#160;of&#160;a&#160;Dag</text><text
 class="terminal-3628713872-r2" x="915" y="532.4" textLength="12.2" 
clip-path="url(#terminal-3628713872-line-21)">
-</text><text class="terminal-3628713872-r2" x="915" y="556.8" 
textLength="12.2" clip-path="url(#terminal-3628713872-line-22)">
-</text><text class="terminal-3628713872-r1" x="0" y="581.2" textLength="97.6" 
clip-path="url(#terminal-3628713872-line-23)">Options:</text><text 
class="terminal-3628713872-r2" x="915" y="581.2" textLength="12.2" 
clip-path="url(#terminal-3628713872-line-23)">
-</text><text class="terminal-3628713872-r4" x="24.4" y="605.6" 
textLength="24.4" clip-path="url(#terminal-3628713872-line-24)">-h</text><text 
class="terminal-3628713872-r2" x="48.8" y="605.6" textLength="24.4" 
clip-path="url(#terminal-3628713872-line-24)">,&#160;</text><text 
class="terminal-3628713872-r4" x="73.2" y="605.6" textLength="73.2" 
clip-path="url(#terminal-3628713872-line-24)">--help</text><text 
class="terminal-3628713872-r2" x="268.4" y="605.6" textLength="378.2" 
clip-path="ur [...]
+    <g class="terminal-323819653-matrix">
+    <text class="terminal-323819653-r1" x="0" y="20" textLength="73.2" 
clip-path="url(#terminal-323819653-line-0)">Usage:</text><text 
class="terminal-323819653-r3" x="85.4" y="20" textLength="183" 
clip-path="url(#terminal-323819653-line-0)">airflowctl&#160;dags</text><text 
class="terminal-323819653-r2" x="268.4" y="20" textLength="24.4" 
clip-path="url(#terminal-323819653-line-0)">&#160;[</text><text 
class="terminal-323819653-r4" x="292.8" y="20" textLength="24.4" 
clip-path="url(#terminal [...]
+</text><text class="terminal-323819653-r2" x="915" y="44.4" textLength="12.2" 
clip-path="url(#terminal-323819653-line-1)">
+</text><text class="terminal-323819653-r2" x="0" y="68.8" textLength="280.6" 
clip-path="url(#terminal-323819653-line-2)">Perform&#160;Dags&#160;operations</text><text
 class="terminal-323819653-r2" x="915" y="68.8" textLength="12.2" 
clip-path="url(#terminal-323819653-line-2)">
+</text><text class="terminal-323819653-r2" x="915" y="93.2" textLength="12.2" 
clip-path="url(#terminal-323819653-line-3)">
+</text><text class="terminal-323819653-r1" x="0" y="117.6" textLength="256.2" 
clip-path="url(#terminal-323819653-line-4)">Positional&#160;Arguments:</text><text
 class="terminal-323819653-r2" x="915" y="117.6" textLength="12.2" 
clip-path="url(#terminal-323819653-line-4)">
+</text><text class="terminal-323819653-r4" x="24.4" y="142" textLength="85.4" 
clip-path="url(#terminal-323819653-line-5)">COMMAND</text><text 
class="terminal-323819653-r2" x="915" y="142" textLength="12.2" 
clip-path="url(#terminal-323819653-line-5)">
+</text><text class="terminal-323819653-r4" x="48.8" y="166.4" 
textLength="73.2" 
clip-path="url(#terminal-323819653-line-6)">delete</text><text 
class="terminal-323819653-r2" x="268.4" y="166.4" textLength="268.4" 
clip-path="url(#terminal-323819653-line-6)">Delete&#160;a&#160;Dag&#160;by&#160;its&#160;ID</text><text
 class="terminal-323819653-r2" x="915" y="166.4" textLength="12.2" 
clip-path="url(#terminal-323819653-line-6)">
+</text><text class="terminal-323819653-r4" x="48.8" y="190.8" 
textLength="36.6" clip-path="url(#terminal-323819653-line-7)">get</text><text 
class="terminal-323819653-r2" x="268.4" y="190.8" textLength="292.8" 
clip-path="url(#terminal-323819653-line-7)">Retrieve&#160;a&#160;Dag&#160;by&#160;its&#160;ID</text><text
 class="terminal-323819653-r2" x="915" y="190.8" textLength="12.2" 
clip-path="url(#terminal-323819653-line-7)">
+</text><text class="terminal-323819653-r4" x="48.8" y="215.2" 
textLength="134.2" 
clip-path="url(#terminal-323819653-line-8)">get-details</text><text 
class="terminal-323819653-r2" x="268.4" y="215.2" textLength="475.8" 
clip-path="url(#terminal-323819653-line-8)">Retrieve&#160;detailed&#160;information&#160;for&#160;a&#160;Dag</text><text
 class="terminal-323819653-r2" x="915" y="215.2" textLength="12.2" 
clip-path="url(#terminal-323819653-line-8)">
+</text><text class="terminal-323819653-r4" x="48.8" y="239.6" 
textLength="195.2" 
clip-path="url(#terminal-323819653-line-9)">get-import-error</text><text 
class="terminal-323819653-r2" x="268.4" y="239.6" textLength="451.4" 
clip-path="url(#terminal-323819653-line-9)">Retrieve&#160;a&#160;Dag&#160;import&#160;error&#160;by&#160;its&#160;ID</text><text
 class="terminal-323819653-r2" x="915" y="239.6" textLength="12.2" 
clip-path="url(#terminal-323819653-line-9)">
+</text><text class="terminal-323819653-r4" x="48.8" y="264" textLength="109.8" 
clip-path="url(#terminal-323819653-line-10)">get-stats</text><text 
class="terminal-323819653-r2" x="268.4" y="264" textLength="536.8" 
clip-path="url(#terminal-323819653-line-10)">Retrieve&#160;run&#160;statistics&#160;for&#160;one&#160;or&#160;more&#160;Dags</text><text
 class="terminal-323819653-r2" x="915" y="264" textLength="12.2" 
clip-path="url(#terminal-323819653-line-10)">
+</text><text class="terminal-323819653-r4" x="48.8" y="288.4" 
textLength="97.6" 
clip-path="url(#terminal-323819653-line-11)">get-tags</text><text 
class="terminal-323819653-r2" x="268.4" y="288.4" textLength="366" 
clip-path="url(#terminal-323819653-line-11)">List&#160;all&#160;tags&#160;used&#160;across&#160;Dags</text><text
 class="terminal-323819653-r2" x="915" y="288.4" textLength="12.2" 
clip-path="url(#terminal-323819653-line-11)">
+</text><text class="terminal-323819653-r4" x="48.8" y="312.8" 
textLength="134.2" 
clip-path="url(#terminal-323819653-line-12)">get-version</text><text 
class="terminal-323819653-r2" x="268.4" y="312.8" textLength="439.2" 
clip-path="url(#terminal-323819653-line-12)">Retrieve&#160;a&#160;specific&#160;version&#160;of&#160;a&#160;Dag</text><text
 class="terminal-323819653-r2" x="915" y="312.8" textLength="12.2" 
clip-path="url(#terminal-323819653-line-12)">
+</text><text class="terminal-323819653-r4" x="48.8" y="337.2" 
textLength="48.8" clip-path="url(#terminal-323819653-line-13)">list</text><text 
class="terminal-323819653-r2" x="268.4" y="337.2" textLength="158.6" 
clip-path="url(#terminal-323819653-line-13)">List&#160;all&#160;Dags</text><text
 class="terminal-323819653-r2" x="915" y="337.2" textLength="12.2" 
clip-path="url(#terminal-323819653-line-13)">
+</text><text class="terminal-323819653-r4" x="48.8" y="361.6" 
textLength="219.6" 
clip-path="url(#terminal-323819653-line-14)">list-import-errors</text><text 
class="terminal-323819653-r2" x="915" y="361.6" textLength="12.2" 
clip-path="url(#terminal-323819653-line-14)">
+</text><text class="terminal-323819653-r2" x="268.4" y="386" 
textLength="317.2" 
clip-path="url(#terminal-323819653-line-15)">List&#160;all&#160;Dag&#160;import&#160;errors</text><text
 class="terminal-323819653-r2" x="915" y="386" textLength="12.2" 
clip-path="url(#terminal-323819653-line-15)">
+</text><text class="terminal-323819653-r4" x="48.8" y="410.4" 
textLength="146.4" 
clip-path="url(#terminal-323819653-line-16)">list-version</text><text 
class="terminal-323819653-r2" x="268.4" y="410.4" textLength="317.2" 
clip-path="url(#terminal-323819653-line-16)">List&#160;all&#160;versions&#160;of&#160;a&#160;Dag</text><text
 class="terminal-323819653-r2" x="915" y="410.4" textLength="12.2" 
clip-path="url(#terminal-323819653-line-16)">
+</text><text class="terminal-323819653-r4" x="48.8" y="434.8" 
textLength="146.4" 
clip-path="url(#terminal-323819653-line-17)">list-warning</text><text 
class="terminal-323819653-r2" x="268.4" y="434.8" textLength="256.2" 
clip-path="url(#terminal-323819653-line-17)">List&#160;all&#160;Dag&#160;warnings</text><text
 class="terminal-323819653-r2" x="915" y="434.8" textLength="12.2" 
clip-path="url(#terminal-323819653-line-17)">
+</text><text class="terminal-323819653-r4" x="48.8" y="459.2" 
textLength="170.8" 
clip-path="url(#terminal-323819653-line-18)">next-execution</text><text 
class="terminal-323819653-r2" x="268.4" y="459.2" textLength="585.6" 
clip-path="url(#terminal-323819653-line-18)">Show&#160;the&#160;next&#160;scheduled&#160;execution&#160;time&#160;for&#160;a&#160;Dag</text><text
 class="terminal-323819653-r2" x="915" y="459.2" textLength="12.2" 
clip-path="url(#terminal-323819653-line-18)">
+</text><text class="terminal-323819653-r4" x="48.8" y="483.6" textLength="61" 
clip-path="url(#terminal-323819653-line-19)">pause</text><text 
class="terminal-323819653-r2" x="268.4" y="483.6" textLength="134.2" 
clip-path="url(#terminal-323819653-line-19)">Pause&#160;a&#160;Dag</text><text 
class="terminal-323819653-r2" x="915" y="483.6" textLength="12.2" 
clip-path="url(#terminal-323819653-line-19)">
+</text><text class="terminal-323819653-r4" x="48.8" y="508" textLength="85.4" 
clip-path="url(#terminal-323819653-line-20)">trigger</text><text 
class="terminal-323819653-r2" x="268.4" y="508" textLength="256.2" 
clip-path="url(#terminal-323819653-line-20)">Trigger&#160;a&#160;new&#160;Dag&#160;run</text><text
 class="terminal-323819653-r2" x="915" y="508" textLength="12.2" 
clip-path="url(#terminal-323819653-line-20)">
+</text><text class="terminal-323819653-r4" x="48.8" y="532.4" 
textLength="85.4" 
clip-path="url(#terminal-323819653-line-21)">unpause</text><text 
class="terminal-323819653-r2" x="268.4" y="532.4" textLength="158.6" 
clip-path="url(#terminal-323819653-line-21)">Unpause&#160;a&#160;Dag</text><text
 class="terminal-323819653-r2" x="915" y="532.4" textLength="12.2" 
clip-path="url(#terminal-323819653-line-21)">
+</text><text class="terminal-323819653-r4" x="48.8" y="556.8" 
textLength="73.2" 
clip-path="url(#terminal-323819653-line-22)">update</text><text 
class="terminal-323819653-r2" x="268.4" y="556.8" textLength="317.2" 
clip-path="url(#terminal-323819653-line-22)">Update&#160;properties&#160;of&#160;a&#160;Dag</text><text
 class="terminal-323819653-r2" x="915" y="556.8" textLength="12.2" 
clip-path="url(#terminal-323819653-line-22)">
+</text><text class="terminal-323819653-r2" x="915" y="581.2" textLength="12.2" 
clip-path="url(#terminal-323819653-line-23)">
+</text><text class="terminal-323819653-r1" x="0" y="605.6" textLength="97.6" 
clip-path="url(#terminal-323819653-line-24)">Options:</text><text 
class="terminal-323819653-r2" x="915" y="605.6" textLength="12.2" 
clip-path="url(#terminal-323819653-line-24)">
+</text><text class="terminal-323819653-r4" x="24.4" y="630" textLength="24.4" 
clip-path="url(#terminal-323819653-line-25)">-h</text><text 
class="terminal-323819653-r2" x="48.8" y="630" textLength="24.4" 
clip-path="url(#terminal-323819653-line-25)">,&#160;</text><text 
class="terminal-323819653-r4" x="73.2" y="630" textLength="73.2" 
clip-path="url(#terminal-323819653-line-25)">--help</text><text 
class="terminal-323819653-r2" x="268.4" y="630" textLength="378.2" 
clip-path="url(#terminal-323 [...]
 </text>
     </g>
     </g>
diff --git a/airflow-ctl/src/airflowctl/ctl/cli_config.py 
b/airflow-ctl/src/airflowctl/ctl/cli_config.py
index 1d3c30121b6..11ff4542e01 100755
--- a/airflow-ctl/src/airflowctl/ctl/cli_config.py
+++ b/airflow-ctl/src/airflowctl/ctl/cli_config.py
@@ -959,6 +959,15 @@ CONNECTION_COMMANDS = (
 )
 
 DAG_COMMANDS = (
+    ActionCommand(
+        name="next-execution",
+        help="Show the next scheduled execution time for a Dag",
+        
func=lazy_load_command("airflowctl.ctl.commands.dag_command.next_execution"),
+        args=(
+            ARG_DAG_ID,
+            ARG_OUTPUT,
+        ),
+    ),
     ActionCommand(
         name="pause",
         help="Pause a Dag",
diff --git a/airflow-ctl/src/airflowctl/ctl/commands/dag_command.py 
b/airflow-ctl/src/airflowctl/ctl/commands/dag_command.py
index 33b3d7c95fe..301821f9c2c 100644
--- a/airflow-ctl/src/airflowctl/ctl/commands/dag_command.py
+++ b/airflow-ctl/src/airflowctl/ctl/commands/dag_command.py
@@ -72,3 +72,34 @@ def unpause(args, api_client=NEW_API_CLIENT) -> None:
         api_client=api_client,
         output=args.output,
     )
+
+
+_NEXT_EXECUTION_FIELDS = (
+    "next_dagrun_logical_date",
+    "next_dagrun_data_interval_start",
+    "next_dagrun_data_interval_end",
+    "next_dagrun_run_after",
+)
+
+
+@provide_api_client(kind=ClientKind.CLI)
+def next_execution(args, api_client=NEW_API_CLIENT) -> dict | None:
+    """Show next scheduled execution time for a DAG."""
+    try:
+        response = api_client.dags.get(dag_id=args.dag_id)
+    except ServerResponseError as e:
+        rich.print(f"[red]Error retrieving DAG {args.dag_id}: {e}[/red]")
+        sys.exit(1)
+
+    next_exec_data = {field: getattr(response, field) for field in 
_NEXT_EXECUTION_FIELDS}
+
+    if all(value is None for value in next_exec_data.values()):
+        rich.print(f"[yellow]No upcoming run scheduled for DAG 
{args.dag_id}.[/yellow]")
+        return None
+
+    result = next_exec_data
+    AirflowConsole().print_as(
+        data=[result],
+        output=args.output,
+    )
+    return result
diff --git a/airflow-ctl/tests/airflow_ctl/ctl/commands/test_dag_command.py 
b/airflow-ctl/tests/airflow_ctl/ctl/commands/test_dag_command.py
index 8eb65d46686..405eb030065 100644
--- a/airflow-ctl/tests/airflow_ctl/ctl/commands/test_dag_command.py
+++ b/airflow-ctl/tests/airflow_ctl/ctl/commands/test_dag_command.py
@@ -90,6 +90,36 @@ class TestDagCommands:
         is_stale=False,
     )
 
+    dag_response_no_schedule = DAGResponse(
+        dag_id=dag_id,
+        dag_display_name=dag_display_name,
+        is_paused=True,
+        last_parsed_time=datetime.datetime(2024, 12, 31, 23, 59, 59),
+        last_expired=datetime.datetime(2025, 1, 1, 0, 0, 0),
+        fileloc="fileloc",
+        relative_fileloc="relative_fileloc",
+        description="description",
+        timetable_summary=None,
+        timetable_description=None,
+        timetable_partitioned=False,
+        timetable_periodic=False,
+        tags=[],
+        max_active_tasks=1,
+        max_active_runs=1,
+        max_consecutive_failed_dag_runs=1,
+        has_task_concurrency_limits=False,
+        has_import_errors=False,
+        next_dagrun_logical_date=None,
+        next_dagrun_data_interval_start=None,
+        next_dagrun_data_interval_end=None,
+        next_dagrun_run_after=None,
+        owners=["apache-airflow"],
+        is_backfillable=False,
+        file_token="file_token",
+        bundle_name="bundle_name",
+        is_stale=False,
+    )
+
     def test_pause_dag(self, api_client_maker, monkeypatch):
         api_client = api_client_maker(
             path=f"/api/v2/dags/{self.dag_id}",
@@ -143,3 +173,45 @@ class TestDagCommands:
                 self.parser.parse_args(["dags", "unpause", self.dag_id]),
                 api_client=api_client,
             )
+
+    def test_next_execution(self, api_client_maker):
+        api_client = api_client_maker(
+            path=f"/api/v2/dags/{self.dag_id}",
+            response_json=self.dag_response_paused.model_dump(mode="json"),
+            expected_http_status_code=200,
+            kind=ClientKind.CLI,
+        )
+        result = dag_command.next_execution(
+            self.parser.parse_args(["dags", "next-execution", self.dag_id]),
+            api_client=api_client,
+        )
+        assert result["next_dagrun_logical_date"] == datetime.datetime(2025, 
1, 1, 0, 0, 0)
+        assert result["next_dagrun_data_interval_start"] == 
datetime.datetime(2025, 1, 1, 0, 0, 0)
+        assert result["next_dagrun_data_interval_end"] == 
datetime.datetime(2025, 1, 1, 0, 0, 0)
+        assert result["next_dagrun_run_after"] == datetime.datetime(2025, 1, 
1, 0, 0, 0)
+
+    def test_next_execution_no_schedule(self, api_client_maker):
+        api_client = api_client_maker(
+            path=f"/api/v2/dags/{self.dag_id}",
+            
response_json=self.dag_response_no_schedule.model_dump(mode="json"),
+            expected_http_status_code=200,
+            kind=ClientKind.CLI,
+        )
+        result = dag_command.next_execution(
+            self.parser.parse_args(["dags", "next-execution", self.dag_id]),
+            api_client=api_client,
+        )
+        assert result is None
+
+    def test_next_execution_fail(self, api_client_maker):
+        api_client = api_client_maker(
+            path=f"/api/v2/dags/{self.dag_id}",
+            response_json={"detail": "DAG not found"},
+            expected_http_status_code=404,
+            kind=ClientKind.CLI,
+        )
+        with pytest.raises(SystemExit):
+            dag_command.next_execution(
+                self.parser.parse_args(["dags", "next-execution", 
self.dag_id]),
+                api_client=api_client,
+            )

Reply via email to