From f6b370ca42e940fa0340b2876f1dfffd3ab7fc1a Mon Sep 17 00:00:00 2001 From: Lubomír Sedlář Date: May 29 2017 14:12:45 +0000 Subject: Add utility for creating latest symlinks Signed-off-by: Lubomír Sedlář --- diff --git a/README.rst b/README.rst index 4ff5d12..a0ec583 100644 --- a/README.rst +++ b/README.rst @@ -28,6 +28,9 @@ Contents **compose-create-next-dir** create compose dir for next compose +**compose-latest-symlink** + create a symbolic link with a well-known name to the given compose + Related tools ------------- diff --git a/bin/compose-latest-symlink b/bin/compose-latest-symlink new file mode 100755 index 0000000..9c03fab --- /dev/null +++ b/bin/compose-latest-symlink @@ -0,0 +1,24 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +import argparse +import os +import sys + +here = sys.path[0] +if here != '/usr/bin': + sys.path.insert(0, os.path.dirname(here)) + +from compose_utils import symlink + + +def main(args): + parser = argparse.ArgumentParser() + parser.add_argument('COMPOSE') + parser.add_argument('--minor-version', action='store_true') + + args = parser.parse_args() + symlink.create_latest_link(args.COMPOSE, args.minor_version) + +if __name__ == '__main__': + main(sys.argv[1:]) diff --git a/compose_utils/symlink.py b/compose_utils/symlink.py new file mode 100644 index 0000000..3d17ba0 --- /dev/null +++ b/compose_utils/symlink.py @@ -0,0 +1,43 @@ +# -*- coding: utf-8 -*- + +import errno +import os +import productmd + + +class ComposeSymlink(object): + def __init__(self, compose_path): + self.compose = productmd.compose.Compose(compose_path) + # The path points to the compose/ subdirectory. + self.path = os.path.dirname(self.compose.compose_path) + + def _link(self, name): + """Create a symbolic link with given name at the same location as the + compose. Existing file at the given path will be deleted. + """ + symlink = os.path.join(self.path, '..', name) + try: + os.unlink(symlink) + except OSError as exc: + if exc.errno != errno.ENOENT: + raise + + os.symlink(os.path.basename(self.path), symlink) + + def get_latest_link_name(self, minor_version=False): + """Get name of latest- symlink for current compose.""" + fmt = 'latest-{0.release.short}-{0.release.major_version}' + if minor_version: + fmt += '.{0.release.minor_version}' + if self.compose.info.release.is_layered: + fmt += '-{0.base_product.short}-{0.base_product.version}' + return fmt.format(self.compose.info) + + def create_latest_link(self, minor_version=False): + """Create a latest- symlink with optional minor version.""" + self._link(self.get_latest_link_name(minor_version=minor_version)) + + +def create_latest_link(compose_path, minor_version=False): + """Convenience function for easy creation of latest symlink.""" + ComposeSymlink(compose_path).create_latest_link(minor_version) diff --git a/doc/compose-latest-symlink.1 b/doc/compose-latest-symlink.1 new file mode 100644 index 0000000..e90db94 --- /dev/null +++ b/doc/compose-latest-symlink.1 @@ -0,0 +1,25 @@ +.TH compose-latest-symlink 1 +.SH NAME +compose-latest-symlink \- create a symlink with a well-known name +.SH SYNOPSIS +.B compose-latest-symlink +[\fIOPTIONS\fR...] +\fICOMPOSE_PATH\fR +.SH DESCRIPTION +.B compose-latest-symlink +creates a symbolic link pointing to the given compose. The link has a fixed +name and allows to put updated composes into the same location. +.SH OPTIONS +.TP +.BR \-h ", " \-\-help +Print help and exit. +.TP +.BR \-\-minor-version +Include minor version in the symlink name. +.SH EXIT CODE +On success, this tools exits with status code of \fB0\fR. +On failure, the exit code will be \fB1\fR. +.SH BUGS +Please report bugs at +.br +https://pagure.io/compose-utils/issues diff --git a/setup.py b/setup.py index aed1ac5..568f58e 100644 --- a/setup.py +++ b/setup.py @@ -17,6 +17,7 @@ setup( scripts=[ 'bin/compose-changelog', 'bin/compose-create-legacy-composeinfo', + 'bin/compose-latest-symlink', 'bin/compose-list', 'bin/compose-partial-copy', 'bin/compose-report-package-moves', diff --git a/tests/test_symlink.py b/tests/test_symlink.py new file mode 100644 index 0000000..01215dd --- /dev/null +++ b/tests/test_symlink.py @@ -0,0 +1,66 @@ +# -*- coding: utf-8 -*- + +import os +import shutil +import tempfile +import unittest + +from compose_utils import symlink + +from . import helpers + + +class TestLatestSymlink(unittest.TestCase): + def setUp(self): + self.compose_dir = tempfile.mkdtemp() + self.compose = helpers.get_compose('DP-1.0-20160315.t.1') + self.compose_path = os.path.join(self.compose_dir, 'DP-1.0-20160315.t.1') + + def tearDown(self): + shutil.rmtree(self.compose_dir) + + def _write_compose(self, is_layered=False): + if is_layered: + self.compose.info.release.is_layered = True + self.compose.info.base_product.name = 'Base Product' + self.compose.info.base_product.type = 'ga' + self.compose.info.base_product.short = 'BP' + self.compose.info.base_product.version = '15' + + path = os.path.join(self.compose_dir, self.compose.info.compose.id) + metadata_dir = os.path.join(path, 'compose', 'metadata') + os.makedirs(metadata_dir) + self.compose.info.dump(os.path.join(metadata_dir, 'composeinfo.json')) + return path + + def test_simple(self): + path = self._write_compose() + symlink.create_latest_link(path) + self.assertEqual(os.readlink(os.path.join(self.compose_dir, 'latest-DP-1')), + 'DP-1.0-20160315.t.1') + + def test_layered(self): + path = self._write_compose(is_layered=True) + symlink.create_latest_link(path) + self.assertEqual(os.readlink(os.path.join(self.compose_dir, 'latest-DP-1-BP-15')), + 'DP-1.0-20160315.t.1') + + def test_with_minor_version(self): + path = self._write_compose() + symlink.create_latest_link(path, minor_version=True) + self.assertEqual(os.readlink(os.path.join(self.compose_dir, 'latest-DP-1.0')), + 'DP-1.0-20160315.t.1') + + def test_layered_with_minor_version(self): + path = self._write_compose(is_layered=True) + symlink.create_latest_link(path, minor_version=True) + self.assertEqual(os.readlink(os.path.join(self.compose_dir, 'latest-DP-1.0-BP-15')), + 'DP-1.0-20160315.t.1') + + def test_remove_existing(self): + os.symlink('foo', os.path.join(self.compose_dir, 'latest-DP-1')) + + path = self._write_compose() + symlink.create_latest_link(path) + self.assertEqual(os.readlink(os.path.join(self.compose_dir, 'latest-DP-1')), + 'DP-1.0-20160315.t.1')