Initial commit
This commit is contained in:
parent
54ac6f1e2e
commit
c6cd3ce48c
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
*.egg-info
|
||||
build
|
||||
dist
|
||||
index.html
|
70
README.rst
Normal file
70
README.rst
Normal file
@ -0,0 +1,70 @@
|
||||
bou
|
||||
===
|
||||
|
||||
Bou (pronounced "bow") is a simple builder or task runner which uses a YAML file for task configuration.
|
||||
|
||||
"Bou" is `Afrikaans`_ for "build".
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
Install bou with pip:
|
||||
|
||||
.. code-block::
|
||||
|
||||
$ pip install bou
|
||||
|
||||
|
||||
Task Configuration
|
||||
------------------
|
||||
|
||||
When run without any parameters, bou will search for a file named ``bou.yaml``, ``bou.yml``, ``build.yaml`` or ``build.yml``
|
||||
|
||||
Here's a basic example:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
stages:
|
||||
- build
|
||||
- test
|
||||
steps:
|
||||
build:
|
||||
stage: build
|
||||
script:
|
||||
- make
|
||||
test:
|
||||
stage: test
|
||||
script:
|
||||
- make test
|
||||
|
||||
|
||||
Environment Variables
|
||||
---------------------
|
||||
|
||||
Bou also supports setting environment variables, both at a global level, as well as at a step level. As a convenience,
|
||||
bou sets up an initial environment variable named ``BASE_DIR`` which is the directory the build file is in.
|
||||
|
||||
Environment variables defined at a global level are set first when a step is run, and then the step-level environment
|
||||
variables are set.
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
stages:
|
||||
- build
|
||||
environment:
|
||||
- PYTHON=python3
|
||||
steps:
|
||||
build:
|
||||
stage: build
|
||||
environment:
|
||||
- SOURCE=$BASE_DIR/src
|
||||
script:
|
||||
- $PYTHON $SOURCE/setup.py build
|
||||
|
||||
|
||||
Source Code
|
||||
-----------
|
||||
|
||||
The source code to bou is available on my personal Git server: https://git.snyman.info/superfly/bou
|
||||
|
||||
.. _Afrikaans: https://en.wikipedia.org/wiki/Afrikaans
|
106
bou.py
Executable file
106
bou.py
Executable file
@ -0,0 +1,106 @@
|
||||
#!/usr/bin/env python3
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
from argparse import ArgumentParser
|
||||
from pathlib import Path
|
||||
from subprocess import Popen, PIPE, STDOUT
|
||||
|
||||
import yaml
|
||||
|
||||
BUILD_FILES = ['build.yaml', 'build.yml', 'bou.yaml', 'bou.yml']
|
||||
ENV_VAR = re.compile(r'(\$([A-Za-z0-9_]+))')
|
||||
|
||||
|
||||
def parse_args():
|
||||
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')
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def setup_env(env, base_path):
|
||||
"""Set up the environment dictionary, resolving shell variables"""
|
||||
if isinstance(env, list):
|
||||
env = {pair.split('=')[0]: pair.split('=')[1] for pair in env}
|
||||
env = dict(BASE_DIR=str(base_path), **env)
|
||||
for key, value in env.items():
|
||||
match = ENV_VAR.search(value)
|
||||
if match:
|
||||
value = value.replace(match.group(1), env[match.group(2)])
|
||||
env[key] = value
|
||||
return dict(**os.environ, **env)
|
||||
|
||||
|
||||
def setup_step(config, step_name):
|
||||
"""Prepare a step for usage"""
|
||||
step = config['steps'][step_name]
|
||||
step['environment'] = config.get('environment', []) + step.get('environment', [])
|
||||
return step
|
||||
|
||||
|
||||
def get_steps_for_stage(config, stage_name):
|
||||
steps = []
|
||||
for step_name in config['steps'].keys():
|
||||
if config['steps'][step_name]['stage'] == stage_name:
|
||||
steps.append(setup_step(config, step_name))
|
||||
return steps
|
||||
|
||||
|
||||
def run_step(step, base_path):
|
||||
script = step['script']
|
||||
if isinstance(script, list):
|
||||
script = os.linesep.join(script)
|
||||
env = setup_env(step['environment'], base_path)
|
||||
proc = Popen([script], shell=True, stdout=PIPE, stderr=STDOUT, env=env)
|
||||
for output in iter(lambda: proc.stdout.read(1), b''):
|
||||
sys.stdout.buffer.write(output)
|
||||
sys.stdout.buffer.flush()
|
||||
return proc.returncode == 0
|
||||
|
||||
|
||||
def run_stage(config, stage_name, base_path):
|
||||
for step in get_steps_for_stage(config, stage_name):
|
||||
result = run_step(step, base_path)
|
||||
if not result:
|
||||
break
|
||||
|
||||
|
||||
def get_build_file():
|
||||
"""Determine the local build file"""
|
||||
base_path = Path.cwd()
|
||||
for child in base_path.iterdir():
|
||||
if child.name in BUILD_FILES:
|
||||
return child.resolve()
|
||||
return None
|
||||
|
||||
|
||||
def main():
|
||||
"""Run the build system"""
|
||||
args = parse_args()
|
||||
if args.build_file:
|
||||
build_file = Path(args.build_file).resolve()
|
||||
else:
|
||||
build_file = get_build_file()
|
||||
if not build_file:
|
||||
print('Could not find a valid build file')
|
||||
return 1
|
||||
base_path = build_file.parent
|
||||
config = yaml.full_load(build_file.open())
|
||||
if args.stage_or_step:
|
||||
if args.stage_or_step in config['stages']:
|
||||
run_stage(config, args.stage_or_step, base_path)
|
||||
elif args.stage_or_step in config['steps'].keys():
|
||||
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']:
|
||||
run_stage(config, stage_name, base_path)
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
3
pyproject.toml
Normal file
3
pyproject.toml
Normal file
@ -0,0 +1,3 @@
|
||||
[build-system]
|
||||
requires = ["setuptools", "wheel"]
|
||||
build-backend = "setuptools.build_meta"
|
36
setup.cfg
Normal file
36
setup.cfg
Normal file
@ -0,0 +1,36 @@
|
||||
[metadata]
|
||||
name = bou
|
||||
version = 0.0.1
|
||||
author = Raoul Snyman
|
||||
author_email = raoul@snyman.info
|
||||
description = Simple YAML-driven build or task runner
|
||||
long_description = file:README.rst
|
||||
long_description_content_type = text/restructuredtext
|
||||
url = https://bou-project.org
|
||||
license = MIT
|
||||
classifiers =
|
||||
Development Status :: 3 - Alpha,
|
||||
Intended Audience :: Developers,
|
||||
License :: OSI Approved :: MIT License,
|
||||
Operating System :: POSIX,
|
||||
Programming Language :: Python :: 3,
|
||||
Programming Language :: Python :: 3.7,
|
||||
Programming Language :: Python :: 3.8,
|
||||
Programming Language :: Python :: 3.9,
|
||||
Programming Language :: Python :: 3.10,
|
||||
Topic :: Utilities
|
||||
keywords = build, task
|
||||
|
||||
[options]
|
||||
py_modules = bou
|
||||
python_requires = >=3.7
|
||||
|
||||
[options.entry_points]
|
||||
console_scripts =
|
||||
bou = bou:main
|
||||
|
||||
[wheel]
|
||||
universal = 1
|
||||
|
||||
[flake8]
|
||||
max-line-length = 120
|
Loading…
Reference in New Issue
Block a user