Skip to content

Commit 6efdd7a

Browse files
committed
setup: use setuptools-scm to generate the version
This commit changes the versioning scheme of non-release versions of mkosi to align with the standard Python version scheme [1]. Unfortunately this scheme is not fully compatible with the UAPI group version format specification. Concretely, this changes 26~devel to 25.3.post1.dev244+g0eac3718 The used format ensures that for prereleases the following inequalities still hold 25.3 < 25.3.post1.dev244+g0eac3718 < 25.4 < 26 Usage of this new format has the following benefits: 1. It is compatible with PyPI and allows publishing mkosi there in the future 2. Since it contains the number of commits since the last tag as well as part of the commit hash in the "local part" of the version, it becomes easier for bug reporters to show the version they are on no matter how mkosi has been installed. 3. The version no longer needs to be managed in multiple places. The current implementation moves the version from mkosi/sandbox.py to a new file _version.py that obtains the version either via importlib metadata (in case mkosi has been installed) or by calling setuptools-scm directly, to support the bin/ shims. Since the version is no longer available in mkosi/sandbox.py, it passed as an environment variable for scripts. [1] https://packaging.python.org/en/latest/specifications/version-specifiers/
1 parent 45761bf commit 6efdd7a

File tree

10 files changed

+113
-13
lines changed

10 files changed

+113
-13
lines changed

mkosi/__init__.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
from pathlib import Path
2828
from typing import Any, Optional, Union, cast
2929

30+
from mkosi._version import __version__
3031
from mkosi.archive import can_extract_tar, extract_tar, make_cpio, make_tar
3132
from mkosi.bootloader import (
3233
efi_boot_binary,
@@ -127,7 +128,6 @@
127128
MOUNT_ATTR_RDONLY,
128129
MS_REC,
129130
MS_SLAVE,
130-
__version__,
131131
acquire_privileges,
132132
have_effective_cap,
133133
join_new_session_keyring,
@@ -598,6 +598,7 @@ def run_configure_scripts(config: Config) -> Config:
598598
SRCDIR="/work/src",
599599
MKOSI_UID=str(os.getuid()),
600600
MKOSI_GID=str(os.getgid()),
601+
MKOSI_VERSION=__version__,
601602
)
602603

603604
if config.profiles:
@@ -639,6 +640,7 @@ def run_sync_scripts(config: Config) -> None:
639640
MKOSI_UID=str(os.getuid()),
640641
MKOSI_GID=str(os.getgid()),
641642
MKOSI_CONFIG="/work/config.json",
643+
MKOSI_VERSION=__version__,
642644
CACHED=one_zero(have_cache(config)),
643645
)
644646

@@ -764,6 +766,7 @@ def run_prepare_scripts(context: Context, build: bool) -> None:
764766
MKOSI_UID=str(os.getuid()),
765767
MKOSI_GID=str(os.getgid()),
766768
MKOSI_CONFIG="/work/config.json",
769+
MKOSI_VERSION=__version__,
767770
WITH_DOCS=one_zero(context.config.with_docs),
768771
WITH_NETWORK=one_zero(context.config.with_network),
769772
WITH_TESTS=one_zero(context.config.with_tests),
@@ -833,6 +836,7 @@ def run_build_scripts(context: Context) -> None:
833836
MKOSI_UID=str(os.getuid()),
834837
MKOSI_GID=str(os.getgid()),
835838
MKOSI_CONFIG="/work/config.json",
839+
MKOSI_VERSION=__version__,
836840
WITH_DOCS=one_zero(context.config.with_docs),
837841
WITH_NETWORK=one_zero(context.config.with_network),
838842
WITH_TESTS=one_zero(context.config.with_tests),
@@ -907,6 +911,7 @@ def run_postinst_scripts(context: Context) -> None:
907911
MKOSI_UID=str(os.getuid()),
908912
MKOSI_GID=str(os.getgid()),
909913
MKOSI_CONFIG="/work/config.json",
914+
MKOSI_VERSION=__version__,
910915
WITH_NETWORK=one_zero(context.config.with_network),
911916
)
912917

@@ -975,6 +980,7 @@ def run_finalize_scripts(context: Context) -> None:
975980
MKOSI_UID=str(os.getuid()),
976981
MKOSI_GID=str(os.getgid()),
977982
MKOSI_CONFIG="/work/config.json",
983+
MKOSI_VERSION=__version__,
978984
WITH_NETWORK=one_zero(context.config.with_network),
979985
)
980986

@@ -1036,6 +1042,7 @@ def run_postoutput_scripts(context: Context) -> None:
10361042
MKOSI_UID=str(os.getuid()),
10371043
MKOSI_GID=str(os.getgid()),
10381044
MKOSI_CONFIG="/work/config.json",
1045+
MKOSI_VERSION=__version__,
10391046
)
10401047

