Skip to content

Commit 064e178

Browse files
committed
feat: add dbx patch command for creating Evergreen patches
Adds a new `patch` command that runs `evergreen patch -p <project> -u` in the target repository, with the same path-detection support as test, sync, install, and just (accepts `.` or any path as the repo argument). Also adds Evergreen project names to config.toml for mongo-python-driver, django-mongodb-backend, libmongocrypt, mongo-orchestration, and ai-ml-pipeline-testing.
1 parent 4d5a2f7 commit 064e178

4 files changed

Lines changed: 152 additions & 0 deletions

File tree

src/dbx_python_cli/cli.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
list,
1818
log,
1919
open,
20+
patch,
2021
project,
2122
remove,
2223
status,
@@ -67,6 +68,7 @@ def get_help_text():
6768
app.add_typer(list.app, name="list")
6869
app.add_typer(log.app, name="log")
6970
app.add_typer(open.app, name="open")
71+
app.add_typer(patch.app, name="patch")
7072
app.add_typer(project.app, name="project")
7173
app.add_typer(remove.app, name="remove")
7274
app.add_typer(status.app, name="status")
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
"""Patch command for creating Evergreen patches."""
2+
3+
import subprocess
4+
from pathlib import Path
5+
6+
import typer
7+
8+
from dbx_python_cli.utils.repo import (
9+
find_repo_by_name,
10+
find_repo_by_path,
11+
get_base_dir,
12+
get_config,
13+
get_evergreen_project_name,
14+
)
15+
16+
app = typer.Typer(
17+
help="Create Evergreen patches",
18+
no_args_is_help=True,
19+
invoke_without_command=True,
20+
context_settings={
21+
"allow_interspersed_args": True,
22+
"help_option_names": ["-h", "--help"],
23+
},
24+
)
25+
26+
27+
@app.callback()
28+
def patch_callback(
29+
ctx: typer.Context,
30+
repo_name: str = typer.Argument(
31+
None,
32+
help="Repository name to patch (e.g., mongo-python-driver)",
33+
),
34+
patch_args: list[str] = typer.Argument(
35+
None,
36+
help="Additional arguments to pass to evergreen patch",
37+
),
38+
):
39+
"""Create an Evergreen patch for a repository.
40+
41+
Usage::
42+
43+
dbx patch <repo_name>
44+
dbx patch . # Use current directory
45+
46+
Examples::
47+
48+
dbx patch mongo-python-driver # Patch mongo-python-driver
49+
dbx patch . # Patch repo in current directory
50+
"""
51+
verbose = ctx.obj.get("verbose", False) if ctx.obj else False
52+
53+
if not repo_name:
54+
typer.echo("❌ Error: Repository name is required", err=True)
55+
typer.echo("\nUsage: dbx patch <repo-name>")
56+
raise typer.Exit(1)
57+
58+
if patch_args is None:
59+
patch_args = []
60+
61+
try:
62+
config = get_config()
63+
base_dir = get_base_dir(config)
64+
65+
if verbose:
66+
typer.echo(f"[verbose] Using base directory: {base_dir}")
67+
68+
# Detect path-like inputs: ".", "..", absolute paths, relative paths with /
69+
_is_path_like = (
70+
repo_name in (".", "..")
71+
or repo_name.startswith(("./", "../", "/", "~/"))
72+
or "/" in repo_name
73+
or Path(repo_name).is_dir()
74+
)
75+
76+
if _is_path_like:
77+
repo = find_repo_by_path(repo_name, base_dir, config)
78+
if not repo:
79+
typer.echo(
80+
f"❌ Error: No managed repository found at '{Path(repo_name).resolve()}'",
81+
err=True,
82+
)
83+
typer.echo("\nUse 'dbx list' to see available repositories")
84+
raise typer.Exit(1)
85+
repo_name = repo["name"]
86+
else:
87+
repo = find_repo_by_name(repo_name, base_dir, config)
88+
if not repo:
89+
typer.echo(f"❌ Error: Repository '{repo_name}' not found", err=True)
90+
typer.echo("\nUse 'dbx list' to see available repositories")
91+
raise typer.Exit(1)
92+
93+
project_name = get_evergreen_project_name(config, repo_name)
94+
if not project_name:
95+
typer.echo(
96+
f"❌ Error: No evergreen project_name configured for '{repo_name}'",
97+
err=True,
98+
)
99+
typer.echo(
100+
f"\nAdd to config.toml:\n [evergreen.{repo_name}]\n project_name = \"<evg-project>\"",
101+
err=True,
102+
)
103+
raise typer.Exit(1)
104+
105+
cmd = ["evergreen", "patch", "-p", project_name, "-u"] + list(patch_args)
106+
107+
if verbose:
108+
typer.echo(f"[verbose] Running command: {' '.join(cmd)}")
109+
typer.echo(f"[verbose] Working directory: {repo['path']}\n")
110+
111+
typer.echo(f"🌲 Running evergreen patch for {repo_name} (project: {project_name})...")
112+
113+
result = subprocess.run(cmd, cwd=str(repo["path"]), check=False)
114+
115+
if result.returncode != 0:
116+
raise typer.Exit(result.returncode)
117+
118+
except typer.Exit:
119+
raise
120+
except Exception as e:
121+
typer.echo(f"❌ Error: {e}", err=True)
122+
raise typer.Exit(1)

src/dbx_python_cli/config.toml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,21 @@ django = "mongodb-6.0.x"
181181
# Environment variables for test runs
182182
[repo.groups.django.test_env]
183183

184+
[evergreen.mongo-python-driver]
185+
project_name = "mongo-python-driver"
186+
187+
[evergreen.django-mongodb-backend]
188+
project_name = "django-mongodb-backend"
189+
190+
[evergreen.libmongocrypt]
191+
project_name = "libmongocrypt"
192+
193+
[evergreen.mongo-orchestration]
194+
project_name = "mongo-orchestration"
195+
196+
[evergreen.ai-ml-pipeline-testing]
197+
project_name = "ai-ml-pipeline-testing"
198+
184199
[repo.groups.django-3p]
185200
# Python version for the group's virtual environment
186201
python_version = "3.13"

src/dbx_python_cli/utils/repo.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,19 @@ def get_install_extras(config, group_name, repo_name):
174174
return install_extras_config.get(repo_name, [])
175175

176176

177+
def get_evergreen_project_name(config, repo_name):
178+
"""Get the Evergreen project name for a repository.
179+
180+
Args:
181+
config: Configuration dictionary
182+
repo_name: Name of the repository
183+
184+
Returns:
185+
str: Evergreen project name, or None if not configured
186+
"""
187+
return config.get("evergreen", {}).get(repo_name, {}).get("project_name")
188+
189+
177190
def get_install_groups(config, group_name, repo_name):
178191
"""
179192
Get default dependency groups to install for a repository.

0 commit comments

Comments
 (0)