Skip to content

Scmversion #3606

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
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
10 changes: 9 additions & 1 deletion mkosi/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
from pathlib import Path
from typing import Any, Optional, Union, cast

from mkosi._version import __version__
from mkosi.archive import can_extract_tar, extract_tar, make_cpio, make_tar
from mkosi.bootloader import (
efi_boot_binary,
Expand Down Expand Up @@ -128,7 +129,6 @@
MOUNT_ATTR_RDONLY,
MS_REC,
MS_SLAVE,
__version__,
acquire_privileges,
have_effective_cap,
join_new_session_keyring,
Expand Down Expand Up @@ -600,6 +600,7 @@ def run_configure_scripts(config: Config) -> Config:
MKOSI_UID=str(os.getuid()),
MKOSI_GID=str(os.getgid()),
MKOSI_DEBUG=one_zero(ARG_DEBUG.get()),
MKOSI_VERSION=__version__,
)

if config.profiles:
Expand Down Expand Up @@ -641,6 +642,7 @@ def run_sync_scripts(config: Config) -> None:
MKOSI_UID=str(os.getuid()),
MKOSI_GID=str(os.getgid()),
MKOSI_CONFIG="/work/config.json",
MKOSI_VERSION=__version__,
CACHED=one_zero(have_cache(config)),
MKOSI_DEBUG=one_zero(ARG_DEBUG.get()),
)
Expand Down Expand Up @@ -760,6 +762,7 @@ def run_prepare_scripts(context: Context, build: bool) -> None:
MKOSI_UID=str(os.getuid()),
MKOSI_GID=str(os.getgid()),
MKOSI_CONFIG="/work/config.json",
MKOSI_VERSION=__version__,
WITH_DOCS=one_zero(context.config.with_docs),
WITH_NETWORK=one_zero(context.config.with_network),
WITH_TESTS=one_zero(context.config.with_tests),
Expand Down Expand Up @@ -830,6 +833,7 @@ def run_build_scripts(context: Context) -> None:
MKOSI_UID=str(os.getuid()),
MKOSI_GID=str(os.getgid()),
MKOSI_CONFIG="/work/config.json",
MKOSI_VERSION=__version__,
WITH_DOCS=one_zero(context.config.with_docs),
WITH_NETWORK=one_zero(context.config.with_network),
WITH_TESTS=one_zero(context.config.with_tests),
Expand Down Expand Up @@ -905,6 +909,7 @@ def run_postinst_scripts(context: Context) -> None:
MKOSI_UID=str(os.getuid()),
MKOSI_GID=str(os.getgid()),
MKOSI_CONFIG="/work/config.json",
MKOSI_VERSION=__version__,
WITH_NETWORK=one_zero(context.config.with_network),
MKOSI_DEBUG=one_zero(ARG_DEBUG.get()),
)
Expand Down Expand Up @@ -974,6 +979,7 @@ def run_finalize_scripts(context: Context) -> None:
MKOSI_UID=str(os.getuid()),
MKOSI_GID=str(os.getgid()),
MKOSI_CONFIG="/work/config.json",
MKOSI_VERSION=__version__,
WITH_NETWORK=one_zero(context.config.with_network),
MKOSI_DEBUG=one_zero(ARG_DEBUG.get()),
)
Expand Down Expand Up @@ -1037,6 +1043,7 @@ def run_postoutput_scripts(context: Context) -> None:
MKOSI_GID=str(os.getgid()),
MKOSI_CONFIG="/work/config.json",
MKOSI_DEBUG=one_zero(ARG_DEBUG.get()),
MKOSI_VERSION=__version__,
)

if context.config.profiles:
Expand Down Expand Up @@ -4569,6 +4576,7 @@ def run_clean_scripts(config: Config) -> None:
MKOSI_GID=str(os.getgid()),
MKOSI_CONFIG="/work/config.json",
MKOSI_DEBUG=one_zero(ARG_DEBUG.get()),
MKOSI_VERSION=__version__,
)

if config.profiles:
Expand Down
2 changes: 2 additions & 0 deletions mkosi/_staticversion.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
__version__ = "25.3.post0"
85 changes: 85 additions & 0 deletions mkosi/_version.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
# The __version__ generation here supports the following modes
# 1. The version is obtained from the environment variable MKOSI_VERSION, to trump all other e.g. for
# debugging purposes
# 2. By default the version is obtained von the Python distribution's metadata on installed packages, unless
# a. the installed version of mkosi lacks this file
# b. the path of that file is not equal to the path of this particular file
# 3. If mkosi has not been installed as a Python package or the metadata pertains to a different mkosi than
# is being called the version is
# b. generated from the output of git describe
# c. looked up in a static version file from resources
# 4. If no version can be found, it is set to "0"

