Skip to content
Merged
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
12 changes: 12 additions & 0 deletions commodore/cli/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from dotenv import load_dotenv, find_dotenv
from commodore import __git_version__, __version__
from commodore.config import Config
from commodore.version import version_info

import commodore.cli.options as options

Expand All @@ -30,6 +31,16 @@ def _version():
CONTEXT_SETTINGS = {"help_option_names": ["-h", "--help"]}


@click.command(name="version", short_help="Extended Commodore version information")
@options.pass_config
def commodore_version(config: Config):
"""Print extended Commodore version information.

This command returns exit code 127 if a required external dependency (helm,
kustomize, jb) can't be found in the PATH."""
version_info(config, _version())


@click.group(context_settings=CONTEXT_SETTINGS)
@click.version_option(_version(), prog_name="commodore")
@options.verbosity
Expand Down Expand Up @@ -66,6 +77,7 @@ def commodore(ctx, working_dir, verbose, request_timeout):
commodore.add_command(package_group)
commodore.add_command(commodore_login)
commodore.add_command(commodore_fetch_token)
commodore.add_command(commodore_version)


def main():
Expand Down
98 changes: 98 additions & 0 deletions commodore/version.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
"""Extended Commodore version information"""

import os
import subprocess # nosec
import shutil
import sys

from importlib import metadata

import click
import reclass_rs

from commodore.config import Config

from pygobuildinfo import get_go_build_info


def _native_find_so(dep) -> os.PathLike[str]:
files = metadata.files(dep)
if not files:
raise ValueError(
f"Unable to parse build info for {dep}: no package files found"
)
so_files = [p.locate() for p in files if p.name.endswith(".so")]
if len(so_files) != 1:
raise ValueError(f"Unable to parse build info for {dep}: no unique *.so found")
return so_files[0]


def _gojsonnet_buildinfo():
try:
so_file = _native_find_so("gojsonnet")
except ValueError as e:
return str(e)
gobuildinfo = get_go_build_info(str(so_file))
return f"Go compiler: {gobuildinfo['GoVersion']}"


def _reclass_rs_buildinfo():
if hasattr(reclass_rs, "buildinfo"):
buildinfo = reclass_rs.buildinfo()
return f"Rust compiler: {buildinfo['rustc_version']}"
# For reclass_rs 0.8.0 and older, we just return an informational message
return "Parsing build info not supported for reclass-rs <= 0.8.0"


_buildinfo = {
"gojsonnet": _gojsonnet_buildinfo,
"reclass-rs": _reclass_rs_buildinfo,
"kapitan": lambda: "",
}


def _get_tool_info(tool) -> tuple[str, bool]:
tool_path = shutil.which(tool)
if not tool_path:
return "NOT FOUND IN PATH", False

version_arg = {
"jb": ["--version"],
"helm": ["version", "--template", "{{.Version}}"],
"kustomize": ["version"],
}
tool_version = (
subprocess.run(
[tool_path] + version_arg[tool],
stderr=subprocess.STDOUT,
stdout=subprocess.PIPE,
)
.stdout.decode("utf-8")
.strip()
)
return f"{tool_path}, version: {tool_version}", True


def version_info(config: Config, version: str):
exit_code = 0
click.secho(f"Commodore {version}", bold=True)
click.echo("")
click.secho("Core dependency versions", bold=True)
for dep in ["kapitan", "gojsonnet", "reclass-rs"]:
dep_ver = metadata.version(dep)
dep_buildinfo = _buildinfo[dep]()
if dep_buildinfo:
dep_buildinfo = f", build info: {dep_buildinfo}"
click.echo(f"{dep}: {dep_ver}{dep_buildinfo}")
click.echo("")
click.secho("External tool versions", bold=True)
for tool in ["helm", "jb", "kustomize"]:
tool_info, found = _get_tool_info(tool)
fgcolor = None
bold = False
if not found:
fgcolor = "red"
bold = True
exit_code = 127
click.secho(f"{tool}: {tool_info}", fg=fgcolor, bold=bold)
sys.exit(exit_code)
4 changes: 4 additions & 0 deletions docs/modules/ROOT/pages/reference/cli.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -654,3 +654,7 @@ However, labels added by previous runs can't be removed since we've got no easy
*--template-version* TEXT::
The component template version (Git tree-ish) to use.
If not provided, the currently active template version will be used.

== Version

Show extended version information for Commodore.
8 changes: 8 additions & 0 deletions docs/modules/ROOT/pages/reference/commands.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -277,3 +277,11 @@ The command bases each PR on the default branch of the corresponding package rep

The command requires a GitHub Access token with the 'public_repo' permission, which is required to create PRs on public repositories.
If you want to manage private repos, the access token may require additional permissions.

== Version

commodore version

Show extended version information for Commodore.
The command displays the installed version for the `kapitan`, `gojsonnet` and `reclass-rs` Python packages.
Additionally, the command displays the path and version of the required external commands `helm`, `jb` and `kustomize`.
88 changes: 81 additions & 7 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ pyjwt = "2.10.1"
PyGithub = "2.6.1"
reclass-rs = "0.9.0"
gojsonnet = "0.21.0"
pygobuildinfo = "0.1.25"

[tool.poetry.dev-dependencies]
tox = "3.28.0"
Expand Down
5 changes: 5 additions & 0 deletions tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,3 +102,8 @@ def test_package_update_command():
def test_package_sync_command():
exit_status = call("commodore package sync --help", shell=True)
assert exit_status == 0


def test_version_command():
exit_status = call("commodore version --help", shell=True)
assert exit_status == 0
Loading