This is an automated email from the ASF dual-hosted git repository.
o-nikolas 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 562d62e9355 add 'teams sync' cli command (#66418)
562d62e9355 is described below
commit 562d62e9355a1e8a6ba1d7ce334f373a862d2c64
Author: stephen-bracken <[email protected]>
AuthorDate: Tue May 12 23:47:48 2026 +0100
add 'teams sync' cli command (#66418)
Co-authored-by: Stephen Bracken <email-protected>
---
airflow-core/src/airflow/cli/cli_config.py | 7 ++++
.../src/airflow/cli/commands/team_command.py | 28 +++++++++++++++
.../tests/unit/cli/commands/test_team_command.py | 41 ++++++++++++++++++++++
3 files changed, 76 insertions(+)
diff --git a/airflow-core/src/airflow/cli/cli_config.py
b/airflow-core/src/airflow/cli/cli_config.py
index 990325de74b..4c44ab39d67 100644
--- a/airflow-core/src/airflow/cli/cli_config.py
+++ b/airflow-core/src/airflow/cli/cli_config.py
@@ -1523,6 +1523,13 @@ TEAMS_COMMANDS = (
func=lazy_load_command("airflow.cli.commands.team_command.team_list"),
args=(ARG_OUTPUT, ARG_VERBOSE),
),
+ ActionCommand(
+ name="sync",
+ help="Sync teams",
+ description=("Sync missing teams from the dag bundle config into the
database.\n"),
+ func=lazy_load_command("airflow.cli.commands.team_command.team_sync"),
+ args=(ARG_VERBOSE,),
+ ),
)
DB_COMMANDS = (
ActionCommand(
diff --git a/airflow-core/src/airflow/cli/commands/team_command.py
b/airflow-core/src/airflow/cli/commands/team_command.py
index 13702d516c4..7928b3d716b 100644
--- a/airflow-core/src/airflow/cli/commands/team_command.py
+++ b/airflow-core/src/airflow/cli/commands/team_command.py
@@ -25,6 +25,7 @@ from sqlalchemy import func, select
from sqlalchemy.exc import IntegrityError
from airflow.cli.simple_table import AirflowConsole
+from airflow.dag_processing.bundles.manager import DagBundlesManager
from airflow.models.connection import Connection
from airflow.models.pool import Pool
from airflow.models.team import Team, dag_bundle_team_association_table
@@ -155,3 +156,30 @@ def team_list(args, session=NEW_SESSION):
print(NO_TEAMS_LIST_MSG)
else:
_show_teams(teams=teams, output=args.output)
+
+
+@cli_utils.action_cli
+@providers_configuration_loaded
+@provide_session
+def team_sync(args, session=NEW_SESSION):
+ """Sync missing teams from the dag bundle config."""
+ dag_bundle_teams = {
+ bundle.team_name
+ for bundle in DagBundlesManager()._bundle_config.values()
+ if bundle.team_name is not None
+ }
+
+ teams_added = 0
+
+ try:
+ for team_name in dag_bundle_teams - Team.get_all_team_names(session):
+ team = Team(name=team_name)
+ session.add(team)
+ teams_added += 1
+ session.commit()
+ except Exception as e:
+ session.rollback()
+ raise SystemExit(f"Failed to sync teams: {e}")
+
+ if teams_added > 0:
+ print(f"{teams_added} teams added.")
diff --git a/airflow-core/tests/unit/cli/commands/test_team_command.py
b/airflow-core/tests/unit/cli/commands/test_team_command.py
index 55892489f8b..49ade1c19bc 100644
--- a/airflow-core/tests/unit/cli/commands/test_team_command.py
+++ b/airflow-core/tests/unit/cli/commands/test_team_command.py
@@ -17,6 +17,7 @@
# under the License.
from __future__ import annotations
+import json
from unittest.mock import patch
import pytest
@@ -30,6 +31,7 @@ from airflow.models.dagbundle import DagBundleModel
from airflow.models.team import Team, dag_bundle_team_association_table
from airflow.settings import Session
+from tests_common.test_utils.config import conf_vars
from tests_common.test_utils.db import (
clear_db_connections,
clear_db_dag_bundles,
@@ -362,3 +364,42 @@ class TestCliTeams:
assert "integration-1" in team_names
assert "integration-2" not in team_names
assert "integration-3" in team_names
+
+ def test_team_sync(self):
+ bundle_config = [
+ {
+ "name": "bundleone",
+ "classpath":
"airflow.dag_processing.bundles.local.LocalDagBundle",
+ "kwargs": {"path": "/dev/null", "refresh_interval": 0},
+ "team_name": "team1",
+ },
+ {
+ "name": "bundletwo",
+ "classpath":
"airflow.dag_processing.bundles.local.LocalDagBundle",
+ "kwargs": {"path": "/dev/null", "refresh_interval": 300},
+ "team_name": "team2",
+ },
+ {
+ "name": "bundlethree",
+ "classpath":
"airflow.dag_processing.bundles.local.LocalDagBundle",
+ "kwargs": {"path": "/dev/null", "refresh_interval": 300},
+ },
+ ]
+
+ teams = self.session.scalars(select(Team)).all()
+ assert len(teams) == 0
+
+ with conf_vars(
+ {
+ ("core", "multi_team"): "True",
+ ("dag_processor", "dag_bundle_config_list"):
json.dumps(bundle_config),
+ }
+ ):
+ team_command.team_sync(self.parser.parse_args(["teams", "sync"]))
+
+ teams = self.session.scalars(select(Team)).all()
+ assert len(teams) == 2
+
+ team_names = [team.name for team in teams]
+ assert "team1" in team_names
+ assert "team2" in team_names