#!/usr/bin/env python3
"""
Builds a build metadata file for an artifact.
"""
import argparse
import subprocess
import json
import sys
from enum import Enum, EnumMeta
from typing import Any, Dict, List, Union
# A slightly more Rust-y feeling enum
# Thanks to: https://stackoverflow.com/a/65225753
class MetaEnum(EnumMeta):
def __contains__(self: type[Any], member: object) -> bool:
try:
self(member)
except ValueError:
return False
return True
class BaseEnum(Enum, metaclass=MetaEnum):
pass
class PlatformArch(BaseEnum):
Aarch64 = "aarch64"
X86_64 = "x86_64"
class PlatformOS(BaseEnum):
Darwin = "darwin"
Linux = "linux"
Windows = "windows"
class Variant(BaseEnum):
Binary = "binary"
Container = "container"
Omnibus = "omnibus"
Rootfs = "rootfs"
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument(
"--artifact-file",
required=True,
help="Path to the artifact file to compute the b3sum",
)
parser.add_argument(
"--git-info-json",
required=True,
help="Path to the Git metadata JSON file",
)
parser.add_argument(
"--build-metadata-out-file",
required=True,
help="Path to write the build metadata JSON file",
)
parser.add_argument(
"--name",
required=True,
help="Name of artifact that was built",
)
parser.add_argument(
"--variant",
required=True,
choices=[variant.value for variant in Variant],
help="Artifact variant kind",
)
parser.add_argument(
"--arch",
required=True,
choices=[arch.value for arch in PlatformArch],
help="Target architecture",
)
parser.add_argument(
"--os",
required=True,
choices=[os.value for os in PlatformOS],
help="Target operating system",
)
parser.add_argument(
"--author",
required=True,
help="Author to be used in metadata",
)
parser.add_argument(
"--source-url",
required=True,
help="Source code URL to be used in metadata",
)
parser.add_argument(
"--license",
required=True,
help="Image license to be used in metadata",
)
parser.add_argument(
"--organization",
help="Organization for artifact",
)
return parser.parse_args()
def main() -> int:
args = parse_args()
git_info = load_git_info(args.git_info_json)
# argparse validates these are valid enum values
arch = PlatformArch(args.arch)
os = PlatformOS(args.os)
b3sum = compute_b3sum(args.artifact_file)
build_metadata = compute_build_metadata(
git_info,
args.name,
Variant(args.variant),
arch,
os,
b3sum,
args.organization,
)
write_json(args.build_metadata_out_file, build_metadata)
return 0
def write_json(output: str, metadata: Union[Dict[str, str], List[str]]):
with open(output, "w") as file:
json.dump(metadata, file, sort_keys=True)
def load_git_info(git_info_file: str) -> Dict[str, str | int | bool]:
with open(git_info_file) as file:
return json.load(file)
def compute_b3sum(artifact_file: str) -> str:
cmd = [
"b3sum",
"--no-names",
artifact_file,
]
result = subprocess.run(cmd, capture_output=True)
# Print out stderr from process if it failed
if result.returncode != 0:
sys.stderr.write(result.stderr.decode("ascii"))
result.check_returncode()
b3sum = result.stdout.decode("ascii").rstrip()
return b3sum
def compute_build_metadata(
git_info: Dict[str, str | int | bool],
family: str,
variant: Variant,
platform_arch: PlatformArch,
platform_os: PlatformOS,
b3sum: str,
organization: str | None,
) -> Dict[str, str]:
metadata = {
"family": family,
"variant": variant.value,
"version": git_info.get("canonical_version"),
"arch": platform_arch.value,
"os": platform_os.value,
"commit": git_info.get("commit_hash"),
"branch": git_info.get("branch"),
"b3sum": b3sum,
}
if organization:
metadata["organization"] = organization
return metadata
if __name__ == "__main__":
sys.exit(main())