dump plugin: add option to create lockfile
This patch adds the --lock option to the dump plugin. When enabled, the output only contains the resolved refspecs of each repo (as valid kas format). By that, floating branches can be used in the projects kas files and these can be pinned to fixed revisions, when required. When using --lock in combination with --inplace, a lockfile named <filename>.lock.<ext> is created next to the <filename>.<ext>. In case multiple files are added to the kas CLI, the lockfile is only created for the first file (by considering the merged information from all files). Signed-off-by: Felix Moessbauer <felix.moessbauer@siemens.com> [Jan: fold in Python 3.6 support] Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com>
This commit is contained in:
parent
fe4031ce01
commit
7c0953c08e
@ -22,13 +22,17 @@
|
|||||||
"""
|
"""
|
||||||
This plugin implements the ``kas dump`` command.
|
This plugin implements the ``kas dump`` command.
|
||||||
|
|
||||||
When this command is executed, kas will parse all referenced config
|
When this command is executed in default mode, kas will parse all
|
||||||
files, expand includes and print a flattened yaml version of the
|
referenced config files, expand includes and print a flattened yaml version
|
||||||
configuration to stdout. This config is semantically identical to the
|
of the configuration to stdout. This config is semantically identical to
|
||||||
input, but does not include any references to other configuration files.
|
the input, but does not include any references to other configuration
|
||||||
The output of this command can be used to further analyse the build
|
files. The output of this command can be used to further analyse the build
|
||||||
configuration.
|
configuration.
|
||||||
|
|
||||||
|
When running with --lock, a lock-file is created which only contains the
|
||||||
|
exact refspecs of each repository. This file can be used to pin the
|
||||||
|
refspecs of floating branches, while still keeping an easy update path.
|
||||||
|
|
||||||
Please note:
|
Please note:
|
||||||
|
|
||||||
- the dumped config is semantically identical but not bit-by-bit identical
|
- the dumped config is semantically identical but not bit-by-bit identical
|
||||||
@ -43,12 +47,22 @@
|
|||||||
The generated config can be used as input for kas::
|
The generated config can be used as input for kas::
|
||||||
|
|
||||||
kas build kas-project-expanded.yml
|
kas build kas-project-expanded.yml
|
||||||
|
|
||||||
|
Example of the locking mechanism (call again to regenerate lockfile):
|
||||||
|
|
||||||
|
kas dump --lock --inplace --update kas-project.yml
|
||||||
|
|
||||||
|
The generated lockfile will automatically be used to pin the revisions:
|
||||||
|
|
||||||
|
kas build kas-project.yml
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import sys
|
import sys
|
||||||
import json
|
import json
|
||||||
import yaml
|
import yaml
|
||||||
|
from typing import TypeVar, TextIO
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
from kas.context import get_context
|
from kas.context import get_context
|
||||||
from kas.plugins.checkout import Checkout
|
from kas.plugins.checkout import Checkout
|
||||||
@ -57,6 +71,38 @@ __license__ = 'MIT'
|
|||||||
__copyright__ = 'Copyright (c) Siemens AG, 2022'
|
__copyright__ = 'Copyright (c) Siemens AG, 2022'
|
||||||
|
|
||||||
|
|
||||||
|
class IoTarget:
|
||||||
|
StrOrTextIO = TypeVar('StrOrTextIO', str, TextIO)
|
||||||
|
|
||||||
|
target: StrOrTextIO
|
||||||
|
managed: bool
|
||||||
|
|
||||||
|
def __init__(self, target, managed):
|
||||||
|
self.target = target
|
||||||
|
self.managed = managed
|
||||||
|
|
||||||
|
|
||||||
|
class IoTargetMonitor:
|
||||||
|
"""
|
||||||
|
Simple monitor to unify access to file targets that need
|
||||||
|
to be closed (files) and ambient ones (stdout / stderr)
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, target: IoTarget):
|
||||||
|
self._target = target
|
||||||
|
self._file = None
|
||||||
|
|
||||||
|
def __enter__(self):
|
||||||
|
if self._target.managed:
|
||||||
|
self._file = open(self._target.target, 'w')
|
||||||
|
return self._file
|
||||||
|
return self._target.target
|
||||||
|
|
||||||
|
def __exit__(self, exc_type, exc_value, traceback):
|
||||||
|
if self._target.managed:
|
||||||
|
self._file.close()
|
||||||
|
|
||||||
|
|
||||||
class Dump(Checkout):
|
class Dump(Checkout):
|
||||||
"""
|
"""
|
||||||
Implements a kas plugin that combines multiple kas configurations
|
Implements a kas plugin that combines multiple kas configurations
|
||||||
@ -94,6 +140,7 @@ class Dump(Checkout):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def setup_parser(cls, parser):
|
def setup_parser(cls, parser):
|
||||||
super().setup_parser(parser)
|
super().setup_parser(parser)
|
||||||
|
lk_or_env = parser.add_mutually_exclusive_group()
|
||||||
parser.add_argument('--format',
|
parser.add_argument('--format',
|
||||||
choices=['yaml', 'json'],
|
choices=['yaml', 'json'],
|
||||||
default='yaml',
|
default='yaml',
|
||||||
@ -105,9 +152,15 @@ class Dump(Checkout):
|
|||||||
parser.add_argument('--resolve-refs',
|
parser.add_argument('--resolve-refs',
|
||||||
action='store_true',
|
action='store_true',
|
||||||
help='Replace floating refs with exact SHAs')
|
help='Replace floating refs with exact SHAs')
|
||||||
parser.add_argument('--resolve-env',
|
lk_or_env.add_argument('--resolve-env',
|
||||||
|
action='store_true',
|
||||||
|
help='Set env defaults to captured env value')
|
||||||
|
lk_or_env.add_argument('--lock',
|
||||||
|
action='store_true',
|
||||||
|
help='Create lockfile with exact SHAs')
|
||||||
|
parser.add_argument('-i', '--inplace',
|
||||||
action='store_true',
|
action='store_true',
|
||||||
help='Set env defaults to captured env value')
|
help='Update lockfile in-place (reqires --lock)')
|
||||||
|
|
||||||
def run(self, args):
|
def run(self, args):
|
||||||
args.skip += [
|
args.skip += [
|
||||||
@ -119,14 +172,32 @@ class Dump(Checkout):
|
|||||||
|
|
||||||
super().run(args)
|
super().run(args)
|
||||||
ctx = get_context()
|
ctx = get_context()
|
||||||
config_expanded = ctx.config.get_config()
|
schema_v = 14 if args.lock else 7
|
||||||
|
config_expanded = {'header': {'version': schema_v}} if args.lock \
|
||||||
|
else ctx.config.get_config()
|
||||||
|
repos = ctx.config.get_repos()
|
||||||
|
output = IoTarget(target=sys.stdout, managed=False)
|
||||||
|
|
||||||
|
if args.inplace and not args.lock:
|
||||||
|
logging.error('--inplace requires --lock')
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
if args.lock:
|
||||||
|
args.resolve_refs = True
|
||||||
|
# when locking, only consider repos managed by kas
|
||||||
|
repos = [r for r in repos if not r.operations_disabled]
|
||||||
|
config_expanded['overrides'] = \
|
||||||
|
{'repos': {r.name: {'refspec': r.revision} for r in repos}}
|
||||||
|
|
||||||
|
if args.lock and args.inplace:
|
||||||
|
lockfile = ctx.config.handler.get_lockfile()
|
||||||
|
output = IoTarget(target=lockfile, managed=True)
|
||||||
|
|
||||||
# includes are already expanded, delete the key
|
# includes are already expanded, delete the key
|
||||||
if 'includes' in config_expanded['header']:
|
if 'includes' in config_expanded['header']:
|
||||||
del config_expanded['header']['includes']
|
del config_expanded['header']['includes']
|
||||||
|
|
||||||
if args.resolve_refs:
|
if args.resolve_refs and not args.lock:
|
||||||
repos = ctx.config.get_repos()
|
|
||||||
for r in repos:
|
for r in repos:
|
||||||
if r.refspec:
|
if r.refspec:
|
||||||
config_expanded['repos'][r.name]['refspec'] = r.revision
|
config_expanded['repos'][r.name]['refspec'] = r.revision
|
||||||
@ -134,17 +205,18 @@ class Dump(Checkout):
|
|||||||
if args.resolve_env and 'env' in config_expanded:
|
if args.resolve_env and 'env' in config_expanded:
|
||||||
config_expanded['env'] = ctx.config.get_environment()
|
config_expanded['env'] = ctx.config.get_environment()
|
||||||
|
|
||||||
if args.format == 'json':
|
with IoTargetMonitor(output) as f:
|
||||||
json.dump(config_expanded, sys.stdout, indent=args.indent)
|
if args.format == 'json':
|
||||||
sys.stdout.write('\n')
|
json.dump(config_expanded, f, indent=args.indent)
|
||||||
elif args.format == 'yaml':
|
sys.stdout.write('\n')
|
||||||
yaml.dump(
|
elif args.format == 'yaml':
|
||||||
config_expanded, sys.stdout,
|
yaml.dump(
|
||||||
indent=args.indent,
|
config_expanded, f,
|
||||||
Dumper=self.KasYamlDumper)
|
indent=args.indent,
|
||||||
else:
|
Dumper=self.KasYamlDumper)
|
||||||
logging.error('invalid format %s', args.format)
|
else:
|
||||||
sys.exit(1)
|
logging.error('invalid format %s', args.format)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
__KAS_PLUGINS__ = [Dump]
|
__KAS_PLUGINS__ = [Dump]
|
||||||
|
Loading…
Reference in New Issue
Block a user