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

potiuk pushed a commit to branch v3-1-test
in repository https://gitbox.apache.org/repos/asf/airflow.git


The following commit(s) were added to refs/heads/v3-1-test by this push:
     new 29d8a85e1f4 [v3-1-test] Add --open-ide flag and enhance --confirm in 
setup_idea.py (#63632) (#63644)
29d8a85e1f4 is described below

commit 29d8a85e1f42cb93970009651a395fd8e0737629
Author: Jarek Potiuk <[email protected]>
AuthorDate: Sun Mar 15 14:43:27 2026 +0100

    [v3-1-test] Add --open-ide flag and enhance --confirm in setup_idea.py 
(#63632) (#63644)
    
    * Add --confirm and --open-ide flags to setup_idea.py
    
    - --confirm now auto-answers yes to all prompts (IDE close, process
      kill, file overwrite) instead of only the IDE close prompt
    - --open-ide opens IntelliJ IDEA or PyCharm in the project directory
      after setup completes, supporting macOS (open -a) and Linux
      (Toolbox scripts and PATH lookup)
    
    * Add AGENTS.md for IDE setup with --confirm and --open-ide usage
    
    * Update setup_idea docs with --confirm and --open-ide descriptions
    (cherry picked from commit e4dbc55b032579cde1fc43a007bf385e935cdf31)
---
 .../contributors_quick_start_pycharm_intellij.rst  | 341 +++++++++++++++++++++
 dev/ide_setup/AGENTS.md                            |  22 ++
 dev/ide_setup/setup_idea.py                        |  83 ++++-
 3 files changed, 434 insertions(+), 12 deletions(-)

diff --git 
a/contributing-docs/quick-start-ide/contributors_quick_start_pycharm_intellij.rst
 
b/contributing-docs/quick-start-ide/contributors_quick_start_pycharm_intellij.rst
new file mode 100644
index 00000000000..7666ffc7d71
--- /dev/null
+++ 
b/contributing-docs/quick-start-ide/contributors_quick_start_pycharm_intellij.rst
@@ -0,0 +1,341 @@
+ .. 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.
+
+.. contents:: Table of Contents
+   :depth: 2
+   :local:
+
+Setup your project
+##################
+
+1. Open your IDE or source code editor and select the option to clone the 
repository
+
+   .. raw:: html
+
+      <div align="center" style="padding-bottom:10px">
+        <img src="images/pycharm_clone.png"
+             alt="Cloning github fork to Pycharm">
+      </div>
+
+2. Paste the repository link in the URL field and submit.
+
+   .. raw:: html
+
+      <div align="center" style="padding-bottom:10px">
+        <img src="images/pycharm_click_on_clone.png"
+             alt="Cloning github fork to Pycharm">
+      </div>
+
+3. Run the ``dev/ide_setup/setup_idea.py`` script to configure the project 
automatically.
+   The script runs ``uv sync`` to create the ``.venv`` virtualenv, detects the 
Python SDK, and
+   generates the ``.idea/airflow.iml``, ``.idea/modules.xml``, and 
``.idea/misc.xml`` files.
+
+   The script supports two modes — **single-module** and **multi-module** — and
+   **auto-detects** which one to use based on the installed IDE:
+
+   * If **IntelliJ IDEA** is detected → defaults to **multi-module**.
+   * If only **PyCharm** is detected (or no IDE is found) → defaults to 
**single-module**.
+
+   You can override auto-detection with ``--multi-module`` or 
``--single-module``.
+
+   **Single-module mode** — all source roots are registered under one IntelliJ 
module.
+   This works in both PyCharm and IntelliJ IDEA:
+
+    .. code-block:: bash
+
+      $ uv run dev/ide_setup/setup_idea.py --single-module
+
+   **Multi-module mode** — each distribution/package gets its own IntelliJ 
module with a
+   separate ``.iml`` file (e.g. ``airflow-core/airflow-core.iml``,
+   ``providers/amazon/providers-amazon.iml``).  This gives better per-module 
SDK control and
+   cleaner project structure in the IDE's Project view.
+
+    .. code-block:: bash
+
+      $ uv run dev/ide_setup/setup_idea.py --multi-module
+
+   .. note::
+
+      **Multi-module mode requires IntelliJ IDEA Ultimate** — it does **not** 
work in PyCharm
+      (Community or Professional).  PyCharm does not support multiple content 
roots pointing to
+      sub-directories of the project root that each carry their own ``.iml`` 
module file; it
+      silently ignores or mishandles sub-modules.  IntelliJ IDEA Ultimate with 
the Python plugin
+      handles this correctly because it has full support for the IntelliJ 
multi-module project
+      model.  If you use PyCharm, stick with single-module mode.
+
+   Then restart PyCharm/IntelliJ IDEA.
+
+   .. raw:: html
+
+      <div align="center" style="padding-bottom:10px">
+        <img src="images/pycharm-airflow.iml.png"
+             alt="airflow.iml">
+      </div>
+
+   .. raw:: html
+
+        <div align="center" style="padding-bottom:10px">
+          <img src="images/pycharm-modules.xml.png"
+              alt="modules.xml">
+        </div>
+
+   Script options
+   ==============
+
+   ``--python VERSION``
+     Choose the Python minor version for the virtualenv (e.g. ``3.12``).  The 
version is passed
+     to ``uv sync --python`` and must be compatible with the project's 
``requires-python``
+     constraint.  When omitted, ``uv`` picks the default version.
+
+      .. code-block:: bash
+
+        $ uv run dev/ide_setup/setup_idea.py --python 3.12
+
+   ``--multi-module`` / ``--single-module``
+     Control whether the project is configured as a single IntelliJ module 
(all source roots in
+     one ``.iml`` file) or as multiple modules (one ``.iml`` per 
distribution/package, e.g.
+     ``airflow-core/airflow-core.iml``, 
``providers/amazon/providers-amazon.iml``).
+
+     **By default the script auto-detects which IDE is installed** and picks 
the appropriate
+     mode: multi-module when IntelliJ IDEA is found, single-module when only 
PyCharm is found
+     (or when no IDE can be detected).  Use ``--multi-module`` or 
``--single-module`` to
+     override the auto-detected default.
+
+     In multi-module mode the script also creates a dedicated ``dev/breeze`` 
virtualenv
+     (via a second ``uv sync``) with its own Python SDK named *Python X.Y 
(breeze)*.
+     All other sub-modules inherit the project-level SDK.
+
+      .. code-block:: bash
+
+        # Force multi-module (requires IntelliJ IDEA)
+        $ uv run dev/ide_setup/setup_idea.py --multi-module
+
+        # Force single-module (works in both PyCharm and IntelliJ IDEA)
+        $ uv run dev/ide_setup/setup_idea.py --single-module
+
+   ``--confirm``
+     Automatically answer yes to all interactive confirmation prompts (IDE 
close, process kill,
+     file overwrite).  Useful for non-interactive, scripted, or agent-driven 
runs.
+
+      .. code-block:: bash
+
+        $ uv run dev/ide_setup/setup_idea.py --confirm
+
+   ``--open-ide``
+     Open IntelliJ IDEA or PyCharm in the project directory after setup 
completes.  On macOS
+     uses ``open -a``, on Linux looks for JetBrains Toolbox launcher scripts 
and falls back to
+     commands on ``PATH``.  Prefers IntelliJ IDEA when both IDEs are installed.
+
+      .. code-block:: bash
+
+        $ uv run dev/ide_setup/setup_idea.py --open-ide
+
+        # Combine with --confirm for fully non-interactive setup + open
+        $ uv run dev/ide_setup/setup_idea.py --confirm --open-ide
+
+   ``--no-kill``
+     Do not attempt to detect and kill running PyCharm/IntelliJ IDEA 
processes.  By default,
+     the script looks for running IDE processes, asks for confirmation, sends 
``SIGTERM``, and
+     falls back to ``SIGKILL`` if they don't exit within 5 seconds.  Use 
``--no-kill`` to
+     disable this behaviour and fall back to the manual confirmation prompt 
instead.
+
+      .. code-block:: bash
+
+        $ uv run dev/ide_setup/setup_idea.py --no-kill
+
+   ``--idea-path PATH``
+     Path to the JetBrains configuration directory to update instead of 
auto-detecting all
+     installed IDEs.  Can point to the base JetBrains directory
+     (e.g. ``~/Library/Application Support/JetBrains``) or a specific product 
directory
+     (e.g. ``.../JetBrains/IntelliJIdea2025.1``).  Useful when auto-detection 
does not find
+     your IDE or when you want to target a specific installation.
+
+      .. code-block:: bash
+
+        $ uv run dev/ide_setup/setup_idea.py --idea-path 
~/Library/Application\ Support/JetBrains/IntelliJIdea2025.1
+
+   ``--exclude MODULE_OR_GROUP``
+     Exclude modules from the generated project configuration.  Can be 
specified multiple times.
+     Useful when you only work on a subset of the codebase and want faster IDE 
indexing.
+
+     A value can be either a module path relative to the project root (e.g. 
``providers/amazon``,
+     ``dev/breeze``) or one of the recognised group names:
+
+     * ``providers`` — all provider modules under ``providers/``
+     * ``shared`` — all shared libraries under ``shared/``
+     * ``dev`` — the ``dev`` module
+     * ``tests`` — test-only modules (``docker-tests``, ``kubernetes-tests``, 
etc.)
+
+     Examples:
+
+      .. code-block:: bash
+
+        # Exclude all providers and shared libraries
+        $ uv run dev/ide_setup/setup_idea.py --exclude providers --exclude 
shared
+
+        # Exclude a single provider
+        $ uv run dev/ide_setup/setup_idea.py --exclude providers/amazon
+
+        # Multi-module with only core modules
+        $ uv run dev/ide_setup/setup_idea.py --multi-module --exclude 
providers --exclude shared
+
+   Options can be combined freely.  For instance, to create a multi-module 
project with
+   Python 3.12 excluding all providers:
+
+    .. code-block:: bash
+
+      $ uv run dev/ide_setup/setup_idea.py --multi-module --python 3.12 
--exclude providers
+
+   What the script generates
+   =========================
+
+   * ``.idea/airflow.iml`` — root module definition with source roots 
(single-module mode) or
+     exclude-only root module (multi-module mode).
+   * ``.idea/modules.xml`` — module registry listing all IntelliJ modules.
+   * ``.idea/misc.xml`` — project-level Python SDK reference (derived from 
``.venv``).
+   * ``.idea/.name`` — sets the PyCharm project name to ``airflow-<dirname>`` 
so the
+     auto-detected SDK name matches the configuration.
+   * ``<module>/<module>.iml`` — per-module files (multi-module mode only).
+
+   The script also registers the Python SDKs (root project and Breeze) in the 
global
+   JetBrains ``jdk.table.xml`` configuration using the ``uv (<name>)`` naming 
convention
+   that matches PyCharm's auto-detected uv interpreters.  This means the SDKs 
are
+   immediately available when you open the project — no manual interpreter 
setup needed.
+
+   The script also configures project-wide exclusion patterns (``__pycache__``,
+   ``node_modules``, ``*.egg-info``, cache directories, etc.) so that IntelliJ 
does not
+   index or search generated/build artifacts.
+
+4. Alternatively, you can configure your project manually. Configure the 
source root directories
+   for ``airflow-core``, ``task-sdk``, ``airflow-ctl`` and ``devel-common``. 
You also have to set
+   "source" and "tests" root directories for each provider you want to develop 
(!).
+
+   In Airflow 3.0 we split ``airflow-core``, ``task-sdk``, ``airflow-ctl``, 
``devel-common``,
+   and each provider to be separate distribution — each with separate 
``pyproject.toml`` file,
+   so you need to separately add ``src`` and ``tests`` directories for each 
provider you develop
+   to be respectively "source roots" and "test roots".
+
+   .. raw:: html
+
+      <div align="center" style="padding-bottom:10px">
+        <img src="images/pycharm_add_provider_sources_and_tests.png"
+             alt="Adding Source Root directories to Pycharm">
+      </div>
+
+   You also need to add ``task-sdk`` sources (and ``devel-common`` in similar 
way).
+
+   .. raw:: html
+
+      <div align="center" style="padding-bottom:10px">
+        <img src="images/pycharm_add_task_sdk_sources.png"
+             alt="Adding Source Root directories to Pycharm">
+      </div>
+
+5. If you configured the project manually (step 4), configure the Python 
interpreter to use
+   the virtualenv created by ``uv sync``:  go to ``File → Settings → Project → 
Python Interpreter``,
+   click the gear icon, choose *Add Interpreter → Existing*, and point to 
``.venv/bin/python``.
+   If you used the setup script (step 3), the SDK is already registered 
globally — just
+   restart the IDE and the interpreter will be available automatically.
+
+    .. raw:: html
+
+        <div align="center" style="padding-bottom:10px">
+          <img src="images/pycharm_add_interpreter.png"
+              alt="Configuring Python Interpreter">
+        </div>
+
+6. It is recommended to invalidate caches and restart PyCharm after setting up 
the project.
+
+   .. raw:: html
+
+      <div align="center" style="padding-bottom:10px">
+        <img src="images/pycharm_invalidate_caches.png"
+             alt="Invalidate caches and restart Pycharm">
+      </div>
+
+
+
+Setting up debugging
+####################
+
+It requires "airflow-env" virtual environment configured locally.
+
+1. Configuring Airflow database connection
+
+- Airflow is by default configured to use SQLite database. Configuration can 
be seen on local machine
+  ``~/airflow/airflow.cfg`` under ``sql_alchemy_conn``.
+
+- Installing required dependency for MySQL connection in ``airflow-env`` on 
local machine.
+
+  .. code-block:: bash
+
+    $ pyenv activate airflow-env
+    $ pip install PyMySQL
+
+- Now set ``sql_alchemy_conn = 
mysql+pymysql://root:@127.0.0.1:23306/airflow?charset=utf8mb4`` in file
+  ``~/airflow/airflow.cfg`` on local machine.
+
+2. Debugging an example Dag
+
+- Add Interpreter to PyCharm pointing interpreter path to 
``~/.pyenv/versions/airflow-env/bin/python``, which is virtual
+  environment ``airflow-env`` created with pyenv earlier. For adding an 
Interpreter go to ``File -> Setting -> Project:
+  airflow -> Python Interpreter``.
+
+  .. raw:: html
+
+    <div align="center" style="padding-bottom:10px">
+      <img src="images/pycharm_add_interpreter.png"
+           alt="Adding existing interpreter">
+    </div>
+
+- In PyCharm IDE open Airflow project, directory ``/files/dags`` of local 
machine is by default mounted to docker
+  machine when breeze Airflow is started. So any Dag file present in this 
directory will be picked automatically by
+  scheduler running in docker machine and same can be seen on 
``http://127.0.0.1:28080``.
+
+- Copy any example Dag present in the ``/airflow/example_dags`` directory to 
``/files/dags/``.
+
+- Add a ``__main__`` block at the end of your Dag file to make it runnable:
+
+  .. code-block:: python
+
+    if __name__ == "__main__":
+        dag.test()
+
+- Run the file.
+
+Creating a branch
+#################
+
+1. Click on the branch symbol in the status bar
+
+   .. raw:: html
+
+      <div align="center" style="padding-bottom:10px">
+        <img src="images/pycharm_creating_branch_1.png"
+             alt="Creating a new branch">
+      </div>
+
+2. Give a name to a branch and checkout
+
+   .. raw:: html
+
+      <div align="center" style="padding-bottom:10px">
+        <img src="images/pycharm_creating_branch_2.png"
+             alt="Giving a name to a branch">
+      </div>
+
+Follow the `Quick start 
<../03b_contributors_quick_start_seasoned_developers.rst>`_ for typical 
development tasks.
diff --git a/dev/ide_setup/AGENTS.md b/dev/ide_setup/AGENTS.md
new file mode 100644
index 00000000000..ebb878d86a1
--- /dev/null
+++ b/dev/ide_setup/AGENTS.md
@@ -0,0 +1,22 @@
+ <!-- SPDX-License-Identifier: Apache-2.0
+      https://www.apache.org/licenses/LICENSE-2.0 -->
+
+<!-- START doctoc generated TOC please keep comment here to allow auto update 
-->
+<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
+**Table of Contents**  *generated with 
[DocToc](https://github.com/thlorenz/doctoc)*
+
+- [IDE Setup](#ide-setup)
+
+<!-- END doctoc generated TOC please keep comment here to allow auto update -->
+
+# IDE Setup
+
+When the user wants to open the project in IntelliJ IDEA or PyCharm (e.g. to 
review
+a PR, inspect code, or work on changes), run:
+
+```bash
+uv run dev/ide_setup/setup_idea.py --confirm --open-ide
+```
+
+This regenerates the IDE configuration files without prompts (`--confirm`) and
+opens the IDE in the project directory automatically (`--open-ide`).
diff --git a/dev/ide_setup/setup_idea.py b/dev/ide_setup/setup_idea.py
index a19cbfb2bb6..c1f5cabb58b 100755
--- a/dev/ide_setup/setup_idea.py
+++ b/dev/ide_setup/setup_idea.py
@@ -28,6 +28,7 @@ import argparse
 import os
 import platform
 import re
+import shutil
 import signal
 import subprocess
 import sys
@@ -461,7 +462,7 @@ def _find_jetbrains_pids() -> list[tuple[int, str]]:
     return []
 
 
-def _kill_jetbrains_ides() -> bool:
+def _kill_jetbrains_ides(*, confirm: bool = False) -> bool:
     """Attempt to gracefully terminate running JetBrains IDE processes.
 
     Sends SIGTERM first and waits briefly, then SIGKILL if processes remain.
@@ -473,9 +474,10 @@ def _kill_jetbrains_ides() -> bool:
     print("[yellow]Detected running JetBrains IDE process(es):[/]")
     for pid, comm in pids:
         print(f"  PID {pid}: {comm}")
-    should_kill = Confirm.ask("\nKill these processes to proceed?")
-    if not should_kill:
-        return True
+    if not confirm:
+        should_kill = Confirm.ask("\nKill these processes to proceed?")
+        if not should_kill:
+            return True
     for pid, _comm in pids:
         try:
             os.kill(pid, signal.SIGTERM)
@@ -499,6 +501,53 @@ def _kill_jetbrains_ides() -> bool:
     return True
 
 
+def _open_ide(*, project_dir: Path, idea_path: Path | None = None) -> None:
+    """Open IntelliJ IDEA or PyCharm in *project_dir*.
+
+    On macOS uses ``open -a``, on Linux uses the IDE's launcher script.
+    Prefers IntelliJ IDEA over PyCharm when both are installed.
+    """
+    system = platform.system()
+    has_intellij, has_pycharm = _detect_installed_ides(idea_path)
+    if system == "Darwin":
+        # Try to find the .app bundle via common Toolbox / standalone paths.
+        app_name = None
+        if has_intellij:
+            app_name = "IntelliJ IDEA"
+        elif has_pycharm:
+            app_name = "PyCharm"
+        if app_name:
+            print(f"[cyan]Opening {app_name}...[/]")
+            subprocess.Popen(["open", "-a", app_name, str(project_dir)])
+            return
+    elif system == "Linux":
+        # JetBrains Toolbox symlinks launchers into 
~/.local/share/JetBrains/Toolbox/scripts/
+        toolbox_scripts = Path.home() / ".local" / "share" / "JetBrains" / 
"Toolbox" / "scripts"
+        for cmd_name, is_match in [("idea", has_intellij), ("pycharm", 
has_pycharm)]:
+            if not is_match:
+                continue
+            script = toolbox_scripts / cmd_name
+            if script.exists():
+                label = "IntelliJ IDEA" if cmd_name == "idea" else "PyCharm"
+                print(f"[cyan]Opening {label}...[/]")
+                subprocess.Popen([str(script), str(project_dir)])
+                return
+        # Fall back to shell commands on PATH.
+        for cmd_name, is_match in [("idea", has_intellij), ("pycharm", 
has_pycharm)]:
+            if not is_match:
+                continue
+            cmd_path = shutil.which(cmd_name)
+            if cmd_path:
+                label = "IntelliJ IDEA" if cmd_name == "idea" else "PyCharm"
+                print(f"[cyan]Opening {label}...[/]")
+                subprocess.Popen([cmd_path, str(project_dir)])
+                return
+    print(
+        "[yellow]Could not find IntelliJ IDEA or PyCharm to open 
automatically.[/]\n"
+        "[yellow]Please open the IDE manually in:[/] " + str(project_dir)
+    )
+
+
 def _find_jetbrains_config_base() -> Path | None:
     """Return the base JetBrains configuration directory for the current 
platform."""
     system = platform.system()
@@ -974,8 +1023,14 @@ def _build_parser() -> argparse.ArgumentParser:
     parser.add_argument(
         "--confirm",
         action="store_true",
-        help="Skip the confirmation prompt asking whether PyCharm/IntelliJ 
IDEA "
-        "has been closed. Useful for non-interactive or scripted runs.",
+        help="Automatically answer yes to all confirmation prompts (IDE close, 
"
+        "process kill, file overwrite). Useful for non-interactive or scripted 
runs.",
+    )
+    parser.add_argument(
+        "--open-ide",
+        action="store_true",
+        help="Open IntelliJ IDEA or PyCharm in the project directory after "
+        "setup completes. Uses the detected IDE installation.",
     )
     parser.add_argument(
         "--no-kill",
@@ -1088,7 +1143,7 @@ def main():
     if not args.no_kill:
         pids = _find_jetbrains_pids()
         if pids:
-            _kill_jetbrains_ides()
+            _kill_jetbrains_ides(confirm=args.confirm)
         else:
             print("[green]No running IntelliJ IDEA / PyCharm processes 
detected — safe to proceed.[/]\n")
     elif not args.confirm:
@@ -1141,10 +1196,11 @@ def main():
             print(f"  [dim]·[/] {len(previous_iml_files)} sub-module .iml 
file(s)")
         print()
 
-    should_continue = Confirm.ask("Overwrite the files?")
-    if not should_continue:
-        print("[yellow]Skipped\n")
-        return
+    if not args.confirm:
+        should_continue = Confirm.ask("Overwrite the files?")
+        if not should_continue:
+            print("[yellow]Skipped\n")
+            return
 
     print()
     cleanup_previous_setup()
@@ -1159,7 +1215,10 @@ def main():
     register_sdk(breeze_sdk_name, BREEZE_PATH, BREEZE_PATH, 
idea_path=idea_path)
 
     print("\n[green]Success[/]\n")
-    print("[yellow]Important:[/] Restart PyCharm/IntelliJ IDEA to pick up the 
new configuration.\n")
+    if args.open_ide:
+        _open_ide(project_dir=ROOT_AIRFLOW_FOLDER_PATH, idea_path=idea_path)
+    else:
+        print("[yellow]Important:[/] Restart PyCharm/IntelliJ IDEA to pick up 
the new configuration.\n")
 
 
 if __name__ == "__main__":

Reply via email to