From 766c3f77b11177f8c831000d96b5f90936f12405 Mon Sep 17 00:00:00 2001 From: Andrew Lewis Date: Fri, 29 Sep 2023 11:37:18 +0200 Subject: [PATCH] [Test] Multiarchify tests --- .drone.jsonnet | 175 +++++++++++------ .drone.yml | 351 ++++++++++++++++++++++++++++++---- test/tools/merge_coveralls.py | 34 +++- 3 files changed, 458 insertions(+), 102 deletions(-) diff --git a/.drone.jsonnet b/.drone.jsonnet index 1123c25df..383ccdc01 100644 --- a/.drone.jsonnet +++ b/.drone.jsonnet @@ -3,7 +3,7 @@ local docker_pipeline = { type: 'docker', }; -local trigger = { +local default_trigger = { trigger: { event: [ 'push', @@ -14,6 +14,64 @@ local trigger = { }, }; +local platform(arch) = { + platform: { + os: 'linux', + arch: arch, + }, +}; + +local coveralls_attribs = { + branch: [ + 'master', + ], + event: [ + 'push', + 'tag', + ], +}; + +local coveralls_trigger = { + trigger: coveralls_attribs, +}; + +local coveralls_when = { + when: coveralls_attribs, +}; + +local notify_pipeline = { + name: 'notify', + depends_on: [ + 'default-amd64', + 'default-arm64', + 'default-noarch', + ], + steps: [ + { + name: 'notify', + image: 'drillster/drone-email', + pull: 'if-not-exists', + settings: { + from: 'noreply@rspamd.com', + host: { + from_secret: 'email_host', + }, + username: { + from_secret: 'email_username', + }, + password: { + from_secret: 'email_password', + }, + }, + }, + ], + trigger: { + status: [ + 'failure', + ], + }, +} + docker_pipeline; + local pipeline(arch) = { local rspamd_volumes = { volumes: [ @@ -23,11 +81,10 @@ local pipeline(arch) = { }, ], }, - name: 'default', - platform: { - os: 'linux', - arch: arch, - }, + local hyperscan_altroot = if (arch) == 'amd64' then '' else '-DHYPERSCAN_ROOT_DIR=/vectorscan', + depends_on: [ + ], + name: 'default-' + arch, steps: [ { name: 'prepare', @@ -47,7 +104,7 @@ local pipeline(arch) = { commands: [ 'test "$(id -un)" = nobody', 'cd /rspamd/build', - 'cmake -DCMAKE_INSTALL_PREFIX=/rspamd/install -DCMAKE_RULE_MESSAGES=OFF -DCMAKE_VERBOSE_MAKEFILE=ON -DENABLE_COVERAGE=ON -DENABLE_LIBUNWIND=ON -DENABLE_HYPERSCAN=ON -GNinja $DRONE_WORKSPACE\n', + 'cmake -DCMAKE_INSTALL_PREFIX=/rspamd/install -DCMAKE_RULE_MESSAGES=OFF -DCMAKE_VERBOSE_MAKEFILE=ON -DENABLE_COVERAGE=ON -DENABLE_LIBUNWIND=ON -DENABLE_HYPERSCAN=ON ' + hyperscan_altroot + ' -GNinja $DRONE_WORKSPACE\n', 'ncpu=$(getconf _NPROCESSORS_ONLN)', 'ninja -j $ncpu install', 'ninja -j $ncpu rspamd-test', @@ -66,7 +123,7 @@ local pipeline(arch) = { 'cd /rspamd/fedora/build', "export LDFLAGS='-fuse-ld=lld'", 'export ASAN_OPTIONS=detect_leaks=0', - 'cmake -DCMAKE_INSTALL_PREFIX=/rspamd/fedora/install -DCMAKE_C_COMPILER=/usr/bin/clang -DCMAKE_CXX_COMPILER=/usr/bin/clang++ -DCMAKE_RULE_MESSAGES=OFF -DCMAKE_VERBOSE_MAKEFILE=ON -DENABLE_CLANG_PLUGIN=ON -DENABLE_FULL_DEBUG=ON -DENABLE_HYPERSCAN=ON -DSANITIZE=address $DRONE_WORKSPACE\n', + 'cmake -DCMAKE_INSTALL_PREFIX=/rspamd/fedora/install -DCMAKE_C_COMPILER=/usr/bin/clang -DCMAKE_CXX_COMPILER=/usr/bin/clang++ -DCMAKE_RULE_MESSAGES=OFF -DCMAKE_VERBOSE_MAKEFILE=ON -DENABLE_CLANG_PLUGIN=ON -DENABLE_FULL_DEBUG=ON -DENABLE_HYPERSCAN=ON ' + hyperscan_altroot + ' -DSANITIZE=address $DRONE_WORKSPACE\n', 'ncpu=$(getconf _NPROCESSORS_ONLN)', 'make -j $ncpu install', 'make -j $ncpu rspamd-test', @@ -160,37 +217,49 @@ local pipeline(arch) = { 'cd /rspamd/build', '$DRONE_WORKSPACE/test/tools/gcov_coveralls.py --exclude test --prefix /rspamd/build --prefix $DRONE_WORKSPACE --out coverage.c.json', 'luacov-coveralls -o coverage.functional.lua.json --dryrun', - '$DRONE_WORKSPACE/test/tools/merge_coveralls.py --root $DRONE_WORKSPACE --input coverage.c.json unit_test_lua.json coverage.functional.lua.json --token=$COVERALLS_REPO_TOKEN', + '$DRONE_WORKSPACE/test/tools/merge_coveralls.py --parallel --root $DRONE_WORKSPACE --input coverage.c.json unit_test_lua.json coverage.functional.lua.json --token=$COVERALLS_REPO_TOKEN', ], environment: { COVERALLS_REPO_TOKEN: { from_secret: 'coveralls_repo_token', }, }, - when: { - branch: [ - 'master', - ], - event: [ - 'push', - 'tag', - ], - }, - } + rspamd_volumes, + } + coveralls_when + rspamd_volumes, + ], + volumes: [ { - name: 'eslint', - image: 'node:18-alpine', - pull: 'if-not-exists', - failure: 'ignore', + name: 'rspamd', + temp: {}, + }, + ], +} + platform(arch) + default_trigger + docker_pipeline; + +local close_coveralls = { + name: 'close_coveralls', + depends_on: [ + 'default-amd64', + 'default-arm64', + ], + steps: [ + { + name: 'close_coveralls', + image: 'rspamd/ci:ubuntu-test-func', + pull: 'always', commands: [ - 'npm install', - './node_modules/.bin/eslint -v', - './node_modules/.bin/eslint ./', - './node_modules/.bin/stylelint -v', - 'npm show stylelint-config-standard version', - './node_modules/.bin/stylelint ./**/*.css ./**/*.html ./**/*.js', + '$DRONE_WORKSPACE/test/tools/merge_coveralls.py --parallel-close --token=$COVERALLS_REPO_TOKEN', ], + environment: { + COVERALLS_REPO_TOKEN: { + from_secret: 'coveralls_repo_token', + }, + }, }, + ], +} + coveralls_trigger + docker_pipeline; + +local noarch_pipeline = { + name: 'default-noarch', + steps: [ { name: 'perl-tidyall', image: 'rspamd/ci:perl-tidyall', @@ -203,43 +272,21 @@ local pipeline(arch) = { ], }, { - name: 'notify', - image: 'drillster/drone-email', + name: 'eslint', + image: 'node:18-alpine', pull: 'if-not-exists', - depends_on: [ - 'rspamd-test', - 'test-fedora-clang', - 'functional', - 'send-coverage', - 'eslint', - 'perl-tidyall', + failure: 'ignore', + commands: [ + 'npm install', + './node_modules/.bin/eslint -v', + './node_modules/.bin/eslint ./', + './node_modules/.bin/stylelint -v', + 'npm show stylelint-config-standard version', + './node_modules/.bin/stylelint ./**/*.css ./**/*.html ./**/*.js', ], - settings: { - from: 'noreply@rspamd.com', - host: { - from_secret: 'email_host', - }, - username: { - from_secret: 'email_username', - }, - password: { - from_secret: 'email_password', - }, - }, - when: { - status: [ - 'failure', - ], - }, - }, - ], - volumes: [ - { - name: 'rspamd', - temp: {}, }, ], -} + trigger + docker_pipeline; +} + default_trigger + docker_pipeline; local signature = { kind: 'signature', @@ -248,5 +295,9 @@ local signature = { [ pipeline('amd64'), + pipeline('arm64'), + close_coveralls, + noarch_pipeline, + notify_pipeline, signature, ] diff --git a/.drone.yml b/.drone.yml index 4dc5b0d66..f97e10fd7 100644 --- a/.drone.yml +++ b/.drone.yml @@ -1,7 +1,8 @@ --- { + "depends_on": [ ], "kind": "pipeline", - "name": "default", + "name": "default-amd64", "platform": { "arch": "amd64", "os": "linux" @@ -25,7 +26,7 @@ "commands": [ "test \"$(id -un)\" = nobody", "cd /rspamd/build", - "cmake -DCMAKE_INSTALL_PREFIX=/rspamd/install -DCMAKE_RULE_MESSAGES=OFF -DCMAKE_VERBOSE_MAKEFILE=ON -DENABLE_COVERAGE=ON -DENABLE_LIBUNWIND=ON -DENABLE_HYPERSCAN=ON -GNinja $DRONE_WORKSPACE\n", + "cmake -DCMAKE_INSTALL_PREFIX=/rspamd/install -DCMAKE_RULE_MESSAGES=OFF -DCMAKE_VERBOSE_MAKEFILE=ON -DENABLE_COVERAGE=ON -DENABLE_LIBUNWIND=ON -DENABLE_HYPERSCAN=ON -GNinja $DRONE_WORKSPACE\n", "ncpu=$(getconf _NPROCESSORS_ONLN)", "ninja -j $ncpu install", "ninja -j $ncpu rspamd-test", @@ -50,7 +51,7 @@ "cd /rspamd/fedora/build", "export LDFLAGS='-fuse-ld=lld'", "export ASAN_OPTIONS=detect_leaks=0", - "cmake -DCMAKE_INSTALL_PREFIX=/rspamd/fedora/install -DCMAKE_C_COMPILER=/usr/bin/clang -DCMAKE_CXX_COMPILER=/usr/bin/clang++ -DCMAKE_RULE_MESSAGES=OFF -DCMAKE_VERBOSE_MAKEFILE=ON -DENABLE_CLANG_PLUGIN=ON -DENABLE_FULL_DEBUG=ON -DENABLE_HYPERSCAN=ON -DSANITIZE=address $DRONE_WORKSPACE\n", + "cmake -DCMAKE_INSTALL_PREFIX=/rspamd/fedora/install -DCMAKE_C_COMPILER=/usr/bin/clang -DCMAKE_CXX_COMPILER=/usr/bin/clang++ -DCMAKE_RULE_MESSAGES=OFF -DCMAKE_VERBOSE_MAKEFILE=ON -DENABLE_CLANG_PLUGIN=ON -DENABLE_FULL_DEBUG=ON -DENABLE_HYPERSCAN=ON -DSANITIZE=address $DRONE_WORKSPACE\n", "ncpu=$(getconf _NPROCESSORS_ONLN)", "make -j $ncpu install", "make -j $ncpu rspamd-test", @@ -167,7 +168,7 @@ "cd /rspamd/build", "$DRONE_WORKSPACE/test/tools/gcov_coveralls.py --exclude test --prefix /rspamd/build --prefix $DRONE_WORKSPACE --out coverage.c.json", "luacov-coveralls -o coverage.functional.lua.json --dryrun", - "$DRONE_WORKSPACE/test/tools/merge_coveralls.py --root $DRONE_WORKSPACE --input coverage.c.json unit_test_lua.json coverage.functional.lua.json --token=$COVERALLS_REPO_TOKEN" + "$DRONE_WORKSPACE/test/tools/merge_coveralls.py --parallel --root $DRONE_WORKSPACE --input coverage.c.json unit_test_lua.json coverage.functional.lua.json --token=$COVERALLS_REPO_TOKEN" ], "depends_on": [ "functional", @@ -196,21 +197,280 @@ "tag" ] } + } + ], + "trigger": { + "event": [ + "push", + "tag", + "pull_request", + "custom" + ] + }, + "type": "docker", + "volumes": [ + { + "name": "rspamd", + "temp": { } + } + ] +} +--- +{ + "depends_on": [ ], + "kind": "pipeline", + "name": "default-arm64", + "platform": { + "arch": "arm64", + "os": "linux" + }, + "steps": [ + { + "commands": [ + "install -d -o nobody -g nogroup /rspamd/build /rspamd/install /rspamd/fedora/build /rspamd/fedora/install" + ], + "image": "ubuntu:22.04", + "name": "prepare", + "pull": "if-not-exists", + "volumes": [ + { + "name": "rspamd", + "path": "/rspamd" + } + ] }, { "commands": [ - "npm install", - "./node_modules/.bin/eslint -v", - "./node_modules/.bin/eslint ./", - "./node_modules/.bin/stylelint -v", - "npm show stylelint-config-standard version", - "./node_modules/.bin/stylelint ./**/*.css ./**/*.html ./**/*.js" + "test \"$(id -un)\" = nobody", + "cd /rspamd/build", + "cmake -DCMAKE_INSTALL_PREFIX=/rspamd/install -DCMAKE_RULE_MESSAGES=OFF -DCMAKE_VERBOSE_MAKEFILE=ON -DENABLE_COVERAGE=ON -DENABLE_LIBUNWIND=ON -DENABLE_HYPERSCAN=ON -DHYPERSCAN_ROOT_DIR=/vectorscan -GNinja $DRONE_WORKSPACE\n", + "ncpu=$(getconf _NPROCESSORS_ONLN)", + "ninja -j $ncpu install", + "ninja -j $ncpu rspamd-test", + "ninja -j $ncpu rspamd-test-cxx" ], - "failure": "ignore", - "image": "node:18-alpine", - "name": "eslint", - "pull": "if-not-exists" + "depends_on": [ + "prepare" + ], + "image": "rspamd/ci:ubuntu-build", + "name": "build", + "pull": "always", + "volumes": [ + { + "name": "rspamd", + "path": "/rspamd" + } + ] }, + { + "commands": [ + "test \"$(id -un)\" = nobody", + "cd /rspamd/fedora/build", + "export LDFLAGS='-fuse-ld=lld'", + "export ASAN_OPTIONS=detect_leaks=0", + "cmake -DCMAKE_INSTALL_PREFIX=/rspamd/fedora/install -DCMAKE_C_COMPILER=/usr/bin/clang -DCMAKE_CXX_COMPILER=/usr/bin/clang++ -DCMAKE_RULE_MESSAGES=OFF -DCMAKE_VERBOSE_MAKEFILE=ON -DENABLE_CLANG_PLUGIN=ON -DENABLE_FULL_DEBUG=ON -DENABLE_HYPERSCAN=ON -DHYPERSCAN_ROOT_DIR=/vectorscan -DSANITIZE=address $DRONE_WORKSPACE\n", + "ncpu=$(getconf _NPROCESSORS_ONLN)", + "make -j $ncpu install", + "make -j $ncpu rspamd-test", + "make -j $ncpu rspamd-test-cxx" + ], + "depends_on": [ + "prepare" + ], + "image": "rspamd/ci:fedora-build", + "name": "build-clang", + "pull": "always", + "volumes": [ + { + "name": "rspamd", + "path": "/rspamd" + } + ] + }, + { + "commands": [ + "test \"$(id -un)\" = nobody", + "ulimit -c unlimited", + "cd /rspamd/build/test", + "set +e", + "env RSPAMD_LUA_EXPENSIVE_TESTS=1 ./rspamd-test -p /rspamd/lua; EXIT_CODE=$?", + "set -e", + "if [ $EXIT_CODE -gt 128 ]; then gdb --batch -ex 'thread apply all bt full' -c /var/tmp/*.rspamd-test.core ./rspamd-test; exit $EXIT_CODE; fi; if [ $EXIT_CODE -ne 0 ]; then exit $EXIT_CODE; fi\n", + "luacov-coveralls -o /rspamd/build/unit_test_lua.json --dryrun", + "set +e", + "./rspamd-test-cxx -s; EXIT_CODE=$?", + "set -e", + "if [ $EXIT_CODE -gt 128 ]; then gdb --batch -ex 'thread apply all bt full' -c /var/tmp/*.rspamd-test-cxx.core ./rspamd-test-cxx; exit $EXIT_CODE; fi\n", + "exit $EXIT_CODE" + ], + "depends_on": [ + "build" + ], + "image": "rspamd/ci:ubuntu-test", + "name": "rspamd-test", + "pull": "always", + "volumes": [ + { + "name": "rspamd", + "path": "/rspamd" + } + ] + }, + { + "commands": [ + "test \"$(id -un)\" = nobody", + "ulimit -c 2097152", + "ulimit -s unlimited", + "export ASAN_OPTIONS=\"detect_leaks=0:print_stacktrace=1:disable_coredump=0\"", + "export UBSAN_OPTIONS=\"print_stacktrace=1:print_summary=0:log_path=/tmp/ubsan\"", + "cd /rspamd/fedora/build/test", + "set +e", + "env RSPAMD_LUA_EXPENSIVE_TESTS=1 ./rspamd-test -p /rspamd/lua; EXIT_CODE=$?", + "set -e", + "if [ $EXIT_CODE -gt 128 ]; then gdb --batch -ex 'bt' -c /var/tmp/*.rspamd-test.core ./rspamd-test; fi\n", + "set +e", + "./rspamd-test-cxx -s; EXIT_CODE=$?", + "set -e", + "if [ $EXIT_CODE -gt 128 ]; then gdb --batch -ex 'thread apply all bt full' -c /var/tmp/*.rspamd-test-cxx.core ./rspamd-test-cxx; exit $EXIT_CODE; fi\n", + "cat /tmp/ubsan.* || true", + "exit $EXIT_CODE" + ], + "depends_on": [ + "build-clang" + ], + "image": "rspamd/ci:fedora-test", + "name": "test-fedora-clang", + "pull": "always", + "volumes": [ + { + "name": "rspamd", + "path": "/rspamd" + } + ] + }, + { + "commands": [ + "cd /rspamd/build", + "ulimit -c unlimited", + "ulimit -s unlimited", + "umask 0000", + "set +e", + "RSPAMD_INSTALLROOT=/rspamd/install robot --removekeywords wuks --exclude isbroken $DRONE_WORKSPACE/test/functional/cases; EXIT_CODE=$?", + "set -e", + "if [ -n \"$HTTP_PUT_AUTH\" ]; then $DRONE_WORKSPACE/test/tools/http_put.py log.html report.html https://$DRONE_SYSTEM_HOSTNAME/testlogs/$DRONE_REPO/$DRONE_BUILD_NUMBER/; fi\n", + "core_files=$(find /var/tmp/ -name '*.core')", + "for core in $core_files; do exe=$(gdb --batch -ex 'info proc mappings' -c $core | tail -1 | awk '{print $5}'); gdb --batch -ex 'bt' -c $core $exe; echo '---'; done\n", + "exit $EXIT_CODE" + ], + "depends_on": [ + "build" + ], + "environment": { + "HTTP_PUT_AUTH": { + "from_secret": "http_put_auth" + } + }, + "image": "rspamd/ci:ubuntu-test-func", + "name": "functional", + "pull": "always", + "volumes": [ + { + "name": "rspamd", + "path": "/rspamd" + } + ] + }, + { + "commands": [ + "cd /rspamd/build", + "$DRONE_WORKSPACE/test/tools/gcov_coveralls.py --exclude test --prefix /rspamd/build --prefix $DRONE_WORKSPACE --out coverage.c.json", + "luacov-coveralls -o coverage.functional.lua.json --dryrun", + "$DRONE_WORKSPACE/test/tools/merge_coveralls.py --parallel --root $DRONE_WORKSPACE --input coverage.c.json unit_test_lua.json coverage.functional.lua.json --token=$COVERALLS_REPO_TOKEN" + ], + "depends_on": [ + "functional", + "rspamd-test" + ], + "environment": { + "COVERALLS_REPO_TOKEN": { + "from_secret": "coveralls_repo_token" + } + }, + "image": "rspamd/ci:ubuntu-test", + "name": "send-coverage", + "pull": "if-not-exists", + "volumes": [ + { + "name": "rspamd", + "path": "/rspamd" + } + ], + "when": { + "branch": [ + "master" + ], + "event": [ + "push", + "tag" + ] + } + } + ], + "trigger": { + "event": [ + "push", + "tag", + "pull_request", + "custom" + ] + }, + "type": "docker", + "volumes": [ + { + "name": "rspamd", + "temp": { } + } + ] +} +--- +{ + "depends_on": [ + "default-amd64", + "default-arm64" + ], + "kind": "pipeline", + "name": "close_coveralls", + "steps": [ + { + "commands": [ + "$DRONE_WORKSPACE/test/tools/merge_coveralls.py --parallel-close --token=$COVERALLS_REPO_TOKEN" + ], + "environment": { + "COVERALLS_REPO_TOKEN": { + "from_secret": "coveralls_repo_token" + } + }, + "image": "rspamd/ci:ubuntu-test-func", + "name": "close_coveralls", + "pull": "always" + } + ], + "trigger": { + "branch": [ + "master" + ], + "event": [ + "push", + "tag" + ] + }, + "type": "docker" +} +--- +{ + "kind": "pipeline", + "name": "default-noarch", + "steps": [ { "commands": [ "tidyall --version", @@ -223,14 +483,41 @@ "pull": "if-not-exists" }, { - "depends_on": [ - "rspamd-test", - "test-fedora-clang", - "functional", - "send-coverage", - "eslint", - "perl-tidyall" + "commands": [ + "npm install", + "./node_modules/.bin/eslint -v", + "./node_modules/.bin/eslint ./", + "./node_modules/.bin/stylelint -v", + "npm show stylelint-config-standard version", + "./node_modules/.bin/stylelint ./**/*.css ./**/*.html ./**/*.js" ], + "failure": "ignore", + "image": "node:18-alpine", + "name": "eslint", + "pull": "if-not-exists" + } + ], + "trigger": { + "event": [ + "push", + "tag", + "pull_request", + "custom" + ] + }, + "type": "docker" +} +--- +{ + "depends_on": [ + "default-amd64", + "default-arm64", + "default-noarch" + ], + "kind": "pipeline", + "name": "notify", + "steps": [ + { "image": "drillster/drone-email", "name": "notify", "pull": "if-not-exists", @@ -245,33 +532,19 @@ "username": { "from_secret": "email_username" } - }, - "when": { - "status": [ - "failure" - ] } } ], "trigger": { - "event": [ - "push", - "tag", - "pull_request", - "custom" + "status": [ + "failure" ] }, - "type": "docker", - "volumes": [ - { - "name": "rspamd", - "temp": { } - } - ] + "type": "docker" } --- { - "hmac": "0000000000000000000000000000000000000000000000000000000000000000", + "hmac": "5ad01ebb42030e1fa7f8a1ed3a9b14eb6d535b6d62f66a644fe9d614bdc26a2d", "kind": "signature" } ... diff --git a/test/tools/merge_coveralls.py b/test/tools/merge_coveralls.py index ff6736f6e..1d294cc98 100755 --- a/test/tools/merge_coveralls.py +++ b/test/tools/merge_coveralls.py @@ -37,12 +37,14 @@ path_mapping = [ ] parser = argparse.ArgumentParser(description='') -parser.add_argument('--input', required=True, nargs='+', help='input files') +parser.add_argument('--input', nargs='+', help='input files') parser.add_argument('--output', help='output file)') parser.add_argument('--root', default="/rspamd/src/github.com/rspamd/rspamd", help='repository root)') parser.add_argument('--install-dir', default="/rspamd/install", help='install root)') parser.add_argument('--build-dir', default="/rspamd/build", help='build root)') parser.add_argument('--token', help='If present, the file will be uploaded to coveralls)') +parser.add_argument('--parallel', action='store_true', help='If present, this is a parallel build)') +parser.add_argument('--parallel-close', action='store_true', help='If present, close parallel build and exit)') def merge_coverage_vectors(c1, c2): @@ -97,9 +99,37 @@ def prepare_path_mapping(): path_mapping[i] = (new_key, path_mapping[i][1]) +def close_parallel_build(): + j = {'payload':{'status': 'done'}} + j['payload']['build_num'] = os.getenv('DRONE_BUILD_NUMBER') + query_str = {'repo_token': args.token} + try: + r = requests.post('https://coveralls.io/webhook', params=query_str, json=j) + r.raise_for_status() + except requests.exceptions.RequestException as e: + print("Failed to send data to coveralls: %s" % e) + sys.exit() + + try: + response = r.json() + if 'url' in response: + print("[coveralls] URL %s" % response['url']) + if 'error' in response: + print("[coveralls] ERROR: %s" % response['error']) + except json.decoder.JSONDecodeError: + print("Bad response: '%s'" % r.text) + if __name__ == '__main__': args = parser.parse_args() + if args.parallel_close: + close_parallel_build() + sys.exit(0) + + if not args.input: + print("error: the following arguments are required: --input") + sys.exit(1) + repository_root = os.path.abspath(os.path.expanduser(args.root)) install_dir = os.path.normpath(os.path.expanduser(args.install_dir)) build_dir = os.path.normpath(os.path.expanduser(args.build_dir)) @@ -123,6 +153,8 @@ if __name__ == '__main__': if 'service_job_id' not in j1 and 'service_job_id' in j2: j1['service_job_id'] = j2['service_job_id'] + if args.parallel: + j1['parallel'] = True if os.getenv('CIRCLECI'): j1['service_name'] = 'circleci' -- 2.39.5