Skip to content

Instantly share code, notes, and snippets.

@arrdem
Last active July 10, 2024 03:07
Show Gist options
  • Save arrdem/7c5158052fb86df3b3eff2470f179b0b to your computer and use it in GitHub Desktop.
Save arrdem/7c5158052fb86df3b3eff2470f179b0b to your computer and use it in GitHub Desktop.
Monoversion

Monoversion

This script exists to compute the 'monoversion' of the repo. The monoversion is designed to represent in a somewhat human readable format what commit was released and roughly what repository state that represents.

This script is intended for use under Bazel as a --workspace_status_command, so that we can build artifacts out of Bazel which include a somewhat meaningful version numbers.

The format

Monoversion versions are formatted as follows

let y = <year>
    w = <week>
    cw = <commits since first commit of the week to the merge base>
    cb = <commits since the merge base>
    id = <sha7 of HEAD>
    marker = ".dirty" if `git status --porcelain` else ""
in
    f"{y}.{w}.{cw}" + f"-{cb}.{id:11}{marker}" if cb > 0 else ""

Version format properties

  • Compliant SemVer version https://semver.org/#backusnaur-form-grammar-for-valid-semver-versions
  • Sorts by date and sequence of commits to main under PyPi/Maven/Semver comparison
  • Releases not from main are immediately obvious because they aren't +0 and are tagged with the commit
  • If a branch is rebased the main stem will change but the sequence will remain
  • The current date is not a factor; the only consideration is commit dates
  • Dirty states are explicitly marked

Examples

  • 2024.23.1 The 23rd week of 2024, 1st commit of the week.
  • 2024.28.52-1.fad96ae6fa8 The 28th week of 2024, 52 commits into the week, one commit into a branch.
  • 2024.28.46-0.9aae26a05be.dirty The 28th week of 2024, 46 commits since the first commit of the week, +0 (on main), last commit 9aae26a05be, dirty.

Credits

Somewhat inspired by https://blog.aspect.build/versioning-releases-from-a-monorepo Somewhat inspired by https://calver.org/

#!/usr/bin/env python3
import argparse
from datetime import datetime, timedelta
from subprocess import CalledProcessError, check_call, check_output
import pytz
PARSER = argparse.ArgumentParser(__name__)
PARSER.add_argument("--start", default="HEAD")
PARSER.add_argument("--end", default="origin/main")
PARSER.add_argument("--epoch", type=int, default=0)
def _rev_parse(obj):
return check_output(["git", "rev-parse", obj]).decode("utf-8").strip()
def main():
opts = PARSER.parse_args()
start = _rev_parse(opts.start)
end = _rev_parse(opts.end)
parent = check_output(["git", "merge-base", start, end]).decode("utf-8").strip()
previous_timestamp = int(
check_output(["git", "show", "-s", "--pretty=%ct", parent]).decode("utf-8").strip()
)
# Normalize to the UTC timezone Gregorian calendar
previous_date = datetime.fromtimestamp(previous_timestamp, tz=pytz.utc)
# Take the previous date and subtract the number of days since monday (day 0
# in Python), then truncate the time part to 0. This gives you monday of the
# same week. Note that this WILL NOT underflow from Monday to the previous
# Monday because we're subtracting 0.
monday_date = (previous_date - timedelta(days=previous_date.weekday())).replace(
hour=0, minute=0, second=0
)
# Count commits between the merge base and the first commit of its week
week_commits = (
check_output(
[
"git",
"log",
"--oneline",
f"--since={int(monday_date.timestamp())}",
f"--until={previous_timestamp}",
]
)
.splitlines()
.__len__()
)
# Count revisions between us and the merge base.
# Note we can skip doing that if we're at the merge base.
branch_commits = 0
if parent != start:
branch_commits = (
check_output(["git", "rev-list", "--ancestry-path", f"{parent}...{start}"])
.splitlines()
.__len__()
)
# Figure out if we need a dirty marker
suffix = ""
try:
check_call(["git", "diff", "--quiet"])
except CalledProcessError:
suffix = ".dirty"
tail = f"-{branch_commits}.{start[:11]}{suffix}" if branch_commits else ""
print(f"{monday_date.year}.{monday_date.strftime('%V')}.{week_commits}{tail}")
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment