Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions .github/workflows/pytest.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -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

Expand Down
29 changes: 29 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -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`.
85 changes: 85 additions & 0 deletions genstrat.py
Original file line number Diff line number Diff line change
@@ -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:]))
16 changes: 0 additions & 16 deletions strategy.json

This file was deleted.