Skip to content

Commit 3f7efa4

Browse files
committed
Misc updates
- Add `dm repo run` command - Add env vars for django testing - Update gitignore - Use AWS if env vars set - Local fallback - Add `-n` to `dm repo log` - Add shell dep map - Add test examples - Explain generated configs - Add ref to drivers evergreen tools - Add shell dep map for drivers evergreen tools - Add evergreen config for pymongo - Fix qe settings - proj -> project - Remove footguns
1 parent 4298881 commit 3f7efa4

File tree

7 files changed

+817
-25
lines changed

7 files changed

+817
-25
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@ mongocryptd.pid
77
.python-version
88
build/
99
uv.lock
10+
secrets-export.sh

SHELL_DEPENDENCY_MAP.md

Lines changed: 649 additions & 0 deletions
Large diffs are not rendered by default.

django_mongodb_cli/repo.py

Lines changed: 72 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import typer
22
import os
3+
import shlex
34

45
from .utils import Package, Repo, Test
56

@@ -106,9 +107,7 @@ def cd(
106107
ctx: typer.Context,
107108
repo_name: str = typer.Argument(None),
108109
):
109-
"""
110-
Change directory to the specified repository.
111-
"""
110+
"""Change directory to the specified repository."""
112111
repo = Repo()
113112
repo.ctx = ctx
114113

@@ -122,6 +121,52 @@ def cd(
122121
)
123122

124123

124+
@repo.command()
125+
def run(
126+
ctx: typer.Context,
127+
repo_name: str = typer.Argument(..., help="Repository name"),
128+
command: list[str] = typer.Argument(
129+
..., metavar="CMD...", help="Command (and args) to run in the repo directory"
130+
),
131+
):
132+
"""Run an arbitrary command inside the repository directory.
133+
134+
Examples:
135+
dm repo run mongo-python-driver just setup tests encryption
136+
dm repo run mongo-python-driver "just setup tests encryption"
137+
138+
Environment variables can be configured per-repo under
139+
``[tool.django-mongodb-cli.run.<repo_name>.env_vars]`` in ``pyproject.toml``.
140+
"""
141+
repo = Repo()
142+
repo.ctx = ctx
143+
144+
# Allow a single shell-style string (e.g. "just setup tests encryption").
145+
if len(command) == 1:
146+
command = shlex.split(command[0])
147+
148+
path, _ = repo.ensure_repo(repo_name)
149+
if not path:
150+
return
151+
152+
# Build environment from current process plus any configured env_vars.
153+
env = os.environ.copy()
154+
run_cfg = repo.run_cfg(repo_name)
155+
env_vars_list = run_cfg.get("env_vars")
156+
if env_vars_list:
157+
repo.info("Setting environment variables from pyproject.toml:")
158+
for item in env_vars_list:
159+
name = item.get("name")
160+
value = str(item.get("value"))
161+
if name is None:
162+
continue
163+
env[name] = value
164+
typer.echo(f" {name}={value}")
165+
166+
repo.info(f"Running in {path}: {' '.join(command)}")
167+
repo.run(command, cwd=path, env=env)
168+
169+
125170
@repo.command()
126171
def checkout(
127172
ctx: typer.Context,
@@ -344,18 +389,28 @@ def log(
344389
all_repos: bool = typer.Option(
345390
False, "--all-repos", "-a", help="Show logs of all repositories"
346391
),
392+
n: int = typer.Option(
393+
10,
394+
"-n",
395+
"--lines",
396+
min=1,
397+
help="Number of log entries to show (per repository)",
398+
),
347399
):
348400
"""
349401
Show logs for the specified repository.
402+
403+
By default, shows the last 10 entries. Use ``-n/--lines`` to change the
404+
number of entries displayed (per repository).
350405
If --all-repos is used, show logs for all repositories.
351406
"""
352407
repo_command(
353408
all_repos,
354409
repo_name,
355410
all_msg="Showing logs for all repositories...",
356411
missing_msg="Please specify a repository name or use --all-repos to show logs of all repositories.",
357-
single_func=lambda repo_name: Repo().get_repo_log(repo_name),
358-
all_func=lambda repo_name: Repo().get_repo_log(repo_name),
412+
single_func=lambda repo_name: Repo().get_repo_log(repo_name, n),
413+
all_func=lambda repo_name: Repo().get_repo_log(repo_name, n),
359414
)
360415

361416

@@ -448,6 +503,18 @@ def reset_repo(name):
448503
)
449504

450505

