mumble/scripts/mumble-version.py
Robert Adam 6d01625af8 MAINT(snapshot-versions): Change snapshot version format
Until now the snapshot versions looked something like
1.4.0~444~g4afab7c~snapshot. They include the Mumble version which they
are a snapshot for at the beginning, then the revision number (the
amount of commits since the last tag in git's history), followed by the
latest commit's hash (prefixed by "g") and finally the word "snapshot".

The problem with this approach is that it requires knowledge of the git
history at least until the most recent tag. In addition to that it also
requires someone to think about moving the "tag-for-ci" that marks a
release-state in the master branch to be set on every release.

The history requirement is not an issue for a normal clone of the
repository but if someone (e.g. the CI) performs a shallow clone of the
repository, then the script fails.

In order to address this problem, this commit changes the version format
to be 1.4.0~2020-07-02~g4afab7c67~snapshot. The revision number got
replaced by the date of the most recent commit (in ISO format) and the
commit hash is now abbreviated by git itself as it sees fit instead of
manually truncating the commit hash at 7 characters.

That way the script doesn't require knowledge of the latest tag and all
the history after that. That way the script can now be run in a shallow
clone of depth 1.
2020-07-03 13:41:56 +02:00

131 lines
4.8 KiB
Python
Executable File

#!/usr/bin/env python
#
# Copyright 2005-2020 The Mumble Developers. All rights reserved.
# Use of this source code is governed by a BSD-style license
# that can be found in the LICENSE file at the root of the
# Mumble source tree or at <https://www.mumble.info/LICENSE>.
#
# This script returns the Mumble version string for a Mumble Git
# repository. The script must be run from within a Mumble Git
# repository.
# This is a replacement for `git describe` to make snapshots
# use the future, untagged version number rather than the previous.
#
# The version is of form 1.3.0~2020-07-02~g4f336a2~snapshot.
# It includes the target release version rather than the previous
# release (as git describe does).
#
# Detailed description:
#
# Once upon a time, Mumble used the output of `git describe` as
# its version string.
#
# If a commit was tagged, it was a "release", and got a simple
# string which was the name of the tag.
#
# If a commit wasn't tagged, it got '1.2.6-234-gf552ag1', which
# consists of the number of commits since the latest tag, and
# the commit hash of the latest commit.
#
# However, the output of `git describe` was found to be confusing
# in practice. This is because the base version of the `git describe`
# output is the latest tag, which is to say: the *previous*
# version of Mumble.
#
# So, a user running a snapshot that would become 1.3.0 would be
# running a version like 1.2.6-234-gf552ag1. This is confusing
# simply by looking at the version numbers, but the way versioning
# works inside the Mumble client made it worse: the client's version
# was refered to as "1.3.0" in several places, but the actual version
# number said 1.2.6-234-gf552ag1.
#
# This script is the replacement for `git describe`. It outputs the
# *actual* base version of the Mumble tree, rather than the latest tag.
# This means that snapshots for Mumble 1.3.0 now have the base version
# '1.3.0'.
#
# It also changes the version string: Instead of using dashes
# as a separator in the version string, it now uses tildes. This allows
# Debian's dpkg version comparer to correctly sort snapshot versions
# before release versions. The new string also includes 'snapshot' in the
# version string to denote to users that the given version is a pre-release
# snapshot.
# Furthermore the version number does use the latest commit's date rather
# than therevision number since the last tag.
# A full new-style version string looks like this:
# 1.3.0~2020-07-02~g4f336a2~snapshot.
from __future__ import (unicode_literals, print_function, division)
import os
import platform
import subprocess
import sys
def strip(s):
s = s.replace('\r', '')
s = s.replace('\n', '')
return s
def cmd(args):
shell = platform.system() == 'Windows'
p = subprocess.Popen(args, shell=shell, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdout, stderr = p.communicate()
if p.returncode != 0:
raise Exception('cmd: {0} failed with status {1}: {2}'.format(args, p.returncode, stderr))
return stdout.decode('utf-8')
# Reads the version from src/mumble.pri
def readMumblePriVersion():
sourceTreeRoot = strip(cmd(['git', 'rev-parse', '--show-toplevel']))
version = None
with open(os.path.join(sourceTreeRoot, 'src', 'mumble.pri'), 'r') as f:
for line in f:
if 'VERSION' in line:
line = line.replace('VERSION', '')
line = line.replace('=', '')
line = line.replace('\t', '')
line = line.replace(' ', '')
line = strip(line)
version = line
break
if version is None:
raise Exception('unable to read version from mumble.pri')
return version
def main():
# Get all tags associated with the latest commit
latestCommitTags = [x for x in cmd(['git', 'tag', '--points-at', 'HEAD']).split("\n") if x]
if len(latestCommitTags) > 1:
raise RuntimeError("Encountered commit with multiple tags: %s" % latestCommitTags)
if len(latestCommitTags) == 1:
# Most recent commit is tagged -> this is a tagged release version
# Use the tag as the version-string
version = latestCommitTags[0]
else:
# This is a snapshot (i.e. built from a non-tagged commit)
# Get the date of the most recent commit
latestCommitDate = cmd(['git', 'log', '-1', '--format=%cd', '--date=short']).strip()
# Get the hash of the most recent commit (shortened)
latestCommitHash = cmd(['git', 'rev-parse', '--short' , 'HEAD']).strip()
# Get the Mumble version that is set in the mumble.pri file
mumblePriVersion = readMumblePriVersion()
if len(mumblePriVersion) == 0 or not '.' in mumblePriVersion:
raise Exception('bad mumblePriVersion: "{0}"'.format(mumblePriVersion))
version = '{0}~{1}~g{2}~snapshot'.format(mumblePriVersion, latestCommitDate, latestCommitHash)
end = ''
if '--newline' in sys.argv:
end = None
print(version, end=end)
if __name__ == '__main__':
main()