Files
cibuildwheel/bin/run_example_ci_configs.py
T

171 lines
5.9 KiB
Python
Executable File

#!/usr/bin/env python3
from __future__ import annotations
import os
import shutil
import sys
import textwrap
import time
from collections import namedtuple
from glob import glob
from pathlib import Path
from subprocess import run
from urllib.parse import quote
import click
def shell(cmd, **kwargs):
return run([cmd], shell=True, **kwargs)
def git_repo_has_changes():
unstaged_changes = shell('git diff-index --quiet HEAD --').returncode != 0
staged_changes = shell('git diff-index --quiet --cached HEAD --').returncode != 0
return unstaged_changes or staged_changes
def generate_basic_project(path):
sys.path.insert(0, '')
from test.test_projects.c import new_c_project
project = new_c_project()
project.generate(path)
CIService = namedtuple('CIService', 'name dst_config_path badge_md')
services = [
CIService(
name='appveyor',
dst_config_path='appveyor.yml',
badge_md='[![Build status](https://ci.appveyor.com/api/projects/status/wbsgxshp05tt1tif/branch/{branch}?svg=true)](https://ci.appveyor.com/project/joerick/cibuildwheel/branch/{branch})',
),
CIService(
name='azure-pipelines',
dst_config_path='azure-pipelines.yml',
badge_md='[![Build Status](https://dev.azure.com/joerick0429/cibuildwheel/_apis/build/status/joerick.cibuildwheel?branchName={branch})](https://dev.azure.com/joerick0429/cibuildwheel/_build/latest?definitionId=2&branchName={branch})',
),
CIService(
name='circleci',
dst_config_path='.circleci/config.yml',
badge_md='[![CircleCI](https://circleci.com/gh/joerick/cibuildwheel/tree/{branch_escaped}.svg?style=svg)](https://circleci.com/gh/joerick/cibuildwheel/tree/{branch})',
),
CIService(
name='github',
dst_config_path='.github/workflows/example.yml',
badge_md='[![Build](https://github.com/joerick/cibuildwheel/workflows/Build/badge.svg?branch={branch})](https://github.com/joerick/cibuildwheel/actions)',
),
CIService(
name='travis-ci',
dst_config_path='.travis.yml',
badge_md='[![Build Status](https://travis-ci.org/joerick/cibuildwheel.svg?branch={branch})](https://travis-ci.org/joerick/cibuildwheel)',
),
CIService(
name='gitlab',
dst_config_path='.gitlab-ci.yml',
badge_md='[![Gitlab](https://gitlab.com/joerick/cibuildwheel/badges/{branch}/pipeline.svg)](https://gitlab.com/joerick/cibuildwheel/-/commits/{branch})',
),
]
def ci_service_for_config_file(config_file):
service_name = Path(config_file).name.rsplit('-', 1)[0]
for service in services:
if service.name == service_name:
return service
raise ValueError(f'unknown ci service for config file {config_file}')
@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:
raise Exception('You cannot specify more than one config per CI service')
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)
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}
'''
)
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/joerick/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 -- .')
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)