You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@airflow.apache.org by po...@apache.org on 2020/08/18 17:40:50 UTC
[airflow] branch master updated: You can disable spellcheck or
documentation when building docs. (#10377)
This is an automated email from the ASF dual-hosted git repository.
potiuk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airflow.git
The following commit(s) were added to refs/heads/master by this push:
new 9228bf2 You can disable spellcheck or documentation when building docs. (#10377)
9228bf2 is described below
commit 9228bf2bd08fad26f89831c7c163e02d3d3b7e31
Author: Jarek Potiuk <ja...@polidea.com>
AuthorDate: Tue Aug 18 19:40:18 2020 +0200
You can disable spellcheck or documentation when building docs. (#10377)
This cleans up the document building process and replaces it
with breeze-only. The original instructions with
`pip install -e .[doc]` stopped working so there is no
point keeping them.
Extracted from #10368
---
.pre-commit-config.yaml | 12 +-
BREEZE.rst | 6 +-
CONTRIBUTING.rst | 25 ++--
breeze | 8 +-
docs/{build => build_docs.py} | 238 +++++++++++++++++++-----------
scripts/ci/docs/ci_docs.sh | 2 +-
scripts/ci/in_container/run_docs_build.sh | 4 +-
scripts/ci/libraries/_runs.sh | 2 +-
8 files changed, 182 insertions(+), 115 deletions(-)
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index a4cd756..264c04d 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -22,7 +22,7 @@ default_language_version:
minimum_pre_commit_version: "1.20.0"
repos:
- repo: https://github.com/Lucas-C/pre-commit-hooks
- rev: v1.1.7
+ rev: v1.1.9
hooks:
- id: forbid-tabs
exclude: ^docs/Makefile$|^clients/gen/go.sh
@@ -100,7 +100,7 @@ repos:
- id: insert-license
name: Add license for all XML files
exclude: ^\.github/.*$
- types: [xml]
+ files: .*\.xml$
args:
- --comment-style
- "<!--||-->"
@@ -141,7 +141,7 @@ repos:
hooks:
- id: check-hooks-apply
- repo: https://github.com/pre-commit/pre-commit-hooks
- rev: v3.1.0
+ rev: v3.2.0
hooks:
- id: check-merge-conflict
- id: debug-statements
@@ -156,12 +156,12 @@ repos:
args:
- --remove
- repo: https://github.com/pre-commit/pygrep-hooks
- rev: v1.5.1
+ rev: v1.6.0
hooks:
- id: rst-backticks
- id: python-no-log-warn
- repo: https://github.com/adrienverge/yamllint
- rev: v1.23.0
+ rev: v1.24.2
hooks:
- id: yamllint
name: Check yaml files with yamllint
@@ -169,7 +169,7 @@ repos:
types: [yaml]
exclude: ^.*init_git_sync\.template\.yaml$|^.*airflow\.template\.yaml$|^chart/templates/.*\.yaml$
- repo: https://github.com/timothycrosley/isort
- rev: 5.0.8
+ rev: 5.4.2
hooks:
- id: isort
name: Run isort to sort imports
diff --git a/BREEZE.rst b/BREEZE.rst
index d492897..c3823e3 100644
--- a/BREEZE.rst
+++ b/BREEZE.rst
@@ -1111,14 +1111,16 @@ This is the current syntax for `./breeze <./breeze>`_:
Detailed usage for command: build-docs
- breeze build-docs
+ breeze build-docs [-- <EXTRA_ARGS>]
Builds Airflow documentation. The documentation is build inside docker container - to
maintain the same build environment for everyone. Appropriate sources are mapped from
the host to the container so that latest sources are used. The folders where documentation
- is generated ('docs/build') are also mounted to the container - this way results of
+ is generated ('docs/_build') are also mounted to the container - this way results of
the documentation build is available in the host.
+ The possible extra args are: --docs-only, --spellcheck-only, --help
+
####################################################################################################
diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst
index e44286e..cb5d600 100644
--- a/CONTRIBUTING.rst
+++ b/CONTRIBUTING.rst
@@ -587,27 +587,32 @@ Documentation
The latest API documentation (for the master branch) is usually available
`here <https://airflow.readthedocs.io/en/latest/>`__.
-To generate a local version:
+To generate a local version you can use `<BREEZE.rst>`_.
-1. Set up an Airflow development environment.
+The documentation build consists of verifying consistency of documentation and two steps:
-2. Install the ``doc`` extra.
+* spell checking
+* building documentation
+
+You can only run one of the steps via ``--spellcheck-only`` or ``--docs-only``.
.. code-block:: bash
- pip install -e '.[doc]'
+ ./breeze build-docs
+
+or just to run spell-check
+
+.. code-block:: bash
+ ./breeze build-docs -- --spellcheck-only
-3. Generate and serve the documentation as follows:
+or just to run documentation building
.. code-block:: bash
- cd docs
- ./build
- ./start_doc_server.sh
+ ./breeze build-docs -- --docs-only
-.. note::
- The docs build script ``build`` requires Python 3.6 or greater.
+Also documentation is available as downloadable artifact in GitHub Actions after the CI builds your PR.
**Known issues:**
diff --git a/breeze b/breeze
index d8c8103..29573cd 100755
--- a/breeze
+++ b/breeze
@@ -1126,13 +1126,15 @@ ${CMDNAME} exec [-- <EXTRA_ARGS>]
webserver, workers, database console and interactive terminal.
"
export DETAILED_USAGE_BUILD_DOCS="
-${CMDNAME} build-docs
+${CMDNAME} build-docs [-- <EXTRA_ARGS>]
Builds Airflow documentation. The documentation is build inside docker container - to
maintain the same build environment for everyone. Appropriate sources are mapped from
the host to the container so that latest sources are used. The folders where documentation
- is generated ('docs/build') are also mounted to the container - this way results of
+ is generated ('docs/_build') are also mounted to the container - this way results of
the documentation build is available in the host.
+
+ The possible extra args are: --docs-only, --spellcheck-only, --help
"
# shellcheck disable=SC2089
DETAILED_USAGE_BUILD_IMAGE="
@@ -2210,7 +2212,7 @@ function run_breeze_command {
perform_kind_cluster_operation "${KIND_CLUSTER_OPERATION}"
;;
build_docs)
- run_docs
+ run_docs "${@}"
;;
*)
echo >&2
diff --git a/docs/build b/docs/build_docs.py
similarity index 75%
rename from docs/build
rename to docs/build_docs.py
index e831e7f..a0a7124 100755
--- a/docs/build
+++ b/docs/build_docs.py
@@ -15,8 +15,8 @@
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
+import argparse
import ast
-import atexit
import os
import re
import shlex
@@ -33,12 +33,15 @@ from typing import Iterable, List, NamedTuple, Optional, Set
if __name__ != "__main__":
raise Exception(
"This file is intended to be executed as an executable program. You cannot use it as a module."
- "To run this script, run the ./build command"
+ "To run this script, run the ./build_docs.py command"
)
@total_ordering
class DocBuildError(NamedTuple):
+ """
+ Errors found in docs build.
+ """
file_path: Optional[str]
line_no: Optional[int]
message: str
@@ -49,7 +52,7 @@ class DocBuildError(NamedTuple):
return left == right
def __ne__(self, other):
- return not (self == other)
+ return not self == other
def __lt__(self, other):
file_path_a = self.file_path or ''
@@ -61,60 +64,29 @@ class DocBuildError(NamedTuple):
build_errors: List[DocBuildError] = []
-os.chdir(os.path.dirname(os.path.abspath(__file__)))
-
-ROOT_PROJECT_DIR = os.path.abspath(os.path.join(os.getcwd(), os.pardir))
+ROOT_PROJECT_DIR = os.path.abspath(os.path.join(os.path.dirname(os.path.realpath(__file__)), os.pardir))
ROOT_PACKAGE_DIR = os.path.join(ROOT_PROJECT_DIR, "airflow")
+DOCS_DIR = os.path.join(ROOT_PROJECT_DIR, "docs")
+
+_API_DIR = os.path.join(DOCS_DIR, "_api")
+_BUILD_DIR = os.path.join(DOCS_DIR, "_build")
def clean_files() -> None:
- print("Removing content of the _build and _api folders")
- with suppress(FileNotFoundError):
- for filename in glob("_build/*"):
- shutil.rmtree(f"_build/{filename}")
- with suppress(FileNotFoundError):
- for filename in glob("_api/*"):
- shutil.rmtree(f"_api/{filename}")
- print("Removed content of the _build and _api folders")
-
-
-def prepare_directories() -> None:
- if os.path.exists("/.dockerenv"):
- # This script can be run both - in container and outside of it.
- # Here we are inside the container which means that we should (when the host is Linux)
- # fix permissions of the _build and _api folders via sudo.
- # Those files are mounted from the host via docs folder and we might not have permissions to
- # write to those directories (and remove the _api folder).
- # We know we have sudo capabilities inside the container.
- print("Creating the _build and _api folders in case they do not exist")
- run(["sudo", "mkdir", "-pv", "_build"], check=True)
- run(["sudo", "mkdir", "-pv", "_api"], check=True)
- print("Created the _build and _api folders in case they do not exist")
-
- def restore_ownership() -> None:
- # We are inside the container which means that we should fix back the permissions of the
- # _build and _api folder files, so that they can be accessed by the host user
- # The _api folder should be deleted by then but just in case we should change the ownership
- host_user_id = os.environ["HOST_USER_ID"]
- host_group_id = os.environ["HOST_GROUP_ID"]
- print(f"Changing ownership of docs/_build folder back to {host_user_id}:{host_group_id}")
- run(["sudo", "chown", "-R", f'{host_user_id}:{host_group_id}', "_build"], check=True)
- if os.path.exists("_api"):
- run(["sudo", "chown", "-R", f'{host_user_id}:{host_group_id}', "_api"], check=True)
-
- print(f"Changed ownership of docs/_build folder back to {host_user_id}:{host_group_id}")
-
- atexit.register(restore_ownership)
-
- else:
- # We are outside the container so we simply make sure that the directories exist
- print("Creating the _build and _api folders in case they do not exist")
- run(["mkdir", "-pv", "_build"], check=True)
- run(["mkdir", "-pv", "_api"], check=True)
- print("Creating the _build and _api folders in case they do not exist")
+ """
+ Cleanup all artifacts generated by previous builds.
+ """
+ shutil.rmtree(_API_DIR, ignore_errors=True)
+ shutil.rmtree(_BUILD_DIR, ignore_errors=True)
+ os.makedirs(_API_DIR, exist_ok=True)
+ os.makedirs(_BUILD_DIR, exist_ok=True)
+ print(f"Recreated content of the ${_BUILD_DIR} and ${_API_DIR} folders")
def display_errors_summary() -> None:
+ """
+ Displays summary of errors
+ """
for warning_no, error in enumerate(sorted(build_errors), 1):
print("=" * 20, f"Error {warning_no:3}", "=" * 20)
print(error.message)
@@ -129,10 +101,14 @@ def display_errors_summary() -> None:
print("=" * 50)
-def find_existing_guide_operator_names():
+def find_existing_guide_operator_names() -> Set[str]:
+ """
+ Find names of existing operators.
+ :return names of existing operators.
+ """
operator_names = set()
- paths = glob("howto/operator/**/*.rst", recursive=True)
+ paths = glob(f"${DOCS_DIR}/howto/operator/**/*.rst", recursive=True)
for path in paths:
with open(path) as f:
operator_names |= set(re.findall(".. _howto/operator:(.+?):", f.read()))
@@ -141,11 +117,25 @@ def find_existing_guide_operator_names():
def extract_ast_class_def_by_name(ast_tree, class_name):
+ """
+ Extracts class definition by name
+ :param ast_tree: AST tree
+ :param class_name: name of the class.
+ :return: class node found
+ """
class ClassVisitor(ast.NodeVisitor):
+ """
+ Visitor.
+ """
def __init__(self):
self.found_class_node = None
- def visit_ClassDef(self, node):
+ def visit_ClassDef(self, node): # pylint: disable=invalid-name
+ """
+ Visit class definition.
+ :param node: node.
+ :return:
+ """
if node.name == class_name:
self.found_class_node = node
@@ -155,19 +145,23 @@ def extract_ast_class_def_by_name(ast_tree, class_name):
return visitor.found_class_node
-def check_guide_links_in_operator_descriptions():
+def check_guide_links_in_operator_descriptions() -> None:
+ """
+ Check if there are links to guides in operator's descriptions.
+
+ """
def generate_build_error(path, line_no, operator_name):
return DocBuildError(
- file_path=path,
- line_no=line_no,
- message=(
- f"Link to the guide is missing in operator's description: {operator_name}.\n"
- f"Please add link to the guide to the description in the following form:\n"
- f"\n"
- f".. seealso::\n"
- f" For more information on how to use this operator, take a look at the guide:\n"
- f" :ref:`howto/operator:{operator_name}`\n"
- )
+ file_path=path,
+ line_no=line_no,
+ message=(
+ f"Link to the guide is missing in operator's description: {operator_name}.\n"
+ f"Please add link to the guide to the description in the following form:\n"
+ f"\n"
+ f".. seealso::\n"
+ f" For more information on how to use this operator, take a look at the guide:\n"
+ f" :ref:`howto/operator:{operator_name}`\n"
+ )
)
# Extract operators for which there are existing .rst guides
@@ -213,6 +207,12 @@ def check_guide_links_in_operator_descriptions():
def assert_file_not_contains(file_path: str, pattern: str, message: str) -> None:
+ """
+ Asserts that file does not contain the pattern. Return message error if it does.
+ :param file_path: file
+ :param pattern: pattern
+ :param message: message to return
+ """
with open(file_path, "rb", 0) as doc_file:
pattern_compiled = re.compile(pattern)
@@ -223,6 +223,12 @@ def assert_file_not_contains(file_path: str, pattern: str, message: str) -> None
def filter_file_list_by_pattern(file_paths: Iterable[str], pattern: str) -> List[str]:
+ """
+ Filters file list to those tha content matches the pattern
+ :param file_paths: file paths to check
+ :param pattern: pattern to match
+ :return: list of files matching the pattern
+ """
output_paths = []
pattern_compiled = re.compile(pattern)
for file_path in file_paths:
@@ -234,20 +240,28 @@ def filter_file_list_by_pattern(file_paths: Iterable[str], pattern: str) -> List
def find_modules(deprecated_only: bool = False) -> Set[str]:
+ """
+ Finds all modules.
+ :param deprecated_only: whether only deprecated modules should be found.
+ :return: set of all modules found
+ """
file_paths = glob(f"{ROOT_PACKAGE_DIR}/**/*.py", recursive=True)
# Exclude __init__.py
- file_paths = (f for f in file_paths if not f.endswith("__init__.py"))
+ file_paths = [f for f in file_paths if not f.endswith("__init__.py")]
if deprecated_only:
file_paths = filter_file_list_by_pattern(file_paths, r"This module is deprecated.")
# Make path relative
- file_paths = (os.path.relpath(f, ROOT_PROJECT_DIR) for f in file_paths)
+ file_paths = [os.path.relpath(f, ROOT_PROJECT_DIR) for f in file_paths]
# Convert filename to module
modules_names = {file_path.rpartition(".")[0].replace("/", ".") for file_path in file_paths}
return modules_names
def check_class_links_in_operators_and_hooks_ref() -> None:
- with open("operators-and-hooks-ref.rst") as ref_file:
+ """
+ Checks classes and links in the operators and hooks ref.
+ """
+ with open(os.path.join(DOCS_DIR, "operators-and-hooks-ref.rst")) as ref_file:
content = ref_file.read()
current_modules_in_file = set(re.findall(r":mod:`(.+?)`", content))
@@ -278,21 +292,24 @@ def check_class_links_in_operators_and_hooks_ref() -> None:
def check_guide_links_in_operators_and_hooks_ref() -> None:
- all_guides = glob("howto/operator/**/*.rst", recursive=True)
+ """
+ Checks all guide links in operators and hooks references.
+ """
+ all_guides = glob(f"{DOCS_DIR}/howto/operator/**/*.rst", recursive=True)
# Remove extension
- all_guides = (
- guide.rpartition(".")[0]
+ all_guides = [
+ os.path.relpath(guide, DOCS_DIR).rpartition(".")[0]
for guide in all_guides
if "_partials" not in guide
- )
+ ]
# Remove partials and index
- all_guides = (
+ all_guides = [
guide
for guide in all_guides
if "/_partials/" not in guide and not guide.endswith("index")
- )
+ ]
- with open("operators-and-hooks-ref.rst") as ref_file:
+ with open(os.path.join(DOCS_DIR, "operators-and-hooks-ref.rst")) as ref_file:
content = ref_file.read()
missing_guides = [
@@ -320,7 +337,10 @@ def check_guide_links_in_operators_and_hooks_ref() -> None:
def check_exampleinclude_for_example_dags():
- all_docs_files = glob("**/*rst", recursive=True)
+ """
+ Checks all exampleincludes for example dags.
+ """
+ all_docs_files = glob(f"${DOCS_DIR}/**/*rst", recursive=True)
for doc_file in all_docs_files:
assert_file_not_contains(
@@ -328,13 +348,16 @@ def check_exampleinclude_for_example_dags():
pattern=r"literalinclude::.+example_dags",
message=(
"literalinclude directive is is prohibited for example DAGs. \n"
- "You should use a exampleinclude directive to include example DAGs."
+ "You should use the exampleinclude directive to include example DAGs."
)
)
def check_enforce_code_block():
- all_docs_files = glob("**/*rst", recursive=True)
+ """
+ Checks all code:: blocks.
+ """
+ all_docs_files = glob(f"${DOCS_DIR}/**/*rst", recursive=True)
for doc_file in all_docs_files:
assert_file_not_contains(
@@ -367,11 +390,15 @@ MISSING_GOOGLE_DOC_GUIDES = {
def check_google_guides():
- doc_files = glob(f"{ROOT_PROJECT_DIR}/docs/howto/operator/google/**/*.rst", recursive=True)
+ """
+ Checks Google guides.
+
+ """
+ doc_files = glob(f"{DOCS_DIR}/howto/operator/google/**/*.rst", recursive=True)
doc_names = {f.split("/")[-1].rsplit(".")[0] for f in doc_files}
operators_files = chain(*[
- glob(f"{ROOT_PROJECT_DIR}/airflow/providers/google/*/{resource_type}/*.py")
+ glob(f"{ROOT_PACKAGE_DIR}/providers/google/*/{resource_type}/*.py")
for resource_type in ["operators", "sensors", "transfers"]
])
operators_files = (f for f in operators_files if not f.endswith("__init__.py"))
@@ -405,6 +432,13 @@ def check_google_guides():
def prepare_code_snippet(file_path: str, line_no: int, context_lines_count: int = 5) -> str:
+ """
+ Prepares code snippet.
+ :param file_path: file path
+ :param line_no: line number
+ :param context_lines_count: number of lines of context.
+ :return:
+ """
def guess_lexer_for_filename(filename):
from pygments.lexers import get_lexer_for_filename
from pygments.util import ClassNotFound
@@ -439,12 +473,18 @@ def prepare_code_snippet(file_path: str, line_no: int, context_lines_count: int
def parse_sphinx_warnings(warning_text: str) -> List[DocBuildError]:
+ """
+ Parses warnings from Sphinx.
+ :param warning_text: warning to parse
+ :return: list of DocBuildErrors.
+ """
sphinx_build_errors = []
for sphinx_warning in warning_text.split("\n"):
if not sphinx_warning:
continue
warning_parts = sphinx_warning.split(":", 2)
if len(warning_parts) == 3:
+ # noinspection PyBroadException
try:
sphinx_build_errors.append(
DocBuildError(
@@ -464,6 +504,11 @@ def parse_sphinx_warnings(warning_text: str) -> List[DocBuildError]:
def check_spelling() -> None:
+ """
+ Checks spelling for sphinx.
+
+ :return:
+ """
build_cmd = [
"sphinx-build",
"-W",
@@ -471,7 +516,7 @@ def check_spelling() -> None:
"spelling",
"-d", # path for the cached environment and doctree files
"_build/doctrees",
- "-D", # override the extensions because one of them throws an error on the spelling builder
+ "-D", # override the extensions because one of them throws an error on the spelling builder
"""extensions=sphinxarg.ext\
,autoapi.extension\
,sphinxcontrib.spelling\
@@ -490,7 +535,7 @@ def check_spelling() -> None:
]
print("Executing cmd: ", " ".join([shlex.quote(c) for c in build_cmd]))
- completed_proc = run(build_cmd)
+ completed_proc = run(build_cmd, cwd=DOCS_DIR) # pylint: disable=subprocess-run-check
if completed_proc.returncode != 0:
build_errors.append(
DocBuildError(
@@ -502,6 +547,9 @@ def check_spelling() -> None:
def build_sphinx_docs() -> None:
+ """
+ Build documentation for sphinx.
+ """
with NamedTemporaryFile() as tmp_file:
build_cmd = [
"sphinx-build",
@@ -517,7 +565,7 @@ def build_sphinx_docs() -> None:
]
print("Executing cmd: ", " ".join([shlex.quote(c) for c in build_cmd]))
- completed_proc = run(build_cmd)
+ completed_proc = run(build_cmd, cwd=DOCS_DIR) # pylint: disable=subprocess-run-check
if completed_proc.returncode != 0:
build_errors.append(
DocBuildError(
@@ -535,6 +583,11 @@ def build_sphinx_docs() -> None:
def print_build_errors_and_exit(message) -> None:
+ """
+ Prints build errors and exists.
+ :param message:
+ :return:
+ """
if build_errors:
display_errors_summary()
print()
@@ -544,9 +597,14 @@ def print_build_errors_and_exit(message) -> None:
sys.exit(1)
-print("Current working directory: ", os.getcwd())
+parser = argparse.ArgumentParser(description='Builds documentation and runs spell checking')
+parser.add_argument('--docs-only', dest='docs_only', action='store_true',
+ help='Only build documentation')
+parser.add_argument('--spellcheck-only', dest='spellcheck_only', action='store_true',
+ help='Only perform spellchecking')
+
+args = parser.parse_args()
-prepare_directories()
clean_files()
check_guide_links_in_operator_descriptions()
@@ -564,10 +622,10 @@ Invitation link: https://apache-airflow-slack.herokuapp.com/\
print_build_errors_and_exit("The documentation has errors. Fix them to build documentation.")
-check_spelling()
-
-print_build_errors_and_exit("The documentation has spelling errors. Fix them to build documentation.")
-
-build_sphinx_docs()
+if not args.docs_only:
+ check_spelling()
+ print_build_errors_and_exit("The documentation has spelling errors. Fix them to build documentation.")
-print_build_errors_and_exit("The documentation has errors.")
+if not args.spellcheck_only:
+ build_sphinx_docs()
+ print_build_errors_and_exit("The documentation has errors.")
diff --git a/scripts/ci/docs/ci_docs.sh b/scripts/ci/docs/ci_docs.sh
index 605ce85..3e370f7 100755
--- a/scripts/ci/docs/ci_docs.sh
+++ b/scripts/ci/docs/ci_docs.sh
@@ -34,4 +34,4 @@ prepare_ci_build
rebuild_ci_image_if_needed
-run_docs
+run_docs "${@}"
diff --git a/scripts/ci/in_container/run_docs_build.sh b/scripts/ci/in_container/run_docs_build.sh
index 6e1b022..498ec02 100755
--- a/scripts/ci/in_container/run_docs_build.sh
+++ b/scripts/ci/in_container/run_docs_build.sh
@@ -26,9 +26,9 @@ trap "${HANDLERS}${HANDLERS:+;}in_container_fix_ownership" EXIT
sudo rm -rf "${AIRFLOW_SOURCES}/docs/_build/*"
sudo rm -rf "${AIRFLOW_SOURCES}/docs/_api/*"
-sudo -E "${AIRFLOW_SOURCES}/docs/build"
+sudo -E "${AIRFLOW_SOURCES}/docs/build_docs.py" "${@}"
-if [[ ${CI} == "true" ]]; then
+if [[ ${CI} == "true" && -d "${AIRFLOW_SOURCES}/docs/_build/html" ]]; then
rm -rf "/files/documentation"
cp -r "${AIRFLOW_SOURCES}/docs/_build/html" "/files/documentation"
fi
diff --git a/scripts/ci/libraries/_runs.sh b/scripts/ci/libraries/_runs.sh
index 9608d57..53d1055 100644
--- a/scripts/ci/libraries/_runs.sh
+++ b/scripts/ci/libraries/_runs.sh
@@ -21,7 +21,7 @@ function run_docs() {
verbose_docker run "${EXTRA_DOCKER_FLAGS[@]}" -t \
--entrypoint "/usr/local/bin/dumb-init" \
"${AIRFLOW_CI_IMAGE}" \
- "--" "/opt/airflow/docs/build" \
+ "--" "/opt/airflow/docs/build_docs.py" "${@}" \
| tee -a "${OUTPUT_LOG}"
}