Skip to content

Commit 0c4d2a1

Browse files
Merge remote-tracking branch 'origin/master' into lucia/package_size_analyzer_improvements
2 parents a8f0d8b + 68aa1fc commit 0c4d2a1

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

62 files changed

+8158
-233
lines changed

.coveragerc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,3 +55,6 @@ exclude_lines =
5555

5656
# Ignore non-runnable code
5757
if __name__ == .__main__.:
58+
59+
# Ignore TYPE_CHECKING blocks
60+
if TYPE_CHECKING:

.cursor/rules/documentation.mdc

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
---
2+
description:
3+
globs: *md
4+
alwaysApply: false
5+
---
6+
## New files added
7+
When a new file is added make sure to make it available through the navigation configuration in the mkdocs file. If it is not clear where it should go, ask.
8+
9+
## Style
10+
Maintain style consistent. The style should be technical and professional.
11+
12+
Do not start lines/paragraphs with an inline code.

.cursor/rules/python-type-hinting.mdc

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
---
2+
description:
3+
globs: **/*.py
4+
alwaysApply: false
5+
---
6+
## Generating new code
7+
When generating python code, always add type hinting to the methods. Use modern syntaxis, for example, instead of using `Optional[str]` use `str | None` and instead of using `List[str]` use `list[str]`.
8+
9+
If a method yields a value but we are not returning anything or we do not accept anything sent to the generator, it is better to type the method as Iterator to explicitely expose the API of the method as simply something the caller can iterate over.
10+
11+
## Refactoring code
12+
When refactoring existing code, never add type hints to method that are not type hinted unless asked explicitely.
13+
14+
## The case of AnyStr
15+
AnyStr is normally used to define the type of a variable that can be either a string or bytes. This is soon to be deprecated and, instead, type parameter lits are a better solution. If AnyStr is used as type of several arguments in a given method signature, it is better to use type parameter lists and define the function as a generic function.
16+
17+
```python
18+
# Soon to be deprecated
19+
def func(a: AnySTr, b: AnyStr):
20+
pass
21+
22+
# Preferred
23+
def func[T: (str, bytes)](a: T, b: T):
24+
pass
25+
```
26+
27+
This way, whether a and b are either strings or bytes, they cannot be mixed.
28+
29+
If a single argument is present in the function, `str | bytes` is preferred.

.github/CODEOWNERS

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,11 @@ datadog_checks_base/datadog_checks/base/checks/windows/ @DataDog/wi
302302
/metabase/manifest.json @DataDog/saas-integrations @DataDog/documentation
303303
/metabase/assets/logs/ @DataDog/saas-integrations @DataDog/documentation @DataDog/logs-backend
304304

305+
/microsoft_dns/ @DataDog/agent-integrations
306+
/microsoft_dns/*.md @DataDog/agent-integrations @DataDog/documentation
307+
/microsoft_dns/manifest.json @DataDog/agent-integrations @DataDog/documentation
308+
/microsoft_dns/assets/logs/ @DataDog/agent-integrations @DataDog/documentation @DataDog/logs-backend @DataDog/logs-core
309+
305310
/microsoft_sysmon/ @DataDog/agent-integrations
306311
/microsoft_sysmon/*.md @DataDog/agent-integrations @DataDog/documentation
307312
/microsoft_sysmon/manifest.json @DataDog/agent-integrations @DataDog/documentation

.github/workflows/config/labeler.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -403,6 +403,8 @@ integration/mesos_slave:
403403
- mesos_slave/**/*
404404
integration/metabase:
405405
- metabase/**/*
406+
integration/microsoft_dns:
407+
- microsoft_dns/**/*
406408
integration/microsoft_sysmon:
407409
- microsoft_sysmon/**/*
408410
integration/milvus:

.gitignore

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,4 +124,7 @@ benchmark_*.svg
124124
.kube
125125

126126
## Direnv
127-
.envrc
127+
.envrc
128+
129+
## Local overrides for ddev
130+
.ddev.toml

datadog_checks_dev/datadog_checks/dev/tooling/constants.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727
'-x': 'here',
2828
}
2929

