This is an automated email from the ASF dual-hosted git repository.
potiuk 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 bfa22c05a82 Added plugins command to airflowctl (#64935)
bfa22c05a82 is described below
commit bfa22c05a822e5b7931dda233a1f86309aabc8f1
Author: Justin Pakzad <[email protected]>
AuthorDate: Fri Apr 10 04:36:15 2026 -0400
Added plugins command to airflowctl (#64935)
* Added plugins command into airflowctl
* Add missing docstring to test
---
.../airflowctl_tests/test_airflowctl_commands.py | 3 +
airflow-ctl/docs/images/command_hashes.txt | 3 +-
airflow-ctl/docs/images/output_main.svg | 130 +++++++++++----------
airflow-ctl/docs/images/output_plugins.svg | 105 +++++++++++++++++
airflow-ctl/src/airflowctl/api/client.py | 7 ++
airflow-ctl/src/airflowctl/api/operations.py | 18 +++
.../tests/airflow_ctl/api/test_operations.py | 54 +++++++++
.../in_container/run_capture_airflowctl_help.py | 1 +
8 files changed, 257 insertions(+), 64 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 c004f5ccfdb..de07d0d3751 100644
--- a/airflow-ctl-tests/tests/airflowctl_tests/test_airflowctl_commands.py
+++ b/airflow-ctl-tests/tests/airflowctl_tests/test_airflowctl_commands.py
@@ -125,6 +125,9 @@ TEST_COMMANDS = [
"variables delete --variable-key=test_import_var_with_desc",
# Version command
"version --remote",
+ # Plugins command
+ "plugins list",
+ "plugins list-import-errors",
]
DATE_PARAM_1 = date_param()
diff --git a/airflow-ctl/docs/images/command_hashes.txt
b/airflow-ctl/docs/images/command_hashes.txt
index b0089d41d1f..3e03b177158 100644
--- a/airflow-ctl/docs/images/command_hashes.txt
+++ b/airflow-ctl/docs/images/command_hashes.txt
@@ -1,4 +1,4 @@
-main:65249416abad6ad24c276fb44326ae15
+main:27a22c00dcf32e7a1a4f06672dc8e3c8
assets:b3ae2b933e54528bf486ff28e887804d
auth:d79e9c7d00c432bdbcbc2a86e2e32053
backfill:bbce9859a2d1ce054ad22db92dea8c05
@@ -11,4 +11,5 @@ pools:03fc7d948cbecf16ff8d640eb8f0ce43
providers:1c0afb2dff31d93ab2934b032a2250ab
variables:0354f8f4b0dde1c3771ed1568692c6ae
version:31f4efdf8de0dbaaa4fac71ff7efecc3
+plugins:118ea0beda9b3935eedc67b0511f7e4a
auth login:9fe2bb1dd5c602beea2eefb33a2b20a8
diff --git a/airflow-ctl/docs/images/output_main.svg
b/airflow-ctl/docs/images/output_main.svg
index 8e4ef71bdb0..f586877bce8 100644
--- a/airflow-ctl/docs/images/output_main.svg
+++ b/airflow-ctl/docs/images/output_main.svg
@@ -1,4 +1,4 @@
-<svg class="rich-terminal" viewBox="0 0 933 684.4"
xmlns="http://www.w3.org/2000/svg">
+<svg class="rich-terminal" viewBox="0 0 933 708.8"
xmlns="http://www.w3.org/2000/svg">
<!-- Generated with Rich https://www.textualize.io -->
<style>
@@ -19,142 +19,146 @@
font-weight: 700;
}
- .terminal-4108169915-matrix {
+ .terminal-2670787131-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-4108169915-title {
+ .terminal-2670787131-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-4108169915-r1 { fill: #ff8700 }
-.terminal-4108169915-r2 { fill: #c5c8c6 }
-.terminal-4108169915-r3 { fill: #808080 }
-.terminal-4108169915-r4 { fill: #68a0b3 }
+ .terminal-2670787131-r1 { fill: #ff8700 }
+.terminal-2670787131-r2 { fill: #c5c8c6 }
+.terminal-2670787131-r3 { fill: #808080 }
+.terminal-2670787131-r4 { fill: #68a0b3 }
</style>
<defs>
- <clipPath id="terminal-4108169915-clip-terminal">
- <rect x="0" y="0" width="914.0" height="633.4" />
+ <clipPath id="terminal-2670787131-clip-terminal">
+ <rect x="0" y="0" width="914.0" height="657.8" />
</clipPath>
- <clipPath id="terminal-4108169915-line-0">
+ <clipPath id="terminal-2670787131-line-0">
<rect x="0" y="1.5" width="915" height="24.65"/>
</clipPath>
-<clipPath id="terminal-4108169915-line-1">
+<clipPath id="terminal-2670787131-line-1">
<rect x="0" y="25.9" width="915" height="24.65"/>
</clipPath>
-<clipPath id="terminal-4108169915-line-2">
+<clipPath id="terminal-2670787131-line-2">
<rect x="0" y="50.3" width="915" height="24.65"/>
</clipPath>
-<clipPath id="terminal-4108169915-line-3">
+<clipPath id="terminal-2670787131-line-3">
<rect x="0" y="74.7" width="915" height="24.65"/>
</clipPath>
-<clipPath id="terminal-4108169915-line-4">
+<clipPath id="terminal-2670787131-line-4">
<rect x="0" y="99.1" width="915" height="24.65"/>
</clipPath>
-<clipPath id="terminal-4108169915-line-5">
+<clipPath id="terminal-2670787131-line-5">
<rect x="0" y="123.5" width="915" height="24.65"/>
</clipPath>
-<clipPath id="terminal-4108169915-line-6">
+<clipPath id="terminal-2670787131-line-6">
<rect x="0" y="147.9" width="915" height="24.65"/>
</clipPath>
-<clipPath id="terminal-4108169915-line-7">
+<clipPath id="terminal-2670787131-line-7">
<rect x="0" y="172.3" width="915" height="24.65"/>
</clipPath>
-<clipPath id="terminal-4108169915-line-8">
+<clipPath id="terminal-2670787131-line-8">
<rect x="0" y="196.7" width="915" height="24.65"/>
</clipPath>
-<clipPath id="terminal-4108169915-line-9">
+<clipPath id="terminal-2670787131-line-9">
<rect x="0" y="221.1" width="915" height="24.65"/>
</clipPath>
-<clipPath id="terminal-4108169915-line-10">
+<clipPath id="terminal-2670787131-line-10">
<rect x="0" y="245.5" width="915" height="24.65"/>
</clipPath>
-<clipPath id="terminal-4108169915-line-11">
+<clipPath id="terminal-2670787131-line-11">
<rect x="0" y="269.9" width="915" height="24.65"/>
</clipPath>
-<clipPath id="terminal-4108169915-line-12">
+<clipPath id="terminal-2670787131-line-12">
<rect x="0" y="294.3" width="915" height="24.65"/>
</clipPath>
-<clipPath id="terminal-4108169915-line-13">
+<clipPath id="terminal-2670787131-line-13">
<rect x="0" y="318.7" width="915" height="24.65"/>
</clipPath>
-<clipPath id="terminal-4108169915-line-14">
+<clipPath id="terminal-2670787131-line-14">
<rect x="0" y="343.1" width="915" height="24.65"/>
</clipPath>
-<clipPath id="terminal-4108169915-line-15">
+<clipPath id="terminal-2670787131-line-15">
<rect x="0" y="367.5" width="915" height="24.65"/>
</clipPath>
-<clipPath id="terminal-4108169915-line-16">
+<clipPath id="terminal-2670787131-line-16">
<rect x="0" y="391.9" width="915" height="24.65"/>
</clipPath>
-<clipPath id="terminal-4108169915-line-17">
+<clipPath id="terminal-2670787131-line-17">
<rect x="0" y="416.3" width="915" height="24.65"/>
</clipPath>
-<clipPath id="terminal-4108169915-line-18">
+<clipPath id="terminal-2670787131-line-18">
<rect x="0" y="440.7" width="915" height="24.65"/>
</clipPath>
-<clipPath id="terminal-4108169915-line-19">
+<clipPath id="terminal-2670787131-line-19">
<rect x="0" y="465.1" width="915" height="24.65"/>
</clipPath>
-<clipPath id="terminal-4108169915-line-20">
+<clipPath id="terminal-2670787131-line-20">
<rect x="0" y="489.5" width="915" height="24.65"/>
</clipPath>
-<clipPath id="terminal-4108169915-line-21">
+<clipPath id="terminal-2670787131-line-21">
<rect x="0" y="513.9" width="915" height="24.65"/>
</clipPath>
-<clipPath id="terminal-4108169915-line-22">
+<clipPath id="terminal-2670787131-line-22">
<rect x="0" y="538.3" width="915" height="24.65"/>
</clipPath>
-<clipPath id="terminal-4108169915-line-23">
+<clipPath id="terminal-2670787131-line-23">
<rect x="0" y="562.7" width="915" height="24.65"/>
</clipPath>
-<clipPath id="terminal-4108169915-line-24">
+<clipPath id="terminal-2670787131-line-24">
<rect x="0" y="587.1" width="915" height="24.65"/>
</clipPath>
+<clipPath id="terminal-2670787131-line-25">
+ <rect x="0" y="611.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="682.4" rx="8"/>
+ <rect fill="#292929" stroke="rgba(255,255,255,0.35)" stroke-width="1"
x="1" y="1" width="931" height="706.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-4108169915-clip-terminal)">
+ <g transform="translate(9, 41)"
clip-path="url(#terminal-2670787131-clip-terminal)">
- <g class="terminal-4108169915-matrix">
- <text class="terminal-4108169915-r1" x="0" y="20" textLength="73.2"
clip-path="url(#terminal-4108169915-line-0)">Usage:</text><text
class="terminal-4108169915-r3" x="85.4" y="20" textLength="122"
clip-path="url(#terminal-4108169915-line-0)">airflowctl</text><text
class="terminal-4108169915-r2" x="207.4" y="20" textLength="24.4"
clip-path="url(#terminal-4108169915-line-0)"> [</text><text
class="terminal-4108169915-r4" x="231.8" y="20" textLength="24.4"
clip-path="url(#terminal-41 [...]
-</text><text class="terminal-4108169915-r2" x="915" y="44.4" textLength="12.2"
clip-path="url(#terminal-4108169915-line-1)">
-</text><text class="terminal-4108169915-r1" x="0" y="68.8" textLength="256.2"
clip-path="url(#terminal-4108169915-line-2)">Positional Arguments:</text><text
class="terminal-4108169915-r2" x="915" y="68.8" textLength="12.2"
clip-path="url(#terminal-4108169915-line-2)">
-</text><text class="terminal-4108169915-r4" x="24.4" y="93.2"
textLength="195.2"
clip-path="url(#terminal-4108169915-line-3)">GROUP_OR_COMMAND</text><text
class="terminal-4108169915-r2" x="915" y="93.2" textLength="12.2"
clip-path="url(#terminal-4108169915-line-3)">
-</text><text class="terminal-4108169915-r2" x="915" y="117.6"
textLength="12.2" clip-path="url(#terminal-4108169915-line-4)">
-</text><text class="terminal-4108169915-r4" x="0" y="142" textLength="122"
clip-path="url(#terminal-4108169915-line-5)">    Groups</text><text
class="terminal-4108169915-r2" x="915" y="142" textLength="12.2"
clip-path="url(#terminal-4108169915-line-5)">
-</text><text class="terminal-4108169915-r4" x="73.2" y="166.4"
textLength="73.2"
clip-path="url(#terminal-4108169915-line-6)">assets</text><text
class="terminal-4108169915-r2" x="244" y="166.4" textLength="305"
clip-path="url(#terminal-4108169915-line-6)">Perform Assets operations</text><text
class="terminal-4108169915-r2" x="915" y="166.4" textLength="12.2"
clip-path="url(#terminal-4108169915-line-6)">
-</text><text class="terminal-4108169915-r4" x="73.2" y="190.8"
textLength="48.8" clip-path="url(#terminal-4108169915-line-7)">auth</text><text
class="terminal-4108169915-r2" x="244" y="190.8" textLength="646.6"
clip-path="url(#terminal-4108169915-line-7)">Manage authentication for CLI. Either pass token from</text><text
class="terminal-4108169915-r2" x="915" y="190.8" textLength="12.2"
clip-path="url(#terminal-4108169915-line-7)">
-</text><text class="terminal-4108169915-r2" x="244" y="215.2"
textLength="622.2"
clip-path="url(#terminal-4108169915-line-8)">environment variable/parameter or pass username and</text><text
class="terminal-4108169915-r2" x="915" y="215.2" textLength="12.2"
clip-path="url(#terminal-4108169915-line-8)">
-</text><text class="terminal-4108169915-r2" x="244" y="239.6"
textLength="109.8"
clip-path="url(#terminal-4108169915-line-9)">password.</text><text
class="terminal-4108169915-r2" x="915" y="239.6" textLength="12.2"
clip-path="url(#terminal-4108169915-line-9)">
-</text><text class="terminal-4108169915-r4" x="73.2" y="264" textLength="97.6"
clip-path="url(#terminal-4108169915-line-10)">backfill</text><text
class="terminal-4108169915-r2" x="244" y="264" textLength="329.4"
clip-path="url(#terminal-4108169915-line-10)">Perform Backfill operations</text><text
class="terminal-4108169915-r2" x="915" y="264" textLength="12.2"
clip-path="url(#terminal-4108169915-line-10)">
-</text><text class="terminal-4108169915-r4" x="73.2" y="288.4"
textLength="73.2"
clip-path="url(#terminal-4108169915-line-11)">config</text><text
class="terminal-4108169915-r2" x="244" y="288.4" textLength="305"
clip-path="url(#terminal-4108169915-line-11)">Perform Config operations</text><text
class="terminal-4108169915-r2" x="915" y="288.4" textLength="12.2"
clip-path="url(#terminal-4108169915-line-11)">
-</text><text class="terminal-4108169915-r4" x="73.2" y="312.8"
textLength="134.2"
clip-path="url(#terminal-4108169915-line-12)">connections</text><text
class="terminal-4108169915-r2" x="244" y="312.8" textLength="366"
clip-path="url(#terminal-4108169915-line-12)">Perform Connections operations</text><text
class="terminal-4108169915-r2" x="915" y="312.8" textLength="12.2"
clip-path="url(#terminal-4108169915-line-12)">
-</text><text class="terminal-4108169915-r4" x="73.2" y="337.2"
textLength="73.2"
clip-path="url(#terminal-4108169915-line-13)">dagrun</text><text
class="terminal-4108169915-r2" x="244" y="337.2" textLength="305"
clip-path="url(#terminal-4108169915-line-13)">Perform DagRun operations</text><text
class="terminal-4108169915-r2" x="915" y="337.2" textLength="12.2"
clip-path="url(#terminal-4108169915-line-13)">
-</text><text class="terminal-4108169915-r4" x="73.2" y="361.6"
textLength="48.8"
clip-path="url(#terminal-4108169915-line-14)">dags</text><text
class="terminal-4108169915-r2" x="244" y="361.6" textLength="280.6"
clip-path="url(#terminal-4108169915-line-14)">Perform Dags operations</text><text
class="terminal-4108169915-r2" x="915" y="361.6" textLength="12.2"
clip-path="url(#terminal-4108169915-line-14)">
-</text><text class="terminal-4108169915-r4" x="73.2" y="386" textLength="48.8"
clip-path="url(#terminal-4108169915-line-15)">jobs</text><text
class="terminal-4108169915-r2" x="244" y="386" textLength="280.6"
clip-path="url(#terminal-4108169915-line-15)">Perform Jobs operations</text><text
class="terminal-4108169915-r2" x="915" y="386" textLength="12.2"
clip-path="url(#terminal-4108169915-line-15)">
-</text><text class="terminal-4108169915-r4" x="73.2" y="410.4" textLength="61"
clip-path="url(#terminal-4108169915-line-16)">pools</text><text
class="terminal-4108169915-r2" x="244" y="410.4" textLength="292.8"
clip-path="url(#terminal-4108169915-line-16)">Perform Pools operations</text><text
class="terminal-4108169915-r2" x="915" y="410.4" textLength="12.2"
clip-path="url(#terminal-4108169915-line-16)">
-</text><text class="terminal-4108169915-r4" x="73.2" y="434.8"
textLength="109.8"
clip-path="url(#terminal-4108169915-line-17)">providers</text><text
class="terminal-4108169915-r2" x="244" y="434.8" textLength="341.6"
clip-path="url(#terminal-4108169915-line-17)">Perform Providers operations</text><text
class="terminal-4108169915-r2" x="915" y="434.8" textLength="12.2"
clip-path="url(#terminal-4108169915-line-17)">
-</text><text class="terminal-4108169915-r4" x="73.2" y="459.2"
textLength="109.8"
clip-path="url(#terminal-4108169915-line-18)">variables</text><text
class="terminal-4108169915-r2" x="244" y="459.2" textLength="341.6"
clip-path="url(#terminal-4108169915-line-18)">Perform Variables operations</text><text
class="terminal-4108169915-r2" x="915" y="459.2" textLength="12.2"
clip-path="url(#terminal-4108169915-line-18)">
-</text><text class="terminal-4108169915-r4" x="73.2" y="483.6"
textLength="48.8"
clip-path="url(#terminal-4108169915-line-19)">xcom</text><text
class="terminal-4108169915-r2" x="244" y="483.6" textLength="280.6"
clip-path="url(#terminal-4108169915-line-19)">Perform XCom operations</text><text
class="terminal-4108169915-r2" x="915" y="483.6" textLength="12.2"
clip-path="url(#terminal-4108169915-line-19)">
-</text><text class="terminal-4108169915-r2" x="915" y="508" textLength="12.2"
clip-path="url(#terminal-4108169915-line-20)">
-</text><text class="terminal-4108169915-r4" x="0" y="532.4" textLength="158.6"
clip-path="url(#terminal-4108169915-line-21)">    Commands:</text><text
class="terminal-4108169915-r2" x="915" y="532.4" textLength="12.2"
clip-path="url(#terminal-4108169915-line-21)">
-</text><text class="terminal-4108169915-r4" x="73.2" y="556.8"
textLength="85.4"
clip-path="url(#terminal-4108169915-line-22)">version</text><text
class="terminal-4108169915-r2" x="244" y="556.8" textLength="292.8"
clip-path="url(#terminal-4108169915-line-22)">Show version information</text><text
class="terminal-4108169915-r2" x="915" y="556.8" textLength="12.2"
clip-path="url(#terminal-4108169915-line-22)">
-</text><text class="terminal-4108169915-r2" x="915" y="581.2"
textLength="12.2" clip-path="url(#terminal-4108169915-line-23)">
-</text><text class="terminal-4108169915-r1" x="0" y="605.6" textLength="97.6"
clip-path="url(#terminal-4108169915-line-24)">Options:</text><text
class="terminal-4108169915-r2" x="915" y="605.6" textLength="12.2"
clip-path="url(#terminal-4108169915-line-24)">
-</text><text class="terminal-4108169915-r4" x="24.4" y="630" textLength="24.4"
clip-path="url(#terminal-4108169915-line-25)">-h</text><text
class="terminal-4108169915-r2" x="48.8" y="630" textLength="24.4"
clip-path="url(#terminal-4108169915-line-25)">, </text><text
class="terminal-4108169915-r4" x="73.2" y="630" textLength="73.2"
clip-path="url(#terminal-4108169915-line-25)">--help</text><text
class="terminal-4108169915-r2" x="244" y="630" textLength="378.2"
clip-path="url(#termina [...]
+ <g class="terminal-2670787131-matrix">
+ <text class="terminal-2670787131-r1" x="0" y="20" textLength="73.2"
clip-path="url(#terminal-2670787131-line-0)">Usage:</text><text
class="terminal-2670787131-r3" x="85.4" y="20" textLength="122"
clip-path="url(#terminal-2670787131-line-0)">airflowctl</text><text
class="terminal-2670787131-r2" x="207.4" y="20" textLength="24.4"
clip-path="url(#terminal-2670787131-line-0)"> [</text><text
class="terminal-2670787131-r4" x="231.8" y="20" textLength="24.4"
clip-path="url(#terminal-26 [...]
+</text><text class="terminal-2670787131-r2" x="915" y="44.4" textLength="12.2"
clip-path="url(#terminal-2670787131-line-1)">
+</text><text class="terminal-2670787131-r1" x="0" y="68.8" textLength="256.2"
clip-path="url(#terminal-2670787131-line-2)">Positional Arguments:</text><text
class="terminal-2670787131-r2" x="915" y="68.8" textLength="12.2"
clip-path="url(#terminal-2670787131-line-2)">
+</text><text class="terminal-2670787131-r4" x="24.4" y="93.2"
textLength="195.2"
clip-path="url(#terminal-2670787131-line-3)">GROUP_OR_COMMAND</text><text
class="terminal-2670787131-r2" x="915" y="93.2" textLength="12.2"
clip-path="url(#terminal-2670787131-line-3)">
+</text><text class="terminal-2670787131-r2" x="915" y="117.6"
textLength="12.2" clip-path="url(#terminal-2670787131-line-4)">
+</text><text class="terminal-2670787131-r4" x="0" y="142" textLength="122"
clip-path="url(#terminal-2670787131-line-5)">    Groups</text><text
class="terminal-2670787131-r2" x="915" y="142" textLength="12.2"
clip-path="url(#terminal-2670787131-line-5)">
+</text><text class="terminal-2670787131-r4" x="73.2" y="166.4"
textLength="73.2"
clip-path="url(#terminal-2670787131-line-6)">assets</text><text
class="terminal-2670787131-r2" x="244" y="166.4" textLength="305"
clip-path="url(#terminal-2670787131-line-6)">Perform Assets operations</text><text
class="terminal-2670787131-r2" x="915" y="166.4" textLength="12.2"
clip-path="url(#terminal-2670787131-line-6)">
+</text><text class="terminal-2670787131-r4" x="73.2" y="190.8"
textLength="48.8" clip-path="url(#terminal-2670787131-line-7)">auth</text><text
class="terminal-2670787131-r2" x="244" y="190.8" textLength="646.6"
clip-path="url(#terminal-2670787131-line-7)">Manage authentication for CLI. Either pass token from</text><text
class="terminal-2670787131-r2" x="915" y="190.8" textLength="12.2"
clip-path="url(#terminal-2670787131-line-7)">
+</text><text class="terminal-2670787131-r2" x="244" y="215.2"
textLength="622.2"
clip-path="url(#terminal-2670787131-line-8)">environment variable/parameter or pass username and</text><text
class="terminal-2670787131-r2" x="915" y="215.2" textLength="12.2"
clip-path="url(#terminal-2670787131-line-8)">
+</text><text class="terminal-2670787131-r2" x="244" y="239.6"
textLength="109.8"
clip-path="url(#terminal-2670787131-line-9)">password.</text><text
class="terminal-2670787131-r2" x="915" y="239.6" textLength="12.2"
clip-path="url(#terminal-2670787131-line-9)">
+</text><text class="terminal-2670787131-r4" x="73.2" y="264" textLength="97.6"
clip-path="url(#terminal-2670787131-line-10)">backfill</text><text
class="terminal-2670787131-r2" x="244" y="264" textLength="329.4"
clip-path="url(#terminal-2670787131-line-10)">Perform Backfill operations</text><text
class="terminal-2670787131-r2" x="915" y="264" textLength="12.2"
clip-path="url(#terminal-2670787131-line-10)">
+</text><text class="terminal-2670787131-r4" x="73.2" y="288.4"
textLength="73.2"
clip-path="url(#terminal-2670787131-line-11)">config</text><text
class="terminal-2670787131-r2" x="244" y="288.4" textLength="305"
clip-path="url(#terminal-2670787131-line-11)">Perform Config operations</text><text
class="terminal-2670787131-r2" x="915" y="288.4" textLength="12.2"
clip-path="url(#terminal-2670787131-line-11)">
+</text><text class="terminal-2670787131-r4" x="73.2" y="312.8"
textLength="134.2"
clip-path="url(#terminal-2670787131-line-12)">connections</text><text
class="terminal-2670787131-r2" x="244" y="312.8" textLength="366"
clip-path="url(#terminal-2670787131-line-12)">Perform Connections operations</text><text
class="terminal-2670787131-r2" x="915" y="312.8" textLength="12.2"
clip-path="url(#terminal-2670787131-line-12)">
+</text><text class="terminal-2670787131-r4" x="73.2" y="337.2"
textLength="73.2"
clip-path="url(#terminal-2670787131-line-13)">dagrun</text><text
class="terminal-2670787131-r2" x="244" y="337.2" textLength="305"
clip-path="url(#terminal-2670787131-line-13)">Perform DagRun operations</text><text
class="terminal-2670787131-r2" x="915" y="337.2" textLength="12.2"
clip-path="url(#terminal-2670787131-line-13)">
+</text><text class="terminal-2670787131-r4" x="73.2" y="361.6"
textLength="48.8"
clip-path="url(#terminal-2670787131-line-14)">dags</text><text
class="terminal-2670787131-r2" x="244" y="361.6" textLength="280.6"
clip-path="url(#terminal-2670787131-line-14)">Perform Dags operations</text><text
class="terminal-2670787131-r2" x="915" y="361.6" textLength="12.2"
clip-path="url(#terminal-2670787131-line-14)">
+</text><text class="terminal-2670787131-r4" x="73.2" y="386" textLength="48.8"
clip-path="url(#terminal-2670787131-line-15)">jobs</text><text
class="terminal-2670787131-r2" x="244" y="386" textLength="280.6"
clip-path="url(#terminal-2670787131-line-15)">Perform Jobs operations</text><text
class="terminal-2670787131-r2" x="915" y="386" textLength="12.2"
clip-path="url(#terminal-2670787131-line-15)">
+</text><text class="terminal-2670787131-r4" x="73.2" y="410.4"
textLength="85.4"
clip-path="url(#terminal-2670787131-line-16)">plugins</text><text
class="terminal-2670787131-r2" x="244" y="410.4" textLength="317.2"
clip-path="url(#terminal-2670787131-line-16)">Perform Plugins operations</text><text
class="terminal-2670787131-r2" x="915" y="410.4" textLength="12.2"
clip-path="url(#terminal-2670787131-line-16)">
+</text><text class="terminal-2670787131-r4" x="73.2" y="434.8" textLength="61"
clip-path="url(#terminal-2670787131-line-17)">pools</text><text
class="terminal-2670787131-r2" x="244" y="434.8" textLength="292.8"
clip-path="url(#terminal-2670787131-line-17)">Perform Pools operations</text><text
class="terminal-2670787131-r2" x="915" y="434.8" textLength="12.2"
clip-path="url(#terminal-2670787131-line-17)">
+</text><text class="terminal-2670787131-r4" x="73.2" y="459.2"
textLength="109.8"
clip-path="url(#terminal-2670787131-line-18)">providers</text><text
class="terminal-2670787131-r2" x="244" y="459.2" textLength="341.6"
clip-path="url(#terminal-2670787131-line-18)">Perform Providers operations</text><text
class="terminal-2670787131-r2" x="915" y="459.2" textLength="12.2"
clip-path="url(#terminal-2670787131-line-18)">
+</text><text class="terminal-2670787131-r4" x="73.2" y="483.6"
textLength="109.8"
clip-path="url(#terminal-2670787131-line-19)">variables</text><text
class="terminal-2670787131-r2" x="244" y="483.6" textLength="341.6"
clip-path="url(#terminal-2670787131-line-19)">Perform Variables operations</text><text
class="terminal-2670787131-r2" x="915" y="483.6" textLength="12.2"
clip-path="url(#terminal-2670787131-line-19)">
+</text><text class="terminal-2670787131-r4" x="73.2" y="508" textLength="48.8"
clip-path="url(#terminal-2670787131-line-20)">xcom</text><text
class="terminal-2670787131-r2" x="244" y="508" textLength="280.6"
clip-path="url(#terminal-2670787131-line-20)">Perform XCom operations</text><text
class="terminal-2670787131-r2" x="915" y="508" textLength="12.2"
clip-path="url(#terminal-2670787131-line-20)">
+</text><text class="terminal-2670787131-r2" x="915" y="532.4"
textLength="12.2" clip-path="url(#terminal-2670787131-line-21)">
+</text><text class="terminal-2670787131-r4" x="0" y="556.8" textLength="158.6"
clip-path="url(#terminal-2670787131-line-22)">    Commands:</text><text
class="terminal-2670787131-r2" x="915" y="556.8" textLength="12.2"
clip-path="url(#terminal-2670787131-line-22)">
+</text><text class="terminal-2670787131-r4" x="73.2" y="581.2"
textLength="85.4"
clip-path="url(#terminal-2670787131-line-23)">version</text><text
class="terminal-2670787131-r2" x="244" y="581.2" textLength="292.8"
clip-path="url(#terminal-2670787131-line-23)">Show version information</text><text
class="terminal-2670787131-r2" x="915" y="581.2" textLength="12.2"
clip-path="url(#terminal-2670787131-line-23)">
+</text><text class="terminal-2670787131-r2" x="915" y="605.6"
textLength="12.2" clip-path="url(#terminal-2670787131-line-24)">
+</text><text class="terminal-2670787131-r1" x="0" y="630" textLength="97.6"
clip-path="url(#terminal-2670787131-line-25)">Options:</text><text
class="terminal-2670787131-r2" x="915" y="630" textLength="12.2"
clip-path="url(#terminal-2670787131-line-25)">
+</text><text class="terminal-2670787131-r4" x="24.4" y="654.4"
textLength="24.4" clip-path="url(#terminal-2670787131-line-26)">-h</text><text
class="terminal-2670787131-r2" x="48.8" y="654.4" textLength="24.4"
clip-path="url(#terminal-2670787131-line-26)">, </text><text
class="terminal-2670787131-r4" x="73.2" y="654.4" textLength="73.2"
clip-path="url(#terminal-2670787131-line-26)">--help</text><text
class="terminal-2670787131-r2" x="244" y="654.4" textLength="378.2"
clip-path="url( [...]
</text>
</g>
</g>
diff --git a/airflow-ctl/docs/images/output_plugins.svg
b/airflow-ctl/docs/images/output_plugins.svg
new file mode 100644
index 00000000000..21dfd097508
--- /dev/null
+++ b/airflow-ctl/docs/images/output_plugins.svg
@@ -0,0 +1,105 @@
+<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>
+
+ @font-face {
+ font-family: "Fira Code";
+ src: local("FiraCode-Regular"),
+
url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff2/FiraCode-Regular.woff2")
format("woff2"),
+
url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff/FiraCode-Regular.woff")
format("woff");
+ font-style: normal;
+ font-weight: 400;
+ }
+ @font-face {
+ font-family: "Fira Code";
+ src: local("FiraCode-Bold"),
+
url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff2/FiraCode-Bold.woff2")
format("woff2"),
+
url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff/FiraCode-Bold.woff")
format("woff");
+ font-style: bold;
+ font-weight: 700;
+ }
+
+ .terminal-2458333931-matrix {
+ font-family: Fira Code, monospace;
+ font-size: 20px;
+ line-height: 24.4px;
+ font-variant-east-asian: full-width;
+ }
+
+ .terminal-2458333931-title {
+ font-size: 18px;
+ font-weight: bold;
+ font-family: arial;
+ }
+
+ .terminal-2458333931-r1 { fill: #ff8700 }
+.terminal-2458333931-r2 { fill: #c5c8c6 }
+.terminal-2458333931-r3 { fill: #808080 }
+.terminal-2458333931-r4 { fill: #68a0b3 }
+ </style>
+
+ <defs>
+ <clipPath id="terminal-2458333931-clip-terminal">
+ <rect x="0" y="0" width="914.0" height="291.79999999999995" />
+ </clipPath>
+ <clipPath id="terminal-2458333931-line-0">
+ <rect x="0" y="1.5" width="915" height="24.65"/>
+ </clipPath>
+<clipPath id="terminal-2458333931-line-1">
+ <rect x="0" y="25.9" width="915" height="24.65"/>
+ </clipPath>
+<clipPath id="terminal-2458333931-line-2">
+ <rect x="0" y="50.3" width="915" height="24.65"/>
+ </clipPath>
+<clipPath id="terminal-2458333931-line-3">
+ <rect x="0" y="74.7" width="915" height="24.65"/>
+ </clipPath>
+<clipPath id="terminal-2458333931-line-4">
+ <rect x="0" y="99.1" width="915" height="24.65"/>
+ </clipPath>
+<clipPath id="terminal-2458333931-line-5">
+ <rect x="0" y="123.5" width="915" height="24.65"/>
+ </clipPath>
+<clipPath id="terminal-2458333931-line-6">
+ <rect x="0" y="147.9" width="915" height="24.65"/>
+ </clipPath>
+<clipPath id="terminal-2458333931-line-7">
+ <rect x="0" y="172.3" width="915" height="24.65"/>
+ </clipPath>
+<clipPath id="terminal-2458333931-line-8">
+ <rect x="0" y="196.7" width="915" height="24.65"/>
+ </clipPath>
+<clipPath id="terminal-2458333931-line-9">
+ <rect x="0" y="221.1" width="915" height="24.65"/>
+ </clipPath>
+<clipPath id="terminal-2458333931-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="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-2458333931-clip-terminal)">
+
+ <g class="terminal-2458333931-matrix">
+ <text class="terminal-2458333931-r1" x="0" y="20" textLength="73.2"
clip-path="url(#terminal-2458333931-line-0)">Usage:</text><text
class="terminal-2458333931-r3" x="85.4" y="20" textLength="219.6"
clip-path="url(#terminal-2458333931-line-0)">airflowctl plugins</text><text
class="terminal-2458333931-r2" x="305" y="20" textLength="24.4"
clip-path="url(#terminal-2458333931-line-0)"> [</text><text
class="terminal-2458333931-r4" x="329.4" y="20" textLength="24.4"
clip-path="url [...]
+</text><text class="terminal-2458333931-r2" x="915" y="44.4" textLength="12.2"
clip-path="url(#terminal-2458333931-line-1)">
+</text><text class="terminal-2458333931-r2" x="0" y="68.8" textLength="317.2"
clip-path="url(#terminal-2458333931-line-2)">Perform Plugins operations</text><text
class="terminal-2458333931-r2" x="915" y="68.8" textLength="12.2"
clip-path="url(#terminal-2458333931-line-2)">
+</text><text class="terminal-2458333931-r2" x="915" y="93.2" textLength="12.2"
clip-path="url(#terminal-2458333931-line-3)">
+</text><text class="terminal-2458333931-r1" x="0" y="117.6" textLength="256.2"
clip-path="url(#terminal-2458333931-line-4)">Positional Arguments:</text><text
class="terminal-2458333931-r2" x="915" y="117.6" textLength="12.2"
clip-path="url(#terminal-2458333931-line-4)">
+</text><text class="terminal-2458333931-r4" x="24.4" y="142" textLength="85.4"
clip-path="url(#terminal-2458333931-line-5)">COMMAND</text><text
class="terminal-2458333931-r2" x="915" y="142" textLength="12.2"
clip-path="url(#terminal-2458333931-line-5)">
+</text><text class="terminal-2458333931-r4" x="48.8" y="166.4"
textLength="48.8" clip-path="url(#terminal-2458333931-line-6)">list</text><text
class="terminal-2458333931-r2" x="268.4" y="166.4" textLength="268.4"
clip-path="url(#terminal-2458333931-line-6)">Perform list operation</text><text
class="terminal-2458333931-r2" x="915" y="166.4" textLength="12.2"
clip-path="url(#terminal-2458333931-line-6)">
+</text><text class="terminal-2458333931-r4" x="48.8" y="190.8"
textLength="219.6"
clip-path="url(#terminal-2458333931-line-7)">list-import-errors</text><text
class="terminal-2458333931-r2" x="915" y="190.8" textLength="12.2"
clip-path="url(#terminal-2458333931-line-7)">
+</text><text class="terminal-2458333931-r2" x="268.4" y="215.2"
textLength="439.2"
clip-path="url(#terminal-2458333931-line-8)">Perform list_import_errors operation</text><text
class="terminal-2458333931-r2" x="915" y="215.2" textLength="12.2"
clip-path="url(#terminal-2458333931-line-8)">
+</text><text class="terminal-2458333931-r2" x="915" y="239.6"
textLength="12.2" clip-path="url(#terminal-2458333931-line-9)">
+</text><text class="terminal-2458333931-r1" x="0" y="264" textLength="97.6"
clip-path="url(#terminal-2458333931-line-10)">Options:</text><text
class="terminal-2458333931-r2" x="915" y="264" textLength="12.2"
clip-path="url(#terminal-2458333931-line-10)">
+</text><text class="terminal-2458333931-r4" x="24.4" y="288.4"
textLength="24.4" clip-path="url(#terminal-2458333931-line-11)">-h</text><text
class="terminal-2458333931-r2" x="48.8" y="288.4" textLength="24.4"
clip-path="url(#terminal-2458333931-line-11)">, </text><text
class="terminal-2458333931-r4" x="73.2" y="288.4" textLength="73.2"
clip-path="url(#terminal-2458333931-line-11)">--help</text><text
class="terminal-2458333931-r2" x="268.4" y="288.4" textLength="378.2"
clip-path="ur [...]
+</text>
+ </g>
+ </g>
+</svg>
diff --git a/airflow-ctl/src/airflowctl/api/client.py
b/airflow-ctl/src/airflowctl/api/client.py
index a8b3d3931d6..51786bfcfcb 100644
--- a/airflow-ctl/src/airflowctl/api/client.py
+++ b/airflow-ctl/src/airflowctl/api/client.py
@@ -52,6 +52,7 @@ from airflowctl.api.operations import (
DagsOperations,
JobsOperations,
LoginOperations,
+ PluginsOperations,
PoolsOperations,
ProvidersOperations,
ServerResponseError,
@@ -434,6 +435,12 @@ class Client(httpx.Client):
"""Operations related to XComs."""
return XComOperations(self)
+ @lru_cache() # type: ignore[prop-decorator]
+ @property
+ def plugins(self):
+ """Operations related to plugins."""
+ return PluginsOperations(self)
+
# API Client Decorator for CLI Actions
@contextlib.contextmanager
diff --git a/airflow-ctl/src/airflowctl/api/operations.py
b/airflow-ctl/src/airflowctl/api/operations.py
index 6b382252505..a2b2b43ab2f 100644
--- a/airflow-ctl/src/airflowctl/api/operations.py
+++ b/airflow-ctl/src/airflowctl/api/operations.py
@@ -59,6 +59,8 @@ from airflowctl.api.datamodels.generated import (
ImportErrorCollectionResponse,
ImportErrorResponse,
JobCollectionResponse,
+ PluginCollectionResponse,
+ PluginImportErrorCollectionResponse,
PoolBody,
PoolCollectionResponse,
PoolPatchBody,
@@ -893,3 +895,19 @@ class XComOperations(BaseOperations):
return key
except ServerResponseError as e:
raise e
+
+
+class PluginsOperations(BaseOperations):
+ """Plugins operations."""
+
+ def list(self) -> PluginCollectionResponse | ServerResponseError:
+ """List all plugins from the API server."""
+ return super().execute_list(path="plugins",
data_model=PluginCollectionResponse)
+
+ def list_import_errors(self) -> PluginImportErrorCollectionResponse |
ServerResponseError:
+ """List plugin import errors from the API server."""
+ try:
+ self.response = self.client.get("plugins/importErrors")
+ return
PluginImportErrorCollectionResponse.model_validate_json(self.response.content)
+ except ServerResponseError as e:
+ raise e
diff --git a/airflow-ctl/tests/airflow_ctl/api/test_operations.py
b/airflow-ctl/tests/airflow_ctl/api/test_operations.py
index 44c5ab3231b..c7b60be3c5c 100644
--- a/airflow-ctl/tests/airflow_ctl/api/test_operations.py
+++ b/airflow-ctl/tests/airflow_ctl/api/test_operations.py
@@ -79,6 +79,10 @@ from airflowctl.api.datamodels.generated import (
ImportErrorResponse,
JobCollectionResponse,
JobResponse,
+ PluginCollectionResponse,
+ PluginImportErrorCollectionResponse,
+ PluginImportErrorResponse,
+ PluginResponse,
PoolBody,
PoolCollectionResponse,
PoolResponse,
@@ -1732,3 +1736,53 @@ class TestXComOperations:
map_index=self.map_index,
)
assert response == self.key
+
+
+class TestPluginsOperations:
+ plugin_response = PluginResponse(
+ name="test-plugin",
+ macros=[],
+ flask_blueprints=[],
+ fastapi_apps=[],
+ fastapi_root_middlewares=[],
+ external_views=[],
+ react_apps=[],
+ appbuilder_views=[],
+ appbuilder_menu_items=[],
+ global_operator_extra_links=[],
+ operator_extra_links=[],
+ source="test-source",
+ listeners=[],
+ timetables=[],
+ )
+ plugin_collection_response =
PluginCollectionResponse(plugins=[plugin_response], total_entries=1)
+ plugin_import_error_response = PluginImportErrorResponse(
+ source="plugins/test_plugin.py", error="something went wrong"
+ )
+ plugin_import_error_collection_response =
PluginImportErrorCollectionResponse(
+ import_errors=[plugin_import_error_response], total_entries=1
+ )
+
+ def test_list(self):
+ """Test listing plugins"""
+
+ def handle_request(request: httpx.Request) -> httpx.Response:
+ assert request.url.path == ("/api/v2/plugins")
+ return httpx.Response(200,
json=json.loads(self.plugin_collection_response.model_dump_json()))
+
+ client = make_api_client(transport=httpx.MockTransport(handle_request))
+ response = client.plugins.list()
+ assert response == self.plugin_collection_response
+
+ def test_list_import_errors(self):
+ """Test listing plugin import errors"""
+
+ def handle_request(request: httpx.Request) -> httpx.Response:
+ assert request.url.path == "/api/v2/plugins/importErrors"
+ return httpx.Response(
+ 200,
json=json.loads(self.plugin_import_error_collection_response.model_dump_json())
+ )
+
+ client = make_api_client(transport=httpx.MockTransport(handle_request))
+ response = client.plugins.list_import_errors()
+ assert response == self.plugin_import_error_collection_response
diff --git a/scripts/in_container/run_capture_airflowctl_help.py
b/scripts/in_container/run_capture_airflowctl_help.py
index 3fac46ebd56..9529dbe0439 100644
--- a/scripts/in_container/run_capture_airflowctl_help.py
+++ b/scripts/in_container/run_capture_airflowctl_help.py
@@ -47,6 +47,7 @@ COMMANDS = [
"providers",
"variables",
"version",
+ "plugins",
]
SUBCOMMANDS = [