From 93af309b90bab7ab4e6952900ee8eecdc988eeb3 Mon Sep 17 00:00:00 2001 From: Lubomír Sedlář Date: May 17 2023 12:09:12 +0000 Subject: [PATCH 1/2] Rework compose-update-latest-symlinks Handle multiple links in the same directory. --- diff --git a/bin/compose-update-latest-symlinks b/bin/compose-update-latest-symlinks index 7ba80af..5cf3c19 100755 --- a/bin/compose-update-latest-symlinks +++ b/bin/compose-update-latest-symlinks @@ -14,11 +14,16 @@ from compose_utils import symlink def main(args): parser = argparse.ArgumentParser() + parser.add_argument("PATH", help="Path to the generated composes") parser.add_argument( - "-p", "--path", help="Path to the generated composes", required=True + "-n", "--dry-run", action="store_true", help="Do not make any changes" ) args = parser.parse_args() - symlink.update_symlinks(args.path) + try: + symlink.update_symlinks(args.PATH, dry_run=args.dry_run) + except RuntimeError as exc: + print(str(exc), file=sys.stderr) + sys.exit(1) if __name__ == "__main__": diff --git a/compose_utils/symlink.py b/compose_utils/symlink.py index 33f2453..4f92c1b 100644 --- a/compose_utils/symlink.py +++ b/compose_utils/symlink.py @@ -6,7 +6,7 @@ The `minor_version` argument was deprecated in 0.1.27 and should be removed in 0 import os import warnings -import sys +from itertools import groupby import re import productmd @@ -97,30 +97,41 @@ def _deprecated_arg(arg, replace): ) -def update_symlinks(compose_path): +def update_symlinks(compose_path, dry_run=False): """Update the symlinks of the minor and major version of the compose""" - # Sorting filter for the symlink versions def natsort(s): return [int(t) if t.isdigit() else t.lower() for t in re.split(r'(\d+)', s)] - links = sorted(filter(lambda x: os.path.islink(os.path.join(compose_path, x)), os.listdir(compose_path)), key=natsort) + + def ver_len(s): + return s.count(".") + # Find all symlinks in the directory. + all_symlinks = filter( + lambda x: os.path.islink(os.path.join(compose_path, x)), os.listdir(compose_path) + ) + # Filter the ones with most version components. + links = sorted(all_symlinks, key=ver_len, reverse=True) if not links: - warnings.warn("No full symlink of a compose found in the directory %s" % compose_path) - sys.exit(1) - latest_link = links[-1] - full_link = os.path.join(compose_path, latest_link) - tmp_link = full_link - for i in range(latest_link.count(".")): - version = tmp_link[:tmp_link.rfind(".")] + raise RuntimeError( + "No latest symlink found in the directory %r" % compose_path, + ) + + to_create = {} + + for latest_link in list(next(groupby(links, key=ver_len))[1]): + for i in range(latest_link.count(".")): + version = latest_link[:latest_link.rfind(".")] + if version in to_create: + if natsort(latest_link) > natsort(to_create[version]): + to_create[version] = latest_link + else: + to_create[version] = latest_link + latest_link = latest_link[:latest_link.rfind(".")] + + for link, dest in sorted(to_create.items(), reverse=True): # Use the temp file in order to overwrite the existing symlinks temp = os.path.join(compose_path, ".link.tmp") # Create the symlink for the version - os.symlink( - os.path.relpath( - full_link, - compose_path - ), - temp - ) - os.rename(temp, version) - print("DONE: Create the symlink: %s" % version) - tmp_link = tmp_link[:tmp_link.rfind(".")] + print("%s -> %s" % (os.path.join(compose_path, link), dest)) + if not dry_run: + os.symlink(dest, temp) + os.rename(temp, os.path.join(compose_path, link)) diff --git a/doc/compose-update-latest-symlinks.1 b/doc/compose-update-latest-symlinks.1 index c3575b3..6d207a0 100644 --- a/doc/compose-update-latest-symlinks.1 +++ b/doc/compose-update-latest-symlinks.1 @@ -7,16 +7,26 @@ compose-update-latest-symlinks \- Create the symbolic links of minor and major v \fICOMPOSE_PATH\fR .SH DESCRIPTION .B compose-update-latest-symlinks -creates symbolic links of minor and major version pointing to the latest generated compose. - -It requires the compose path as an argument variable, which shows the path of generated composes. +creates and updates symbolic links of minor and major version pointing to the +latest generated compose. +.P +The required path should point to a directory where symlinks should be updated. +.P +The tool finds all symlinks of form \fIlatest--X.Y.Z\fR with the longest +version. It than creates shorter symlinks with fewer version components. +.P +If the directory contains \fIlatest-X-1.0.0\fR, \fIlatest-X-1.0.1\fR and +\fIlatest-X-1.1.0\fR, it will create \fIlatest-X-1.0 -> latest-X-1.0.1\fR, +\fIlatest-X-1.1 -> latest-X-1.1.0\fR and \fIlatest-X-1 -> latest-X-1.1\fR. +.P +The is an expectation that only one product will be present in the directory. .SH OPTIONS .TP .BR \-h ", " \-\-help Print help and exit. .TP -.BR \-p ", " \-\-path -The path of generated composes. +.BR \-n ", " \-\-dry-run +Only print what symlinks would be created, do not actually perform any actions. .SH EXIT CODE This tool exits with status code of \fB0\fR on success and non-zero value on failure. diff --git a/tests/test_symlink.py b/tests/test_symlink.py index b3227d5..9ae40f5 100644 --- a/tests/test_symlink.py +++ b/tests/test_symlink.py @@ -152,7 +152,7 @@ class TestUpdateSymlinks(unittest.TestCase): symlink.update_symlinks(self.compose_dir) - self.assert_symlink('latest-DP-1', 'latest-DP-1.0.0') + self.assert_symlink('latest-DP-1', 'latest-DP-1.0') self.assert_symlink('latest-DP-1.0', 'latest-DP-1.0.0') def test_scenario(self): @@ -160,7 +160,7 @@ class TestUpdateSymlinks(unittest.TestCase): self.create_full_symlink('latest-DP-1.%d.0' % y) symlink.update_symlinks(self.compose_dir) - self.assert_symlink('latest-DP-1', 'latest-DP-1.3.0') + self.assert_symlink('latest-DP-1', 'latest-DP-1.3') self.assert_symlink('latest-DP-1.1', 'latest-DP-1.1.0') self.assert_symlink('latest-DP-1.2', 'latest-DP-1.2.0') self.assert_symlink('latest-DP-1.3', 'latest-DP-1.3.0') From fac76ba176cecab5cec3fca379f48e863e00273b Mon Sep 17 00:00:00 2001 From: Lubomír Sedlář Date: May 17 2023 12:17:30 +0000 Subject: [PATCH 2/2] Update tox configuration --- diff --git a/tox.ini b/tox.ini index 00f86a1..febbfba 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist=py27,py35,py36,py37,py38,py39,coverage +envlist=py27,py36,py38,py39,py310,py311,coverage skip_missing_interpreters = true minversion=3.12.0