* chore: improve mypy Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com> * chore(types): type functions in tests Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com> * chore(types): No partial types in tests Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com> --------- Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
180 lines
6.4 KiB
Python
Executable File
180 lines
6.4 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
import shutil
|
|
import subprocess
|
|
import sys
|
|
import textwrap
|
|
import time
|
|
import typing
|
|
from glob import glob
|
|
from pathlib import Path
|
|
from urllib.parse import quote
|
|
|
|
import click
|
|
|
|
|
|
def shell(cmd: str, *, check: bool, **kwargs: object) -> subprocess.CompletedProcess[str]:
|
|
return subprocess.run([cmd], shell=True, check=check, **kwargs) # type: ignore[call-overload, no-any-return]
|
|
|
|
|
|
def git_repo_has_changes() -> bool:
|
|
unstaged_changes = shell("git diff-index --quiet HEAD --", check=False).returncode != 0
|
|
staged_changes = shell("git diff-index --quiet --cached HEAD --", check=False).returncode != 0
|
|
return unstaged_changes or staged_changes
|
|
|
|
|
|
def generate_basic_project(path: Path) -> None:
|
|
sys.path.insert(0, "")
|
|
from test.test_projects.c import new_c_project
|
|
|
|
project = new_c_project()
|
|
project.generate(path)
|
|
|
|
|
|
class CIService(typing.NamedTuple):
|
|
name: str
|
|
dst_config_path: str
|
|
badge_md: str
|
|
|
|
|
|
services = [
|
|
CIService(
|
|
name="appveyor",
|
|
dst_config_path="appveyor.yml",
|
|
badge_md="[](https://ci.appveyor.com/project/joerick/cibuildwheel/branch/{branch})",
|
|
),
|
|
CIService(
|
|
name="azure-pipelines",
|
|
dst_config_path="azure-pipelines.yml",
|
|
badge_md="[](https://dev.azure.com/joerick0429/cibuildwheel/_build/latest?definitionId=2&branchName={branch})",
|
|
),
|
|
CIService(
|
|
name="circleci",
|
|
dst_config_path=".circleci/config.yml",
|
|
badge_md="[](https://circleci.com/gh/pypa/cibuildwheel/tree/{branch})",
|
|
),
|
|
CIService(
|
|
name="github",
|
|
dst_config_path=".github/workflows/example.yml",
|
|
badge_md="[](https://github.com/pypa/cibuildwheel/actions)",
|
|
),
|
|
CIService(
|
|
name="travis-ci",
|
|
dst_config_path=".travis.yml",
|
|
badge_md="[](https://app.travis-ci.com/pypa/cibuildwheel)",
|
|
),
|
|
CIService(
|
|
name="gitlab",
|
|
dst_config_path=".gitlab-ci.yml",
|
|
badge_md="[](https://gitlab.com/joerick/cibuildwheel/-/commits/{branch})",
|
|
),
|
|
CIService(
|
|
name="cirrus-ci",
|
|
dst_config_path=".cirrus.yml",
|
|
badge_md="[](https://cirrus-ci.com/github/pypa/cibuildwheel/{branch})",
|
|
),
|
|
]
|
|
|
|
|
|
def ci_service_for_config_file(config_file: str) -> CIService:
|
|
filename = Path(config_file).name
|
|
|
|
try:
|
|
return next(s for s in services if filename.startswith(s.name))
|
|
except StopIteration:
|
|
msg = f"unknown ci service for config file {config_file}"
|
|
raise ValueError(msg) from None
|
|
|
|
|
|
@click.command()
|
|
@click.argument("config_files", nargs=-1, type=click.Path())
|
|
def run_example_ci_configs(config_files=None):
|
|
"""
|
|
Test the example configs. If no files are specified, will test
|
|
examples/*-minimal.yml
|
|
"""
|
|
|
|
if len(config_files) == 0:
|
|
config_files = glob("examples/*-minimal.yml")
|
|
|
|
# check each CI service has at most 1 config file
|
|
configs_by_service = {}
|
|
for config_file in config_files:
|
|
service = ci_service_for_config_file(config_file)
|
|
if service.name in configs_by_service:
|
|
msg = "You cannot specify more than one config per CI service"
|
|
raise Exception(msg)
|
|
configs_by_service[service.name] = config_file
|
|
|
|
if git_repo_has_changes():
|
|
print("Your git repo has uncommitted changes. Commit or stash before continuing.")
|
|
sys.exit(1)
|
|
|
|
previous_branch = shell(
|
|
"git rev-parse --abbrev-ref HEAD", check=True, capture_output=True, encoding="utf8"
|
|
).stdout.strip()
|
|
|
|
timestamp = time.strftime("%Y-%m-%dT%H-%M-%S", time.gmtime())
|
|
branch_name = f"example-config-test---{previous_branch}-{timestamp}"
|
|
|
|
try:
|
|
shell(f"git checkout --orphan {branch_name}", check=True)
|
|
|
|
example_project = Path("example_root")
|
|
generate_basic_project(example_project)
|
|
|
|
for config_file in config_files:
|
|
service = ci_service_for_config_file(config_file)
|
|
src_config_file = Path(config_file)
|
|
dst_config_file = example_project / service.dst_config_path
|
|
|
|
dst_config_file.parent.mkdir(parents=True, exist_ok=True)
|
|
shutil.copyfile(src_config_file, dst_config_file)
|
|
|
|
subprocess.run(["git", "add", example_project], check=True)
|
|
message = textwrap.dedent(
|
|
f"""\
|
|
Test example minimal configs
|
|
|
|
Testing files: {config_files}
|
|
Generated from branch: {previous_branch}
|
|
Time: {timestamp}
|
|
"""
|
|
)
|
|
subprocess.run(["git", "commit", "--no-verify", "--message", message], check=True)
|
|
shell(f"git subtree --prefix={example_project} push origin {branch_name}", check=True)
|
|
|
|
print("---")
|
|
print()
|
|
print("> **Examples test run**")
|
|
print("> ")
|
|
print(f"> Branch: [{branch_name}](https://github.com/pypa/cibuildwheel/tree/{branch_name})")
|
|
print("> ")
|
|
print("> | Service | Config | Status |")
|
|
print("> |---|---|---|")
|
|
for config_file in config_files:
|
|
service = ci_service_for_config_file(config_file)
|
|
badge = service.badge_md.format(
|
|
branch=branch_name, branch_escaped=quote(branch_name, safe="")
|
|
)
|
|
print(f"> | {service.name} | `{config_file}` | {badge} |")
|
|
print("> ")
|
|
print("> Generated by `bin/run_example_ci_config.py`")
|
|
print()
|
|
print("---")
|
|
finally:
|
|
# remove any local changes
|
|
shutil.rmtree(example_project, ignore_errors=True)
|
|
shell("git checkout -- .", check=True)
|
|
shell(f"git checkout {previous_branch}", check=True)
|
|
shell(f"git branch -D --force {branch_name}", check=True)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
os.chdir(os.path.dirname(__file__))
|
|
os.chdir("..")
|
|
run_example_ci_configs(standalone_mode=True)
|