Skip to content

Commit 0c435e4

Browse files
committed
tools: add dir-version to print a package version from git references
1 parent 909520e commit 0c435e4

3 files changed

Lines changed: 178 additions & 0 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ build
2020
*.dylib
2121
*.so
2222
*.a
23+
__pycache__
2324

2425
#ignore editor temporary files
2526
.*.swp

tools/dir-version/DirVersion.py

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
#! /usr/bin/env python3
2+
3+
# Daemon BSD Source Code
4+
# Copyright (c) 2024, Daemon Developers
5+
# All rights reserved.
6+
#
7+
# Redistribution and use in source and binary forms, with or without
8+
# modification, are permitted provided that the following conditions are met:
9+
# * Redistributions of source code must retain the above copyright
10+
# notice, this list of conditions and the following disclaimer.
11+
# * Redistributions in binary form must reproduce the above copyright
12+
# notice, this list of conditions and the following disclaimer in the
13+
# documentation and/or other materials provided with the distribution.
14+
# * Neither the name of the <organization> nor the
15+
# names of its contributors may be used to endorse or promote products
16+
# derived from this software without specific prior written permission.
17+
#
18+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
19+
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20+
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21+
# DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
22+
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23+
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24+
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25+
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26+
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27+
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28+
29+
import datetime
30+
import os
31+
import subprocess
32+
import sys
33+
import time
34+
35+
class _DirVersion():
36+
git_short_ref_length = 7
37+
38+
def __init__(self, source_dir, is_permissive, is_quiet, is_whole):
39+
if not os.path.isdir(source_dir):
40+
raise(ValueError, "not a directory")
41+
42+
self.is_permissive = is_permissive
43+
self.process_stderr = None
44+
45+
if is_quiet:
46+
self.process_stderr = subprocess.DEVNULL
47+
48+
self.is_whole = is_whole
49+
50+
self.source_dir_realpath = os.path.realpath(source_dir)
51+
52+
self.git_command_list = ["git", "-C", self.source_dir_realpath]
53+
54+
def runGitCommand(self, command_list, is_permissive=False):
55+
command_list = self.git_command_list + command_list
56+
57+
process_check = not (self.is_permissive or is_permissive)
58+
59+
process = subprocess.run(command_list,
60+
stdout=subprocess.PIPE, stderr=self.process_stderr, check=process_check, text=True)
61+
62+
return process.stdout.rstrip(), process.returncode
63+
64+
def getDateString(self, timestamp):
65+
return datetime.datetime.utcfromtimestamp(timestamp).strftime('%Y%m%d-%H%M%S')
66+
67+
def isDirtyGit(self):
68+
if self.is_whole:
69+
# Git prints the Git repository root directory.
70+
git_show_toplevel_string, git_show_toplevel_returncode = \
71+
self.runGitCommand(["rev-parse", "--show-toplevel"])
72+
73+
lookup_dir = git_show_toplevel_string.splitlines()[0]
74+
else:
75+
lookup_dir = self.source_dir_realpath
76+
77+
# Git returns 1 if there is at least one modified file in the given directory.
78+
git_diff_quiet_string, git_diff_quiet_returncode \
79+
= self.runGitCommand(["diff", "--quiet", lookup_dir], is_permissive=True)
80+
81+
if git_diff_quiet_returncode != 0:
82+
return True
83+
84+
# Git prints the list of untracked files the given directory.
85+
git_ls_untracked_string, git_ls_untracked_returncode \
86+
= self.runGitCommand(["ls-files", "-z", "--others", "--exclude-standard", lookup_dir])
87+
88+
untracked_file_list = git_ls_untracked_string.split('\0')[:-1]
89+
90+
return len(untracked_file_list) > 0
91+
92+
def getVersionString(self):
93+
tag_string="0"
94+
date_string="-" + self.getDateString(time.time())
95+
ref_string=""
96+
dirt_string="+dirty"
97+
98+
git_last_commit_string, git_last_commit_returncode \
99+
= self.runGitCommand(["rev-parse", "HEAD", "--"])
100+
101+
if git_last_commit_returncode == 0:
102+
git_last_commit_short_string = git_last_commit_string[:self.git_short_ref_length]
103+
ref_string = "-" + git_last_commit_short_string
104+
105+
git_last_commit_timestamp_string, git_last_commit_timestamp_returncode \
106+
= self.runGitCommand(["log", "-1", "--pretty=format:%ct"])
107+
108+
if git_last_commit_timestamp_returncode == 0:
109+
date_string = "-" + self.getDateString(int(git_last_commit_timestamp_string))
110+
111+
git_closest_tag_string, git_closest_tag_returncode \
112+
= self.runGitCommand(["describe", "--tags", "--abbrev=0", "--match", "v[0-9].*"])
113+
114+
if git_closest_tag_returncode == 0:
115+
git_closest_tag_version_string = git_closest_tag_string[1:]
116+
tag_string = git_closest_tag_version_string
117+
118+
git_describe_tag_string, git_describe_tag_returncode \
119+
= self.runGitCommand(["describe", "--tags", "--match", "v[0-9].*"])
120+
git_describe_version_string = git_describe_tag_string[1:]
121+
122+
if git_describe_tag_returncode == 0:
123+
if git_closest_tag_version_string == git_describe_version_string:
124+
date_string = ""
125+
ref_string = ""
126+
127+
if not self.isDirtyGit():
128+
dirt_string = ""
129+
130+
return tag_string + date_string + ref_string + dirt_string
131+
132+
133+
def getVersionString(source_dir, is_permissive=False, is_quiet=False, is_whole=False):
134+
dirVersion = _DirVersion(source_dir, is_permissive, is_quiet, is_whole)
135+
return dirVersion.getVersionString()

tools/dir-version/dir-version

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
#! /usr/bin/env python3
2+
3+
# Daemon BSD Source Code
4+
# Copyright (c) 2024, Daemon Developers
5+
# All rights reserved.
6+
#
7+
# Redistribution and use in source and binary forms, with or without
8+
# modification, are permitted provided that the following conditions are met:
9+
# * Redistributions of source code must retain the above copyright
10+
# notice, this list of conditions and the following disclaimer.
11+
# * Redistributions in binary form must reproduce the above copyright
12+
# notice, this list of conditions and the following disclaimer in the
13+
# documentation and/or other materials provided with the distribution.
14+
# * Neither the name of the <organization> nor the
15+
# names of its contributors may be used to endorse or promote products
16+
# derived from this software without specific prior written permission.
17+
#
18+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
19+
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20+
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21+
# DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
22+
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23+
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24+
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25+
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26+
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27+
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28+
29+
import DirVersion
30+
31+
import argparse
32+
33+
parser = argparse.ArgumentParser(description="Print repository version string")
34+
35+
parser.add_argument("-p", "--permissive", dest="is_permissive", help="ignore Git errors", action="store_true")
36+
parser.add_argument("-q", "--quiet", dest="is_quiet", help="silence Git errors", action="store_true")
37+
parser.add_argument("-w", "--whole", dest="is_whole", help="look for dirt in whole repository", action="store_true")
38+
parser.add_argument(dest="source_dir", nargs="?", metavar="DIRNAME", default=".", help="repository path")
39+
40+
args = parser.parse_args()
41+
42+
print(DirVersion.getVersionString(args.source_dir, is_permissive=args.is_permissive, is_quiet=args.is_quiet, is_whole=args.is_whole))

0 commit comments

Comments
 (0)