10411048
if context.config.profiles:
@@ -4573,6 +4580,7 @@ def run_clean_scripts(config: Config) -> None:
45734580
MKOSI_UID=str(os.getuid()),
45744581
MKOSI_GID=str(os.getgid()),
45754582
MKOSI_CONFIG="/work/config.json",
4583+
MKOSI_VERSION=__version__,
45764584
)
45774585

45784586
if config.profiles:

mkosi/_version.py

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
# SPDX-License-Identifier: LGPL-2.1-or-later
2+
# The __version__ generation here supports the following modes
3+
# 1. By default the version is obtained von the Python distribution's metadata on installed packages
4+
# 2. If mkosi has not been installed as a Python package or the metadata pertains to a different mkosi than
5+
# is being called the version is
6+
# a. obtained from the environment variable MKOSI_VERSION
7+
# b. generated from the output of git describe
8+
# c. looked up in a static version file from resources
9+
# 3. If no version can be found, it is set to "0"
10+
11+
import datetime
12+
import importlib.metadata
13+
import importlib.resources
14+
import logging
15+
import os
16+
import subprocess
17+
from importlib.metadata import PackageNotFoundError
18+
from pathlib import Path
19+
from typing import Optional
20+
21+
22+
def version_from_git() -> Optional[str]:
23+
try:
24+
p = subprocess.run(
25+
["git", "describe"],
26+
cwd=Path(__file__).parent.parent,
27+
check=True,
28+
text=True,
29+
capture_output=True,
30+
)
31+
# output has form like v25.3-244-g8f491df9 when not on a tag, else just the tag
32+
tag, *rest = p.stdout.strip().split("-")
33+
tag = tag.lstrip("v")
34+
if rest:
35+
numcommits, commit = rest
36+
return f"{tag}.post1.dev{numcommits}+{commit}.d{datetime.datetime.now():%Y%m%d}"
37+
38+
# we are exactly on a tag
39+
return tag
40+
except (subprocess.CalledProcessError, NotADirectoryError, FileNotFoundError):
41+
return None
42+
43+
44+
def version_from_resources() -> Optional[str]:
45+
try:
46+
import mkosi.resources
47+
from mkosi.util import resource_path
48+
49+
with resource_path(mkosi.resources) as resources:
50+
return (resources / "staticversion").read_text().strip()
51+
except (ImportError, FileNotFoundError):
52+
return None
53+
54+
55+
try:
56+
# If the file importlib.metadata things we are talking about is not this on, let's pretend we didn't find
57+
# anything at all and fall back
58+
mkosi_files = [p for p in importlib.metadata.files("mkosi") or [] if p.name == "_version.py"]
59+
if not mkosi_files or os.fspath(mkosi_files[0]) != __file__:
60+
raise PackageNotFoundError()
61+
62+
__version__ = importlib.metadata.version("mkosi")
63+
except PackageNotFoundError:
64+
# This branch means, that mkosi was not installed as a package as Python is aware, this is most likely
65+
# the case if mkosi was run via the bin/mkosi shim in mkosi's repository.
66+
67+
fallback = os.getenv("MKOSI_VERSION") or version_from_git() or version_from_resources() or "0"
68+
69+
if fallback == "0":
70+
logging.warning("Unable to determine mkosi version")
71+
72+
__version__ = fallback

mkosi/config.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,12 @@
3030
from pathlib import Path
3131
from typing import Any, Callable, Generic, Optional, TypeVar, Union, cast
3232