30+
# If we add or remove a new repo choice, make sure to keep the
31+
# enum in ddev/src/ddev/utils/metadata.py in sync.
3032
REPO_CHOICES = {
3133
'core': 'integrations-core',
3234
'extras': 'integrations-extras',

ddev/changelog.d/19877.added

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Allow ddev to override configuration values from a local .ddev.toml file found either in the local directory or any parent directory. This allows modifying ddev behavior when running it in different directories.

ddev/src/ddev/cli/__init__.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -95,9 +95,9 @@ def ddev(
9595

9696
if config_file:
9797
app.config_file.path = Path(config_file).resolve()
98-
if not app.config_file.path.is_file():
98+
if not app.config_file.global_path.is_file():
9999
app.abort(f'The selected config file `{str(app.config_file.path)}` does not exist.')
100-
elif not app.config_file.path.is_file():
100+
elif not app.config_file.global_path.is_file():
101101
if app.verbose:
102102
app.display_waiting('No config file found, creating one with default settings now...')
103103

@@ -107,8 +107,14 @@ def ddev(
107107
app.display_success('Success! Please see `ddev config`.')
108108
except OSError: # no cov
109109
app.abort(
110-
f'Unable to create config file located at `{str(app.config_file.path)}`. Please check your permissions.'
110+
f'Unable to create config file located at `{str(app.config_file.global_path)}`.'
111+
'Please check your permissions.'
111112
)
113+
114+
if app.verbose:
115+
if app.config_file.overrides_available():
116+
app.display_info('Local override config file found. Values from this file will override global values.')
117+
112118
if org is not None:
113119
app.config.org = org
114120

ddev/src/ddev/cli/application.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
from ddev.cli.terminal import Terminal
1111
from ddev.config.constants import AppEnvVars, ConfigEnvVars, VerbosityLevels
12-
from ddev.config.file import ConfigFile, RootConfig
12+
from ddev.config.file import ConfigFileWithOverrides, RootConfig
1313
from ddev.repo.core import Repository
1414
from ddev.utils.fs import Path
1515
from ddev.utils.github import GitHubManager
@@ -22,7 +22,7 @@ def __init__(self, exit_func, *args, **kwargs):
2222
self.platform = Platform(self.output)
2323
self.__exit_func = exit_func
2424

25-
self.config_file = ConfigFile()
25+
self.config_file = ConfigFileWithOverrides()
2626
self.quiet = self.verbosity < VerbosityLevels.INFO
2727
self.verbose = self.verbosity > VerbosityLevels.INFO
2828

@@ -35,7 +35,7 @@ def __init__(self, exit_func, *args, **kwargs):
3535

3636
@property
3737
def config(self) -> RootConfig:
38-
return self.config_file.model
38+
return self.config_file.combined_model
3939

4040
@property
4141
def repo(self) -> Repository:

ddev/src/ddev/cli/config/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from ddev.cli.config.edit import edit
88
from ddev.cli.config.explore import explore
99
from ddev.cli.config.find import find
10+
from ddev.cli.config.override import override
1011
from ddev.cli.config.restore import restore
1112
from ddev.cli.config.set import set_value
1213
from ddev.cli.config.show import show
@@ -23,3 +24,4 @@ def config():
2324
config.add_command(restore)
2425
config.add_command(set_value)
2526
config.add_command(show)
27+
config.add_command(override)

ddev/src/ddev/cli/config/edit.py

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,25 @@
11
# (C) Datadog, Inc. 2024-present
22
# All rights reserved
33
# Licensed under a 3-clause BSD style license (see LICENSE)
4+
from __future__ import annotations
5+
6+
from typing import TYPE_CHECKING
7+
48
import click
59

10+
from ddev.config.file import DDEV_TOML
11+
12+
if TYPE_CHECKING:
13+
from ddev.cli.application import Application
14+
615

716
@click.command(short_help='Edit the config file with your default editor')
17+
@click.option('--overrides', is_flag=True, help=f'Edit the local config file ({DDEV_TOML})')
818
@click.pass_obj
9-
def edit(app):
19+
def edit(app: Application, overrides: bool):
1020
"""Edit the config file with your default editor."""
11-
click.edit(filename=str(app.config_file.path))
21+
if overrides and not app.config_file.overrides_available():
22+
app.abort('No local config file found.')
23+
24+
file_to_edit = app.config_file.overrides_path if overrides else app.config_file.path
25+
click.edit(filename=str(file_to_edit))

ddev/src/ddev/cli/config/find.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@
44
import click
55

66

7-
@click.command(short_help='Show the location of the config file')
7+
@click.command(short_help="Show the location of the config file")
88
@click.pass_obj
99
def find(app):
1010
"""Show the location of the config file."""
1111
app.display(str(app.config_file.path))
12+
if app.config_file.overrides_available():
13+
app.output(f"----- Overrides applied from {app.config_file.pretty_overrides_path}", style="yellow")

ddev/src/ddev/cli/config/override.py

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
# (C) Datadog, Inc. 2025-present
2+
# All rights reserved
3+
# Licensed under a 3-clause BSD style license (see LICENSE)
4+
from __future__ import annotations
5+
6+
from typing import TYPE_CHECKING
7+
8+
import click
9+
10+
if TYPE_CHECKING:
11+
from ddev.cli.application import Application
12+
13+
14+
def repo_to_override(app: Application) -> str:
15+
from ddev.utils.metadata import (
16+
InvalidMetadataError,
17+
PyProjectNotFoundError,
18+
RepoNotFoundError,
19+
ValidRepo,
20+
pyproject_metadata,
21+
)
22+
23+
try:
24+
metadata = pyproject_metadata()
25+
if metadata is None:
26+
raise RepoNotFoundError()
27+
repo = metadata.repo.value
28+
except (PyProjectNotFoundError, RepoNotFoundError):
29+
app.display_error(
30+
"The current repo could not be inferred. Either this is not a repository or the root of "
31+
"the repo is missing the ddev tool configuration in its pyproject.toml file."
32+
)
33+
34+
repo = app.prompt(
35+
"What repo are you trying to override? ",
36+
type=click.Choice([item.value for item in ValidRepo]),
37+
show_choices=True,
38+
)
39+
except InvalidMetadataError as e:
40+
from rich.markup import escape
41+
42+
# Ensure escaping to avoid rich reading the table name as style markup
43+
app.display_error(escape(str(e)))
44+
app.abort()
45+
except Exception as e:
46+
app.display_error(f"An unexpected error occurred: {e}")
47+
app.abort()
48+
49+
return repo
50+
51+
52+
@click.command()
53+
@click.pass_obj
54+
def override(app: Application):
55+
"""
56+
Overrides the repo configuration with a `.ddev.toml` file in the current working directory.
57+
58+
The command tries to identify the repo you are in by reading the `repo` field in the `[tool.ddev]` table in
59+
the `pyproject.toml` file located at the root of your git repository.
60+
61+
If the current directory is not part of a git repository, the repository root does not have a `pyproject.toml`
62+
file, or the file exists but has no `[tool.ddev]` table, you will be prompted to specify which repo
63+
configuration to override.
64+
"""
65+
from rich.syntax import Syntax
66+
67+
from ddev.config.file import DDEV_TOML, RootConfig, deep_merge_with_list_handling
68+
from ddev.config.utils import scrub_config
69+
from ddev.utils.fs import Path
70+
from ddev.utils.toml import dumps_toml_data
71+
72+
app.config_file.overrides_path = Path.cwd() / DDEV_TOML
73+
repo = repo_to_override(app)
74+
75+
local_repo_config = {
76+
"repo": repo,
77+
"repos": {repo: str(app.config_file.overrides_path.resolve().parent)},
78+
}
79+
80+
if app.config_file.overrides_path.exists():
81+
app.display_info("Local config file already exists. Updating...")
82+
local_config = app.config_file.overrides_model.raw_data
83+
config = deep_merge_with_list_handling(local_config, local_repo_config)
84+
else:
85+
config = local_repo_config
86+
87+
app.config_file.overrides_model = RootConfig(config)
88+
app.config_file.update()
89+
90+
app.display_success(f"Local repo configuration added in {app.config_file.pretty_overrides_path}\n")
91+
app.display("Local config content:")
92+
scrub_config(app.config_file.overrides_model.raw_data)
93+
app.output(
94+
Syntax(dumps_toml_data(app.config_file.overrides_model.raw_data).rstrip(), "toml", background_color="default")
95+
)

ddev/src/ddev/cli/config/restore.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,26 @@
11
# (C) Datadog, Inc. 2024-present
22
# All rights reserved
33
# Licensed under a 3-clause BSD style license (see LICENSE)
4+
from __future__ import annotations
5+
6+
from typing import TYPE_CHECKING
7+
48
import click
59

10+
if TYPE_CHECKING:
11+
from ddev.cli.application import Application
12+
613

714
@click.command(short_help='Restore the config file to default settings')
815
@click.pass_obj
9-
def restore(app):
16+
def restore(app: Application):
1017
"""Restore the config file to default settings."""
1118
app.config_file.restore()
1219
app.display_success('Settings were successfully restored.')
20+
if app.config_file.overrides_available():
21+
delete_overrides = click.confirm(
22+
f"Overrides file found in '{app.config_file.overrides_path}'. Do you want to delete it?"
23+
)
24+
if delete_overrides:
25+
app.config_file.overrides_path.unlink()
26+
app.display_success('Overrides deleted.')

0 commit comments

Comments
 (0)