import datetime
import importlib.metadata
import logging
import os
import subprocess
from importlib.metadata import PackageNotFoundError
from pathlib import Path
from typing import Optional


def version_from_metadata() -> Optional[str]:
try:
dist = importlib.metadata.distribution("mkosi")

this_file = dist.locate_file("mkosi/_version.py")
# If the installed version is too old, it might not have the _version.py file
if not this_file.exists():
return None

# If the file importlib.metadata thinks we are talking about is not this one, let's pretend we didn't
# find anything at all and fall back
if this_file != Path(__file__):
return None

return importlib.metadata.version("mkosi")
except PackageNotFoundError:
return None


def version_from_git() -> Optional[str]:
try:
p = subprocess.run(
["git", "describe"],
cwd=Path(__file__).parent.parent,
check=True,
text=True,
capture_output=True,
)
# output has form like v25.3-244-g8f491df9 when not on a tag, else just the tag
tag, *rest = p.stdout.strip().split("-")
tag = tag.lstrip("v")
if rest:
numcommits, commit = rest
return f"{tag}.post1.dev{numcommits}+{commit}.d{datetime.datetime.now():%Y%m%d}"

# we are exactly on a tag
return tag
except (subprocess.CalledProcessError, NotADirectoryError, FileNotFoundError):
return None
Comment on lines +43 to +62
Copy link
Contributor

Choose a reason for hiding this comment

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

So this completely goes against the convention we adopted in systemd to have the devel version be of the next version we've developing, which is fundamentally incompatible with git describe since that will always return the latest tag which is the previous version.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

To document the current state for others:

We're a bit between the rock of UAPI version format spec and the hard place of Python's public version identifiers.

Both are compatible as far as the set of allowed characters for Python's scheme is (mostly) subset (disregarding the local part divided by + and the epoch divided by !, but systemd-analyze compare-version will happily treat them as separators, so that's fine), unfortunately the semantics are different. Whereas the UAPI spec follows RPM an .deb and has sigils that raise or lower a version, allowing not having to keep state and being able to compare version strings character by character, Python has multicharater substrings like .dev or .rc that take their place.

Using a version scheme anchored on the last release and counting commits since, allows to fit into both schemes, as described above, since this will hold

25 < 25.3 < 25.3.post1.dev244+g0eac3718 < 25.4 < 26

Unfortunately, there is no way to construct a version starting with the next release number, that compares smaller than that release under both the UAPI version spec and Python's, e.g. 26rc244 would compare smaller than 26 in under Python's scheme, but compares larger under the UAPI scheme, simply because it is shorter.



def version_from_static() -> Optional[str]:
try:
import mkosi._staticversion

return mkosi._staticversion.__version__
except ImportError:
return None


def version_fallback() -> str:
logging.warning("Unable to determine mkosi version")
return "0"


__version__ = (
os.getenv("MKOSI_VERSION")
or version_from_metadata()
or version_from_git()
or version_from_static()
or version_fallback()
)
3 changes: 2 additions & 1 deletion mkosi/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,12 @@
from pathlib import Path
from typing import Any, Callable, ClassVar, Generic, Optional, Protocol, TypeVar, Union, cast

