# Generate the matrix of jobs to run tests, build docs, build installers
#
# run `python .github/workflows/matrix.py --json` to see the output
#
# Philosophy:
# - test everything
# - get unittest results back as fast as possible, burning extra CPU time
#   to do that if necessary
# - sphinx is slow, build documentation separately to running the unittests
# - pyinstaller is slow, run it separately to running the unittests

import json
import os
import sys

pretty = "--pretty" in sys.argv
is_push_event = os.environ.get("GITHUB_EVENT_NAME", "") == "push"


test_jobs = []
installer_jobs = []

# List of OS images to use for release builds
# Notes on OS selection:
# - macos
# - windows
# - linux: the release that is used defines the oldest distro on which
#   the Linux pyinstaller output will run as libc is still
#   dynamically linked by pyinstaller.
#   https://pyinstaller.readthedocs.io/en/stable/usage.html#making-gnu-linux-apps-forward-compatible
os_release_list = [
    "ubuntu-22.04",
    "windows-latest",
    "macos-latest",
]

# List of OS images to use for release tests
os_test_list = os_release_list + [
    "ubuntu-latest",
]

# List of python versions to use for release builds
python_release_list = [
    # The current interpreter, set from ci.yml, is the release version
    f"{sys.version_info.major}.{sys.version_info.minor}"
]

# List of python versions to use for tests
python_test_list = python_release_list + [
    # Additional test versions - add more to this list as needed
    "3.12",
    "3.14",
]

# On push events, only test the latest Python version to avoid duplicating
# work when a pull_request event fires for the same commit.
if is_push_event:
    python_test_list = [max(python_test_list, key=lambda v: tuple(int(x) for x in v.split(".")))]


def truthy(val):
    # the json importer in the github actions doesn't cope with true, false,
    # or none particularly nicely; making this output only 0/1 as integers
    # simplifies all the conditionals in the yml file.
    return int(bool(val))


def entry(job_name="Job", os=None, pyver=None, tests=None, docs=None, installer=None):
    # Stuff the values into a dict that will appear in the json;
    # make sure all entries have all keys even if not specified for that
    # job so that validating the matrix or fallbacks for missing keys are
    # not needed in the yml file.
    return {
        "job_name": job_name,
        "os": os or os_release_list[0],
        "python-version": pyver or python_release_list[0],
        "tests": truthy(tests),
        "installer": truthy(installer),
    }


## Construct the list of jobs for build+test
# Test all the OS/pyver combinations as quickly as possible to get unittest
# results back to the author; docs and installer are much slower, so
# leave them for a separate build.
for os in os_test_list:
    for pyver in python_test_list:
        test_jobs.append(
            entry(
                job_name=f"Test ({os}, {pyver})",
                os=os,
                pyver=pyver,
                tests=True,
                installer=False,
            )
        )


## Construct the list of jobs for the installer
# Building the installer needs the docs but not the tests; this is a simple
# time optimisation to get the installer built faster, since the tests
# take a bit of time to run and are already run in the 'test' jobs.
for os in os_release_list:
    for pyver in python_release_list:
        installer_jobs.append(
            entry(
                job_name=f"Installer ({os}, {pyver})",
                os=os,
                pyver=pyver,
                tests=False,
                installer=True,
            )
        )

if not pretty:
    print("test_matrix=", end="")

print(json.dumps(test_jobs, indent=4 if pretty else None))

if not pretty:
    print("installer_matrix=", end="")

print(json.dumps(installer_jobs, indent=4 if pretty else None))
