GNUnet 0.29.1-dev.1-3-g346358e26
 
Loading...
Searching...
No Matches
debian-version.py
Go to the documentation of this file.
1#!/usr/bin/env python3
2# This file is in the public domain.
3"""Derive CI versions from the highest supported SemVer tag in the repository.
4
5Keep this helper and test_version.py identical across Buildbot Debian publishers.
6Only vX.Y.Z and vX.Y.Z-dev.K (optionally with build metadata) are release tags.
7Snapshots use X.Y.(Z+1)-0.N or X.Y.Z-dev.K.N in SemVer, and replace the
8prerelease separator with '~' and 'dev.' with 'dev' for Debian. Metadata and
9commit hashes do not affect either version. N counts tag..HEAD commits; only
10commit identity, not a zero count, makes a build an exact tagged release.
11"""
12
13import re
14import subprocess
15import sys
16
17NUMBER = r"(0|[1-9][0-9]*)"
18TAG = re.compile(
19 rf"v{NUMBER}\.{NUMBER}\.{NUMBER}(?:-dev\.{NUMBER})?"
20 r"(?:\+([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?"
21)
22
23
24def parse_tag(name):
25 """Return a sortable precedence tuple, or None for an unsupported tag."""
26 match = TAG.fullmatch(name)
27 if match is None:
28 return None
29 major, minor, patch, dev, _ = match.groups()
30 return (int(major), int(minor), int(patch), dev is None,
31 0 if dev is None else int(dev))
32
33
34def select_tag(names):
35 candidates = []
36 for name in names:
37 rank = parse_tag(name)
38 if rank is None:
39 print(f"Ignoring unsupported version tag: {name}", file=sys.stderr)
40 else:
41 candidates.append((rank, name))
42 if not candidates:
43 raise ValueError("no supported version tags (expected vX.Y.Z or vX.Y.Z-dev.K)")
44 highest = max(rank for rank, _ in candidates)
45 # Metadata is irrelevant to precedence. Prefer the plain tag, then the
46 # lexically first tag name, independent of Git's configured tag sorting.
47 return min((name for rank, name in candidates if rank == highest),
48 key=lambda name: ("+" in name, name))
49
50
51def versions(tag, exact, count):
52 major, minor, patch, stable, dev = parse_tag(tag)
53 if stable:
54 base = f"{major}.{minor}.{patch if exact else patch + 1}"
55 suffix = "" if exact else f"0.{count}"
56 else:
57 base = f"{major}.{minor}.{patch}"
58 suffix = f"dev.{dev}" + ("" if exact else f".{count}")
59 semver = base + (f"-{suffix}" if suffix else "")
60 debian = base + ("~" + suffix.replace("dev.", "dev", 1) if suffix else "")
61 return semver, debian
62
63
64def git(*args):
65 return subprocess.check_output(["git", *args], text=True).strip()
66
67
68def main():
69 try:
70 if git("rev-parse", "--is-shallow-repository") != "false":
71 raise ValueError("a full Git checkout is required; disable shallow cloning")
72 head = git("rev-parse", "HEAD")
73 # Fetch the scheduled commit, including on detached HEADs, and all tags.
74 # Do not impose a depth limit or move HEAD to the selected tag.
75 subprocess.run(["git", "fetch", "--no-recurse-submodules", "--tags",
76 "origin", head], check=True, stdout=sys.stderr)
77 tag = select_tag(git("tag", "--list").splitlines())
78 tagged_commit = git("rev-parse", f"{tag}^{{commit}}")
79 count = int(git("rev-list", "--count", f"{tag}..{head}"))
80 semver, debian = versions(tag, head == tagged_commit, count)
81 print(f"Version tag: {tag}; HEAD: {head}; commits: {count}; "
82 f"SemVer: {semver}; Debian: {debian}", file=sys.stderr)
83 print(debian)
84 return 0
85 except (OSError, subprocess.CalledProcessError, ValueError) as error:
86 print(f"Cannot determine CI version: {error}", file=sys.stderr)
87 return 1
88
89
90if __name__ == "__main__":
91 sys.exit(main())
#define min(x, y)
#define max(x, y)
versions(tag, exact, count)