From 704416d165cc1e22e6e87a3e3824826e2b491ab0 Mon Sep 17 00:00:00 2001 From: Tim Perkins Date: Mon, 9 Dec 2024 16:44:20 -0500 Subject: [PATCH] Options to specify strategy Adds new arguments the `pytest.yaml` reusable workflow, to make it easier to specify a strategy. To generate the strategy, a small utility `genstrat.py` has been added. By default, this utility will output a file identical to the old `strategy.json` (which has been deleted). It is still possible to use `matrix-filter`, if a user prefers to use that. Resolves: #37 --- .github/workflows/pytest.yaml | 40 +++++++++++++++++ README.md | 29 ++++++++++++ genstrat.py | 85 +++++++++++++++++++++++++++++++++++ strategy.json | 16 ------- 4 files changed, 154 insertions(+), 16 deletions(-) create mode 100644 README.md create mode 100755 genstrat.py delete mode 100644 strategy.json diff --git a/.github/workflows/pytest.yaml b/.github/workflows/pytest.yaml index 5ffdf16..83597cd 100644 --- a/.github/workflows/pytest.yaml +++ b/.github/workflows/pytest.yaml @@ -9,6 +9,30 @@ on: # yamllint disable-line rule:truthy default: true required: false type: boolean + strategy-os: + description: >- + Operating systems to include in the strategy, for example: "macos-latest ubuntu-22.04" + default: '' + required: false + type: string + strategy-python: + description: >- + Python versions to include in the strategy, for example: "3.10 3.11" + default: '' + required: false + type: string + strategy-include-py36: + description: >- + Include old Python 3.6 in the strategy + default: true + required: false + type: boolean + strategy-include-stdeb: + description: >- + Include stdeb in the strategy + default: true + required: false + type: boolean matrix-filter: description: 'jq filter string indicating which configuration(s) should be included' @@ -41,6 +65,22 @@ jobs: repository: ${{ inputs.setup-repository }} - id: load run: | + extra_args=() + strat_os=(${{ inputs.strategy-os }}) + if [ "${#strat_os[@]}" -ne 0 ]; then + extra_args+=(--os "${strat_os[@]}") + fi + strat_py=(${{ inputs.strategy-python }}) + if [ "${#strat_py[@]}" -ne 0 ]; then + extra_args+=(--python "${strat_py[@]}") + fi + if [ "${{ inputs.strategy-include-py36 }}" = "false" ]; then + extra_args+=(--include-py36=no) + fi + if [ "${{ inputs.strategy-include-stdeb }}" = "false" ]; then + extra_args+=(--include-stdeb=no) + fi + ./genstrat.py ${extra_args[@]} > strategy.json strategy=$(jq -c -M '${{ inputs.matrix-filter }}' strategy.json) echo "strategy=${strategy}" >> $GITHUB_OUTPUT diff --git a/README.md b/README.md new file mode 100644 index 0000000..3941b7d --- /dev/null +++ b/README.md @@ -0,0 +1,29 @@ +# Colcon CI + +This repository contains both a GitHub action and a reusable workflow. + +## Action + +The action (`action.yaml`) does the following: + +1. Sets up a virtual environment +2. Installs the package under test +3. Installs necessary dependencies +4. Runs tests using `pytest` +5. Tests publishing of the package (optional) + +## Reusable Workflow + +The reusable workflow (`pytest.yaml`) does the following: + +1. Generates a strategy of which Python and OS versions to test +2. For the matrix of Python and OS versions: + 1. Sets up Python + 3. Runs the test action described above + 2. Uploads code coverage (optional) + +The strategy is generated by `genstrat.py` which is a small utility to generate +a `strategy.json` file. Refer to `genstrat.py --help` for usage, including +default values for OS and Python versions in the strategy matrix. Refer to +`pytest.yaml` for how to specify arguments to the reusable workflow, which +ultimately calls `genstrat.py`. diff --git a/genstrat.py b/genstrat.py new file mode 100755 index 0000000..a2a2f6e --- /dev/null +++ b/genstrat.py @@ -0,0 +1,85 @@ +#!/usr/bin/env python3 + +import argparse +import json +import sys + + +DEFAULT_OS = ("macos-latest", "ubuntu-22.04", "windows-latest") +DEFAULT_PYTHON = ("3.8", "3.9", "3.10", "3.11", "3.12") + +PY36_JSON = '{ "os": "ubuntu-20.04", "python": "3.6" }' +STDEB_JSON = '{ "os": "ubuntu-22.04", "stdeb-check": "1" }' + + +def yes_no_to_bool(yes_no): + assert yes_no in ["yes", "no"] + return yes_no == "yes" + + +def get_parser(): + parser = argparse.ArgumentParser() + parser.add_argument( + "--os", + nargs="+", + action="extend", + help=f"matrix OSs (default: {' '.join(DEFAULT_OS)})", + ) + parser.add_argument( + "--python", + nargs="+", + action="extend", + help=f"matrix Python versions (default: {' '.join(DEFAULT_PYTHON)})", + ) + parser.add_argument( + "-i", + "--include", + action="append", + help="include some arbitrary section (default: '')", + ) + parser.add_argument( + "--include-py36", + choices=["yes", "no"], + default="yes", + help="include the Python 3.6 section (default: yes)", + ) + parser.add_argument( + "--include-stdeb", + choices=["yes", "no"], + default="yes", + help="include the stdeb section (default: yes)", + ) + return parser + + +def parse_args(argv): + parser = get_parser() + args = parser.parse_args(argv) + if args.os is None: + args.os = list(DEFAULT_OS) + if args.python is None: + args.python = list(DEFAULT_PYTHON) + if args.include is None: + args.include = [] + if yes_no_to_bool(args.include_py36): + args.include.append(PY36_JSON) + if yes_no_to_bool(args.include_stdeb): + args.include.append(STDEB_JSON) + return args + + +def main(argv): + args = parse_args(argv) + strategy = {"matrix": {"os": args.os, "python": args.python, "include": []}} + for inc in args.include: + try: + strategy["matrix"]["include"].append(json.loads(inc)) + except json.JSONDecodeError: + print(f"ERROR: Failed to parse: {repr(inc)}", file=sys.stderr) + return 1 + print(json.dumps(strategy)) + return 0 + + +if __name__ == "__main__": + sys.exit(main(sys.argv[1:])) diff --git a/strategy.json b/strategy.json deleted file mode 100644 index db7645c..0000000 --- a/strategy.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "matrix": { - "os": ["macos-latest", "ubuntu-22.04", "windows-latest"], - "python": ["3.8", "3.9", "3.10", "3.11", "3.12"], - "include": [ - { - "os": "ubuntu-20.04", - "python": "3.6" - }, - { - "os": "ubuntu-22.04", - "stdeb-check": "1" - } - ] - } -}