Modify how the stages and steps work

This commit is contained in:
Raoul Snyman 2021-06-04 23:46:17 -07:00
parent cf98ed57be
commit 9e1a48d611
Signed by: raoul
GPG Key ID: F55BCED79626AE9C
3 changed files with 103 additions and 15 deletions

View File

@ -20,28 +20,26 @@ Install bou with pip:
Running bou
-----------
To run bou, simply call it:
To run bou, simply run the command. The build file will be automatically detected.
.. code-block::
$ bou
To specify a build configuration file, use the ``-f`` option:
To specify a build configuration file, use the ``-f`` option.
.. code-block::
$ bou -f /path/to/build.yaml
To specify a stage or a step to run, just add it to the command
To specify a stage or a step to run, just add it to the command. Stages take priority over steps, so if you have a
stage and a step with the same name, the stage will be run.
.. code-block::
$ bou build
$ bou test
.. note::
By default, all stages are run, and stages are run in the order in the build configuration file.
Task Configuration
------------------
@ -90,6 +88,16 @@ variables are set.
- $PYTHON $SOURCE/setup.py build
Stages and Steps
----------------
If no steps or stages are specified, by default bou will attempt to run the following, in order:
1. All of the stages in the ``stages`` section of the task configuration
2. If no stages are specified in the task config, all of the stages discovered in the steps
3. If no stages are found, all of the steps
Source Code
-----------

94
bou.py
View File

@ -13,6 +13,11 @@ ENV_VAR = re.compile(r'(\$([A-Za-z0-9_]+))')
def parse_args():
"""
Parse command line arguments, and return an object with all the arguments
:return: A namespace object with all the supplied arguments
"""
parser = ArgumentParser()
parser.add_argument('-f', '--file', dest='build_file', help='Path to the build file')
parser.add_argument('stage_or_step', nargs='?', default=None, help='Run a particular stage or step')
@ -20,7 +25,14 @@ def parse_args():
def setup_env(env, base_path):
"""Set up the environment dictionary, resolving shell variables"""
"""
Set up the environment dictionary, resolving shell variables
:param env: The environment list or dictionary
:param base_path: The base path that the build runs in
:return: A merged dictionary of environment variables
"""
if isinstance(env, list):
env = {pair.split('=')[0]: pair.split('=')[1] for pair in env}
env = dict(BASE_DIR=str(base_path), **env)
@ -33,13 +45,28 @@ def setup_env(env, base_path):
def setup_step(config, step_name):
"""Prepare a step for usage"""
"""
Prepare a step for usage
:param config: The build configuration
:param step_name: The name of the step to prepare
:return: A step object
"""
step = config['steps'][step_name]
step['environment'] = config.get('environment', []) + step.get('environment', [])
return step
def get_steps_for_stage(config, stage_name):
"""
Get all the steps for a particular stage
:param config: The build configuration
:param stage_name: The name of the stage
:return: A list of step objects
"""
steps = []
for step_name in config['steps'].keys():
if config['steps'][step_name]['stage'] == stage_name:
@ -48,6 +75,14 @@ def get_steps_for_stage(config, stage_name):
def run_step(step, base_path):
"""
Run a particular step
:param step: The step (as a dictionary)
:param base_path: The base path to run this in
:return: Return True if the step passed, or False if the step failed
"""
script = step['script']
if isinstance(script, list):
script = os.linesep.join(script)
@ -60,14 +95,49 @@ def run_step(step, base_path):
def run_stage(config, stage_name, base_path):
"""
Run all the steps in a particular stage
:param config: The build configuration
:param stage_name: The stage to run
:param base_path: The base path of the build
"""
for step in get_steps_for_stage(config, stage_name):
result = run_step(step, base_path)
if not result:
break
def get_all_stages(config):
"""
Return all the stages available in the build configuration
:param config: The build configuration
:return: A list of stages
"""
stages = config.get('stages', [])
for step_name, step in config['steps'].items():
if step['stage'] not in stages:
stages.append(step['stage'])
return stages
def get_all_steps(config):
"""
Return all the steps available in the build configuration
:param config: The build configuration
:return: A list of steps
"""
return list(config.get('steps', {}).keys())
def get_build_file():
"""Determine the local build file"""
"""
Determine the local build file
"""
base_path = Path.cwd()
for child in base_path.iterdir():
if child.name in BUILD_FILES:
@ -76,7 +146,9 @@ def get_build_file():
def main():
"""Run the build system"""
"""
Run the build system
"""
args = parse_args()
if args.build_file:
build_file = Path(args.build_file).resolve()
@ -87,18 +159,26 @@ def main():
return 1
base_path = build_file.parent
config = yaml.full_load(build_file.open())
all_stages = get_all_stages(config)
all_steps = get_all_steps(config)
if args.stage_or_step:
if args.stage_or_step in config['stages']:
if args.stage_or_step in all_stages:
run_stage(config, args.stage_or_step, base_path)
elif args.stage_or_step in config['steps'].keys():
elif args.stage_or_step in all_steps:
step = setup_step(config, args.stage_or_step)
run_step(config, step, base_path)
else:
print('"{stage}" is not a valid stage or step name'.format(stage=args.stage_or_step))
return 2
else:
for stage_name in config['stages']:
stages = config.get('stages', all_stages)
if stages:
for stage_name in stages:
run_stage(config, stage_name, base_path)
else:
for step_name in all_steps:
step = setup_step(config, step_name)
run_step(config, step, base_path)
return 0

View File

@ -1,6 +1,6 @@
[metadata]
name = bou
version = 0.0.1
version = 0.0.2
author = Raoul Snyman
author_email = raoul@snyman.info
description = Simple YAML-driven build or task runner