Skip to content
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
dac06e3
feat(build): Add `build` module and `BuildMetadata` class + tests
Ishirui Sep 11, 2025
6e8f1c9
feat(build): Use UUIDs for `BuildMetadata` IDs
Ishirui Sep 16, 2025
cea5443
feat(build): Add `BuildMetadata.to_file` method
Ishirui Sep 16, 2025
a64d548
feat(build): Add `file_hash` to `BuildMetadata` schema
Ishirui Sep 16, 2025
709f50b
feat(git): Add `__len__` and `__bool__` to `ChangeSet`
Ishirui Oct 13, 2025
a6a463b
feat(build): Add `BuildMetadata.get_canonical_filename` method
Ishirui Sep 16, 2025
d2d90e8
feat(build): Add `ANY` values to `OS` and `Arch` enums
Ishirui Sep 16, 2025
6ee94f4
feat(build): Default `to_file` to writing to a canonical filename
Ishirui Oct 13, 2025
5f5c55b
refactor(build): Allow passing build components as arguments to `Buil…
Ishirui Oct 13, 2025
6cf4ade
feat(build): Include worktree hash digest in canonical filename
Ishirui Oct 13, 2025
1075e38
refactor(build): Make `Platform` a fully-fledged `Struct`
Ishirui Oct 13, 2025
c37d12e
refactor(build): Remove useless enum values + rename `ArtifactFormat.…
Ishirui Oct 20, 2025
7597230
refactor(build): Move source info and uuid to end of `get_canonical_f…
Ishirui Oct 20, 2025
67227fa
feat(fs): Create `Path.hexdigest` method + tests + use it for metadat…
Ishirui Oct 20, 2025
f844c10
refactor(metadata): Improve definition of `ArtifactFormat` and `Artif…
Ishirui Oct 20, 2025
9d5eff5
feat(build): Support format-specific digest methods
Ishirui Oct 23, 2025
3952ec8
feat(build): Support `OTHER` value in enum fields
Ishirui Oct 23, 2025
4700548
refactor(build): Move digest-related logic to own file, and rename `e…
Ishirui Oct 23, 2025
43fbb45
refactor(build): Split `BuildMetadata.this` into two
Ishirui Oct 23, 2025
88ce1ce
feat(build): Add tests for decoding `OTHER` enum values
Ishirui Oct 23, 2025
7ffd638
fix: linter issues
Ishirui Oct 23, 2025
1ac971d
fix(fs): Fix `utils.fs` issues on Windows
Ishirui Oct 27, 2025
d9b5962
refactor(git): Remove `ChangeSet.__len__` and `ChangeSet.__bool__`
Ishirui Oct 31, 2025
df5316d
refactor(build): Rename `comp` to `bin`, following internal discussions
Ishirui Oct 31, 2025
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
3 changes: 3 additions & 0 deletions src/dda/build/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# SPDX-FileCopyrightText: 2025-present Datadog, Inc. <[email protected]>
#
# SPDX-License-Identifier: MIT
3 changes: 3 additions & 0 deletions src/dda/build/metadata/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# SPDX-FileCopyrightText: 2025-present Datadog, Inc. <[email protected]>
#
# SPDX-License-Identifier: MIT
170 changes: 170 additions & 0 deletions src/dda/build/metadata/enums.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
# SPDX-FileCopyrightText: 2025-present Datadog, Inc. <[email protected]>
#
# SPDX-License-Identifier: MIT

from enum import StrEnum, auto
from typing import ClassVar

from msgspec import Struct


class ArtifactType(StrEnum):
"""
The type of the build artifact, one of:
- `comp` (component)
- `dist` (distribution)
- `other` for miscellaneous artifacts: source code tarball, configuration files, etc.
"""

COMP = auto()
DIST = auto()
OTHER = auto()


class ArtifactFormat(StrEnum):
"""
The format of the build artifact.
"""

BIN = auto()
SRC = auto() # Source code tarball
DEB = auto()
RPM = auto()
MSI = auto()
CFG = auto() # Configuration files tarball
DOCKER = auto() # Docker container image

def validate_for_type(self, artifact_type: ArtifactType) -> None:
"""
Validate that the artifact format is valid for the given artifact type.
"""
match artifact_type:
case ArtifactType.COMP:
if self not in {self.BIN, self.SRC}:
msg = f"Invalid artifact format for component artifact: {self}"
raise ValueError(msg)
case ArtifactType.DIST:
if self not in {self.DEB, self.RPM, self.MSI, self.DOCKER}:
msg = f"Invalid artifact format for distribution artifact: {self}"
raise ValueError(msg)
case ArtifactType.OTHER:
if self not in {self.SRC, self.CFG}:
msg = f"Invalid artifact format for other artifact: {self}"
raise ValueError(msg)

def get_file_identifier(self) -> str:
"""
Get the file identifier for the artifact format.
This is the string that will be used to identify the artifact format in the filename.
This usually corresponds to the file extension, but can include some other charactres before it.
"""
match self:
case self.BIN:
return ""
case self.SRC:
return "-source.tar.gz"
case self.DEB:
return ".deb"
case self.RPM:
return ".rpm"
case self.MSI:
return ".msi"
case self.CFG:
return "-config.tar.gz"
case self.DOCKER:
return "-dockerimage.tar.gz"
# Adding a default return value to satisfy mypy, even though we should never reach here
return ""


class OS(StrEnum):
"""
The operating system for which the build artifact is intended.
"""

LINUX = auto()
WINDOWS = auto()
MACOS = auto()

# Any OS - used for indicating compatibility with any operating system
ANY = auto()

@classmethod
def from_alias(cls, alias: str) -> "OS":
"""
Get the OS enum value from an alias.
"""
match alias.lower():
case "linux":
return cls.LINUX
case "windows" | "nt" | "win":
return cls.WINDOWS
case "macos" | "darwin" | "osx":
return cls.MACOS
case "any":
return cls.ANY
case _:
msg = f"Invalid OS identifier: {alias}"
raise ValueError(msg)


class Arch(StrEnum):
"""
The CPU architecture for which the build artifact is intended.
"""

# x86 architectures - canonical name is amd64
AMD64 = auto()

# ARM architectures - canonical name is arm64
ARM64 = auto()

# ARMHF architectures
ARMHF = auto()

# Any architecture - used for indicating compatibility with any CPU architecture
ANY = auto()

@classmethod
def from_alias(cls, alias: str) -> "Arch":
"""
Get the Arch enum value from an alias.
"""
match alias.lower():
case "amd64" | "x86_64" | "x86-64" | "x86" | "x64":
return cls.AMD64
case "arm64" | "aarch64" | "arm" | "aarch":
return cls.ARM64
case "any":
return cls.ANY
case _:
msg = f"Invalid Arch identifier: {alias}"
raise ValueError(msg)


class Platform(Struct, frozen=True):
"""
The platform for which the build artifact is intended.
"""

os: OS
arch: Arch

ANY: ClassVar["Platform"]

@classmethod
def from_alias(cls, os_alias: str, arch_alias: str) -> "Platform":
"""
Get the Platform enum value from an alias.
"""
return cls(os=OS.from_alias(os_alias), arch=Arch.from_alias(arch_alias))

def __str__(self) -> str:
"""
Get the string representation of the platform.
"""
return f"{self.os}-{self.arch}"


# Initialize the ANY class variable after the class is fully defined
Platform.ANY = Platform(os=OS.ANY, arch=Arch.ANY)
Loading