Skip to content
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

build: fix docker tags #370

Merged
merged 11 commits into from
Dec 7, 2023
14 changes: 8 additions & 6 deletions .github/workflows/main.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.10"]
python-version: ["3.11"]
steps:
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
- uses: actions/checkout@v3
Expand All @@ -53,7 +53,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.10"]
python-version: ["3.11"]
steps:
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
- uses: actions/checkout@v3
Expand Down Expand Up @@ -96,6 +96,7 @@ jobs:
echo "Current Software Version: ${sw_version}"
echo "SOFTWARE_VERSION=${sw_version}" >> $GITHUB_ENV
echo "SOFTWARE_VERSION_AS_DOCKER_TAG=$( echo ${sw_version} | tr + _ )" >> $GITHUB_ENV
echo "USE_SOFTWARE_VERSION=$( if [[ $(git tag --points-at HEAD) ]]; then echo true; else echo false; fi )" >> $GITHUB_ENV
# Set Docker image
- name: Set Docker repository
run: |
Expand All @@ -111,10 +112,11 @@ jobs:
tags: |
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=raw,value=${{ env.SOFTWARE_VERSION_AS_DOCKER_TAG }}
type=raw,value=${{ env.SOFTWARE_VERSION_AS_DOCKER_TAG}}.build${{ github.run_number }}
type=semver,enable=${{env.USE_SOFTWARE_VERSION}},pattern={{version}}
type=semver,enable=${{env.USE_SOFTWARE_VERSION}},event=tag,pattern={{major}}.{{minor}}
type=semver,enable=${{env.USE_SOFTWARE_VERSION}},pattern={{major}}.{{minor}}.build${{ github.run_number }}
type=raw,event=tag,value=${{ env.SOFTWARE_VERSION_AS_DOCKER_TAG }}
type=raw,event=tag,value=${{ env.SOFTWARE_VERSION_AS_DOCKER_TAG}}.build${{ github.run_number }}
type=sha
labels: |
org.opencontainers.image.version=${{ env.SOFTWARE_VERSION }}.build${{ github.run_number }}
Expand Down
150 changes: 82 additions & 68 deletions lifemonitor/_version.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,15 @@
"""Git implementation of _version.py."""

import errno
import logging
import os
import re
import subprocess
import sys

# define logger
logger = logging.getLogger(__name__)


def get_keywords():
"""Get the keywords needed to look up the version information."""
Expand Down Expand Up @@ -85,18 +89,19 @@ def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False,
if e.errno == errno.ENOENT:
continue
if verbose:
print("unable to run %s" % dispcmd)
print(e)
logger.debug("unable to run %s", dispcmd)
if logger.isEnabledFor(logging.DEBUG):
logger.exception(e)
return None, None
else:
if verbose:
print("unable to find command, tried %s" % (commands,))
logger.error("unable to find command, tried %s", (commands,))
return None, None
stdout = p.communicate()[0].strip().decode()
if p.returncode != 0:
if verbose:
print("unable to run %s (error)" % dispcmd)
print("stdout was %s" % stdout)
logger.error("unable to run %s (error)", dispcmd)
logger.error("stdout was %s", stdout)
return None, p.returncode
return stdout, p.returncode

Expand Down Expand Up @@ -233,70 +238,71 @@ def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command):
print("Directory %s not under git control" % root)
raise NotThisMethod("'git rev-parse --git-dir' returned error")

# if there is a tag matching tag_prefix, this yields TAG-NUM-gHEX[-dirty]
# if there isn't one, this yields HEX[-dirty] (no NUM)
describe_out, rc = run_command(GITS, ["describe", "--tags", "--dirty",
"--always", "--long",
"--match", "%s*" % tag_prefix],
cwd=root)
# init pieces to store git information
pieces = {}

# try to extract the current branch
branch, rc = run_command(GITS, ["rev-parse", "--abbrev-ref", "HEAD"], cwd=root, hide_stderr=True)
if not branch:
if verbose:
print("Unable to detect git branch")
raise NotThisMethod("'git rev-parse --abbrev-ref HEAD' returned error")
logger.debug("Detected branch: %s", branch)

# declare closest_tag
closest_tag = None

# get all tags sorted by date
all_tags_sorted, rc = run_command(GITS, ["for-each-ref", "--sort=creatordate", "--format='%(refname:short)'", "refs/tags"],
cwd=root)
logger.debug("all_tags_sorted: %r", all_tags_sorted)
if len(all_tags_sorted.splitlines()) == 0:
logger.warning("No tags found")
else:
closest_tag = all_tags_sorted.splitlines()[-1].strip("'")
logger.debug("Closest tag: %r", closest_tag)
pieces["closest-tag"] = closest_tag

# detect the branch of the closest tag
closest_tag_branch = None
if closest_tag is not None:
# get the commit hash of the tag
closest_tag_branch_out, rc = run_command(GITS, ["branch", f"--contains=tags/{closest_tag}"], cwd=root)
logger.debug("Closest Tag branch output: %r", closest_tag_branch_out)
if len(closest_tag_branch_out.splitlines()) == 0:
logger.warning("No branch found for tag %r", closest_tag)
else:
closest_tag_branch = closest_tag_branch_out.splitlines()[0].strip("* ")
logger.debug("Closest tag branch: %r", closest_tag_branch)
pieces["closest-tag-branch"] = closest_tag_branch

