Compare commits
No commits in common. "7beea0c7b68ccd280e3107d1259bd1d74d858a37" and "54ac6f1e2ef927bcb400b187752b4c194cf8aa14" have entirely different histories.
7beea0c7b6
...
54ac6f1e2e
4
.gitignore
vendored
4
.gitignore
vendored
@ -1,4 +0,0 @@
|
|||||||
*.egg-info
|
|
||||||
build
|
|
||||||
dist
|
|
||||||
index.html
|
|
70
README.rst
70
README.rst
@ -1,70 +0,0 @@
|
|||||||
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
106
bou.py
@ -1,106 +0,0 @@
|
|||||||
#!/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())
|
|
@ -1,3 +0,0 @@
|
|||||||
[build-system]
|
|
||||||
requires = ["setuptools", "wheel"]
|
|
||||||
build-backend = "setuptools.build_meta"
|
|
36
setup.cfg
36
setup.cfg
@ -1,36 +0,0 @@
|
|||||||
[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