33+
from mkosi._version import __version__
3334
from mkosi.distributions import Distribution, detect_distribution
3435
from mkosi.log import ARG_DEBUG, ARG_DEBUG_SANDBOX, ARG_DEBUG_SHELL, die
3536
from mkosi.pager import page
3637
from mkosi.run import SandboxProtocol, find_binary, nosandbox, run, sandbox_cmd, workdir
37-
from mkosi.sandbox import Style, __version__
38+
from mkosi.sandbox import Style
3839
from mkosi.user import INVOKING_USER
3940
from mkosi.util import (
4041
PathString,

mkosi/initrd.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,12 @@
1414
from typing import Optional, cast
1515

1616
import mkosi.resources
17+
from mkosi._version import __version__
1718
from mkosi.config import DocFormat, InitrdProfile, OutputFormat
1819
from mkosi.documentation import show_docs
1920
from mkosi.log import ARG_DEBUG, ARG_DEBUG_SHELL, log_notice, log_setup
2021
from mkosi.run import find_binary, run, uncaught_exception_handler
21-
from mkosi.sandbox import __version__, umask
22+
from mkosi.sandbox import umask
2223
from mkosi.tree import copy_tree, move_tree, rmtree
2324
from mkosi.util import PathString, mandatory_variable, resource_path
2425

mkosi/resources/man/mkosi.1.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2555,6 +2555,8 @@ Scripts executed by **mkosi** receive the following environment variables:
25552555
current image. This file can be parsed inside scripts to gain access to all
25562556
settings for the current image.
25572557

2558+
* `$MKOSI_VERSION` is the version string of mkosi.
2559+
25582560
* `$IMAGE_ID` contains the identifier from the `ImageId=` or `--image-id=` setting.
25592561

25602562
* `$IMAGE_VERSION` contains the version from the `ImageVersion=` or `--image-version=` setting.
@@ -2589,6 +2591,7 @@ Consult this table for which script receives which environment variables:
25892591
| `MKOSI_UID` |||||||||
25902592
| `MKOSI_GID` |||||||||
25912593
| `MKOSI_CONFIG` | ||||||||
2594+
| `MKOSI_VERSION` |||||||||
25922595
| `IMAGE_ID` |||||||||
25932596
| `IMAGE_VERSION` |||||||||
25942597

mkosi/resources/staticversion

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
23.3.post0

mkosi/sandbox.py

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,6 @@
1313
import sys
1414
import warnings # noqa: F401 (loaded lazily by os.execvp() which happens too late)
1515

16-
__version__ = "26~devel"
17-
1816
# The following constants are taken from the Linux kernel headers.
1917
AT_EMPTY_PATH = 0x1000
2018
AT_FDCWD = -100
@@ -894,8 +892,18 @@ def main() -> None:
894892
print(HELP, file=sys.stderr)
895893
sys.exit(0)
896894
elif arg == "--version":
897-
print(__version__, file=sys.stderr)
898-
sys.exit(0)
895+
try:
896+
from mkosi._version import __version__
897+
898+
print(__version__, file=sys.stderr)
899+
sys.exit(0)
900+
except ImportError:
901+
try:
902+
print(os.environ["MKOSI_VERSION"])
903+
sys.exit(0)
904+
except KeyError:
905+
print("Cannot determine version", file=sys.stderr)
906+
sys.exit(1)
899907
if arg == "--tmpfs":
900908
fsops.append(TmpfsOperation(argv.pop()))
901909
elif arg == "--dev":

pyproject.toml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
[build-system]
2-
requires = ["setuptools", "setuptools-scm"]
2+
requires = ["setuptools>=64", "setuptools-scm>=8"]
33
build-backend = "setuptools.build_meta"
44

55
[project]
66
name = "mkosi"
77
authors = [
88
{name = "mkosi contributors", email = "[email protected]"},
99
]
10-
version = "25.3"
10+
dynamic = ["version"]
1111
description = "Build Bespoke OS Images"
1212
readme = "README.md"
1313
requires-python = ">=3.9"
@@ -47,8 +47,12 @@ packages = [
4747
"mkosi-tools/**/*",
4848
"mkosi-vm/**/*",
4949
"repart/**/*",
50+
"staticversion",
5051
]
5152

53+
[tool.setuptools_scm]
54+
version_scheme = "no-guess-dev"
55+
5256
[tool.isort]
5357
profile = "black"
5458
include_trailing_comma = true

tools/do-a-release.sh

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,7 @@ if ! git diff-index --quiet HEAD; then
1313
exit 1
1414
fi
1515

16-
sed -r -i "s/^version = \".*\"$/version = \"$VERSION\"/" pyproject.toml
17-
sed -r -i "s/^__version__ = \".*\"$/__version__ = \"$VERSION\"/" mkosi/sandbox.py
16+
printf "%s\n" "$VERSION" >mkosi/resources/staticversion
1817

1918
git add -p pyproject.toml mkosi
2019

@@ -25,8 +24,6 @@ git tag -s "v$VERSION" -m "mkosi $VERSION"
2524
VERSION_MAJOR=${VERSION%%.*}
2625
VERSION="$((VERSION_MAJOR + 1))~devel"
2726

28-
sed -r -i "s/^__version__ = \".*\"$/__version__ = \"$VERSION\"/" mkosi/sandbox.py
29-
3027
git add -p mkosi
3128

3229
git commit -m "Bump version to $VERSION"

tools/generate-zipapp.sh

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@ mkdir -p builddir
1010

1111
cp -r mkosi "${BUILDDIR}/"
1212

13+
# HACK: importlib metadata doesn't seem to be there in a zipapp even if
14+
# properly installed via pip, so let's patch it in there manually.
15+
mkosiversion="$(python3 -m mkosi --version)"
16+
printf '__version__ = "%s"\n' "${mkosiversion#mkosi }" >"${BUILDDIR}/mkosi/resources/staticversion"
17+
1318
python3 -m zipapp \
1419
-p "/usr/bin/env python3" \
1520
-o builddir/mkosi \

0 commit comments

Comments
 (0)