...
 
Commits (55)
stages:
- test
pytest:
image: python:3.7
stage: test
script:
- pip install pipenv
- pipenv install --system --dev
- pytest b5/tests -v
......@@ -12,6 +12,8 @@ packaging = ">=16.0"
[dev-packages]
"e1839a8" = {path = ".", editable = true}
pylint = ">=2.3.1"
pytest = ">=5.3.2"
[requires]
python_version = "3.7"
{
"_meta": {
"hash": {
"sha256": "d0daccc241176ef64b34c280787cddd83e3073c7f019715c77f947adc1463d4a"
"sha256": "4982a583aefa66c8927d063d04b1478775fc813b01f2ce2f5fc60eec504d9a46"
},
"pipfile-spec": 6,
"requires": {
......@@ -18,11 +18,11 @@
"default": {
"jinja2": {
"hashes": [
"sha256:065c4f02ebe7f7cf559e49ee5a95fb800a9e4528727aec6f24402a5374c65013",
"sha256:14dd6caf1527abb21f08f86c784eac40853ba93edb79552aa1e4b8aef1b61c7b"
"sha256:74320bb91f31270f9551d46522e33af46a80c3d619f4a4bf42b3164d30b5911f",
"sha256:9fe95f19286cfefaa917656583d020be14e7859c6b0252588391e47db34527de"
],
"index": "pypi",
"version": "==2.10.1"
"version": "==2.10.3"
},
"markupsafe": {
"hashes": [
......@@ -60,42 +60,42 @@
},
"packaging": {
"hashes": [
"sha256:0c98a5d0be38ed775798ece1b9727178c4469d9c3b4ada66e8e6b7849f8732af",
"sha256:9e1cbf8c12b1f1ce0bb5344b8d7ecf66a6f8a6e91bcb0c84593ed6d3ab5c4ab3"
"sha256:aec3fdbb8bc9e4bb65f0634b9f551ced63983a529d6a8931817d52fdd0816ddb",
"sha256:fe1d8331dfa7cc0a883b49d75fc76380b2ab2734b220fbb87d774e4fd4b851f8"
],
"index": "pypi",
"version": "==19.0"
"version": "==20.0"
},
"pyparsing": {
"hashes": [
"sha256:66c9268862641abcac4a96ba74506e594c884e3f57690a696d21ad8210ed667a",
"sha256:f6c5ef0d7480ad048c054c37632c67fca55299990fff127850181659eea33fc3"
"sha256:4c830582a84fb022400b85429791bc551f1f4871c33f23e44f353119e92f969f",
"sha256:c342dccb5250c08d45fd6f8b4a559613ca603b57498511740e65cd11a2e7dcec"
],
"version": "==2.3.1"
"version": "==2.4.6"
},
"pyyaml": {
"hashes": [
"sha256:1adecc22f88d38052fb787d959f003811ca858b799590a5eaa70e63dca50308c",
"sha256:436bc774ecf7c103814098159fbb84c2715d25980175292c648f2da143909f95",
"sha256:460a5a4248763f6f37ea225d19d5c205677d8d525f6a83357ca622ed541830c2",
"sha256:5a22a9c84653debfbf198d02fe592c176ea548cccce47553f35f466e15cf2fd4",
"sha256:7a5d3f26b89d688db27822343dfa25c599627bc92093e788956372285c6298ad",
"sha256:9372b04a02080752d9e6f990179a4ab840227c6e2ce15b95e1278456664cf2ba",
"sha256:a5dcbebee834eaddf3fa7366316b880ff4062e4bcc9787b78c7fbb4a26ff2dd1",
"sha256:aee5bab92a176e7cd034e57f46e9df9a9862a71f8f37cad167c6fc74c65f5b4e",
"sha256:c51f642898c0bacd335fc119da60baae0824f2cde95b0330b56c0553439f0673",
"sha256:c68ea4d3ba1705da1e0d85da6684ac657912679a649e8868bd850d2c299cce13",
"sha256:e23d0cc5299223dcc37885dae624f382297717e459ea24053709675a976a3e19"
"sha256:059b2ee3194d718896c0ad077dd8c043e5e909d9180f387ce42012662a4946d6",
"sha256:1cf708e2ac57f3aabc87405f04b86354f66799c8e62c28c5fc5f88b5521b2dbf",
"sha256:24521fa2890642614558b492b473bee0ac1f8057a7263156b02e8b14c88ce6f5",
"sha256:4fee71aa5bc6ed9d5f116327c04273e25ae31a3020386916905767ec4fc5317e",
"sha256:70024e02197337533eef7b85b068212420f950319cc8c580261963aefc75f811",
"sha256:74782fbd4d4f87ff04159e986886931456a1894c61229be9eaf4de6f6e44b99e",
"sha256:940532b111b1952befd7db542c370887a8611660d2b9becff75d39355303d82d",
"sha256:cb1f2f5e426dc9f07a7681419fe39cee823bb74f723f36f70399123f439e9b20",
"sha256:dbbb2379c19ed6042e8f11f2a2c66d39cceb8aeace421bfc29d085d93eda3689",
"sha256:e3a057b7a64f1222b56e47bcff5e4b94c4f61faac04c7c4ecb1985e18caa3994",
"sha256:e9f45bd5b92c7974e59bcd2dcc8631a6b6cc380a904725fce7bc08872e691615"
],
"index": "pypi",
"version": "==5.1"
"version": "==5.3"
},
"six": {
"hashes": [
"sha256:3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c",
"sha256:d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73"
"sha256:1f1b7d42e254082a9db6279deae68afb421ceba6158efa6131de7b3003ee93fd",
"sha256:30f610279e8b2578cab6db20741130331735c781b56053c59c4076da27f06b66"
],
"version": "==1.12.0"
"version": "==1.13.0"
},
"termcolor": {
"hashes": [
......@@ -106,17 +106,72 @@
}
},
"develop": {
"astroid": {
"hashes": [
"sha256:71ea07f44df9568a75d0f354c49143a4575d90645e9fead6dfb52c26a85ed13a",
"sha256:840947ebfa8b58f318d42301cf8c0a20fd794a33b61cc4638e28e9e61ba32f42"
],
"version": "==2.3.3"
},
"attrs": {
"hashes": [
"sha256:08a96c641c3a74e44eb59afb61a24f2cb9f4d7188748e76ba4bb5edfa3cb7d1c",
"sha256:f7b7ce16570fe9965acd6d30101a28f62fb4a7f9e926b3bbc9b61f8b04247e72"
],
"version": "==19.3.0"
},
"e1839a8": {
"editable": true,
"path": "."
},
"importlib-metadata": {
"hashes": [
"sha256:bdd9b7c397c273bcc9a11d6629a38487cd07154fa255a467bf704cd2c258e359",
"sha256:f17c015735e1a88296994c0697ecea7e11db24290941983b08c9feb30921e6d8"
],
"markers": "python_version < '3.8'",
"version": "==1.4.0"
},
"isort": {
"hashes": [
"sha256:54da7e92468955c4fceacd0c86bd0ec997b0e1ee80d97f67c35a78b719dccab1",
"sha256:6e811fcb295968434526407adb8796944f1988c5b65e8139058f2014cbe100fd"
],
"version": "==4.3.21"
},
"jinja2": {
"hashes": [
"sha256:065c4f02ebe7f7cf559e49ee5a95fb800a9e4528727aec6f24402a5374c65013",
"sha256:14dd6caf1527abb21f08f86c784eac40853ba93edb79552aa1e4b8aef1b61c7b"
"sha256:74320bb91f31270f9551d46522e33af46a80c3d619f4a4bf42b3164d30b5911f",
"sha256:9fe95f19286cfefaa917656583d020be14e7859c6b0252588391e47db34527de"
],
"index": "pypi",
"version": "==2.10.1"
"version": "==2.10.3"
},
"lazy-object-proxy": {
"hashes": [
"sha256:0c4b206227a8097f05c4dbdd323c50edf81f15db3b8dc064d08c62d37e1a504d",
"sha256:194d092e6f246b906e8f70884e620e459fc54db3259e60cf69a4d66c3fda3449",
"sha256:1be7e4c9f96948003609aa6c974ae59830a6baecc5376c25c92d7d697e684c08",
"sha256:4677f594e474c91da97f489fea5b7daa17b5517190899cf213697e48d3902f5a",
"sha256:48dab84ebd4831077b150572aec802f303117c8cc5c871e182447281ebf3ac50",
"sha256:5541cada25cd173702dbd99f8e22434105456314462326f06dba3e180f203dfd",
"sha256:59f79fef100b09564bc2df42ea2d8d21a64fdcda64979c0fa3db7bdaabaf6239",
"sha256:8d859b89baf8ef7f8bc6b00aa20316483d67f0b1cbf422f5b4dc56701c8f2ffb",
"sha256:9254f4358b9b541e3441b007a0ea0764b9d056afdeafc1a5569eee1cc6c1b9ea",
"sha256:9651375199045a358eb6741df3e02a651e0330be090b3bc79f6d0de31a80ec3e",
"sha256:97bb5884f6f1cdce0099f86b907aa41c970c3c672ac8b9c8352789e103cf3156",
"sha256:9b15f3f4c0f35727d3a0fba4b770b3c4ebbb1fa907dbcc046a1d2799f3edd142",
"sha256:a2238e9d1bb71a56cd710611a1614d1194dc10a175c1e08d75e1a7bcc250d442",
"sha256:a6ae12d08c0bf9909ce12385803a543bfe99b95fe01e752536a60af2b7797c62",
"sha256:ca0a928a3ddbc5725be2dd1cf895ec0a254798915fb3a36af0964a0a4149e3db",
"sha256:cb2c7c57005a6804ab66f106ceb8482da55f5314b7fcb06551db1edae4ad1531",
"sha256:d74bb8693bf9cf75ac3b47a54d716bbb1a92648d5f781fc799347cfc95952383",
"sha256:d945239a5639b3ff35b70a88c5f2f491913eb94871780ebfabb2568bd58afc5a",
"sha256:eba7011090323c1dadf18b3b689845fd96a61ba0a1dfbd7f24b921398affc357",
"sha256:efa1909120ce98bbb3777e8b6f92237f5d5c8ea6758efea36a473e1d38f7d3e4",
"sha256:f3900e8a5de27447acbf900b4750b0ddfd7ec1ea7fbaf11dfa911141bc522af0"
],
"version": "==1.4.3"
},
"markupsafe": {
"hashes": [
......@@ -152,44 +207,88 @@
"index": "pypi",
"version": "==1.1.1"
},
"mccabe": {
"hashes": [
"sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42",
"sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"
],
"version": "==0.6.1"
},
"more-itertools": {
"hashes": [
"sha256:1a2a32c72400d365000412fe08eb4a24ebee89997c18d3d147544f70f5403b39",
"sha256:c468adec578380b6281a114cb8a5db34eb1116277da92d7c46f904f0b52d3288"
],
"version": "==8.1.0"
},
"packaging": {
"hashes": [
"sha256:0c98a5d0be38ed775798ece1b9727178c4469d9c3b4ada66e8e6b7849f8732af",
"sha256:9e1cbf8c12b1f1ce0bb5344b8d7ecf66a6f8a6e91bcb0c84593ed6d3ab5c4ab3"
"sha256:aec3fdbb8bc9e4bb65f0634b9f551ced63983a529d6a8931817d52fdd0816ddb",
"sha256:fe1d8331dfa7cc0a883b49d75fc76380b2ab2734b220fbb87d774e4fd4b851f8"
],
"index": "pypi",
"version": "==19.0"
"version": "==20.0"
},
"pluggy": {
"hashes": [
"sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0",
"sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"
],
"version": "==0.13.1"
},
"py": {
"hashes": [
"sha256:5e27081401262157467ad6e7f851b7aa402c5852dbcb3dae06768434de5752aa",
"sha256:c20fdd83a5dbc0af9efd622bee9a5564e278f6380fffcacc43ba6f43db2813b0"
],
"version": "==1.8.1"
},
"pylint": {
"hashes": [
"sha256:3db5468ad013380e987410a8d6956226963aed94ecb5f9d3a28acca6d9ac36cd",
"sha256:886e6afc935ea2590b462664b161ca9a5e40168ea99e5300935f6591ad467df4"
],
"index": "pypi",
"version": "==2.4.4"
},
"pyparsing": {
"hashes": [
"sha256:66c9268862641abcac4a96ba74506e594c884e3f57690a696d21ad8210ed667a",
"sha256:f6c5ef0d7480ad048c054c37632c67fca55299990fff127850181659eea33fc3"
"sha256:4c830582a84fb022400b85429791bc551f1f4871c33f23e44f353119e92f969f",
"sha256:c342dccb5250c08d45fd6f8b4a559613ca603b57498511740e65cd11a2e7dcec"
],
"version": "==2.4.6"
},
"pytest": {
"hashes": [
"sha256:6b571215b5a790f9b41f19f3531c53a45cf6bb8ef2988bc1ff9afb38270b25fa",
"sha256:e41d489ff43948babd0fad7ad5e49b8735d5d55e26628a58673c39ff61d95de4"
],
"version": "==2.3.1"
"index": "pypi",
"version": "==5.3.2"
},
"pyyaml": {
"hashes": [
"sha256:1adecc22f88d38052fb787d959f003811ca858b799590a5eaa70e63dca50308c",
"sha256:436bc774ecf7c103814098159fbb84c2715d25980175292c648f2da143909f95",
"sha256:460a5a4248763f6f37ea225d19d5c205677d8d525f6a83357ca622ed541830c2",
"sha256:5a22a9c84653debfbf198d02fe592c176ea548cccce47553f35f466e15cf2fd4",
"sha256:7a5d3f26b89d688db27822343dfa25c599627bc92093e788956372285c6298ad",
"sha256:9372b04a02080752d9e6f990179a4ab840227c6e2ce15b95e1278456664cf2ba",
"sha256:a5dcbebee834eaddf3fa7366316b880ff4062e4bcc9787b78c7fbb4a26ff2dd1",
"sha256:aee5bab92a176e7cd034e57f46e9df9a9862a71f8f37cad167c6fc74c65f5b4e",
"sha256:c51f642898c0bacd335fc119da60baae0824f2cde95b0330b56c0553439f0673",
"sha256:c68ea4d3ba1705da1e0d85da6684ac657912679a649e8868bd850d2c299cce13",
"sha256:e23d0cc5299223dcc37885dae624f382297717e459ea24053709675a976a3e19"
"sha256:059b2ee3194d718896c0ad077dd8c043e5e909d9180f387ce42012662a4946d6",
"sha256:1cf708e2ac57f3aabc87405f04b86354f66799c8e62c28c5fc5f88b5521b2dbf",
"sha256:24521fa2890642614558b492b473bee0ac1f8057a7263156b02e8b14c88ce6f5",
"sha256:4fee71aa5bc6ed9d5f116327c04273e25ae31a3020386916905767ec4fc5317e",
"sha256:70024e02197337533eef7b85b068212420f950319cc8c580261963aefc75f811",
"sha256:74782fbd4d4f87ff04159e986886931456a1894c61229be9eaf4de6f6e44b99e",
"sha256:940532b111b1952befd7db542c370887a8611660d2b9becff75d39355303d82d",
"sha256:cb1f2f5e426dc9f07a7681419fe39cee823bb74f723f36f70399123f439e9b20",
"sha256:dbbb2379c19ed6042e8f11f2a2c66d39cceb8aeace421bfc29d085d93eda3689",
"sha256:e3a057b7a64f1222b56e47bcff5e4b94c4f61faac04c7c4ecb1985e18caa3994",
"sha256:e9f45bd5b92c7974e59bcd2dcc8631a6b6cc380a904725fce7bc08872e691615"
],
"index": "pypi",
"version": "==5.1"
"version": "==5.3"
},
"six": {
"hashes": [
"sha256:3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c",
"sha256:d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73"
"sha256:1f1b7d42e254082a9db6279deae68afb421ceba6158efa6131de7b3003ee93fd",
"sha256:30f610279e8b2578cab6db20741130331735c781b56053c59c4076da27f06b66"
],
"version": "==1.12.0"
"version": "==1.13.0"
},
"termcolor": {
"hashes": [
......@@ -197,6 +296,52 @@
],
"index": "pypi",
"version": "==1.1.0"
},
"typed-ast": {
"hashes": [
"sha256:1170afa46a3799e18b4c977777ce137bb53c7485379d9706af8a59f2ea1aa161",
"sha256:18511a0b3e7922276346bcb47e2ef9f38fb90fd31cb9223eed42c85d1312344e",
"sha256:262c247a82d005e43b5b7f69aff746370538e176131c32dda9cb0f324d27141e",
"sha256:2b907eb046d049bcd9892e3076c7a6456c93a25bebfe554e931620c90e6a25b0",
"sha256:354c16e5babd09f5cb0ee000d54cfa38401d8b8891eefa878ac772f827181a3c",
"sha256:48e5b1e71f25cfdef98b013263a88d7145879fbb2d5185f2a0c79fa7ebbeae47",
"sha256:4e0b70c6fc4d010f8107726af5fd37921b666f5b31d9331f0bd24ad9a088e631",
"sha256:630968c5cdee51a11c05a30453f8cd65e0cc1d2ad0d9192819df9978984529f4",
"sha256:66480f95b8167c9c5c5c87f32cf437d585937970f3fc24386f313a4c97b44e34",
"sha256:71211d26ffd12d63a83e079ff258ac9d56a1376a25bc80b1cdcdf601b855b90b",
"sha256:7954560051331d003b4e2b3eb822d9dd2e376fa4f6d98fee32f452f52dd6ebb2",
"sha256:838997f4310012cf2e1ad3803bce2f3402e9ffb71ded61b5ee22617b3a7f6b6e",
"sha256:95bd11af7eafc16e829af2d3df510cecfd4387f6453355188342c3e79a2ec87a",
"sha256:bc6c7d3fa1325a0c6613512a093bc2a2a15aeec350451cbdf9e1d4bffe3e3233",
"sha256:cc34a6f5b426748a507dd5d1de4c1978f2eb5626d51326e43280941206c209e1",
"sha256:d755f03c1e4a51e9b24d899561fec4ccaf51f210d52abdf8c07ee2849b212a36",
"sha256:d7c45933b1bdfaf9f36c579671fec15d25b06c8398f113dab64c18ed1adda01d",
"sha256:d896919306dd0aa22d0132f62a1b78d11aaf4c9fc5b3410d3c666b818191630a",
"sha256:fdc1c9bbf79510b76408840e009ed65958feba92a88833cdceecff93ae8fff66",
"sha256:ffde2fbfad571af120fcbfbbc61c72469e72f550d676c3342492a9dfdefb8f12"
],
"markers": "implementation_name == 'cpython' and python_version < '3.8'",
"version": "==1.4.0"
},
"wcwidth": {
"hashes": [
"sha256:8fd29383f539be45b20bd4df0dc29c20ba48654a41e661925e612311e9f3c603",
"sha256:f28b3e8a6483e5d49e7f8949ac1a78314e740333ae305b4ba5defd3e74fb37a8"
],
"version": "==0.1.8"
},
"wrapt": {
"hashes": [
"sha256:565a021fd19419476b9362b05eeaa094178de64f8361e44468f9e9d7843901e1"
],
"version": "==1.11.2"
},
"zipp": {
"hashes": [
"sha256:3718b1cbcd963c7d4c5511a8240812904164b7f381b647143a89d3b98f9bcd8e",
"sha256:f06903e9f1f43b12d371004b4ac7b06ab39a44adc747266928ae6debfa7b3335"
],
"version": "==0.6.0"
}
}
}
import argparse
import os
import termcolor
import sys
from .lib.state import State
from .lib.module import load_module
import termcolor
from .lib.argumentparser import ExecuteArgumentParser
from .exceptions import B5ExecutionError
from .lib.module import load_module
from .lib.state import State
def main():
try:
parser = argparse.ArgumentParser(
prog='b5-execute',
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
description='b5-execute is not intended to be called directly!',
)
parser.add_argument(
'--state-file', nargs='?',
dest='state_file',
)
parser.add_argument(
'--module', nargs='?',
dest='module',
)
parser.add_argument(
'--method', nargs='?',
dest='method',
)
parser.add_argument(
'--args', nargs=argparse.REMAINDER,
dest='args'
)
args = parser.parse_args()
parser = ExecuteArgumentParser('b5-execute', 'b5-execute is not intended to be called directly!')
parser.add_arguments()
args = parser.parse()
if not args.state_file or not args.module or not args.method:
raise B5ExecutionError('b5-execute is not intended to be called directly!')
state = State.load(open(args.state_file, 'rb'))
if not 'modules' in state.config:
if 'modules' not in state.config:
raise B5ExecutionError('No modules defined')
if not args.module in state.config['modules']:
if args.module not in state.config['modules']:
raise B5ExecutionError('Module not available')
module = load_module(state, args.module)
......@@ -49,6 +31,6 @@ def main():
os.chdir(state.run_path)
method(state, args.args)
except B5ExecutionError as e:
termcolor.cprint(str(e), 'red')
except B5ExecutionError as error:
termcolor.cprint(str(error), 'red')
sys.exit(1)
import argparse
import os
import shutil
import termcolor
import subprocess
import sys
import re
import termcolor
from .lib.skeleton import Skeleton
from .lib.argumentparser import InitArgumentParser
from .exceptions import B5ExecutionError
NON_URL_SKELETON = re.compile('^[A-Za-z0-9_-]+$')
def _run_cmd(cmd, error='Command execution failed, see above'):
try:
subprocess.run(
......@@ -26,40 +23,21 @@ def _run_cmd(cmd, error='Command execution failed, see above'):
def main():
try:
parser = argparse.ArgumentParser(
prog='b5-init',
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
description='b5-init might be used to setup new projects',
)
parser.add_argument(
'-s', '--skeleton', nargs='?',
dest='skeleton', default='basic'
)
parser.add_argument(
'-b', '--branch', nargs='?',
dest='branch',
)
parser.add_argument(
dest='path'
)
args = parser.parse_args()
parser = InitArgumentParser('b5-init', 'b5-init might be used to setup new projects')
parser.add_arguments()
args = parser.parse()
skeleton = args.skeleton
skeleton = Skeleton(args.skeleton)
branch = args.branch
path = args.path
if NON_URL_SKELETON.match(skeleton):
skeleton_url = 'https://git.team23.de/build/b5-skel-{skeleton}.git'.format(skeleton=skeleton)
else:
skeleton_url = skeleton
full_path = os.path.realpath(os.path.join(os.getcwd(), path))
os.makedirs(full_path, exist_ok=True)
if len(os.listdir(full_path)) > 0:
if os.listdir(full_path):
raise B5ExecutionError('Cannot init an existing directory if not empty')
_run_cmd(['git', 'clone', skeleton_url, full_path], 'Could not clone skeleton repository, see above')
_run_cmd(['git', 'clone', skeleton.get_url(), full_path], 'Could not clone skeleton repository, see above')
os.chdir(full_path)
if not branch is None:
_run_cmd(['git', 'checkout', branch], 'Could not checkout required branch, see above')
......@@ -78,8 +56,8 @@ def main():
shutil.rmtree(init_path)
# _run_cmd(['git', 'add', '-A'])
termcolor.cprint('Successful initialized {path}'.format(path=path), 'green')
termcolor.cprint(' skeleton used: {skeleton_url}'.format(skeleton_url=skeleton_url), 'green')
termcolor.cprint(' skeleton used: {skeleton_url}'.format(skeleton_url=skeleton.get_url()), 'green')
termcolor.cprint(' project path: {full_path}'.format(full_path=full_path), 'green')
except B5ExecutionError as e:
termcolor.cprint(str(e), 'red')
except B5ExecutionError as error:
termcolor.cprint(str(error), 'red')
sys.exit(1)
import argparse
from .detect import DETECT
class ArgumentParser:
def __init__(self, prog='b5', description='', formatter_class=argparse.ArgumentDefaultsHelpFormatter):
self.parser = argparse.ArgumentParser(
prog=prog,
formatter_class=formatter_class,
description=description
)
self.defaults = {}
def parse(self, arguments=None, help_as_default=False):
if help_as_default:
if not arguments:
arguments = ['help']
args = self.parser.parse_args(arguments)
for key, value in self.defaults.items():
if not getattr(args, key):
setattr(args, key, value)
return args
def set_default(self, key, value):
self.defaults.update({key: value})
class MainArgumentParser(ArgumentParser):
"""
class for parsing all the b5 command line arguments.
Possible command line argument:
--config -c Path to config (inside run path)
--taskfile -t Path to Taskfile (inside run path)
--project-path -p Project path if not part of parent paths, normally b5 tries to get the project path by itself
--run-path -r Path inside the project b5 will execute in (cd into)
--detect -d Version Control System detection (git or hg)
--shell -s Shell to run the generated script in (should be bash)
--quiet -q Determine if the script should print out stuff
"""
def add_arguments(self):
self.__add_argument_config()
self.__add_argument_taskfile()
self.__add_argument_project_path()
self.__add_argument_run_path()
self.__add_argument_detect()
self.__add_argument_shell()
self.__add_argument_quiet()
self.__add_argument_command()
def __add_argument_config(self):
self.parser.add_argument(
'-c', '--config',
nargs='?',
action='append',
help='Path to config (inside run path)',
dest='configfiles',
)
def __add_argument_taskfile(self):
self.parser.add_argument(
'-t', '--taskfile',
nargs='?',
action='append',
help='Path to Taskfile (inside run path)',
dest='taskfiles',
)
def __add_argument_project_path(self):
self.parser.add_argument(
'-p', '--project-path',
nargs='?',
help='Project path if not part of parent paths, normally b5 tries to get the project path by itself',
dest='project_path',
)
def __add_argument_run_path(self):
self.parser.add_argument(
'-r', '--run-path',
nargs='?',
help='Path inside the project b5 will execute in (cd into)',
dest='run_path',
default='build',
)
def __add_argument_detect(self):
self.parser.add_argument(
'-d', '--detect',
nargs='?',
help='Project detection',
choices=DETECT,
dest='detect',
default='git',
)
def __add_argument_shell(self):
self.parser.add_argument(
'-s', '--shell', nargs='?',
help='Shell to run the generated script in (should be bash)',
dest='shell',
default='/bin/bash',
)
def __add_argument_quiet(self):
self.parser.add_argument(
'-q', '--quiet',
action='store_true',
dest='quiet',
default=False,
)
self.parser.set_defaults()
def __add_argument_command(self):
self.parser.add_argument('command')
self.parser.add_argument('command_args', nargs=argparse.REMAINDER)
class InitArgumentParser(ArgumentParser):
def add_arguments(self):
self.__add_argument_skeleton()
self.__add_argument_branch()
self.__add_argument_path()
def __add_argument_skeleton(self):
self.parser.add_argument(
'-s', '--skeleton',
nargs='?',
dest='skeleton',
default='basic',
)
def __add_argument_branch(self):
self.parser.add_argument(
'-b', '--branch',
nargs='?',
dest='branch',
)
def __add_argument_path(self):
self.parser.add_argument(
dest='path'
)
class ExecuteArgumentParser(ArgumentParser):
def add_arguments(self):
self.__add_argument_state_file()
self.__add_argument_module()
self._add_argument_method()
self.__add_argument_args()
def __add_argument_state_file(self):
self.parser.add_argument(
'--state-file', nargs='?',
dest='state_file',
)
def __add_argument_module(self):
self.parser.add_argument(
'--module', nargs='?',
dest='module',
)
def _add_argument_method(self):
self.parser.add_argument(
'--method', nargs='?',
dest='method',
)
def __add_argument_args(self):
self.parser.add_argument(
'--args',
nargs=argparse.REMAINDER,
dest='args'
)
class TemplateArgumentParser(ArgumentParser):
def add_arguments(self):
self.__add_argument_overwrite()
self.__add_argument_template_file()
self.__add_argument_output_file()
def __add_argument_overwrite(self):
self.parser.add_argument(
'-o', '--overwrite', nargs='?',
help='Control if existing files should be overwritten',
dest='overwrite', default='ask',
choices=['yes', 'if-older', 'no', 'ask', 'ask-if-older']
)
def __add_argument_template_file(self):
self.parser.add_argument('template_file')
def __add_argument_output_file(self):
self.parser.add_argument('output_file', nargs='?')
import yaml
import os
import yaml
from .version import ensure_config_version
from ..exceptions import B5ExecutionError
......@@ -15,8 +16,8 @@ def find_configs(state, configs):
'config': config,
'path': config_path,
})
#if not found_configs:
# raise B5ExecutionError('No config found, tried %s inside %s' % (', '.join(configs), run_path))
# if not found_configs:
# raise B5ExecutionError('No config found, tried %s inside %s' % (', '.join(configs), run_path))
return found_configs
......@@ -40,7 +41,6 @@ def merge_config(cur_config, new_config):
return result_config
def validate_config(config):
if 'version' in config:
ensure_config_version(config['version'])
......@@ -51,8 +51,8 @@ def load_config(state):
configfiles = state.configfiles
config = {}
for configfile in configfiles:
fh = open(configfile['path'], 'r')
file_config = yaml.safe_load(fh)
file_handle = open(configfile['path'], 'r')
file_config = yaml.safe_load(file_handle)
if not isinstance(file_config, dict):
file_config = {}
config = merge_config(config, file_config)
......
......@@ -4,14 +4,14 @@ from importlib import import_module
def import_string(dotted_path):
try:
module_path, class_name = dotted_path.rsplit('.', 1)
except ValueError as err:
except ValueError:
raise ImportError("%s doesn't look like a module path" % dotted_path)
module = import_module(module_path)
try:
return getattr(module, class_name)
except AttributeError as err:
except AttributeError:
raise ImportError('Module "%s" does not define a "%s" attribute/class' % (
module_path, class_name)
)
\ No newline at end of file
)
from ..modules import MODULES
from ..exceptions import B5ExecutionError
from .importutils import import_string
from ..exceptions import B5ExecutionError
from ..modules import MODULES
def load_module(state, module_key):
if not 'modules' in state.config or not module_key in state.config['modules']:
if 'modules' not in state.config or module_key not in state.config['modules']:
raise RuntimeError('Module %s is not defined in config' % module_key)
module_config = state.config['modules'][module_key]
......@@ -17,8 +17,10 @@ def load_module(state, module_key):
module_import_path = module_class_key
if module_class_key in MODULES:
module_import_path = MODULES[module_class_key]
if not '.' in module_import_path:
raise B5ExecutionError('Module seems not to be valid (key=%s/import=%s), please check config' % (module_key, module_import_path))
if '.' not in module_import_path:
raise B5ExecutionError('Module seems not to be valid (key=%s/import=%s), please check config' %
(module_key, module_import_path)
)
try:
module_class = import_string(module_import_path)
......
import os
import re
import shlex
import tempfile
import os
from .module import load_module
RE_KEY_ESCAPE = re.compile('[^a-zA-Z0-9]+')
CONFIG_SUB = '%s_%s'
CONFIG_KEYS = '%s_KEYS'
def modules_script_source(state):
script = []
if 'modules' in state.config:
......@@ -15,12 +21,6 @@ def modules_script_source(state):
def config_script_source(config, prefix='CONFIG'):
import re
RE_KEY_ESCAPE = re.compile('[^a-zA-Z0-9]+')
CONFIG_SUB = '%s_%s'
CONFIG_KEYS = '%s_KEYS'
def _gen_config(config_node, prefix):
script = []
......@@ -85,7 +85,6 @@ def construct_script_source(state):
# run_path and parse Taskfile's
script.append('cd %s\n' % shlex.quote(state.run_path))
for taskfile in state.taskfiles:
#script.append('source %s\n' % shlex.quote(taskfile['path']))
script.append(open(taskfile['path'], 'r').read())
return '\n'.join(script)
......@@ -108,16 +107,16 @@ fi
)
class StoredScriptSource(object):
class StoredScriptSource:
def __init__(self, state, source):
self.state = state
self.source = source
self.fh = tempfile.NamedTemporaryFile(suffix='b5-compiled', delete=False)
self.fh.write(self.source.encode('utf-8'))
self.fh.close()
self.file_handle = tempfile.NamedTemporaryFile(suffix='b5-compiled', delete=False)
self.file_handle.write(self.source.encode('utf-8'))
self.file_handle.close()
def close(self):
os.unlink(self.fh.name)
os.unlink(self.file_handle.name)
def __enter__(self):
return self
......@@ -127,4 +126,4 @@ class StoredScriptSource(object):
@property
def name(self):
return self.fh.name
return self.file_handle.name
import os
import re
import urllib.request
from urllib.error import URLError
class Skeleton:
NON_URL_SKELETON = re.compile('^[A-Za-z0-9_-]+$')
def __init__(self, skeleton_identified):
self._skeleton_identified = skeleton_identified
def get_url(self):
try:
return self._url
except AttributeError:
skeleton_url = self._skeleton_identified
if self.NON_URL_SKELETON.match(skeleton_url):
skeleton_url = 'https://git.team23.de/build/b5-skel-{skeleton}.git'.format(skeleton=self._skeleton_identified)
# If it's not a public repository, clone using ssh in order to allow ssh key file auth
if not self.__is_public_repository(skeleton_url):
skeleton_url = 'git@git.team23.de:build/b5-skel-{skeleton}.git'.format(skeleton=self._skeleton_identified)
self._url = skeleton_url
return self._url
def __is_public_repository(self, url):
request = urllib.request.urlopen(url)
request_url = request.geturl()
if url == request_url or url.rsplit('.', 1)[0] == request_url:
try:
if request.getcode() == 200:
return True
except URLError:
pass
return False
import yaml
import tempfile
import os
import tempfile
import yaml
class StoredState(object):
class StoredState:
def __init__(self, state):
self.state = state
if not self.state.stored_name is None:
if self.state.stored_name is not None:
raise RuntimeError('You may only store the state once')
self.fh = tempfile.NamedTemporaryFile(suffix='b5-state', mode='w', encoding='utf-8', delete=False)
self.file_handle = tempfile.NamedTemporaryFile(suffix='b5-state', mode='w', encoding='utf-8', delete=False)
self.state.stored_name = self.name
yaml.dump({
key: getattr(self.state, key)
for key in state.KEYS
}, self.fh, default_flow_style=False)
self.fh.close()
}, self.file_handle, default_flow_style=False)
self.file_handle.close()
def close(self):
os.unlink(self.fh.name)
os.unlink(self.file_handle.name)
self.state.stored_name = None
def __enter__(self):
......@@ -29,10 +30,10 @@ class StoredState(object):
@property
def name(self):
return self.fh.name
return self.file_handle.name
class State(object):
class State:
KEYS = ('project_path', 'run_path', 'taskfiles', 'configfiles', 'config', 'args', 'stored_name')
taskfiles = []
......@@ -44,7 +45,7 @@ class State(object):
if not hasattr(self, key):
setattr(self, key, None)
for key in kwargs:
if not key in self.KEYS:
if key not in self.KEYS:
raise RuntimeError('Key %s is not a valid state attribute' % key)
setattr(self, key, kwargs[key])
......@@ -52,5 +53,5 @@ class State(object):
return StoredState(self)
@classmethod
def load(cls, fh):
return cls(**yaml.safe_load(fh))
def load(cls, file_handle):
return cls(**yaml.safe_load(file_handle))
......@@ -16,4 +16,3 @@ def find_taskfiles(state, taskfiles):
if not found_taskfiles:
raise B5ExecutionError('No Taskfiles found, tried %s inside %s' % (', '.join(taskfiles), run_path))
return found_taskfiles
import argparse
import os
import subprocess
import sys
import termcolor
from .lib.argumentparser import MainArgumentParser
from . import VERSION
from .exceptions import B5ExecutionError
from .lib.config import load_config
from .lib.detect import detect_project_path, DETECT
from .lib.taskfile import find_taskfiles
from .lib.config import find_configs
from .lib.config import load_config
from .lib.detect import detect_project_path
from .lib.script import StoredScriptSource, construct_script_source, construct_script_run
from .lib.state import State
from . import VERSION
from .lib.taskfile import find_taskfiles
def main():
try:
# Parse all arguments
parser = argparse.ArgumentParser(
prog='b5',
formatter_class=argparse.ArgumentDefaultsHelpFormatter
)
parser.add_argument(
'-q', '--quiet',
action='store_true',
dest='quiet',
)
parser.add_argument(
'-p', '--project-path', nargs='?',
help='Project path if not part of parent paths, normally b5 tries to get the project path by itself',
dest='project_path',
)
parser.add_argument(
'-r', '--run-path', nargs='?',
help='Path inside the project b5 will execute in (cd into)',
dest='run_path', default='build',
)
parser.add_argument(
'-d', '--detect', nargs='?',
help='Project detection',
default='git', choices=DETECT,
dest='detect',
)
parser.add_argument(
'-c', '--config', nargs='?', action='append',
help='Path to config (inside run path)',
dest='configfiles',
)
parser.add_argument(
'-t', '--taskfile', nargs='?', action='append',
help='Path to Taskfile (inside run path)',
dest='taskfiles',
)
parser.add_argument(
'-s', '--shell', nargs='?',
help='Shell to run the generated script in (should be bash)',
dest='shell',
default='/bin/bash',
)
parser.add_argument('command')
parser.add_argument('command_args', nargs=argparse.REMAINDER)
parser.set_defaults(quiet=False)
sys_args = sys.argv[1:]
if not sys_args:
sys_args = ['help']
args = parser.parse_args(args=sys_args)
if args.taskfiles is None:
args.taskfiles = ['~/.b5/Taskfile', 'Taskfile', 'Taskfile.local']
if args.configfiles is None:
args.configfiles = ['~/.b5/config.yml', 'config.yml', 'config.local.yml', 'local.yml']
parser = MainArgumentParser('b5')
parser.add_arguments()
parser.set_default('taskfiles', ['~/.b5/Taskfile', 'Taskfile', 'Taskfile.local'])
parser.set_default('configfiles', ['~/.b5/config.yml', 'config.yml', 'config.local.yml', 'local.yml'])
args = parser.parse(sys.argv[1:], True)
# State vars
state = State(
project_path = args.project_path,
run_path = None,
taskfiles = [],
config = {},
args = vars(args)
project_path=args.project_path,
run_path=None,
taskfiles=[],
config={},
args=vars(args)
)
# Find project dir
......@@ -112,12 +65,10 @@ def main():
construct_script_run(state),
])
with StoredScriptSource(state, script_source) as source:
# print(source.source)
# return
if state.run_path:
os.chdir(state.run_path)
try:
result = subprocess.run(
subprocess.run(
[
args.shell,
source.name,
......@@ -132,7 +83,6 @@ def main():
termcolor.cprint('Task execution failed, see above', color='red')
sys.exit(1)
_print('Task exited ok', color='green')
#print(result)
except B5ExecutionError as e:
termcolor.cprint(str(e), 'red')
except B5ExecutionError as error:
termcolor.cprint(str(error), 'red')
sys.exit(1)
from ..lib.config import merge_config
import re
from ..lib.config import merge_config
CONFIG_PREFIX_RE = re.compile('[^A-Z0-9]')
MODULES = {
......@@ -15,7 +15,7 @@ MODULES = {
}
class BaseModule(object):
class BaseModule:
DEFAULT_CONFIG = {}
def __init__(self, name, config, state, **kwargs):
......
......@@ -50,7 +50,6 @@ class ComposerModule(BaseModule):
)
'''.format(
vendor_path=shlex.quote(self.config['vendor_path']),
base_path=shlex.quote(self.config['base_path']),
)))
script.append(self._script_function_source('composer', '''
......
......@@ -52,7 +52,6 @@ class PipenvModule(BaseModule):
base_path=shlex.quote(self.config['base_path']),
pipenv_bin=shlex.quote(self.config['pipenv_bin']),
install_dev='--dev' if self.config['install_dev'] else '',
name=self.name,
)))
script.append(self._script_function_source('update', '''
......
import argparse
import datetime
import os
import sys
import termcolor
import jinja2
from ..lib.argumentparser import TemplateArgumentParser
from .. import VERSION
from . import BaseModule
class TemplateModule(BaseModule):
def execute_render(self, state, sys_args):
parser = argparse.ArgumentParser(
prog='{name}:render'.format(name=self.name),
formatter_class=argparse.ArgumentDefaultsHelpFormatter
)
parser.add_argument(
'-o', '--overwrite', nargs='?',
help='Control if existing files should be overwritten',
dest='overwrite', default='ask',
choices=['yes', 'if-older', 'no', 'ask', 'ask-if-older']
)
parser.add_argument('template_file')
parser.add_argument('output_file', nargs='?')
args = parser.parse_args(args=sys_args)
parser = TemplateArgumentParser('{name}:render'.format(name=self.name))
parser.add_arguments()
args = parser.parse(sys_args)
template_file = os.path.realpath(os.path.join(state.run_path, args.template_file))
output_file = None
......@@ -85,18 +75,18 @@ class TemplateModule(BaseModule):
'output_file': output_file if output_file else '-',
},
)
except jinja2.UndefinedError as e:
except jinja2.UndefinedError as error:
termcolor.cprint('Template could not be rendered (%s), error message' % args.template_file, color='red')
termcolor.cprint(e.message, color='yellow')
termcolor.cprint(error.message, color='yellow')
sys.exit(1)
if output_file:
try:
with open(output_file, 'w') as fh:
fh.write(rendered)
except IOError as e:
with open(output_file, 'w') as file_handle:
file_handle.write(rendered)
except IOError as error:
termcolor.cprint('Template output could not be saved (%s)' % args.output_file, color='red')
termcolor.cprint(e.message, color='yellow')
termcolor.cprint(str(error), color='yellow')
sys.exit(1)
else:
print(rendered)
......
......@@ -66,7 +66,6 @@ class VirtualenvModule(BaseModule):
"$@"
)
'''.format(
base_path=shlex.quote(self.config['base_path']),
activate_path=shlex.quote(os.path.join(
self.config['env_path'],
'bin',
......
import unittest
from b5.lib.argumentparser import MainArgumentParser, InitArgumentParser, ExecuteArgumentParser, TemplateArgumentParser
class TestMainArgumentParser(unittest.TestCase):
def setUp(self):
self.parser = MainArgumentParser()
self.parser.add_arguments()
def test_config_file_can_be_set(self):
arguments = self.parser.parse(['--config', 'test.yml', 'test'])
self.assertIn('test.yml', arguments.configfiles)
def test_default_configfiles_will_be_set(self):
self.parser.set_default('configfiles', ['~/.b5/config.yml', 'config.yml', 'config.local.yml', 'local.yml'])
arguments = self.parser.parse(['test'])
self.assertIn('~/.b5/config.yml', arguments.configfiles)
self.assertIn('config.yml', arguments.configfiles)
self.assertIn('config.local.yml', arguments.configfiles)
self.assertIn('local.yml', arguments.configfiles)
def test_taskfile_can_be_set(self):
arguments = self.parser.parse(['--taskfile', 'DifferentTaskFile', 'test'])
self.assertIn('DifferentTaskFile', arguments.taskfiles)
def test_default_taskfiles_will_be_set(self):
self.parser.set_default('taskfiles', ['~/.b5/Taskfile', 'Taskfile', 'Taskfile.local'])
arguments = self.parser.parse(['test'])
self.assertIn('~/.b5/Taskfile', arguments.taskfiles)
self.assertIn('Taskfile', arguments.taskfiles)
self.assertIn('Taskfile.local', arguments.taskfiles)
def test_default_run_path_is_set(self):
arguments = self.parser.parse(['test'])
self.assertEqual('build', arguments.run_path)
def test_run_path_can_be_set(self):
arguments = self.parser.parse(['--run-path', 'not_the_build_folder', 'test'])
self.assertEqual('not_the_build_folder', arguments.run_path)
def test_default_shell_is_set(self):
arguments = self.parser.parse(['test'])
self.assertEqual('/bin/bash', arguments.shell)
def test_shell_can_be_set(self):
arguments = self.parser.parse(['--shell', '/bin/sh', 'test'])
self.assertEqual('/bin/sh', arguments.shell)
def test_detect_default_is_set_to_git(self):
arguments = self.parser.parse(['test'])
self.assertEqual('git', arguments.detect)
def test_detect_can_be_set_to_mercurial(self):
arguments = self.parser.parse(['--detect', 'hg', 'test'])
self.assertEqual('hg', arguments.detect)
def test_quiet_is_false_by_default(self):
arguments = self.parser.parse(['test'])
self.assertFalse(arguments.quiet)
def test_quiet_can_be_set_to_true(self):
arguments = self.parser.parse(['--quiet', 'true', 'test'])
self.assertTrue(arguments.quiet)
class TestInitArgumentParserTest(unittest.TestCase):
def setUp(self):
self.parser = InitArgumentParser()
self.parser.add_arguments()
def test_skeleton_can_be_set(self):
arguments = self.parser.parse(['--skeleton', 'test', 'path'])
self.assertEqual('test', arguments.skeleton)
def test_skeleton_can_be_set_with_shortcut(self):
arguments = self.parser.parse(['-s', 'test', 'path'])
self.assertEqual('test', arguments.skeleton)
def test_skeleton_has_basic_as_default(self):
arguments = self.parser.parse(['path'])
self.assertEqual('basic', arguments.skeleton)
def test_branch_can_be_set(self):
arguments = self.parser.parse(['--branch', 'test', 'path'])
self.assertEqual('test', arguments.branch)
def test_branch_can_be_set_with_shortcut(self):
arguments = self.parser.parse(['-b', 'test', 'path'])
self.assertEqual('test', arguments.branch)
class TestExecuteArgumentParser(unittest.TestCase):
def setUp(self):
self.parser = ExecuteArgumentParser()
self.parser.add_arguments()
def test_state_file_can_be_set(self):
arguments = self.parser.parse(['--state-file', 'test'])
self.assertEqual('test', arguments.state_file)
def test_module_can_be_set(self):
arguments = self.parser.parse(['--module', 'test'])
self.assertEqual('test', arguments.module)
def test_method_can_be_set(self):
arguments = self.parser.parse(['--method', 'test'])
self.assertEqual('test', arguments.method)
def test_args_can_be_set(self):
arguments = self.parser.parse(['--args', 'test'])
self.assertIn('test', arguments.args)
class TestTemplateArgumentParser(unittest.TestCase):
def setUp(self):
self.parser = TemplateArgumentParser()
self.parser.add_arguments()
def test_overwrite_can_be_set(self):
arguments = self.parser.parse(['--overwrite', 'yes', 'template_file', 'output_file'])
self.assertEqual('yes', arguments.overwrite)
def test_overwrite_defaults_to_ask(self):
arguments = self.parser.parse(['template_file', 'output_file'])
self.assertEqual('ask', arguments.overwrite)
def test_template_file_can_be_set(self):
arguments = self.parser.parse(['template_file', 'output_file'])
self.assertEqual('template_file', arguments.template_file)
def test_output_file_can_be_set(self):
arguments = self.parser.parse(['template_file', 'output_file'])
self.assertEqual('output_file', arguments.output_file)
if __name__ == '__main__':
unittest.main()
......@@ -35,4 +35,18 @@ task:pypi:release() {
python3 setup.py sdist && \
twine upload dist/*
)
}
\ No newline at end of file
}
task:lint() {
(
cd .. && \
pipenv:run pylint --rcfile=pylintrc --exit-zero b5/
)
}
task:test() {
(
cd .. && \
pipenv:run pytest b5/tests -v
)
}
......@@ -27,7 +27,7 @@ OR
```bash
git clone git@git.team23.de:build/b5.git
cd b5
pip install .
pip3 install .
```
## Development installation (using live version of b5 repository)
......
......@@ -65,6 +65,16 @@ Execute a development shell for the project. May differ based on the project.
most commonly start a docker shell (like `docker-compose run --rm web /bin/bash --login`). We use `b5 django:shell`
instead. This behavior is true for other frameworks providing its own development shell, too.
## Testing
### test