506+
@repo.command()
507+
def show(
508+
ctx: typer.Context,
509+
repo_name: str = typer.Argument(..., help="Repository name"),
510+
commit_hash: str = typer.Argument(..., help="Commit hash to show"),
511+
):
512+
"""Show the git diff for a specific commit hash in the given repository."""
513+
repo = Repo()
514+
repo.ctx = ctx
515+
repo.show_commit(repo_name, commit_hash)
516+
517+
451518
@repo.command()
452519
def set_default(
453520
repo_name: str = typer.Argument(None),

django_mongodb_cli/utils.py

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,16 @@ def run(
5555
args,
5656
cwd: Path | str | None = None,
5757
check: bool = True,
58-
env: str | None = None,
58+
env: dict[str, str] | None = None,
5959
) -> bool:
60+
"""Run a subprocess with optional working directory and environment.
61+
62+
Args:
63+
args: Command and arguments to run.
64+
cwd: Optional working directory.
65+
check: Whether to raise on non-zero exit code.
66+
env: Optional environment mapping to pass to the subprocess.
67+
"""
6068
try:
6169
subprocess.run(args, cwd=str(cwd) if cwd else None, check=check, env=env)
6270
return True
@@ -82,6 +90,14 @@ def tool_cfg(self) -> dict:
8290
def test_cfg(self, repo_name: str) -> dict:
8391
return self.tool_cfg.get("test", {}).get(repo_name, {}) or {}
8492

93+
def run_cfg(self, repo_name: str) -> dict:
94+
"""Return configuration for arbitrary repo commands.
95+
96+
The config is read from [tool.django-mongodb-cli.run.<repo_name>] in
97+
pyproject.toml and can contain keys like ``env_vars``.
98+
"""
99+
return self.tool_cfg.get("run", {}).get(repo_name, {}) or {}
100+
85101
def evergreen_cfg(self, repo_name: str) -> dict:
86102
return self.tool_cfg.get("evergreen", {}).get(repo_name, {}) or {}
87103

@@ -262,9 +278,13 @@ def fetch_repo(self, repo_name: str) -> None:
262278
except GitCommandError as e:
263279
self.err(f"❌ Failed to fetch updates: {e}")
264280

265-
def get_repo_log(self, repo_name: str) -> None:
266-
"""
267-
Get the commit log for the specified repository.
281+
def get_repo_log(self, repo_name: str, max_lines: int | None = None) -> None:
282+
"""Print the commit log for the specified repository.
283+
284+
Args:
285+
repo_name: Logical name of the repository.
286+
max_lines: Maximum number of log entries to display. If ``None``,
287+
a default of 10 entries is used.
268288
"""
269289
self.info(f"Getting commit log for repository: {repo_name}")
270290
_, repo = self.ensure_repo(repo_name)
@@ -277,7 +297,7 @@ def get_repo_log(self, repo_name: str) -> None:
277297
"--date=relative",
278298
"--graph",
279299
).splitlines()
280-
log_max = 10
300+
log_max = max_lines if max_lines is not None else 10
281301
for count, entry in enumerate(log_entries, start=1):
282302
typer.echo(f" - {entry}")
283303
if count >= log_max:
@@ -466,6 +486,19 @@ def get_repo_diff(self, repo_name: str) -> None:
466486
except GitCommandError as e:
467487
self.err(f"❌ Failed to diff working tree: {e}")
468488

489+
def show_commit(self, repo_name: str, commit_hash: str) -> None:
490+
"""Show the diff for a specific commit hash in the given repository."""
491+
self.info(f"Showing diff for {repo_name}@{commit_hash}")
492+
path, repo = self.ensure_repo(repo_name)
493+
if not repo or not path:
494+
return
495+
496+
try:
497+
output = repo.git.show(commit_hash)
498+
typer.echo(output)
499+
except GitCommandError as e:
500+
self.err(f"❌ Failed to show commit {commit_hash}: {e}")
501+
469502
def _list_repos(self) -> tuple[set, set]:
470503
map_repos = set(self.map.keys())
471504

justfile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,12 +76,12 @@ alias s := django-runserver
7676

7777
[group('django')]
7878
django-migrate:
79-
dm proj migrate
79+
dm project migrate
8080
alias m := django-migrate
8181

8282
[group('django')]
8383
django-createsuperuser:
84-
dm proj su
84+
dm project su
8585
alias su := django-createsuperuser
8686

8787
# ---------------------------------------- mongodb ----------------------------------------

pyproject.toml

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ value = "/Users/alex.clark/Developer/django-mongodb-cli/src/drivers-evergreen-to
122122

123123
[[tool.django-mongodb-cli.test.mongo-python-driver.env_vars]]
124124
name = "AWS_PROFILE"
125-
value = "drivers-test-secrets-role-857654397073"
125+
value = "my-profile"
126126

127127
[tool.django-mongodb-cli.test.django]
128128
test_command = "./runtests.py"
@@ -138,9 +138,25 @@ test_dir = "src/django/tests"
138138
clone_dir = "src/django"
139139
test_dirs = [ "src/django/tests", "src/django-mongodb-backend/tests" ]
140140

141-
[[tool.django-mongodb-cli.test.django.env_vars]]
142-
name = "PYMONGOCRYPT_LIB"
143-
value = "/opt/homebrew/lib/libmongocrypt.dylib"
141+
#[[tool.django-mongodb-cli.test.django.env_vars]]
142+
#name = "PYMONGOCRYPT_LIB"
143+
#value = "/opt/homebrew/lib/libmongocrypt.dylib"
144+
145+
#[[tool.django-mongodb-cli.test.django.env_vars]]
146+
#name = "MONGODB_URI"
147+
#value = "mongodb://localhost:64437/?directConnection=true"
148+
149+
[[tool.django-mongodb-cli.run.mongo-python-driver.env_vars]]
150+
name = "DRIVERS_TOOLS"
151+
value = "/Users/alex.clark/Developer/django-mongodb-cli/src/drivers-evergreen-tools"
152+
153+
#[[tool.django-mongodb-cli.run.mongo-python-driver.env_vars]]
154+
#name = "MONGODB_URI"
155+
#value = "mongodb://localhost:64437/?directConnection=true"
156+
157+
[[tool.django-mongodb-cli.run.mongo-python-driver.env_vars]]
158+
name = "AWS_PROFILE"
159+
value = "my-profile"
144160

145161
[tool.django-mongodb-cli.test.django.migrations_dir]
146162
source = "mongo_migrations"
@@ -347,6 +363,10 @@ repo = "git+ssh://git@github.com/mongodb/django-mongodb-backend"
347363
project_name = "django-mongodb"
348364
tasks = ["run-tests"]
349365

366+
[tool.django-mongodb-cli.evergreen.mongo-python-driver]
367+
project_name = "mongo-python-driver"
368+
tasks = ["run-tests"]
369+
350370
[tool.django-mongodb-cli.project.settings]
351371
# path = "settings.base"
352372
path = "settings.qe"

test/settings/qe.py

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,34 @@
66

77
MONGODB_URI = os.environ.get("MONGODB_URI", "mongodb://localhost:27017")
88

9+
# Configure KMS providers.
10+
#
11+
# We prefer AWS when FLE_AWS_KEY/FLE_AWS_SECRET are set, mirroring
12+
# src/mongo-python-driver/test/helpers_shared.py. Otherwise we fall back
13+
# to a local KMS for convenience in local development.
14+
AWS_CREDS = {
15+
"accessKeyId": os.environ.get("FLE_AWS_KEY", ""),
16+
"secretAccessKey": os.environ.get("FLE_AWS_SECRET", ""),
17+
}
18+
_USE_AWS_KMS = any(AWS_CREDS.values())
19+
20+
if _USE_AWS_KMS:
21+
# Use the same demo key ARN and region as the PyMongo QE tests.
22+
_AWS_REGION = os.environ.get("FLE_AWS_KMS_REGION", "us-east-1")
23+
_AWS_KEY_ARN = os.environ.get(
24+
"FLE_AWS_KMS_KEY_ARN",
25+
"arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0",
26+
)
27+
KMS_PROVIDERS = {"aws": AWS_CREDS}
28+
KMS_CREDENTIALS = {"aws": {"key": _AWS_KEY_ARN, "region": _AWS_REGION}}
29+
DEFAULT_KMS_PROVIDER = "aws"
30+
else:
31+
# Local-only fallback
32+
KMS_PROVIDERS = {"local": {"key": os.urandom(96)}}
33+
KMS_CREDENTIALS = {"local": {}}
34+
DEFAULT_KMS_PROVIDER = "local"
35+
36+
937
DATABASES = {
1038
"default": {
1139
"ENGINE": "django_mongodb_backend",
@@ -24,16 +52,10 @@
2452
"OPTIONS": {
2553
"auto_encryption_opts": AutoEncryptionOpts(
2654
key_vault_namespace="djangotests_encrypted.__keyVault",
27-
kms_providers={
28-
"local": {
29-
"key": os.urandom(96),
30-
},
31-
},
55+
kms_providers=KMS_PROVIDERS,
3256
),
3357
},
34-
"KMS_CREDENTIALS": {
35-
"aws": {},
36-
},
58+
"KMS_CREDENTIALS": KMS_CREDENTIALS,
3759
},
3860
}
3961

@@ -57,7 +79,7 @@ def allow_migrate(self, db, app_label, model_name=None, **hints):
5779
return None
5880

5981
def kms_provider(self, model):
60-
return "local"
82+
return DEFAULT_KMS_PROVIDER
6183

6284

6385
DATABASE_ROUTERS = ["django_mongodb_backend.routers.MongoRouter", EncryptedRouter()]

0 commit comments

Comments
 (0)