From 3462964cb03f87a55381bc0a84959e836a906bdd Mon Sep 17 00:00:00 2001 From: Gilles Grandou Date: Mon, 4 Dec 2023 23:32:05 +0100 Subject: [PATCH] add build timestamp to avoid useless image builds * skip build if image timestamp is newer than config file --- runon/runon.py | 31 ++++++++++++++++++++++++++++++- setup.py | 1 + 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/runon/runon.py b/runon/runon.py index 4b229d7..9bd61e8 100755 --- a/runon/runon.py +++ b/runon/runon.py @@ -10,6 +10,8 @@ import os import re import xdg.BaseDirectory import yaml +import datetime +import pytz import subprocess from pprint import pprint @@ -33,6 +35,7 @@ def read_yaml(conf_file): try: with open(conf_file, 'r') as file: conf = yaml.safe_load(file) + conf['stamp'] = datetime.datetime.fromtimestamp(os.path.getmtime(conf_file), tz=pytz.UTC) return conf except yaml.YAMLError as e: print(f'ERROR: bad configuration file:') @@ -62,6 +65,7 @@ def load_config(conf_file, osname): print(f"ERROR: cannot find configuration for distribution {osname}") sys.exit(1) + osconf['stamp'] = conf.get('stamp') osconf['osname'] = osname for k in [ 'dockerfile', 'packages', 'environment', 'binds' ]: if osconf.get(k): @@ -83,6 +87,24 @@ def make_image_name(osname): def build_image(client, conf, update, verbose): + osname = conf.get('osname') + image_name = 'runon-{}'.format(osname) + tag = make_image_name(conf['osname']) + cache_dir = os.path.join(xdg.BaseDirectory.xdg_cache_home, 'runon') + cache_file = os.path.join(cache_dir, image_name) + + if not update and os.path.exists(cache_file): + ts_image = datetime.datetime.fromtimestamp(os.path.getmtime(cache_file), tz=pytz.UTC) + ts_conf = conf.get('stamp') + if verbose: + print('config: {}'.format(ts_conf)) + print('image: {}'.format(ts_image)) + if ts_image and ts_image > ts_conf: + if verbose: + print('image: {} up-to-date'.format(image_name)) + image = client.images.get(tag) + return image + image = conf.get('image') packages = conf['packages'] dockerfile = conf['dockerfile'] @@ -91,7 +113,6 @@ def build_image(client, conf, update, verbose): dockerfile.insert(0, 'FROM {}'.format(image)) for p in packages: dockerfile.append(pkginstall.format(p)) - tag = make_image_name(conf['osname']) try: if verbose: # fallback to external command 'docker build' as there is @@ -112,6 +133,14 @@ def build_image(client, conf, update, verbose): print('Built image {} / {}'.format(image.tags[0], image.short_id)) for l in logs: print(l.get('stream', '').strip('\n')) + + if not os.path.exists(cache_dir): + os.mkdir(cache_dir) + with open(cache_file, 'w') as file: + if verbose: + print('cache: {}'.format(cache_file)) + file.write('') + except (docker.errors.BuildError, KeyboardInterrupt, subprocess.CalledProcessError, docker.errors.ImageNotFound) as e: print('Build Error: {}'.format(e)) print() diff --git a/setup.py b/setup.py index f5264c9..0dc9b23 100644 --- a/setup.py +++ b/setup.py @@ -14,6 +14,7 @@ setup( "pathlib", "pyxdg", "pyyaml", + "pytz", ], entry_points={ "console_scripts": [