Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions src/dda/cli/tools/ddgl/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# SPDX-FileCopyrightText: 2026-present Datadog, Inc. <dev@datadoghq.com>
#
# SPDX-License-Identifier: MIT
from __future__ import annotations

from dda.cli.base import dynamic_group


@dynamic_group(
short_help="Manage ddgl",
)
def cmd() -> None:
pass
61 changes: 61 additions & 0 deletions src/dda/cli/tools/ddgl/setup/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import click

from dda.cli.application import Application
from dda.cli.base import dynamic_command, pass_app
from dda.utils.platform import which


def _check_ddgl() -> bool:
"""Check if ddgl is installed and available on $PATH"""
out = which("ddgl") or ""
return len(out.strip()) > 0


@dynamic_command(short_help="Setup ddgl, creating a config file and adding it to PATH")
@click.option("--dev", is_flag=True, help="Use the latest dev version of ddgl from `main`.")
@click.option("--ref", help="Use ddgl from a specific git ref", default=None)
@click.option("--no-config", is_flag=True, help="Don't overwrite or drop a config file")
@click.option("--force", is_flag=True, help="Force install and overwrite config file")
@pass_app
def cmd(app: Application, *, ref: str | None, dev: bool, no_config: bool, force: bool) -> None:
if not force and _check_ddgl():
app.abort(
"ddgl is already installed. Use --force to reinstall and reset config or `dda tool ddgl update` to update.",
code=0,
)

# Early-exit if ddtool is not on PATH, since the config we drop relies on it for creating gitlab tokens
if not which("ddtool"):
app.abort(
"`ddtool` not found on PATH. This command is only meant for Datadog employees with access to `ddtool`. Please install it using `dogbrew install ddtool`",
code=1,
)

if dev:
ref = "main"

# Use a specific git ref if provided or use latest release on PyPI
package = f"git+https://github.com/DataDog/ddgl-cli@{ref}" if ref else "ddgl"

app.tools.uv.install_tool(package, force=force)

# Make sure ddgl is in path
if not _check_ddgl():
app.abort("ddgl not found in $PATH after install. Maybe run `uv tool update-shell` ?")
Comment thread
Ishirui marked this conversation as resolved.

if not no_config:
from dda.utils.fs import Path

ddgl_config_path = Path(app.subprocess.capture(["ddgl", "config", "path"]).strip())
if not force and ddgl_config_path.exists():
app.display_info(f"Config file found at {ddgl_config_path}, not overwriting. Use --force to overwrite.")
return

# Overwrite the existing file
ddgl_config_path.write_text(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it make sense to have something that by default put ddtool related things in the config? That's an internal thing and would make it probably not working for any external contributor using it

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, it's not ideal, but what would you suggest otherwise ? In order to make this easily usable by our internal contributors we need to put this "default config" somewhere, and it's always going to be opensource whether it's here or in datadog-agent :/

@Ishirui Ishirui Oct 6, 2026 •

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

External contributors wouldn't have access to our gitlabCI anyway, so there is no way to make this work for them at all (in terms of developing on the Agent). If they want to use ddgl with other projects they probably wouldn't use dda to install it anyway, and use the instructions on ddgl's repo itself (uv tool install ddgl)

Does the simple mention of ddtool existing pose a security risk ? Because in that case it's already in the docs 😅

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I do not think mentioning ddtool is an issue, and as you said it is already mentioned in a loooot of places.
I guess we can live with it. But what would be the impact of someone that cannot have ddtool installed running that command?
Not a blocker I think, this is mostly (only) used by us anyway

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If they were to use dda tools ddgl setup, it would then fail on the first invocation of ddgl saying something about an invalid token or command not found.

That gives me an idea: I can just add a check for if ddtool is on PATH and immediately exit if not. I'll add that 👍

"""
gitlab_url="https://gitlab.ddbuild.io"
github_fallback = true
token_command = ["ddtool", "auth", "gitlab", "token"]
"""
)
23 changes: 23 additions & 0 deletions src/dda/cli/tools/ddgl/update/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import click

from dda.cli.application import Application
from dda.cli.base import dynamic_command, pass_app


@dynamic_command(short_help="Upgrade ddgl, optionally to a specific version.")
@click.argument("version", required=False, help="The specific version or git ref to install")
@click.option(
"--git",
"-g",
is_flag=True,
help="Use this flag if you are trying to upgrade to a specific git ref or a version not yet release on PyPI.",
)
@pass_app
def cmd(app: Application, *, git: bool, version: str | None = None) -> None:
package = "ddgl" if not git else "https://github.com/DataDog/ddgl-cli"
if version:
if git:
package += f"@{version}"
Comment thread
Ishirui marked this conversation as resolved.
else:
package += f"=={version}"
app.tools.uv.upgrade_tool(package)
24 changes: 24 additions & 0 deletions src/dda/tools/uv.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,27 @@ def virtual_env(self, path: Path) -> VirtualEnv:
)

return VirtualEnv(path)

def install_tool(self, package: str, force: bool = False) -> None: # noqa: FBT001,FBT002
"""
Run `uv tool install <package>`, installing a tool usable outside of dda.
"""
args = ["tool", "install", package]
if force:
args.append("--force")
self.wait(args, message=f"Installing {package}")

def upgrade_tool(self, package: str) -> None:
"""
Run `uv tool upgrade <package>`. Use package_name==<version> or repo@ref to install a specific version.
"""
# Extract the version
if "==" in package:
name, version = package.split("==")
elif "@" in package:
name, version = package.split("@")
else:
name, version = package, "latest"
args = ["tool", "upgrade", "--prerelease=allow", package]
self.app.display_debug(f"Running {args}")
self.wait(args, message=f"Upgrading {name} to {version}")
Loading