refactor: port all sys.exit over to kas exceptions
This patch replaces all direct invocations of sys.exit outside of the main invocation to KasUserError based exceptions. By that, only one method for returning is used and return codes can be handled consistently. In addition, this makes it possible to handle specific errors differently. Signed-off-by: Felix Moessbauer <felix.moessbauer@siemens.com> Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com>
This commit is contained in:
parent
a5750901c6
commit
222f07de69
@ -34,7 +34,7 @@ import logging
|
|||||||
import signal
|
import signal
|
||||||
import sys
|
import sys
|
||||||
import os
|
import os
|
||||||
from .kasusererror import KasUserError
|
from .kasusererror import KasUserError, CommandExecError
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import colorlog
|
import colorlog
|
||||||
@ -176,6 +176,9 @@ def main():
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
kas(sys.argv[1:])
|
kas(sys.argv[1:])
|
||||||
|
except CommandExecError as err:
|
||||||
|
logging.error('%s', err)
|
||||||
|
sys.exit(err.ret_code if err.forward else 2)
|
||||||
except KasUserError as err:
|
except KasUserError as err:
|
||||||
logging.error('%s', err)
|
logging.error('%s', err)
|
||||||
sys.exit(2)
|
sys.exit(2)
|
||||||
|
@ -43,3 +43,28 @@ class KasUserError(Exception):
|
|||||||
User or input error. Derive all user error exceptions from this class.
|
User or input error. Derive all user error exceptions from this class.
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class CommandExecError(KasUserError):
|
||||||
|
"""
|
||||||
|
Failure in execution of a shell command. The `forward_error_code` parameter
|
||||||
|
can be used to request the receiver of the exception to `sys.exit` with
|
||||||
|
that code instead of a generic one. Only use this in special cases, where
|
||||||
|
the return code can actually be related to a single shell command.
|
||||||
|
"""
|
||||||
|
def __init__(self, command, ret_code,
|
||||||
|
forward_ret_code=False):
|
||||||
|
self.ret_code = ret_code
|
||||||
|
self.forward = forward_ret_code
|
||||||
|
message = ["'{}'".format(c) if ' ' in c else c for c in command]
|
||||||
|
super().__init__('Command "{}" failed with error {}'
|
||||||
|
.format(' '.join(message), ret_code))
|
||||||
|
|
||||||
|
|
||||||
|
class ArgsCombinationError(KasUserError):
|
||||||
|
"""
|
||||||
|
Invalid combination of CLI arguments provided
|
||||||
|
"""
|
||||||
|
def __init__(self, message):
|
||||||
|
super().__init__('Invalid combination of arguments: {}'
|
||||||
|
.format(message))
|
||||||
|
@ -28,7 +28,6 @@ import logging
|
|||||||
import shutil
|
import shutil
|
||||||
import os
|
import os
|
||||||
import pprint
|
import pprint
|
||||||
import sys
|
|
||||||
from .libkas import (ssh_cleanup_agent, ssh_setup_agent, ssh_no_host_key_check,
|
from .libkas import (ssh_cleanup_agent, ssh_setup_agent, ssh_no_host_key_check,
|
||||||
get_build_environ, repos_fetch, repos_apply_patches)
|
get_build_environ, repos_fetch, repos_apply_patches)
|
||||||
from .includehandler import IncludeException
|
from .includehandler import IncludeException
|
||||||
@ -377,8 +376,8 @@ class SetupReposStep(Command):
|
|||||||
ctx.missing_repos = []
|
ctx.missing_repos = []
|
||||||
for repo_name in ctx.missing_repo_names:
|
for repo_name in ctx.missing_repo_names:
|
||||||
if repo_name not in ctx.config.get_repos_config():
|
if repo_name not in ctx.config.get_repos_config():
|
||||||
logging.error('Include references unknown repo: %s', repo_name)
|
raise IncludeException('Include references unknown repo: {}'
|
||||||
sys.exit(1)
|
.format(repo_name))
|
||||||
ctx.missing_repos.append(ctx.config.get_repo(repo_name))
|
ctx.missing_repos.append(ctx.config.get_repo(repo_name))
|
||||||
|
|
||||||
repos_fetch(ctx.missing_repos)
|
repos_fetch(ctx.missing_repos)
|
||||||
|
@ -34,11 +34,36 @@ import pathlib
|
|||||||
import signal
|
import signal
|
||||||
from subprocess import Popen, PIPE
|
from subprocess import Popen, PIPE
|
||||||
from .context import get_context
|
from .context import get_context
|
||||||
|
from .kasusererror import KasUserError, CommandExecError
|
||||||
|
|
||||||
__license__ = 'MIT'
|
__license__ = 'MIT'
|
||||||
__copyright__ = 'Copyright (c) Siemens AG, 2017-2018'
|
__copyright__ = 'Copyright (c) Siemens AG, 2017-2018'
|
||||||
|
|
||||||
|
|
||||||
|
class InitBuildEnvError(KasUserError):
|
||||||
|
"""
|
||||||
|
Error related to the OE / ISAR environment setup scripts
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class EnvNotValidError(KasUserError):
|
||||||
|
"""
|
||||||
|
The caller environment is not suited for the requested operation
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class TaskExecError(KasUserError):
|
||||||
|
"""
|
||||||
|
Similar to :class:`kas.kasusererror.CommandExecError` but for kas
|
||||||
|
internal tasks
|
||||||
|
"""
|
||||||
|
def __init__(self, command, ret_code):
|
||||||
|
self.ret_code = ret_code
|
||||||
|
super().__init__('{} failed: error code {}'.format(command, ret_code))
|
||||||
|
|
||||||
|
|
||||||
class LogOutput:
|
class LogOutput:
|
||||||
"""
|
"""
|
||||||
Handles the log output of executed applications
|
Handles the log output of executed applications
|
||||||
@ -144,7 +169,7 @@ def run_cmd(cmd, cwd, env=None, fail=True, liveupdate=True):
|
|||||||
(ret, output) = loop.run_until_complete(
|
(ret, output) = loop.run_until_complete(
|
||||||
run_cmd_async(cmd, cwd, env, fail, liveupdate))
|
run_cmd_async(cmd, cwd, env, fail, liveupdate))
|
||||||
if ret and fail:
|
if ret and fail:
|
||||||
sys.exit(ret)
|
raise CommandExecError(cmd, ret)
|
||||||
return (ret, output)
|
return (ret, output)
|
||||||
|
|
||||||
|
|
||||||
@ -175,7 +200,7 @@ def repos_fetch(repos):
|
|||||||
|
|
||||||
for task in tasks:
|
for task in tasks:
|
||||||
if task.result():
|
if task.result():
|
||||||
sys.exit(task.result())
|
raise TaskExecError('fetch repos', task.result())
|
||||||
|
|
||||||
|
|
||||||
def repos_apply_patches(repos):
|
def repos_apply_patches(repos):
|
||||||
@ -194,7 +219,7 @@ def repos_apply_patches(repos):
|
|||||||
|
|
||||||
for task in tasks:
|
for task in tasks:
|
||||||
if task.result():
|
if task.result():
|
||||||
sys.exit(task.result())
|
raise TaskExecError('apply patches', task.result())
|
||||||
|
|
||||||
|
|
||||||
def get_build_environ(build_system):
|
def get_build_environ(build_system):
|
||||||
@ -217,15 +242,15 @@ def get_build_environ(build_system):
|
|||||||
for (repo, script) in permutations:
|
for (repo, script) in permutations:
|
||||||
if os.path.exists(repo.path + '/' + script):
|
if os.path.exists(repo.path + '/' + script):
|
||||||
if init_repo:
|
if init_repo:
|
||||||
logging.error('Multiple init scripts found (%s vs. %s). ',
|
raise InitBuildEnvError(
|
||||||
repo.name, init_repo.name)
|
'Multiple init scripts found ({} vs. {}). '
|
||||||
logging.error('Resolve ambiguity by removing one of the repos')
|
'Resolve ambiguity by removing one of the repos'
|
||||||
sys.exit(1)
|
.format(repo.name, init_repo.name))
|
||||||
|
|
||||||
init_repo = repo
|
init_repo = repo
|
||||||
init_script = script
|
init_script = script
|
||||||
if not init_repo:
|
if not init_repo:
|
||||||
logging.error('Did not find any init-build-env script')
|
raise InitBuildEnvError('Did not find any init-build-env script')
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
with tempfile.TemporaryDirectory() as temp_dir:
|
with tempfile.TemporaryDirectory() as temp_dir:
|
||||||
script = """#!/bin/bash
|
script = """#!/bin/bash
|
||||||
@ -412,9 +437,8 @@ def run_handle_preserve_env_arg(ctx, os, args, SetupHome):
|
|||||||
var)
|
var)
|
||||||
|
|
||||||
if not os.isatty(sys.stdout.fileno()):
|
if not os.isatty(sys.stdout.fileno()):
|
||||||
logging.error("Error: --preserve-env can only be "
|
raise EnvNotValidError(
|
||||||
"run from a tty")
|
'--preserve-env can only be run from a tty')
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
ctx.environ = os.environ.copy()
|
ctx.environ = os.environ.copy()
|
||||||
|
|
||||||
|
@ -40,6 +40,7 @@ from kas.config import Config
|
|||||||
from kas.libkas import find_program, run_cmd
|
from kas.libkas import find_program, run_cmd
|
||||||
from kas.libcmds import Macro, Command
|
from kas.libcmds import Macro, Command
|
||||||
from kas.libkas import setup_parser_common_args
|
from kas.libkas import setup_parser_common_args
|
||||||
|
from kas.kasusererror import CommandExecError
|
||||||
|
|
||||||
__license__ = 'MIT'
|
__license__ = 'MIT'
|
||||||
__copyright__ = 'Copyright (c) Siemens AG, 2017-2018'
|
__copyright__ = 'Copyright (c) Siemens AG, 2017-2018'
|
||||||
@ -114,8 +115,7 @@ class BuildCommand(Command):
|
|||||||
logging.info('%s$ %s', ctx.build_dir, ' '.join(cmd))
|
logging.info('%s$ %s', ctx.build_dir, ' '.join(cmd))
|
||||||
ret = subprocess.call(cmd, env=ctx.environ, cwd=ctx.build_dir)
|
ret = subprocess.call(cmd, env=ctx.environ, cwd=ctx.build_dir)
|
||||||
if ret != 0:
|
if ret != 0:
|
||||||
logging.error('Command returned non-zero exit status %d', ret)
|
raise CommandExecError(cmd, ret)
|
||||||
sys.exit(ret)
|
|
||||||
else:
|
else:
|
||||||
run_cmd(cmd, cwd=ctx.build_dir)
|
run_cmd(cmd, cwd=ctx.build_dir)
|
||||||
|
|
||||||
|
@ -63,7 +63,6 @@
|
|||||||
Note, that the lockfiles should be checked-in into the VCS.
|
Note, that the lockfiles should be checked-in into the VCS.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import logging
|
|
||||||
import sys
|
import sys
|
||||||
import json
|
import json
|
||||||
import yaml
|
import yaml
|
||||||
@ -71,11 +70,17 @@ 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
|
||||||
|
from kas.kasusererror import KasUserError, ArgsCombinationError
|
||||||
|
|
||||||
__license__ = 'MIT'
|
__license__ = 'MIT'
|
||||||
__copyright__ = 'Copyright (c) Siemens AG, 2022'
|
__copyright__ = 'Copyright (c) Siemens AG, 2022'
|
||||||
|
|
||||||
|
|
||||||
|
class OutputFormatError(KasUserError):
|
||||||
|
def __init__(self, format):
|
||||||
|
super().__init__('invalid format {}'.format(format))
|
||||||
|
|
||||||
|
|
||||||
class IoTarget:
|
class IoTarget:
|
||||||
StrOrTextIO = TypeVar('StrOrTextIO', str, TextIO)
|
StrOrTextIO = TypeVar('StrOrTextIO', str, TextIO)
|
||||||
|
|
||||||
@ -184,8 +189,7 @@ class Dump(Checkout):
|
|||||||
output = IoTarget(target=sys.stdout, managed=False)
|
output = IoTarget(target=sys.stdout, managed=False)
|
||||||
|
|
||||||
if args.inplace and not args.lock:
|
if args.inplace and not args.lock:
|
||||||
logging.error('--inplace requires --lock')
|
raise ArgsCombinationError('--inplace requires --lock')
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
if args.lock:
|
if args.lock:
|
||||||
args.resolve_refs = True
|
args.resolve_refs = True
|
||||||
@ -220,8 +224,7 @@ class Dump(Checkout):
|
|||||||
indent=args.indent,
|
indent=args.indent,
|
||||||
Dumper=self.KasYamlDumper)
|
Dumper=self.KasYamlDumper)
|
||||||
else:
|
else:
|
||||||
logging.error('invalid format %s', args.format)
|
raise OutputFormatError(args.format)
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
|
|
||||||
__KAS_PLUGINS__ = [Dump]
|
__KAS_PLUGINS__ = [Dump]
|
||||||
|
@ -56,13 +56,13 @@
|
|||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
|
||||||
from kas.context import create_global_context
|
from kas.context import create_global_context
|
||||||
from kas.config import Config
|
from kas.config import Config
|
||||||
from kas.libcmds import Macro, Command, SetupHome
|
from kas.libcmds import Macro, Command, SetupHome
|
||||||
from kas.libkas import setup_parser_common_args
|
from kas.libkas import setup_parser_common_args
|
||||||
from kas.libkas import setup_parser_preserve_env_arg
|
from kas.libkas import setup_parser_preserve_env_arg
|
||||||
from kas.libkas import run_handle_preserve_env_arg
|
from kas.libkas import run_handle_preserve_env_arg
|
||||||
|
from kas.kasusererror import CommandExecError
|
||||||
|
|
||||||
__license__ = 'MIT'
|
__license__ = 'MIT'
|
||||||
__copyright__ = 'Copyright (c) Siemens AG, 2017-2018'
|
__copyright__ = 'Copyright (c) Siemens AG, 2017-2018'
|
||||||
@ -113,8 +113,7 @@ class ForAllReposCommand(Command):
|
|||||||
retcode = subprocess.call(self.command, shell=True, cwd=repo.path,
|
retcode = subprocess.call(self.command, shell=True, cwd=repo.path,
|
||||||
env=env)
|
env=env)
|
||||||
if retcode != 0:
|
if retcode != 0:
|
||||||
logging.error('Command failed with return code %d', retcode)
|
raise CommandExecError(self.command, retcode)
|
||||||
sys.exit(retcode)
|
|
||||||
|
|
||||||
|
|
||||||
__KAS_PLUGINS__ = [ForAllRepos]
|
__KAS_PLUGINS__ = [ForAllRepos]
|
||||||
|
@ -69,7 +69,6 @@
|
|||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import pprint
|
import pprint
|
||||||
import sys
|
|
||||||
import yaml
|
import yaml
|
||||||
from kconfiglib import Kconfig, Symbol, Choice, expr_value, TYPE_TO_STR, \
|
from kconfiglib import Kconfig, Symbol, Choice, expr_value, TYPE_TO_STR, \
|
||||||
MENU, COMMENT, STRING, BOOL, INT, HEX, UNKNOWN
|
MENU, COMMENT, STRING, BOOL, INT, HEX, UNKNOWN
|
||||||
@ -78,6 +77,7 @@ from kas.context import create_global_context
|
|||||||
from kas.config import CONFIG_YAML_FILE
|
from kas.config import CONFIG_YAML_FILE
|
||||||
from kas.includehandler import load_config as load_config_yaml
|
from kas.includehandler import load_config as load_config_yaml
|
||||||
from kas.plugins.build import Build
|
from kas.plugins.build import Build
|
||||||
|
from kas.kasusererror import KasUserError
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from snack import SnackScreen, EntryWindow, ButtonChoiceWindow, \
|
from snack import SnackScreen, EntryWindow, ButtonChoiceWindow, \
|
||||||
@ -92,10 +92,18 @@ __copyright__ = \
|
|||||||
'Copyright (c) Siemens AG, 2021'
|
'Copyright (c) Siemens AG, 2021'
|
||||||
|
|
||||||
|
|
||||||
|
class VariableTypeError(KasUserError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class MissingModuleError(KasUserError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
def check_sym_is_string(sym):
|
def check_sym_is_string(sym):
|
||||||
if sym.type != STRING:
|
if sym.type != STRING:
|
||||||
logging.error('Variable %s must be of string type', sym.name)
|
raise VariableTypeError('Variable {} must be of string type'
|
||||||
sys.exit(1)
|
.format(sym.name))
|
||||||
|
|
||||||
|
|
||||||
def str_representer(dumper, data):
|
def str_representer(dumper, data):
|
||||||
@ -175,10 +183,9 @@ class Menu:
|
|||||||
elif sym.type == HEX:
|
elif sym.type == HEX:
|
||||||
menu_configuration[symname] = int(symvalue, 16)
|
menu_configuration[symname] = int(symvalue, 16)
|
||||||
else:
|
else:
|
||||||
logging.error(
|
raise VariableTypeError(
|
||||||
'Configuration variable %s uses unsupported type',
|
'Configuration variable {} uses unsupported type'
|
||||||
symname)
|
.format(symname))
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
if symname.startswith('KAS_INCLUDE_'):
|
if symname.startswith('KAS_INCLUDE_'):
|
||||||
check_sym_is_string(sym)
|
check_sym_is_string(sym)
|
||||||
@ -241,9 +248,8 @@ class Menu:
|
|||||||
|
|
||||||
def run(self, args):
|
def run(self, args):
|
||||||
if not newt_available:
|
if not newt_available:
|
||||||
logging.error(
|
raise MissingModuleError(
|
||||||
'Menu plugin requires \'python3-newt\' distribution package.')
|
'Menu plugin requires \'python3-newt\' distribution package.')
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
ctx = create_global_context(args)
|
ctx = create_global_context(args)
|
||||||
|
|
||||||
|
@ -40,13 +40,13 @@
|
|||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
|
||||||
from kas.context import create_global_context
|
from kas.context import create_global_context
|
||||||
from kas.config import Config
|
from kas.config import Config
|
||||||
from kas.libcmds import Macro, Command, SetupHome
|
from kas.libcmds import Macro, Command, SetupHome
|
||||||
from kas.libkas import setup_parser_common_args
|
from kas.libkas import setup_parser_common_args
|
||||||
from kas.libkas import setup_parser_preserve_env_arg
|
from kas.libkas import setup_parser_preserve_env_arg
|
||||||
from kas.libkas import run_handle_preserve_env_arg
|
from kas.libkas import run_handle_preserve_env_arg
|
||||||
|
from kas.kasusererror import CommandExecError
|
||||||
|
|
||||||
__license__ = 'MIT'
|
__license__ = 'MIT'
|
||||||
__copyright__ = 'Copyright (c) Siemens AG, 2017-2018'
|
__copyright__ = 'Copyright (c) Siemens AG, 2017-2018'
|
||||||
@ -124,8 +124,8 @@ class ShellCommand(Command):
|
|||||||
cmd.append(self.cmd)
|
cmd.append(self.cmd)
|
||||||
ret = subprocess.call(cmd, env=ctx.environ, cwd=ctx.build_dir)
|
ret = subprocess.call(cmd, env=ctx.environ, cwd=ctx.build_dir)
|
||||||
if ret != 0:
|
if ret != 0:
|
||||||
logging.error('Shell returned non-zero exit status %d', ret)
|
logging.error('Shell returned non-zero exit status')
|
||||||
sys.exit(ret)
|
raise CommandExecError(cmd, ret, True)
|
||||||
|
|
||||||
|
|
||||||
__KAS_PLUGINS__ = [Shell]
|
__KAS_PLUGINS__ = [Shell]
|
||||||
|
30
kas/repos.py
30
kas/repos.py
@ -39,7 +39,14 @@ __copyright__ = 'Copyright (c) Siemens AG, 2017-2018'
|
|||||||
|
|
||||||
class UnsupportedRepoTypeError(KasUserError, NotImplementedError):
|
class UnsupportedRepoTypeError(KasUserError, NotImplementedError):
|
||||||
"""
|
"""
|
||||||
Exception for unsupported / not implemented repository types
|
The requested repo type is unsupported / not implemented
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class RepoRefError(KasUserError):
|
||||||
|
"""
|
||||||
|
The requested repo reference is invalid, missing or could not be found
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@ -51,6 +58,13 @@ class PatchFileNotFound(KasUserError, FileNotFoundError):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class PatchMappingError(KasUserError):
|
||||||
|
"""
|
||||||
|
The requested patch can not be related to a repo
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class Repo:
|
class Repo:
|
||||||
"""
|
"""
|
||||||
Represents a repository in the kas configuration.
|
Represents a repository in the kas configuration.
|
||||||
@ -127,9 +141,10 @@ class Repo:
|
|||||||
'path': patches_dict[p]['path'],
|
'path': patches_dict[p]['path'],
|
||||||
}
|
}
|
||||||
if this_patch['repo'] is None:
|
if this_patch['repo'] is None:
|
||||||
logging.error('No repo specified for patch entry "%s" and no '
|
raise PatchMappingError(
|
||||||
'default repo specified.', p)
|
'No repo specified for patch entry "{}" and no '
|
||||||
sys.exit(1)
|
'default repo specified.'.format(p))
|
||||||
|
|
||||||
patches.append(this_patch)
|
patches.append(this_patch)
|
||||||
|
|
||||||
url = repo_config.get('url', None)
|
url = repo_config.get('url', None)
|
||||||
@ -138,9 +153,10 @@ class Repo:
|
|||||||
refspec = repo_overrides.get('refspec', repo_config.get('refspec',
|
refspec = repo_overrides.get('refspec', repo_config.get('refspec',
|
||||||
repo_defaults.get('refspec', None)))
|
repo_defaults.get('refspec', None)))
|
||||||
if refspec is None and url is not None:
|
if refspec is None and url is not None:
|
||||||
logging.error('No refspec specified for repository "%s". This is '
|
raise RepoRefError('No refspec specified for repository "{}". '
|
||||||
'only allowed for local repositories.', name)
|
'This is only allowed for local repositories.'
|
||||||
sys.exit(1)
|
.format(name))
|
||||||
|
|
||||||
path = repo_config.get('path', None)
|
path = repo_config.get('path', None)
|
||||||
disable_operations = False
|
disable_operations = False
|
||||||
|
|
||||||
|
@ -25,6 +25,7 @@ import pytest
|
|||||||
import shutil
|
import shutil
|
||||||
from kas import kas
|
from kas import kas
|
||||||
from kas.libkas import run_cmd
|
from kas.libkas import run_cmd
|
||||||
|
from kas.repos import RepoRefError
|
||||||
|
|
||||||
|
|
||||||
def test_refspec_switch(changedir, tmpdir):
|
def test_refspec_switch(changedir, tmpdir):
|
||||||
@ -95,5 +96,5 @@ def test_url_no_refspec(changedir, tmpdir):
|
|||||||
tdir = str(tmpdir / 'test_url_no_refspec')
|
tdir = str(tmpdir / 'test_url_no_refspec')
|
||||||
shutil.copytree('tests/test_refspec', tdir)
|
shutil.copytree('tests/test_refspec', tdir)
|
||||||
os.chdir(tdir)
|
os.chdir(tdir)
|
||||||
with pytest.raises(SystemExit):
|
with pytest.raises(RepoRefError):
|
||||||
kas.kas(['shell', 'test4.yml', '-c', 'true'])
|
kas.kas(['shell', 'test4.yml', '-c', 'true'])
|
||||||
|
Loading…
Reference in New Issue
Block a user