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

bugraoz 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 180a9d837a7 feat (airflowctl): Initial airflowctl docs (#49024)
180a9d837a7 is described below

commit 180a9d837a70a181d7766d52995b9d8ca668640e
Author: Bugra Ozturk <bugrao...@users.noreply.github.com>
AuthorDate: Fri May 16 00:20:35 2025 +0200

    feat (airflowctl): Initial airflowctl docs (#49024)
    
    * feat (airflowctl): AIP-81 Initial airflowctl docs and update docs 
according to comments
---
 airflow-ctl/README.md                              |  22 ++
 airflow-ctl/docs/changelog.rst                     |  31 +++
 airflow-ctl/docs/cli-and-env-variables-ref.rst     |  55 ++++
 airflow-ctl/docs/conf.py                           | 294 +++++++++++++++++++++
 airflow-ctl/docs/howto/index.rst                   |  77 ++++++
 airflow-ctl/docs/index.rst                         |  36 +++
 airflow-ctl/docs/installation/dependencies.rst     |  26 ++
 airflow-ctl/docs/installation/index.rst            | 134 ++++++++++
 .../docs/installation/installing-from-pypi.rst     | 163 ++++++++++++
 .../docs/installation/installing-from-sources.rst  | 163 ++++++++++++
 airflow-ctl/docs/installation/prerequisites.rst    |  63 +++++
 .../docs/installation/supported-versions.rst       |  42 +++
 .../airflowctl/__init__.py => docs/redirects.txt}  |  13 +-
 airflow-ctl/docs/security.rst                      |  27 ++
 airflow-ctl/docs/start.rst                         |  42 +++
 airflow-ctl/docs/static/exampleinclude.css         |  86 ++++++
 airflow-ctl/docs/static/gh-jira-links.js           |  36 +++
 airflow-ctl/docs/static/redirects.js               |  29 ++
 airflow-ctl/docs/templates/footer.html             |  29 ++
 airflow-ctl/pyproject.toml                         |  22 +-
 airflow-ctl/src/airflowctl/__init__.py             |   2 +-
 airflow-ctl/src/airflowctl/api/operations.py       |   5 +-
 airflow-ctl/src/airflowctl/ctl/cli_config.py       |  37 ++-
 airflow-ctl/src/airflowctl/ctl/cli_parser.py       |  12 +-
 airflow-ctl/src/airflowctl/exceptions.py           |   4 +
 .../tests/airflow_ctl/api/test_operations.py       |  12 +-
 .../tests/airflow_ctl/ctl/test_cli_config.py       |  14 +-
 devel-common/src/docs/utils/conf_constants.py      |   5 +
 .../src/sphinx_exts/docs_build/docs_builder.py     |   5 +
 29 files changed, 1455 insertions(+), 31 deletions(-)

diff --git a/airflow-ctl/README.md b/airflow-ctl/README.md
index 6c2908b7431..8e543717d9b 100644
--- a/airflow-ctl/README.md
+++ b/airflow-ctl/README.md
@@ -33,11 +33,14 @@ A command-line tool for interacting with Apache Airflow 
instances through the Ai
 
 - Python 3.9 or later (compatible with Python >= 3.9 and < 3.13)
 - Network access to an Apache Airflow instance with REST API enabled
+- Keyring backend installed in operating system for secure token storage
 
 ## Usage
 
 Access the tool from your terminal:
 
+### Command Line
+
 ```bash
 airflowctl --help
 ```
@@ -45,3 +48,22 @@ airflowctl --help
 ## Contributing
 
 Want to help improve Apache Airflow? Check out our [contributing 
documentation](https://github.com/apache/airflow/blob/main/contributing-docs/README.rst).
+
+### Additional Contribution Guidelines
+
+- Please ensure API is running while doing development testing.
+- There are two ways to have a CLI command,
+  - Auto Generated Commands
+  - Implemented Commands
+
+#### Auto Generated Commands
+
+Auto generation of commands directly from operations methods under 
`airflow-ctl/src/airflowctl/api/operations.py`.
+Whenever operation is mapped with proper datamodel and response model, it will 
be automatically added to the command.
+
+You can check each command with `airflowctl <command> --help` to see the 
available options.
+
+#### Implemented Commands
+
+Implemented commands are the ones which are not auto generated and need to be 
implemented manually.
+You can check the implemented commands under 
`airflow-ctl/src/airflowctl/clt/commands/`.
diff --git a/airflow-ctl/docs/changelog.rst b/airflow-ctl/docs/changelog.rst
new file mode 100644
index 00000000000..50a8a3b7757
--- /dev/null
+++ b/airflow-ctl/docs/changelog.rst
@@ -0,0 +1,31 @@
+ .. Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+
+ ..   http://www.apache.org/licenses/LICENSE-2.0
+
+ .. Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+
+
+.. NOTE TO CONTRIBUTORS:
+   Please, only add notes to the Changelog just below the "Changelog" header 
when there are some breaking changes
+   and you want to add an explanation to the users on how they are supposed to 
deal with them.
+   The changelog is updated and maintained semi-automatically by release 
manager.
+
+``apache-airflow-ctl``
+
+Changelog
+---------
+
+1.0.0
+.....
+Initial version of the Airflow CTL.
diff --git a/airflow-ctl/docs/cli-and-env-variables-ref.rst 
b/airflow-ctl/docs/cli-and-env-variables-ref.rst
new file mode 100644
index 00000000000..db45a5d2e27
--- /dev/null
+++ b/airflow-ctl/docs/cli-and-env-variables-ref.rst
@@ -0,0 +1,55 @@
+ .. Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+
+ ..   http://www.apache.org/licenses/LICENSE-2.0
+
+ .. Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+
+Command Line Interface and Environment Variables Reference
+==========================================================
+
+Command Line Interface
+''''''''''''''''''''''
+
+Airflow CTL has a very rich command line interface that allows for
+many types of operation on a DAG, starting services, and supporting
+development and testing.
+
+.. note::
+    For more information on usage CLI, see :doc:`cli-and-env-variables-ref`
+
+.. contents:: Content
+    :local:
+    :depth: 2
+
+.. argparse::
+   :module: airflowctl.ctl.cli_parser
+   :func: get_parser
+   :prog: airflowctl
+
+Environment Variables
+'''''''''''''''''''''
+
+.. envvar:: AIRFLOW_CLI_TOKEN
+
+    The token used to authenticate with the Airflow API. This is only
+    required if you are using the Airflow API and have not set up
+    authentication using a different method. If username and password hasn't 
been used.
+
+.. envvar:: AIRFLOW_CLI_ENVIRONMENT
+
+    Environment name to use for the CLI. This is used to determine
+    which environment to use when running the CLI. This is only
+    required if you have multiple environments set up and want to
+    specify which one to use. If not set, the default environment
+    will be used which is production.
diff --git a/airflow-ctl/docs/conf.py b/airflow-ctl/docs/conf.py
new file mode 100644
index 00000000000..d075866c8bd
--- /dev/null
+++ b/airflow-ctl/docs/conf.py
@@ -0,0 +1,294 @@
+# Disable Flake8 because of all the sphinx imports
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+"""Configuration of Providers docs building."""
+
+from __future__ import annotations
+
+import logging
+import os
+import pathlib
+import re
+from typing import Any
+
+from docs.utils.conf_constants import (
+    AIRFLOW_CTL_DOC_STATIC_PATH,
+    AIRFLOW_CTL_SRC_PATH,
+    AIRFLOW_FAVICON_PATH,
+    AUTOAPI_OPTIONS,
+    BASIC_AUTOAPI_IGNORE_PATTERNS,
+    BASIC_SPHINX_EXTENSIONS,
+    REDOC_SCRIPT_URL,
+    SMARTQUOTES_EXCLUDES,
+    SPELLING_WORDLIST_PATH,
+    SPHINX_DESIGN_STATIC_PATH,
+    SPHINX_REDOC_EXTENSIONS,
+    SUPPRESS_WARNINGS,
+    filter_autoapi_ignore_entries,
+    get_autodoc_mock_imports,
+    get_configs_and_deprecations,
+    get_google_intersphinx_mapping,
+    get_html_context,
+    get_html_sidebars,
+    get_html_theme_options,
+    get_intersphinx_mapping,
+    get_rst_epilogue,
+    get_rst_filepath_from_path,
+    skip_util_classes_extension,
+)
+from packaging.version import Version, parse as parse_version
+
+import airflowctl
+from airflow.configuration import retrieve_configuration_description
+
+PACKAGE_NAME = "apache-airflow-ctl"
+PACKAGE_VERSION = airflowctl.__version__
+SYSTEM_TESTS_DIR: pathlib.Path | None
+# SYSTEM_TESTS_DIR = AIRFLOW_REPO_ROOT_PATH / "airflow-ctl" / "tests" / 
"system" / "core"
+
+os.environ["AIRFLOW_PACKAGE_NAME"] = PACKAGE_NAME
+
+# Hack to allow changing for piece of the code to behave differently while
+# the docs are being built. The main objective was to alter the
+# behavior of the utils.apply_default that was hiding function headers
+os.environ["BUILDING_AIRFLOW_DOCS"] = "TRUE"
+
+# General information about the project.
+project = PACKAGE_NAME
+# # The version info for the project you're documenting
+version = PACKAGE_VERSION
+# The full version, including alpha/beta/rc tags.
+release = PACKAGE_VERSION
+
+rst_epilog = get_rst_epilogue(PACKAGE_VERSION, False)
+
+# The language for content autogenerated by Sphinx. Refer to documentation
+smartquotes_excludes = SMARTQUOTES_EXCLUDES
+
+# Add any Sphinx extension module names here, as strings. They can be
+# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
+# ones.
+extensions = BASIC_SPHINX_EXTENSIONS
+
+# -- Options for sphinxcontrib.redoc 
-------------------------------------------
+# See: https://sphinxcontrib-redoc.readthedocs.io/en/stable/
+
+extensions.extend(SPHINX_REDOC_EXTENSIONS)
+redoc_script_url = REDOC_SCRIPT_URL
+
+extensions.extend(
+    [
+        "autoapi.extension",
+        "sphinx_jinja",
+        "sphinx.ext.graphviz",
+        "sphinxcontrib.httpdomain",
+        "extra_files_with_substitutions",
+    ]
+)
+
+exclude_patterns = [
+    # We only link to selected subpackages.
+    "_api/airflowctl/index.rst",
+    "_api/airflowctl/api/*",
+    "_api/airflowctl/api/datamodels/*",
+    "_api/airflowctl/ctl/*",
+    "_api/airflowctl/exceptions/index.rst",
+    "_api/airflowctl/utils/*",
+    "README.rst",
+]
+
+# Exclude top-level packages
+# do not exclude these top-level modules from the doc build:
+ALLOWED_TOP_LEVEL_FILES = ("exceptions.py",)
+
+
+def add_airflow_ctl_exclude_patterns_to_sphinx(exclude_patterns: list[str]):
+    """
+    Add excluded files to Sphinx exclude patterns.
+
+    Excludes all files from autoapi except the ones we want to allow.
+
+    :param root: The root directory of the package.
+    :param allowed_top_level_files: Tuple of allowed top-level files.
+    :param browsable_packages: Set of browsable packages.
+    :param browsable_utils: Set of browsable utils.
+    :param models_included: Set of included models.
+    """
+    # first - excluded everything that is not allowed or browsable
+    root = AIRFLOW_CTL_SRC_PATH / "airflowctl"
+    for path in root.iterdir():
+        if path.is_file() and path.name not in ALLOWED_TOP_LEVEL_FILES:
+            exclude_patterns.append(get_rst_filepath_from_path(path, 
root.parent))
+            print(f"Excluding {path} from Sphinx docs")
+
+
+add_airflow_ctl_exclude_patterns_to_sphinx(exclude_patterns)
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ["templates"]
+
+# If true, keep warnings as "system message" paragraphs in the built documents.
+keep_warnings = True
+
+# The theme to use for HTML and HTML Help pages.  See the documentation for
+# a list of builtin themes.
+html_theme = "sphinx_airflow_theme"
+
+html_title = "Airflow CTL Documentation"
+
+# A shorter title for the navigation bar.  Default is the same as html_title.
+html_short_title = ""
+
+#  given, this must be the name of an image file that is the favicon of the 
docs
+html_favicon = AIRFLOW_FAVICON_PATH.as_posix()
+
+# Custom static files (such as style sheets) here,
+html_static_path = [AIRFLOW_CTL_DOC_STATIC_PATH.as_posix(), 
SPHINX_DESIGN_STATIC_PATH.as_posix()]
+
+# A list of JavaScript filenames.
+html_js_files = ["gh-jira-links.js", "redirects.js"]
+
+# Substitute in links
+manual_substitutions_in_generated_html = [
+    "installation/installing-from-pypi.html",
+    "installation/installing-from-sources.html",
+    "installation/prerequisites.html",
+]
+
+html_css_files = ["custom.css"]
+
+html_sidebars = get_html_sidebars(PACKAGE_VERSION)
+
+# If false, no index is generated.
+html_use_index = True
+
+# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
+html_show_copyright = False
+
+# html theme options
+html_theme_options: dict[str, Any] = get_html_theme_options()
+
+conf_py_path = "/airflow-ctl/docs/"
+# A dictionary of values to pass into the template engine's context for all 
pages.
+html_context = get_html_context(conf_py_path)
+
+# -- Options for sphinx_jinja ------------------------------------------
+# See: https://github.com/tardyp/sphinx-jinja
+airflowctl_version: Version = parse_version(
+    re.search(  # type: ignore[union-attr,arg-type]
+        r"__version__ = \"([0-9.]*)(\.dev[0-9]*)?\"",
+        (AIRFLOW_CTL_SRC_PATH / "airflowctl" / "__init__.py").read_text(),
+    ).groups(0)[0]
+)
+
+
+config_descriptions = 
retrieve_configuration_description(include_providers=False)
+configs, deprecated_options = get_configs_and_deprecations(airflowctl_version, 
config_descriptions)
+
+jinja_contexts = {
+    "config_ctx": {"configs": configs, "deprecated_options": 
deprecated_options},
+    "quick_start_ctx": {"doc_root_url": 
f"https://airflow.apache.org/docs/apache-airflow/{PACKAGE_VERSION}/"},
+    "official_download_page": {
+        "base_url": f"https://downloads.apache.org/airflow/{PACKAGE_VERSION}";,
+        "closer_lua_url": 
f"https://www.apache.org/dyn/closer.lua/airflow/{PACKAGE_VERSION}";,
+        "airflow_version": PACKAGE_VERSION,
+    },
+}
+
+# -- Options for sphinx.ext.autodoc 
--------------------------------------------
+# See: https://www.sphinx-doc.org/en/master/usage/extensions/autodoc.html
+
+# This value contains a list of modules to be mocked up. This is useful when 
some external dependencies
+# are not met at build time and break the building process.
+autodoc_mock_imports = get_autodoc_mock_imports()
+# The default options for autodoc directives. They are applied to all autodoc 
directives automatically.
+autodoc_default_options = {"show-inheritance": True, "members": True}
+
+autodoc_typehints = "description"
+autodoc_typehints_description_target = "documented"
+autodoc_typehints_format = "short"
+
+
+# -- Options for sphinx.ext.intersphinx 
----------------------------------------
+# See: https://www.sphinx-doc.org/en/master/usage/extensions/intersphinx.html
+
+# This config value contains names of other projects that should
+# be linked to in this documentation.
+# Inventories are only downloaded once by exts/docs_build/fetch_inventories.py.
+intersphinx_mapping = get_intersphinx_mapping()
+intersphinx_mapping.update(get_google_intersphinx_mapping())
+
+# -- Options for sphinx.ext.viewcode 
-------------------------------------------
+# See: https://www.sphinx-doc.org/es/master/usage/extensions/viewcode.html
+
+# If this is True, viewcode extension will emit viewcode-follow-imported event 
to resolve the name of
+# the module by other extensions. The default is True.
+viewcode_follow_imported_members = True
+
+# -- Options for sphinx-autoapi 
------------------------------------------------
+# See: https://sphinx-autoapi.readthedocs.io/en/latest/config.html
+
+# your API documentation from.
+autoapi_dirs = [AIRFLOW_CTL_SRC_PATH.as_posix()]
+
+# A directory that has user-defined templates to override our default 
templates.
+autoapi_template_dir = "autoapi_templates"
+
+# A list of patterns to ignore when finding files
+autoapi_ignore = BASIC_AUTOAPI_IGNORE_PATTERNS
+
+# filter logging
+autoapi_log = logging.getLogger("sphinx.autoapi.mappers.base")
+autoapi_log.addFilter(filter_autoapi_ignore_entries)
+
+# Keep the AutoAPI generated files on the filesystem after the run.
+# Useful for debugging.
+autoapi_keep_files = True
+
+# Relative path to output the AutoAPI files into. This can also be used to 
place the generated documentation
+# anywhere in your documentation hierarchy.
+autoapi_root = "_api"
+
+# Whether to insert the generated documentation into the TOC tree. If this is 
False, the default AutoAPI
+# index page is not generated and you will need to include the generated 
documentation in a
+# TOC tree entry yourself.
+autoapi_add_toctree_entry = False
+
+# By default autoapi will include private members -- we don't want that!
+autoapi_options = AUTOAPI_OPTIONS
+
+suppress_warnings = SUPPRESS_WARNINGS
+
+# -- Options for ext.exampleinclude 
--------------------------------------------
+exampleinclude_sourceroot = os.path.abspath("..")
+
+# -- Options for ext.redirects 
-------------------------------------------------
+redirects_file = "redirects.txt"
+
+# -- Options for sphinxcontrib-spelling 
----------------------------------------
+spelling_word_list_filename = [SPELLING_WORDLIST_PATH.as_posix()]
+spelling_exclude_patterns = ["project.rst", "changelog.rst"]
+
+spelling_ignore_contributor_names = False
+spelling_ignore_importable_modules = True
+
+graphviz_output_format = "svg"
+
+
+def setup(sphinx):
+    sphinx.connect("autoapi-skip-member", skip_util_classes_extension)
diff --git a/airflow-ctl/docs/howto/index.rst b/airflow-ctl/docs/howto/index.rst
new file mode 100644
index 00000000000..78de222b450
--- /dev/null
+++ b/airflow-ctl/docs/howto/index.rst
@@ -0,0 +1,77 @@
+ .. Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+
+ ..   http://www.apache.org/licenses/LICENSE-2.0
+
+ .. Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+
+
+
+How-to Guides
+=============
+
+Setting up the sandbox in the :doc:`/start` section was easy;
+building a production-grade environment requires a bit more work!
+
+These how-to guides will step you through common tasks in using and
+configuring an Airflow CTL environment.
+
+
+How to use Airflow CTL
+----------------------
+
+**Important Note**
+''''''''''''''''''
+Airflow CTL needs the Airflow API running to be able to work. Please, see the 
login section below before use.
+Otherwise, you may get errors.
+
+Login
+'''''
+Airflow CTL needs to be able to connect to the Airflow API. You should pass 
API URL as a parameter to the command
+``--api-url``. The URL should be in the form of ``http(s)://<host>:<port>``.
+You can also set the environment variable ``AIRFLOW_CLI_TOKEN`` to the token 
to use for authentication.
+
+There are two ways to authenticate with the Airflow API:
+1. Using a token acquired from the Airflow API
+
+.. code-block:: bash
+
+  airflowctl auth login --api-url <api_url> --api-token <token> --env 
<env_name:production>
+
+2. Using a username and password
+
+
+.. code-block:: bash
+
+  airflowctl auth login --api-url <api_url> --username <username> --password 
<password> --env <env_name:production>
+
+3. (optional) Using a token acquired from the Airflow API and username and 
password
+
+.. code-block:: bash
+
+  export AIRFLOW_CLI_TOKEN=<token>
+  airflowctl auth login --api-url <api_url> --env <env_name>
+
+In both cases token is securely stored in the keyring backend. Only 
configuration persisted in ``~/.config/airflow`` file
+is the API URL and the environment name. The token is stored in the keyring 
backend and is not persisted in the
+configuration file. The keyring backend is used to securely store the token 
and is not accessible to the user.
+
+
+For more information use
+
+.. code-block:: bash
+
+  airflowctl auth login --help
+
+You are ready to use Airflow CTL now. You can use the command ``airflowctl 
--help`` to see the list of available commands.
+Please, also see :doc:`/cli-and-env-variables-ref` for the list of available 
commands and options.
diff --git a/airflow-ctl/docs/index.rst b/airflow-ctl/docs/index.rst
new file mode 100644
index 00000000000..203cfc8845a
--- /dev/null
+++ b/airflow-ctl/docs/index.rst
@@ -0,0 +1,36 @@
+ .. Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+
+ ..   http://www.apache.org/licenses/LICENSE-2.0
+
+ .. Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+
+What is Airflow CTL®?
+=====================
+Airflow CTL is a command line tool that helps you manage and deploy Apache 
Airflow environments.
+
+.. toctree::
+    :hidden:
+    :caption: Content
+
+    installation/index
+    howto/index
+    security
+    changelog
+
+.. toctree::
+    :hidden:
+    :caption: Usage
+
+    start
+    cli-and-env-variables-ref
diff --git a/airflow-ctl/docs/installation/dependencies.rst 
b/airflow-ctl/docs/installation/dependencies.rst
new file mode 100644
index 00000000000..77d20b60907
--- /dev/null
+++ b/airflow-ctl/docs/installation/dependencies.rst
@@ -0,0 +1,26 @@
+ .. Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+
+ ..   http://www.apache.org/licenses/LICENSE-2.0
+
+ .. Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+
+Dependencies
+------------
+
+Airflow CTL dependencies
+''''''''''''''''''''''''''
+
+The ``apache-airflow-ctl`` PyPI basic package only installs what's needed to 
get started.
+Additional packages can be installed depending on what will be useful in your
+environment.
diff --git a/airflow-ctl/docs/installation/index.rst 
b/airflow-ctl/docs/installation/index.rst
new file mode 100644
index 00000000000..da6c78a905a
--- /dev/null
+++ b/airflow-ctl/docs/installation/index.rst
@@ -0,0 +1,134 @@
+ .. Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+
+ ..   http://www.apache.org/licenses/LICENSE-2.0
+
+ .. Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+
+
+Installation of Airflow CTL®
+----------------------------
+
+.. contents:: :local:
+
+.. toctree::
+    :maxdepth: 1
+    :caption: Installation
+    :hidden:
+
+    Prerequisites <prerequisites>
+    Dependencies <dependencies>
+    Supported versions <supported-versions>
+    Installing from sources <installing-from-sources>
+    Installing from PyPI <installing-from-pypi>
+
+This page describes installations options that you might use when considering 
how to install Airflow®.
+Airflow consists of many components, often distributed among many physical or 
virtual machines, therefore
+installation of Airflow might be quite complex, depending on the options you 
choose.
+
+
+Using released sources
+''''''''''''''''''''''
+
+More details: :doc:`installing-from-sources`
+
+**When this option works best**
+
+* This option is best if you expect to build all your software from sources.
+* Apache Airflow is one of the projects that belong to the `Apache Software 
Foundation <https://www.apache.org/>`__.
+  It is a requirement for all ASF projects that they can be installed using 
official sources released via `Official Apache Downloads 
<https://dlcdn.apache.org/>`__.
+* This is the best choice if you have a strong need to `verify the integrity 
and provenance of the software <https://www.apache.org/dyn/closer.cgi#verify>`__
+
+**Intended users**
+
+* Users who are familiar with installing and building software from sources 
and are conscious about integrity and provenance
+  of the software they use down to the lowest level possible.
+
+**What are you expected to handle**
+
+* You are expected to build and install airflow and its components on your own.
+* You should develop and handle the deployment for all components of Airflow.
+* You are responsible for setting up database, creating and managing database 
schema with ``airflow db`` commands,
+  automated startup and recovery, maintenance, cleanup and upgrades of Airflow 
and the Airflow Providers.
+* You need to setup monitoring of your system allowing you to observe 
resources and react to problems.
+* You are expected to configure and manage appropriate resources for the 
installation (memory, CPU, etc) based
+  on the monitoring of your installation and feedback loop. See the notes 
about requirements.
+
+**What Apache Airflow Community provides for that method**
+
+* You have `instructions 
<https://github.com/apache/airflow/blob/main/INSTALL>`__ on how to build the 
software but due to various environments
+  and tools you might want to use, you might expect that there will be 
problems which are specific to your deployment and environment
+  you will have to diagnose and solve.
+
+**Where to ask for help**
+
+* The ``#user-troubleshooting`` channel on slack can be used for quick general 
troubleshooting questions. The
+  `GitHub discussions <https://github.com/apache/airflow/discussions>`__ if 
you look for longer discussion and have more information to share.
+
+* The ``#user-best-practices`` channel on slack can be used to ask for and 
share best practices on using and deploying airflow.
+
+* If you can provide description of a reproducible problem with Airflow 
software, you can open issue at `GitHub issues 
<https://github.com/apache/airflow/issues>`_
+
+* If you want to contribute back to Airflow, the ``#contributors`` slack 
channel for building the Airflow itself
+
+
+Using PyPI
+'''''''''''
+
+More details:  :doc:`/installation/installing-from-pypi`
+
+**When this option works best**
+
+* This installation method is useful when you are not familiar with Containers 
and Docker and want to install
+  Apache Airflow on physical or virtual machines and you are used to 
installing and running software using custom
+  deployment mechanism.
+
+* The only officially supported mechanism of installation is via ``pip`` using 
constraint mechanisms. The constraint
+  files are managed by Apache Airflow release managers to make sure that you 
can repeatably install Airflow from PyPI with all Providers and
+  required dependencies.
+
+* In case of PyPI installation you could also verify integrity and provenance 
of the packages
+  downloaded from PyPI as described at the installation page, but software you 
download from PyPI is pre-built
+  for you so that you can install it without building, and you do not build 
the software from sources.
+
+**Intended users**
+
+* Users who are familiar with installing and configuring Python applications, 
managing Python environments,
+  dependencies and running software with their custom deployment mechanisms.
+
+**What are you expected to handle**
+
+* You are expected to install Airflow CTL.
+* You should running Airflow API server.
+* You need to setup monitoring of your system allowing you to observe 
resources and react to problems.
+
+**What Apache Airflow Community provides for that method**
+
+* You have :doc:`/installation/installing-from-pypi`
+  on how to install the software but due to various environments and tools you 
might want to use, you might
+  expect that there will be problems which are specific to your deployment and 
environment you will have to
+  diagnose and solve.
+* You have :doc:`/start` where you can see an example of Quick Start with 
running Airflow
+  locally which you can use to start Airflow quickly for local testing and 
development.
+  However, this is just for inspiration. Do not expect :doc:`/start` is ready 
for production installation,
+  you need to build your own production-ready deployment if you follow this 
approach.
+
+**Where to ask for help**
+
+* The ``#user-troubleshooting`` channel on Airflow Slack for quick general
+  troubleshooting questions. The `GitHub discussions 
<https://github.com/apache/airflow/discussions>`__
+  if you look for longer discussion and have more information to share.
+* The ``#user-best-practices`` channel on slack can be used to ask for and 
share best
+  practices on using and deploying airflow.
+* If you can provide description of a reproducible problem with Airflow 
software, you can open
+  issue at `GitHub issues <https://github.com/apache/airflow/issues>`__
diff --git a/airflow-ctl/docs/installation/installing-from-pypi.rst 
b/airflow-ctl/docs/installation/installing-from-pypi.rst
new file mode 100644
index 00000000000..f498c163298
--- /dev/null
+++ b/airflow-ctl/docs/installation/installing-from-pypi.rst
@@ -0,0 +1,163 @@
+ .. Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+
+ ..   http://www.apache.org/licenses/LICENSE-2.0
+
+ .. Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+
+Installation from PyPI
+----------------------
+
+This page describes installations using the ``apache-airflow-ctl`` package 
`published in
+PyPI <https://pypi.org/project/apache-airflow-ctl/>`__.
+
+Installation tools
+''''''''''''''''''
+
+Only ``pip`` installation is currently officially supported.
+
+.. note::
+
+  While there are some successes with using other tools like `poetry 
<https://python-poetry.org/>`_ or
+  `pip-tools <https://pypi.org/project/pip-tools/>`_, they do not share the 
same workflow as
+  ``pip`` - especially when it comes to constraint vs. requirements management.
+  Installing via ``Poetry`` or ``pip-tools`` is not currently supported. If 
you wish to install airflow
+  using those tools you should use the constraints and convert them to 
appropriate
+  format and workflow that your tool requires.
+
+  There are known issues with ``bazel`` that might lead to circular 
dependencies when using it to install
+  Airflow. Please switch to ``pip`` if you encounter such problems. ``Bazel`` 
community works on fixing
+  the problem in `this PR 
<https://github.com/bazelbuild/rules_python/pull/1166>`_ so it might be that
+  newer versions of ``bazel`` will handle it.
+
+Typical command to install airflowctl from scratch in a reproducible way from 
PyPI looks like below:
+
+.. code-block:: bash
+
+    pip install "apache-airflow-ctl==|version|"
+
+Those are just examples, see further for more explanation why those are the 
best practices.
+
+.. note::
+
+   Generally speaking, Python community established practice is to perform 
application installation in a
+   virtualenv created with ``virtualenv`` or ``venv`` tools. You can also use 
``uv`` or ``pipx`` to install
+   Airflow in application dedicated virtual environment created for you. There 
are also other tools that can be used
+   to manage your virtualenv installation and you are free to choose how you 
are managing the environments.
+   Airflow has no limitation regarding to the tool of your choice when it 
comes to virtual environment.
+
+   The only exception where you might consider not using virtualenv is when 
you are building a container
+   image with only Airflow installed - this is for example how Airflow is 
installed in the official Container
+   image.
+
+.. _installation:constraints:
+
+Constraints files
+'''''''''''''''''
+
+Why we need constraints
+=======================
+
+Airflow CTL installation can be tricky because Airflow CTL is both a library 
and an application.
+
+Libraries usually keep their dependencies open and applications usually pin 
them, but we should do neither
+and both at the same time. We decided to keep our dependencies as open as 
possible
+(in ``pyproject.toml``) so users can install different version of libraries if 
needed. This means that
+from time to time plain ``pip install apache-airflow-ctl`` will not work or 
will produce an unusable
+Airflow CTL installation.
+
+Reproducible Airflow CTL installation
+=====================================
+
+In order to have a reproducible installation, we also keep a set of constraint 
files in the
+``constraints-main``, ``constraints-2-0``, ``constraints-2-1`` etc. orphan 
branches and then we create a tag
+for each released version e.g. :subst-code:`constraints-|version|`.
+
+This way, we keep a tested set of dependencies at the moment of release. This 
provides you with the ability
+of having the exact same installation of airflowctl + dependencies as was 
known to be working
+at the moment of release - frozen set of dependencies for that version of 
Airflow CTL. There is a separate
+constraints file for each version of Python that Airflow CTL supports.
+
+You can create the URL to the file substituting the variables in the template 
below.
+
+.. code-block::
+
+  
https://raw.githubusercontent.com/apache/airflow/airflow-ctl/constraints-${AIRFLOWCTL_VERSION}/constraints-${PYTHON_VERSION}.txt
+
+where:
+
+- ``AIRFLOW_CTL_VERSION`` - Airflow CTL version (e.g. :subst-code:`|version|`) 
or ``main``, ``2-0``, for latest development version
+- ``PYTHON_VERSION`` Python version e.g. ``3.9``, ``3.10``
+
+
+Verifying installed dependencies
+================================
+
+You can also always run the ``pip check`` command to test if the set of your 
Python packages is
+consistent and not conflicting.
+
+
+.. code-block:: bash
+
+    > pip check
+    No broken requirements found.
+
+
+When you see such message and the exit code from ``pip check`` is 0, you can 
be sure, that there are no
+conflicting dependencies in your environment.
+
+
+Using your own constraints
+==========================
+
+When you decide to install your own dependencies, or want to upgrade or 
downgrade providers, you might want
+to continue being able to keep reproducible installation of Airflow CTL and 
those dependencies via a single command.
+In order to do that, you can produce your own constraints file and use it to 
install Airflow CTL instead of the
+one provided by the community.
+
+.. code-block:: bash
+
+    pip install "apache-airflow-ctl==|version|"
+    pip freeze > my-constraints.txt
+
+
+Then you can use it to create reproducible installations of your environment 
in a single operation via
+a local constraints file:
+
+.. code-block:: bash
+
+    pip install "apache-airflow-ctl==|version|" --constraint 
"my-constraints.txt"
+
+
+Similarly as in case of Airflow CTL original constraints, you can also host 
your constraints at your own
+repository or server and use it remotely from there.
+
+Fixing Constraints at release time
+''''''''''''''''''''''''''''''''''
+
+The released "versioned" constraints are mostly ``fixed`` when we release 
Airflow CTL version and we only
+update them in exceptional circumstances. For example when we find out that 
the released constraints might prevent
+Airflow CTL from being installed consistently from the scratch.
+
+In normal circumstances, the constraint files are not going to change if new 
version of Airflow CTL
+dependencies are released - not even when those versions contain critical 
security fixes.
+The process of Airflow CTL releases is designed around upgrading dependencies 
automatically where
+applicable but only when we release a new version of Airflow CTL, not for 
already released versions.
+
+Between the releases you can upgrade dependencies on your own and you can keep 
your own constraints
+updated as described in the previous section.
+
+The easiest way to keep-up with the latest released dependencies is to upgrade 
to the latest released
+Airflow CTL version. Whenever we release a new version of Airflow CTL, we 
upgrade all dependencies to the latest
+applicable versions and test them together, so if you want to keep up with 
those tests - staying up-to-date
+with latest version of Airflow CTL is the easiest way to update those 
dependencies.
diff --git a/airflow-ctl/docs/installation/installing-from-sources.rst 
b/airflow-ctl/docs/installation/installing-from-sources.rst
new file mode 100644
index 00000000000..41b52763d55
--- /dev/null
+++ b/airflow-ctl/docs/installation/installing-from-sources.rst
@@ -0,0 +1,163 @@
+ .. Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+
+ ..   http://www.apache.org/licenses/LICENSE-2.0
+
+ .. Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+
+Installing from Sources
+-----------------------
+
+Released packages
+'''''''''''''''''
+
+.. jinja:: official_download_page
+
+    This page describes downloading and verifying Airflow® version
+    ``{{ airflow_version }}`` using officially released packages.
+    You can also install ``Apache Airflow CTL`` - as most Python packages - 
via :doc:`PyPI <installing-from-pypi>`.
+    You can choose different version of Airflow by selecting a different 
version from the drop-down at
+    the top-left of the page.
+
+The ``source``, ``sdist`` and ``whl`` packages released are the "official" 
sources of installation that you
+can use if you want to verify the origin of the packages and want to verify 
checksums and signatures of
+the packages. The packages are available via the
+`Official Apache Software Foundations Downloads <https://dlcdn.apache.org/>`_
+
+As of version 2.8 Airflow follows PEP 517/518 and uses ``pyproject.toml`` file 
to define build dependencies
+and build process and it requires relatively modern versions of packaging 
tools to get airflow built from
+local sources or ``sdist`` packages, as PEP 517 compliant build hooks are used 
to determine dynamic build
+dependencies. In case of ``pip`` it means that at least version 22.1.0 is 
needed (released at the beginning of
+2022) to build or install Airflow from sources. This does not affect the 
ability of installing Airflow from
+released wheel packages.
+
+The |version| downloads of Airflow CTL are available at:
+
+.. jinja:: official_download_page
+
+    * `Sources package for airflow <{{ closer_lua_url }}/apache-airflow-ctl-{{ 
airflowctl_version }}-source.tar.gz>`__ (`asc <{{ base_url 
}}/apache-airflow-ctl-{{ airflowctl_version }}-source.tar.gz.asc>`__, `sha512 
<{{ base_url }}/apache-airflow-ctl-{{ airflowctl_version 
}}-source.tar.gz.sha512>`__)
+    * `Sdist package for airflow meta package <{{ closer_lua_url 
}}/apache-airflow-ctl-{{ airflowctl_version }}.tar.gz>`__ (`asc <{{ base_url 
}}/apache-airflow-ctl-{{ airflowctl_version }}.tar.gz.asc>`__, `sha512 <{{ 
base_url }}/apache-airflow-ctl-{{ airflowctl_version }}.tar.gz.sha512>`__)
+    * `Whl package for airflow meta package <{{ closer_lua_url 
}}/apache_airflow_ctl-{{ airflowctl_version }}-py3-none-any.whl>`__ (`asc <{{ 
base_url }}/apache_airflow_ctl-{{ airflowctl_version 
}}-py3-none-any.whl.asc>`__, `sha512 <{{ base_url }}/apache_airflow_ctl-{{ 
airflowctl_version }}-py3-none-any.whl.sha512>`__)
+    * `Sdist package for airflow core package <{{ closer_lua_url 
}}/apache-airflow_ctl-{{ airflowctl_version }}.tar.gz>`__ (`asc <{{ base_url 
}}/apache-airflow_ctl-{{ airflowctl_version }}.tar.gz.asc>`__, `sha512 <{{ 
base_url }}/apache-airflow_ctl-{{ airflowctl_version }}.tar.gz.sha512>`__)
+    * `Whl package for airflow core package <{{ closer_lua_url 
}}/apache_airflow_ctl-{{ airflowctl_version }}-py3-none-any.whl>`__ (`asc <{{ 
base_url }}/apache_airflow_ctl-{{ airflowctl_version 
}}-py3-none-any.whl.asc>`__, `sha512 <{{ base_url }}/apache_airflow_ctl-{{ 
airflowctl_version }}-py3-none-any.whl.sha512>`__)
+
+If you want to install from the source code, you can download from the sources 
link above, it will contain
+a ``INSTALL`` file containing details on how you can build and install Airflow 
CTL.
+
+Release integrity
+'''''''''''''''''
+
+`PGP signatures KEYS <https://downloads.apache.org/airflowctl/KEYS>`__
+
+It is essential that you verify the integrity of the downloaded files using 
the PGP or SHA signatures.
+The PGP signatures can be verified using GPG or PGP. Please download the KEYS 
as well as the asc
+signature files for relevant distribution. It is recommended to get these 
files from the
+main distribution directory and not from the mirrors.
+
+.. code-block:: bash
+
+    gpg -i KEYS
+
+or
+
+.. code-block:: bash
+
+    pgpk -a KEYS
+
+or
+
+.. code-block:: bash
+
+    pgp -ka KEYS
+
+To verify the binaries/sources you can download the relevant asc files for it 
from main
+distribution directory and follow the below guide.
+
+.. code-block:: bash
+
+    gpg --verify apache-airflow-ctl-********.asc apache-airflow-ctl-*********
+
+or
+
+.. code-block:: bash
+
+    pgpv apache-airflow-ctl-********.asc
+
+or
+
+.. code-block:: bash
+
+    pgp apache-airflow-********.asc
+
+Example:
+
+.. code-block:: console
+    :substitutions:
+
+    $ gpg --verify apache-airflow-ctl-|version|-source.tar.gz.asc 
apache-airflow-ctl-|version|-source.tar.gz
+      gpg: Signature made Sat 11 Sep 12:49:54 2021 BST
+      gpg:                using RSA key 
CDE15C6E4D3A8EC4ECF4BA4B6674E08AD7DE406F
+      gpg:                issuer "kaxiln...@apache.org"
+      gpg: Good signature from "Kaxil Naik <kaxiln...@apache.org>" [unknown]
+      gpg:                 aka "Kaxil Naik <kaxiln...@gmail.com>" [unknown]
+      gpg: WARNING: The key's User ID is not certified with a trusted 
signature!
+      gpg:          There is no indication that the signature belongs to the 
owner.
+      Primary key fingerprint: CDE1 5C6E 4D3A 8EC4 ECF4  BA4B 6674 E08A D7DE 
406F
+
+The "Good signature from ..." is indication that the signatures are correct.
+Do not worry about the "not certified with a trusted signature" warning. Most 
of the certificates used
+by release managers are self signed, that's why you get this warning. By 
importing the server in the
+previous step and importing it via ID from ``KEYS`` page, you know that this 
is a valid Key already.
+
+For SHA512 sum check, download the relevant ``sha512`` and run the following:
+
+.. code-block:: bash
+
+    shasum -a 512 apache-airflow-ctl--********  | diff - 
apache-airflow-ctl--********.sha512
+
+The ``SHASUM`` of the file should match the one provided in ``.sha512`` file.
+
+Example:
+
+.. code-block:: bash
+    :substitutions:
+
+    shasum -a 512 apache-airflow-ctl-|version|-source.tar.gz  | diff - 
apache-airflow-ctl-|version|-source.tar.gz.sha512
+
+
+Verifying PyPI releases
+'''''''''''''''''''''''
+
+You can verify the Airflow CTL ``.whl`` packages from PyPI by locally 
downloading the package and signature
+and SHA sum files with the script below:
+
+
+.. jinja:: official_download_page
+
+    .. code-block:: bash
+
+        #!/bin/bash
+        airflowctl_version="{{ airflowctl_version }}"
+        ctl_download_dir="$(mktemp -d)"
+        pip download --no-deps "apache-airflow-ctl==${airflowctl_version}" 
--dest "${airflow_download_dir}"
+        curl 
"https://downloads.apache.org/airflowctl/${airflowctl_version}/apache_airflow_ctl-${airflowctl_version}-py3-none-any.whl.asc";
 \
+            -L -o 
"${airflowctl_download_dir}/apache_airflow_ctl-${airflowctl_version}-py3-none-any.whl.asc"
+        curl 
"https://downloads.apache.org/airflow/${airflowctl_version}/apache_airflow_ctl-${airflowctl_version}-py3-none-any.whl.sha512";
 \
+            -L -o 
"${airflowctl_download_dir}/apache_airflow_ctl-${airflowctl_version}-py3-none-any.whl.sha512"
+        echo
+        echo "Please verify files downloaded to ${airflowctl_download_dir}"
+        ls -la "${airflowctl_download_dir}"
+        echo
+
+Once you verify the files following the instructions from previous chapter you 
can remove the temporary
+folder created.
diff --git a/airflow-ctl/docs/installation/prerequisites.rst 
b/airflow-ctl/docs/installation/prerequisites.rst
new file mode 100644
index 00000000000..615558e450c
--- /dev/null
+++ b/airflow-ctl/docs/installation/prerequisites.rst
@@ -0,0 +1,63 @@
+ .. Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+
+ ..   http://www.apache.org/licenses/LICENSE-2.0
+
+ .. Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+
+Prerequisites
+-------------
+
+Airflow CTL is tested with:
+
+
+The minimum memory required we recommend Airflow CTL to run with is 4GB, but 
the actual requirements depend
+wildly on the deployment options you have.
+The Keyring backend needs to be installed separately into your operating 
system. This will enhance security. See :doc:`/security` for more information.
+
+Keyring Backend
+'''''''''''''''
+Airflow CTL uses keyring to store the API token securely. This ensures that 
the token is not stored in plain text and is only accessible to authorized 
users.
+
+Recommended keyring backends are:
+* `macOS Keychain <https://en.wikipedia.org/wiki/Keychain_%28software%29>`_
+* `Freedesktop Secret Service 
<http://standards.freedesktop.org/secret-service/>`_ supports many DE including 
GNOME (requires `secretstorage <https://pypi.python.org/pypi/secretstorage>`_)
+* `KDE4 & KDE5 KWallet <https://en.wikipedia.org/wiki/KWallet>`_ (requires 
`dbus <https://pypi.python.org/pypi/dbus-python>`_)
+* `Windows Credential Locker 
<https://docs.microsoft.com/en-us/windows/uwp/security/credential-locker>`_
+
+Third-Party Backends
+====================
+
+In addition to the backends provided by the core keyring package for
+the most common and secure use cases, there
+are additional keyring backend implementations available for other
+use cases. Simply install them to make them available:
+
+- `keyrings.cryptfile <https://pypi.org/project/keyrings.cryptfile>`_
+    - Encrypted text file storage.
+- `keyrings.alt <https://pypi.org/project/keyrings.alt>`_
+    - "alternate", possibly-insecure backends, originally part of the core 
package, but available for opt-in.
+- `gsheet-keyring <https://pypi.org/project/gsheet-keyring>`_
+    - a backend that stores secrets in a Google Sheet. For use with 
`ipython-secrets <https://pypi.org/project/ipython-secrets>`_.
+- `bitwarden-keyring <https://pypi.org/project/bitwarden-keyring/>`_
+    - a backend that stores secrets in the `BitWarden 
<https://bitwarden.com/>`_ password manager.
+- `onepassword-keyring <https://pypi.org/project/onepassword-keyring/>`_
+    - a backend that stores secrets in the `1Password 
<https://1password.com/>`_ password manager.
+- `sagecipher <https://pypi.org/project/sagecipher>`_
+    - an encryption backend which uses the ssh agent protocol's signature 
operation to derive the cipher key.
+- `keyrings.osx_keychain_keys 
<https://pypi.org/project/keyrings.osx-keychain-keys>`_
+    - ``OSX keychain key-management``, for private, public, and symmetric keys.
+- `keyring_pass.PasswordStoreBackend 
<https://github.com/nazarewk/keyring_pass>`_
+    - Password Store (pass) backend for python's keyring
+- `keyring_jeepney <https://pypi.org/project/keyring_jeepney>`__
+    - a pure Python backend using the secret service ``DBus`` API for desktop 
Linux (requires ``keyring<24``).
diff --git a/airflow-ctl/docs/installation/supported-versions.rst 
b/airflow-ctl/docs/installation/supported-versions.rst
new file mode 100644
index 00000000000..823d4e5b461
--- /dev/null
+++ b/airflow-ctl/docs/installation/supported-versions.rst
@@ -0,0 +1,42 @@
+ .. Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+
+ ..   http://www.apache.org/licenses/LICENSE-2.0
+
+ .. Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+
+Supported versions
+------------------
+
+Version Life Cycle
+``````````````````
+
+Apache Airflow CTL is compatible with Apache Airflow® versions 3.0.0 and later.
+
+Apache Airflow CTL version life cycle:
+
+ .. This table is automatically updated by pre-commit 
scripts/ci/pre_commit/supported_versions.py
+ .. Beginning of auto-generated table
+
+=========  =====================  =========  ===============  
=================  ================
+Version    Current Patch/Minor    State      First Release    Limited Support  
  EOL/Terminated
+=========  =====================  =========  ===============  
=================  ================
+1          1.0.0                  Supported  TBD              TBD              
  TBD
+=========  =====================  =========  ===============  
=================  ================
+
+ .. End of auto-generated table
+
+
+Limited support versions will be supported with security and critical bug fix 
only.
+EOL versions will not get any fixes nor support.
+We **highly** recommend installing the latest Airflow CTL release which has 
richer features.
diff --git a/airflow-ctl/src/airflowctl/__init__.py 
b/airflow-ctl/docs/redirects.txt
similarity index 81%
copy from airflow-ctl/src/airflowctl/__init__.py
copy to airflow-ctl/docs/redirects.txt
index 2782fd09001..93082af1472 100644
--- a/airflow-ctl/src/airflowctl/__init__.py
+++ b/airflow-ctl/docs/redirects.txt
@@ -1,4 +1,3 @@
-#
 # Licensed to the Apache Software Foundation (ASF) under one
 # or more contributor license agreements.  See the NOTICE file
 # distributed with this work for additional information
@@ -15,8 +14,14 @@
 # KIND, either express or implied.  See the License for the
 # specific language governing permissions and limitations
 # under the License.
-from __future__ import annotations
 
-__path__ = __import__("pkgutil").extend_path(__path__, __name__)  # type: 
ignore
+# References
+cli-ref.rst cli-and-env-variables-ref.rst
+Quick Start start.rst
+changelog.rst changelog.rst
+
+# Installation.rst
+installation/index.rst
 
-__version__ = "0.0.1"
+# How to
+howto/index.rst
diff --git a/airflow-ctl/docs/security.rst b/airflow-ctl/docs/security.rst
new file mode 100644
index 00000000000..1e83537a7a5
--- /dev/null
+++ b/airflow-ctl/docs/security.rst
@@ -0,0 +1,27 @@
+ .. Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+
+ ..   http://www.apache.org/licenses/LICENSE-2.0
+
+ .. Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+
+Security
+========
+Airflow CTL is leveraging Apache Airflow Public API security features and 
additional layers of security to ensure that your data is safe and secure.
+Airflow CTL facilitates the seamless deployment of CLI and API features 
together, reducing redundancy and simplifying maintenance. Transitioning from 
direct database access to an API-driven model will enhance the CLI's 
capabilities and improve security.
+
+- **Authentication**: Airflow CTL uses authentication to ensure that only 
authorized users can access the system. This is done using an API token. See 
more on https://airflow.apache.org/docs/apache-airflow/stable/security/api.html
+
+- **Keyring**: Airflow CTL uses keyring to store the API token securely. This 
ensures that the token is not stored in plain text and is only accessible to 
authorized users.
+
+Airflow CTL API Token has its own expiration time. The default is 1 hour. You 
can change it in the Airflow configuration file (airflow.cfg) by setting the 
``jwt_cli_expiration_time`` parameter under the ``[api_auth]`` section. The 
value is in seconds. This will impact all users using ``airflowctl``.
diff --git a/airflow-ctl/docs/start.rst b/airflow-ctl/docs/start.rst
new file mode 100644
index 00000000000..523e7696b6d
--- /dev/null
+++ b/airflow-ctl/docs/start.rst
@@ -0,0 +1,42 @@
+ .. Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+
+ ..   http://www.apache.org/licenses/LICENSE-2.0
+
+ .. Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+
+Quick Start
+-----------
+
+Airflow CTL is a command line tool that helps you manage your Airflow 
deployments.
+It is designed to be easy to use and provides a simple interface for managing 
your Airflow environment.
+
+To get started, you can use the following command to create a new Airflow CTL 
environment:
+
+.. code-block:: bash
+
+  airflowctl auth login --username <username> --password <password> --api-url 
<api_url> --env <env_name>
+
+OR
+
+.. code-block:: bash
+
+  export AIRFLOW_CLI_TOKEN=<token>
+  airflowctl auth login --api-url <api_url> --env <env_name>
+
+This command will create a new Airflow CTL environment with the specified 
username and password.
+You can then use the following command to start the Airflow CTL environment:
+
+.. code-block:: bash
+
+  airflowctl --help
diff --git a/airflow-ctl/docs/static/exampleinclude.css 
b/airflow-ctl/docs/static/exampleinclude.css
new file mode 100644
index 00000000000..b4f2a42dcc7
--- /dev/null
+++ b/airflow-ctl/docs/static/exampleinclude.css
@@ -0,0 +1,86 @@
+/*!
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+.example-header {
+  position: relative;
+  background: #9aaa7a;
+  padding: 8px 16px;
+  margin-bottom: 0;
+}
+
+.example-header--with-button {
+  padding-right: 166px;
+}
+
+.example-header::after {
+  content: '';
+  display: table;
+  clear: both;
+}
+
+.example-title {
+  display: block;
+  padding: 4px;
+  margin-right: 16px;
+  color: #fff;
+  overflow-x: auto;
+}
+
+.example-header-button {
+  top: 8px;
+  right: 16px;
+  position: absolute;
+}
+
+.example-header + .highlight-python {
+  margin-top: 0 !important;
+}
+
+.viewcode-button {
+  display: inline-block;
+  padding: 8px 16px;
+  border: 0;
+  margin: 0;
+  outline: 0;
+  border-radius: 2px;
+  box-shadow: 0 3px 6px 0 rgba(0, 0, 0, 0.3);
+  color: #404040;
+  background-color: #e7e7e7;
+  cursor: pointer;
+  font-size: 16px;
+  font-weight: 500;
+  line-height: 1;
+  text-decoration: none;
+  text-overflow: ellipsis;
+  overflow: hidden;
+  text-transform: uppercase;
+  transition: background-color 0.2s;
+  vertical-align: middle;
+  white-space: nowrap;
+}
+
+.viewcode-button:visited {
+  color: #404040;
+}
+
+.viewcode-button:hover,
+.viewcode-button:focus {
+  color: #404040;
+  background-color: #d6d6d6;
+}
diff --git a/airflow-ctl/docs/static/gh-jira-links.js 
b/airflow-ctl/docs/static/gh-jira-links.js
new file mode 100644
index 00000000000..dfbfb15ad88
--- /dev/null
+++ b/airflow-ctl/docs/static/gh-jira-links.js
@@ -0,0 +1,36 @@
+/*!
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+document.addEventListener('DOMContentLoaded', function() {
+  var el = document.getElementById('release-notes');
+  if (el !== null ) {
+    // [AIRFLOW-...]
+    el.innerHTML = el.innerHTML.replace(
+      /\[(AIRFLOW-[\d]+)\]/g,
+      `<a href="https://issues.apache.org/jira/browse/$1";>[$1]</a>`
+    );
+    // (#...)
+    el.innerHTML = el.innerHTML.replace(
+      /#([\d]+)/g,
+      (match, group1) => {
+        return `<a 
href="https://github.com/apache/airflow/pull/${group1}";>#${group1}</a>`
+      }
+    );
+  };
+})
diff --git a/airflow-ctl/docs/static/redirects.js 
b/airflow-ctl/docs/static/redirects.js
new file mode 100644
index 00000000000..4b44336e440
--- /dev/null
+++ b/airflow-ctl/docs/static/redirects.js
@@ -0,0 +1,29 @@
+/*!
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+document.addEventListener("DOMContentLoaded", function () {
+    const redirects = {
+        "zombie-undead-tasks": "task-instance-heartbeat-timeout",
+        "zombie-tasks": "task-instance-heartbeat-timeout",
+    };
+    const fragment = window.location.hash.substring(1);
+    if (redirects[fragment]) {
+        window.location.hash = redirects[fragment];
+    }
+});
diff --git a/airflow-ctl/docs/templates/footer.html 
b/airflow-ctl/docs/templates/footer.html
new file mode 100644
index 00000000000..10c1500f3ee
--- /dev/null
+++ b/airflow-ctl/docs/templates/footer.html
@@ -0,0 +1,29 @@
+{#
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements.  See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership.  The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License.  You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied.  See the License for the
+ specific language governing permissions and limitations
+ under the License.
+#}
+
+{% extends "!footer.html" %}
+
+{% block extrafooter %}
+  <div class="footer">This page uses <a href="https://analytics.google.com/";>
+    Google Analytics</a> to collect statistics. You can disable it by blocking
+    the JavaScript coming from www.google-analytics.com. Check our
+    <a href="{{ pathto('privacy_notice') }}">Privacy Policy</a>
+    for more details.
+  </div>
+{% endblock %}
diff --git a/airflow-ctl/pyproject.toml b/airflow-ctl/pyproject.toml
index 3a4796a32a9..8eeac9ed8b3 100644
--- a/airflow-ctl/pyproject.toml
+++ b/airflow-ctl/pyproject.toml
@@ -96,6 +96,21 @@ exclude_also = [
 ]
 
 [dependency-groups]
+# To build docs:
+#
+#    uv run --group docs build-docs
+#
+# To enable auto-refreshing build with server:
+#
+#    uv run --group docs build-docs --autobuild
+#
+# To see more options:
+#
+#    uv run --group docs build-docs --help
+#
+docs = [
+    "apache-airflow-devel-common[docs]"
+]
 dev = [
     "apache-airflow-ctl[dev]",
 ]
@@ -142,7 +157,6 @@ addopts = [
     "--asyncio-mode=strict",
 ]
 
-
 norecursedirs = [
     ".eggs",
 ]
@@ -164,3 +178,9 @@ pythonpath = "tests"
 # Keep temporary directories (created by `tmp_path`) for 2 recent runs only 
failed tests.
 tmp_path_retention_count = "2"
 tmp_path_retention_policy = "failed"
+
+[tool.uv]
+required-version = ">=0.6.3"
+
+[tool.uv.sources]
+apache-airflow-devel-common = { workspace = true }
diff --git a/airflow-ctl/src/airflowctl/__init__.py 
b/airflow-ctl/src/airflowctl/__init__.py
index 2782fd09001..82f4e830b6a 100644
--- a/airflow-ctl/src/airflowctl/__init__.py
+++ b/airflow-ctl/src/airflowctl/__init__.py
@@ -19,4 +19,4 @@ from __future__ import annotations
 
 __path__ = __import__("pkgutil").extend_path(__path__, __name__)  # type: 
ignore
 
-__version__ = "0.0.1"
+__version__ = "1.0.0"
diff --git a/airflow-ctl/src/airflowctl/api/operations.py 
b/airflow-ctl/src/airflowctl/api/operations.py
index ed015a439d9..20b10a0a92f 100644
--- a/airflow-ctl/src/airflowctl/api/operations.py
+++ b/airflow-ctl/src/airflowctl/api/operations.py
@@ -57,6 +57,7 @@ from airflowctl.api.datamodels.generated import (
     VariableResponse,
     VersionInfo,
 )
+from airflowctl.exceptions import AirflowCtlConnectionException
 
 if TYPE_CHECKING:
     from airflowctl.api.client import Client
@@ -104,7 +105,9 @@ def _check_flag_and_exit_if_server_response_error(func):
                 return _exit_if_server_response_error(response=func(self, 
*args, **kwargs))
             return func(self, *args, **kwargs)
         except httpx.ConnectError as e:
-            raise e
+            if "Connection refused" in str(e):
+                raise AirflowCtlConnectionException("Connection refused. Is 
the API server running?")
+            raise AirflowCtlConnectionException(f"Connection error: {e}")
 
     return wrapped
 
diff --git a/airflow-ctl/src/airflowctl/ctl/cli_config.py 
b/airflow-ctl/src/airflowctl/ctl/cli_config.py
index c8bce27b368..7e56fc61281 100644
--- a/airflow-ctl/src/airflowctl/ctl/cli_config.py
+++ b/airflow-ctl/src/airflowctl/ctl/cli_config.py
@@ -36,7 +36,11 @@ import rich
 import airflowctl.api.datamodels.generated as generated_datamodels
 from airflowctl.api.client import NEW_API_CLIENT, Client, ClientKind, 
provide_api_client
 from airflowctl.api.operations import BaseOperations, ServerResponseError
-from airflowctl.exceptions import AirflowCtlCredentialNotFoundException, 
AirflowCtlNotFoundException
+from airflowctl.exceptions import (
+    AirflowCtlConnectionException,
+    AirflowCtlCredentialNotFoundException,
+    AirflowCtlNotFoundException,
+)
 from airflowctl.utils.module_loading import import_string
 
 BUILD_DOCS = "BUILDING_AIRFLOW_DOCS" in os.environ
@@ -60,6 +64,8 @@ def safe_call_command(function: Callable, args: 
Iterable[Arg]) -> None:
         function(args)
     except AirflowCtlCredentialNotFoundException as e:
         rich.print(f"command failed due to {e}")
+    except AirflowCtlConnectionException as e:
+        rich.print(f"command failed due to {e}")
     except AirflowCtlNotFoundException as e:
         rich.print(f"command failed due to {e}")
 
@@ -216,7 +222,28 @@ class GroupCommand(NamedTuple):
     epilog: str | None = None
 
 
-CLICommand = Union[ActionCommand, GroupCommand]
+class GroupCommandParser(NamedTuple):
+    """ClI command with subcommands."""
+
+    name: str
+    help: str
+    subcommands: Iterable
+    description: str | None = None
+    epilog: str | None = None
+
+    @classmethod
+    def from_group_command(cls, group_command: GroupCommand) -> 
GroupCommandParser:
+        """Create GroupCommandParser from GroupCommand."""
+        return cls(
+            name=group_command.name,
+            help=group_command.help,
+            subcommands=group_command.subcommands,
+            description=group_command.description,
+            epilog=group_command.epilog,
+        )
+
+
+CLICommand = Union[ActionCommand, GroupCommand, GroupCommandParser]
 
 
 class CommandFactory:
@@ -355,7 +382,7 @@ class CommandFactory:
                         arg_flags=("--" + 
self._sanitize_arg_parameter_key(field),),
                         arg_type=field_type.annotation,
                         arg_action=argparse.BooleanOptionalAction if 
field_type.annotation is bool else None,  # type: ignore
-                        arg_help=f"Argument Type: {field_type.annotation}, 
{field} for {parameter_key} operation",
+                        arg_help=f"{field} for {parameter_key} operation",
                         arg_default=False if field_type.annotation is bool 
else None,
                     )
                 )
@@ -370,7 +397,7 @@ class CommandFactory:
                         arg_flags=("--" + 
self._sanitize_arg_parameter_key(field),),
                         arg_type=annotation,
                         arg_action=argparse.BooleanOptionalAction if 
annotation is bool else None,  # type: ignore
-                        arg_help=f"Argument Type: {annotation}, {field} for 
{parameter_key} operation",
+                        arg_help=f"{field} for {parameter_key} operation",
                         arg_default=False if annotation is bool else None,
                     )
                 )
@@ -390,7 +417,7 @@ class CommandFactory:
                                 arg_action=argparse.BooleanOptionalAction
                                 if type(parameter_type) is bool
                                 else None,
-                                arg_help=f"Argument Type: 
{type(parameter_type)}, {parameter_key} for {operation.get('name')} operation 
in {operation.get('parent').name}",
+                                arg_help=f"{parameter_key} for 
{operation.get('name')} operation in {operation.get('parent').name}",
                                 arg_default=False if type(parameter_type) is 
bool else None,
                             )
                         )
diff --git a/airflow-ctl/src/airflowctl/ctl/cli_parser.py 
b/airflow-ctl/src/airflowctl/ctl/cli_parser.py
index 685d46322b4..138f714462a 100644
--- a/airflow-ctl/src/airflowctl/ctl/cli_parser.py
+++ b/airflow-ctl/src/airflowctl/ctl/cli_parser.py
@@ -38,6 +38,7 @@ from airflowctl.ctl.cli_config import (
     ActionCommand,
     DefaultHelpParser,
     GroupCommand,
+    GroupCommandParser,
     core_commands,
 )
 from airflowctl.exceptions import AirflowCtlException
@@ -54,7 +55,10 @@ airflow_commands = core_commands.copy()  # make a copy to 
prevent bad interactio
 log = logging.getLogger(__name__)
 
 
-ALL_COMMANDS_DICT: dict[str, CLICommand] = {sp.name: sp for sp in 
airflow_commands}
+ALL_COMMANDS_DICT: dict[str, CLICommand] = {
+    sp.name: GroupCommandParser.from_group_command(sp) if isinstance(sp, 
GroupCommand) else sp
+    for sp in airflow_commands
+}
 
 
 class AirflowHelpFormatter(RichHelpFormatter):
@@ -69,7 +73,7 @@ class AirflowHelpFormatter(RichHelpFormatter):
             self._indent()
             subactions = action._get_subactions()
             action_subcommands, group_subcommands = partition(
-                lambda d: isinstance(ALL_COMMANDS_DICT[d.dest], GroupCommand), 
subactions
+                lambda d: isinstance(ALL_COMMANDS_DICT[d.dest], 
GroupCommandParser), subactions
             )
             yield Action([], f"\n{' ':{self._current_indent}}Groups", nargs=0)
             self._indent()
@@ -131,7 +135,7 @@ def _add_command(subparsers: argparse._SubParsersAction, 
sub: CLICommand) -> Non
         )
     sub_proc.formatter_class = LazyRichHelpFormatter
 
-    if isinstance(sub, GroupCommand):
+    if isinstance(sub, GroupCommandParser):
         _add_group_command(sub, sub_proc)
     elif isinstance(sub, ActionCommand):
         _add_action_command(sub, sub_proc)
@@ -145,7 +149,7 @@ def _add_action_command(sub: ActionCommand, sub_proc: 
argparse.ArgumentParser) -
     sub_proc.set_defaults(func=sub.func)
 
 
-def _add_group_command(sub: GroupCommand, sub_proc: argparse.ArgumentParser) 
-> None:
+def _add_group_command(sub: GroupCommandParser, sub_proc: 
argparse.ArgumentParser) -> None:
     subcommands = sub.subcommands
     sub_subparsers = sub_proc.add_subparsers(dest="subcommand", 
metavar="COMMAND")
     sub_subparsers.required = True
diff --git a/airflow-ctl/src/airflowctl/exceptions.py 
b/airflow-ctl/src/airflowctl/exceptions.py
index b313e969b2c..515879ec359 100644
--- a/airflow-ctl/src/airflowctl/exceptions.py
+++ b/airflow-ctl/src/airflowctl/exceptions.py
@@ -36,3 +36,7 @@ class AirflowCtlNotFoundException(AirflowCtlException):
 
 class AirflowCtlCredentialNotFoundException(AirflowCtlNotFoundException):
     """Raise when a credential couldn't be found while performing an 
operation."""
+
+
+class AirflowCtlConnectionException(AirflowCtlException):
+    """Raise when a connection error occurs while performing an operation."""
diff --git a/airflow-ctl/tests/airflow_ctl/api/test_operations.py 
b/airflow-ctl/tests/airflow_ctl/api/test_operations.py
index a722d15369e..631e32bd60a 100644
--- a/airflow-ctl/tests/airflow_ctl/api/test_operations.py
+++ b/airflow-ctl/tests/airflow_ctl/api/test_operations.py
@@ -20,8 +20,6 @@ from __future__ import annotations
 import datetime
 import json
 import uuid
-from contextlib import redirect_stdout
-from io import StringIO
 from typing import TYPE_CHECKING
 
 import httpx
@@ -75,6 +73,7 @@ from airflowctl.api.datamodels.generated import (
     VariableResponse,
     VersionInfo,
 )
+from airflowctl.exceptions import AirflowCtlConnectionException
 
 if TYPE_CHECKING:
     from pydantic import NonNegativeInt
@@ -93,13 +92,10 @@ def make_api_client(
 class TestBaseOperations:
     def test_server_connection_refused(self):
         client = make_api_client(base_url="http://localhost";)
-        with (
-            pytest.raises(httpx.ConnectError),
-            redirect_stdout(StringIO()) as stdout,
+        with pytest.raises(
+            AirflowCtlConnectionException, match="Connection refused. Is the 
API server running?"
         ):
-            client.connections.get(1)
-        stdout = stdout.getvalue()
-        assert "" in stdout
+            client.connections.get("1")
 
 
 class TestAssetsOperations:
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 94073d8350f..fe9ed6bad0a 100644
--- a/airflow-ctl/tests/airflow_ctl/ctl/test_cli_config.py
+++ b/airflow-ctl/tests/airflow_ctl/ctl/test_cli_config.py
@@ -46,7 +46,7 @@ def test_args():
         (
             "--dag-id",
             {
-                "help": "Argument Type: <class 'str'>, dag_id for backfill 
operation",
+                "help": "dag_id for backfill operation",
                 "action": None,
                 "default": None,
                 "type": str,
@@ -56,7 +56,7 @@ def test_args():
         (
             "--from-date",
             {
-                "help": "Argument Type: <class 'datetime.datetime'>, from_date 
for backfill operation",
+                "help": "from_date for backfill operation",
                 "action": None,
                 "default": None,
                 "type": datetime.datetime,
@@ -66,7 +66,7 @@ def test_args():
         (
             "--to-date",
             {
-                "help": "Argument Type: <class 'datetime.datetime'>, to_date 
for backfill operation",
+                "help": "to_date for backfill operation",
                 "action": None,
                 "default": None,
                 "type": datetime.datetime,
@@ -76,7 +76,7 @@ def test_args():
         (
             "--run-backwards",
             {
-                "help": "Argument Type: <class 'bool'>, run_backwards for 
backfill operation",
+                "help": "run_backwards for backfill operation",
                 "action": BooleanOptionalAction,
                 "default": False,
                 "type": bool,
@@ -86,7 +86,7 @@ def test_args():
         (
             "--dag-run-conf",
             {
-                "help": "Argument Type: dict[str, typing.Any], dag_run_conf 
for backfill operation",
+                "help": "dag_run_conf for backfill operation",
                 "action": None,
                 "default": None,
                 "type": dict[str, Any],
@@ -96,7 +96,7 @@ def test_args():
         (
             "--reprocess-behavior",
             {
-                "help": "Argument Type: <enum 'ReprocessBehavior'>, 
reprocess_behavior for backfill operation",
+                "help": "reprocess_behavior for backfill operation",
                 "action": None,
                 "default": None,
                 "type": ReprocessBehavior,
@@ -106,7 +106,7 @@ def test_args():
         (
             "--max-active-runs",
             {
-                "help": "Argument Type: <class 'int'>, max_active_runs for 
backfill operation",
+                "help": "max_active_runs for backfill operation",
                 "action": None,
                 "default": None,
                 "type": int,
diff --git a/devel-common/src/docs/utils/conf_constants.py 
b/devel-common/src/docs/utils/conf_constants.py
index a6699b25cee..3a92e385a8b 100644
--- a/devel-common/src/docs/utils/conf_constants.py
+++ b/devel-common/src/docs/utils/conf_constants.py
@@ -45,6 +45,11 @@ AIRFLOW_CORE_DOCKER_COMPOSE_PATH = AIRFLOW_CORE_DOCS_PATH / 
"howto" / "docker-co
 AIRFLOW_CORE_SRC_PATH = AIRFLOW_CORE_ROOT_PATH / "src"
 AIRFLOW_FAVICON_PATH = AIRFLOW_CORE_SRC_PATH / "airflow" / "ui" / "public" / 
"pin_32.png"
 
+AIRFLOW_CTL_ROOT_PATH = AIRFLOW_REPO_ROOT_PATH / "airflow-ctl"
+AIRFLOW_CTL_DOCS_PATH = AIRFLOW_CTL_ROOT_PATH / "docs"
+AIRFLOW_CTL_DOC_STATIC_PATH = AIRFLOW_CTL_DOCS_PATH / "static"
+AIRFLOW_CTL_SRC_PATH = AIRFLOW_CTL_ROOT_PATH / "src"
+
 CHART_PATH = AIRFLOW_CORE_ROOT_PATH / "chart"
 CHART_DOC_PATH = AIRFLOW_CORE_DOCS_PATH / "docs"
 
diff --git a/devel-common/src/sphinx_exts/docs_build/docs_builder.py 
b/devel-common/src/sphinx_exts/docs_build/docs_builder.py
index 44ba83e6f7a..59b3692c695 100644
--- a/devel-common/src/sphinx_exts/docs_build/docs_builder.py
+++ b/devel-common/src/sphinx_exts/docs_build/docs_builder.py
@@ -65,6 +65,8 @@ class AirflowDocsBuilder:
             self.is_docker_stack = True
         if self.package_name == "apache-airflow-providers":
             self.is_providers_summary = True
+        if self.package_name == "apache-airflow-ctl":
+            self.is_airflow_ctl = True
 
     @property
     def _doctree_dir(self) -> Path:
@@ -117,6 +119,8 @@ class AirflowDocsBuilder:
         if self.package_name.startswith("apache-airflow-providers-"):
             package_paths = self.package_name[len("apache-airflow-providers-") 
:].split("-")
             return (AIRFLOW_CONTENT_ROOT_PATH / 
"providers").joinpath(*package_paths) / "docs"
+        if self.package_name == "apache-airflow-ctl":
+            return AIRFLOW_CONTENT_ROOT_PATH / "airflow-ctl" / "docs"
         console.print(f"[red]Unknown package name: {self.package_name}")
         sys.exit(1)
 
@@ -330,6 +334,7 @@ def get_available_packages(include_suspended: bool = False, 
short_form: bool = F
         "apache-airflow",
         *provider_names,
         "apache-airflow-providers",
+        "apache-airflow-ctl",
         "helm-chart",
         "docker-stack",
     ]

Reply via email to