# --long was added in git-1.5.5
if describe_out is None:
raise NotThisMethod("'git describe' failed")
describe_out = describe_out.strip()
full_out, rc = run_command(GITS, ["rev-parse", "HEAD"], cwd=root)
if full_out is None:
raise NotThisMethod("'git rev-parse' failed")
full_out = full_out.strip()

pieces = {}
pieces["branch"] = branch
pieces["long"] = full_out
pieces["short"] = full_out[:7] # maybe improved later
pieces["short"] = full_out[:7]
pieces["error"] = None

# parse describe_out. It will be like TAG-NUM-gHEX[-dirty] or HEX[-dirty]
# TAG might have hyphens.
git_describe = describe_out
# compute distance to latest tag (if any) or to the master branch if no tags are found
distance = 0
distance_from = closest_tag if closest_tag and closest_tag_branch in ("master", branch) else "master"
distance_out, rc = run_command(GITS, ["rev-list", "%s..HEAD" % distance_from, "--count"], cwd=root)
if rc != 0:
distance_out, rc = run_command(GITS, ["rev-list", "origin/%s..HEAD" % distance_from, "--count"], cwd=root)
if distance_out is None:
logger.warning(f"Unable to detect distance from {distance_from}")
else:
distance = int(distance_out.strip())
logger.debug(f"Computed distance from '{distance_from}': %d", distance)
pieces["distance"] = distance

# look for -dirty suffix
dirty = git_describe.endswith("-dirty")
# compute dirty
dirty = True if distance > 0 else False
pieces["dirty"] = dirty
if dirty:
git_describe = git_describe[:git_describe.rindex("-dirty")]

# now we have TAG-NUM-gHEX or HEX

if "-" in git_describe:
# TAG-NUM-gHEX
mo = re.search(r'^(.+)-(\d+)-g([0-9a-f]+)$', git_describe)
if not mo:
# unparseable. Maybe git-describe is misbehaving?
pieces["error"] = ("unable to parse git-describe output: '%s'"
% describe_out)
return pieces

# tag
full_tag = mo.group(1)
if not full_tag.startswith(tag_prefix):
if verbose:
fmt = "tag '%s' doesn't start with prefix '%s'"
print(fmt % (full_tag, tag_prefix))
pieces["error"] = ("tag '%s' doesn't start with prefix '%s'"
% (full_tag, tag_prefix))
return pieces
pieces["closest-tag"] = full_tag[len(tag_prefix):]

# distance: number of commits since tag
pieces["distance"] = int(mo.group(2))

# commit: short hex revision ID
pieces["short"] = mo.group(3)

else:
# HEX: no tags
pieces["closest-tag"] = None
count_out, rc = run_command(GITS, ["rev-list", "HEAD", "--count"],
cwd=root)
pieces["distance"] = int(count_out) # total number of commits

# commit date: see ISO-8601 comment in git_versions_from_keywords()
date = run_command(GITS, ["show", "-s", "--format=%ci", "HEAD"],
Expand Down Expand Up @@ -325,19 +331,22 @@ def render_pep440(pieces):
Exceptions:
1: no tags. git_describe was just HEX. 0+untagged.DISTANCE.gHEX[.dirty]
"""
# determine version name prefix
rendered = None
if pieces["closest-tag"]:
rendered = pieces["closest-tag"]
if pieces["distance"] or pieces["dirty"]:
rendered += plus_or_dot(pieces)
rendered += "%d.g%s" % (pieces["distance"], pieces["short"])
if pieces["dirty"]:
rendered += ".dirty"
else:
# exception #1
rendered = "0+untagged.%d.g%s" % (pieces["distance"],
pieces["short"])
if pieces["branch"] in ("master", pieces["closest-tag-branch"]):
rendered = pieces["closest-tag"]
elif pieces["branch"] not in ("master", "develop"):
rendered = f"develop.{pieces['branch'].replace('/', '-')}"
else:
rendered = pieces["branch"]

if pieces["distance"] or pieces["dirty"]:
rendered += plus_or_dot(pieces)
rendered += "%d.g%s" % (pieces["distance"], pieces["short"])
if pieces["dirty"]:
rendered += ".dirty"

return rendered


Expand Down Expand Up @@ -489,12 +498,15 @@ def get_versions():
verbose = cfg.verbose

try:
logger.debug("Trying keywords...")
return git_versions_from_keywords(get_keywords(), cfg.tag_prefix,
verbose)
except NotThisMethod:
logger.debug("Not keywords")
pass

try:
logger.debug("Trying expanded keywords...")
root = os.path.realpath(__file__)
# versionfile_source is the relative path from the top of the source
# tree (where the .git directory might live) to this file. Invert
Expand All @@ -508,7 +520,9 @@ def get_versions():
"date": None}

try:
logger.debug("Trying pieces_from_vcs...")
pieces = git_pieces_from_vcs(cfg.tag_prefix, root, verbose)
logger.debug("pieces_from_vcs returned %r", pieces)
return render(pieces, cfg.style)
except NotThisMethod:
pass
Expand Down