from mkosi._version import __version__
from mkosi.distributions import Distribution, detect_distribution
from mkosi.log import ARG_DEBUG, ARG_DEBUG_SANDBOX, ARG_DEBUG_SHELL, die
from mkosi.pager import page
from mkosi.run import SandboxProtocol, find_binary, nosandbox, run, sandbox_cmd, workdir
from mkosi.sandbox import Style, __version__
from mkosi.sandbox import Style
from mkosi.user import INVOKING_USER
from mkosi.util import (
PathString,
Expand Down
3 changes: 2 additions & 1 deletion mkosi/initrd.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,12 @@
from typing import Optional, cast

import mkosi.resources
from mkosi._version import __version__
from mkosi.config import DocFormat, InitrdProfile, OutputFormat
from mkosi.documentation import show_docs
from mkosi.log import ARG_DEBUG, ARG_DEBUG_SHELL, log_notice, log_setup
from mkosi.run import find_binary, run, uncaught_exception_handler
from mkosi.sandbox import __version__, umask
from mkosi.sandbox import umask
from mkosi.tree import copy_tree, move_tree, rmtree
from mkosi.util import PathString, mandatory_variable, resource_path

Expand Down
3 changes: 3 additions & 0 deletions mkosi/resources/man/mkosi.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -2578,6 +2578,8 @@ Scripts executed by **mkosi** receive the following environment variables:
current image. This file can be parsed inside scripts to gain access to all
settings for the current image.

* `$MKOSI_VERSION` is the version string of mkosi.

* `$IMAGE_ID` contains the identifier from the `ImageId=` or `--image-id=` setting.

* `$IMAGE_VERSION` contains the version from the `ImageVersion=` or `--image-version=` setting.
Expand Down Expand Up @@ -2608,6 +2610,7 @@ Consult this table for which script receives which environment variables:
| `MKOSI_CONFIG` | | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| `MKOSI_GID` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| `MKOSI_UID` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| `MKOSI_VERSION` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| `OUTPUTDIR` | | | | | ✓ | ✓ | ✓ | ✓ |
| `PACKAGEDIR` | | | ✓ | ✓ | ✓ | ✓ | | |
| `PROFILES` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | | ✓ |
Expand Down
17 changes: 13 additions & 4 deletions mkosi/sandbox.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@
import sys
import warnings # noqa: F401 (loaded lazily by os.execvp() which happens too late)

__version__ = "26~devel"

# The following constants are taken from the Linux kernel headers.
AT_EMPTY_PATH = 0x1000
AT_FDCWD = -100
Expand Down Expand Up @@ -897,8 +895,19 @@ def main() -> None:
print(HELP, file=sys.stderr)
sys.exit(0)
elif arg == "--version":
print(__version__, file=sys.stderr)
sys.exit(0)
try:
# This is a cyclic import, but this code is not run at import time.
from mkosi._version import __version__

print(__version__, file=sys.stderr)
sys.exit(0)
except ImportError:
try:
print(os.environ["MKOSI_VERSION"], file=sys.stderr)
sys.exit(0)
except KeyError:
print("Cannot determine version", file=sys.stderr)
sys.exit(1)
if arg == "--tmpfs":
fsops.append(TmpfsOperation(argv.pop()))
elif arg == "--dev":
Expand Down
14 changes: 11 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,21 +1,26 @@
[build-system]
requires = ["setuptools", "setuptools-scm"]
requires = ["setuptools>=64", "setuptools-scm>=8"]
build-backend = "setuptools.build_meta"

[project]
name = "mkosi"
authors = [
{name = "mkosi contributors", email = "[email protected]"},
]
version = "25.3"
dynamic = ["version"]
description = "Build Bespoke OS Images"
readme = "README.md"
requires-python = ">=3.9"
license = {file = "LICENSE"}

[project.optional-dependencies]
bootable = [
"pefile >= 2021.9.3",
"pefile >= 2021.9.3",
]
dev = [
"ruff",
"mypy",
"pyright",
]

[project.scripts]
Expand Down Expand Up @@ -45,6 +50,9 @@ packages = [
"tmpfiles.d/*",
]

[tool.setuptools_scm]
version_scheme = "no-guess-dev"

[tool.isort]
profile = "black"
include_trailing_comma = true
Expand Down
7 changes: 3 additions & 4 deletions tools/do-a-release.sh
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@ if ! git diff-index --quiet HEAD; then
exit 1
fi

sed -r -i "s/^version = \".*\"$/version = \"$VERSION\"/" pyproject.toml
sed -r -i "s/^__version__ = \".*\"$/__version__ = \"$VERSION\"/" mkosi/sandbox.py
printf '# SPDX-License-Identifier: LGPL-2.1-or-later\n__version__ = "%s"\n' \
"${VERSION}.post0" \
>mkosi/_staticversion.py

git add -p pyproject.toml mkosi

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

sed -r -i "s/^__version__ = \".*\"$/__version__ = \"$VERSION\"/" mkosi/sandbox.py

git add -p mkosi

git commit -m "Bump version to $VERSION"
7 changes: 7 additions & 0 deletions tools/generate-zipapp.sh
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,13 @@ mkdir -p builddir

cp -r mkosi "${BUILDDIR}/"

# HACK: importlib metadata doesn't seem to be there in a zipapp even if
# properly installed via pip, so let's patch it in there manually.
mkosiversion="$(python3 -m mkosi --version)"
printf '# SPDX-License-Identifier: LGPL-2.1-or-later\n__version__ = "%s"\n' \
"${mkosiversion#mkosi }" \
>"${BUILDDIR}/mkosi/_staticversion.py"

python3 -m zipapp \
-p "/usr/bin/env python3" \
-o builddir/mkosi \
Expand Down
Loading