|
| 1 | +#!/usr/bin/env python3 |
| 2 | + |
| 3 | +""" |
| 4 | +This script should use a very simple, functional programming style. |
| 5 | +Avoid Jinja macros in favor of native Python functions. |
| 6 | +
|
| 7 | +Don't go overboard on code generation; use Python only to generate |
| 8 | +content that can't be easily declared statically using CircleCI's YAML API. |
| 9 | +
|
| 10 | +Data declarations (e.g. the nested loops for defining the configuration matrix) |
| 11 | +should be at the top of the file for easy updating. |
| 12 | +
|
| 13 | +See this comment for design rationale: |
| 14 | +https://github.com/pytorch/vision/pull/1321#issuecomment-531033978 |
| 15 | +""" |
| 16 | + |
| 17 | +import jinja2 |
| 18 | +import yaml |
| 19 | +import os.path |
| 20 | + |
| 21 | + |
| 22 | +PYTHON_VERSIONS = ["3.6", "3.7", "3.8"] |
| 23 | + |
| 24 | + |
| 25 | +def build_workflows(prefix='', upload=False, filter_branch=None, indentation=6): |
| 26 | + w = [] |
| 27 | + w += build_download_job(filter_branch) |
| 28 | + for btype in ["wheel", "conda"]: |
| 29 | + for os_type in ["linux", "macos", "windows"]: |
| 30 | + for python_version in PYTHON_VERSIONS: |
| 31 | + w += build_workflow_pair(btype, os_type, python_version, filter_branch, prefix, upload) |
| 32 | + |
| 33 | + return indent(indentation, w) |
| 34 | + |
| 35 | + |
| 36 | +def build_download_job(filter_branch): |
| 37 | + job = { |
| 38 | + "name": "download_third_parties_nix", |
| 39 | + } |
| 40 | + |
| 41 | + if filter_branch: |
| 42 | + job["filters"] = gen_filter_branch_tree(filter_branch) |
| 43 | + return [{"download_third_parties_nix": job}] |
| 44 | + |
| 45 | + |
| 46 | +def build_workflow_pair(btype, os_type, python_version, filter_branch, prefix='', upload=False): |
| 47 | + |
| 48 | + w = [] |
| 49 | + base_workflow_name = "{prefix}binary_{os_type}_{btype}_py{python_version}".format( |
| 50 | + prefix=prefix, |
| 51 | + os_type=os_type, |
| 52 | + btype=btype, |
| 53 | + python_version=python_version, |
| 54 | + ) |
| 55 | + |
| 56 | + w.append(generate_base_workflow(base_workflow_name, python_version, filter_branch, os_type, btype)) |
| 57 | + |
| 58 | + if upload: |
| 59 | + |
| 60 | + is_py3_linux = os_type in ['linux', "windows"] and not python_version.startswith("2.") |
| 61 | + |
| 62 | + w.append(generate_upload_workflow(base_workflow_name, filter_branch, btype)) |
| 63 | + |
| 64 | + if filter_branch == 'nightly' and is_py3_linux: |
| 65 | + pydistro = 'pip' if btype == 'wheel' else 'conda' |
| 66 | + w.append(generate_smoketest_workflow(pydistro, base_workflow_name, filter_branch, python_version, os_type)) |
| 67 | + |
| 68 | + return w |
| 69 | + |
| 70 | + |
| 71 | +def generate_base_workflow(base_workflow_name, python_version, filter_branch, os_type, btype): |
| 72 | + |
| 73 | + d = { |
| 74 | + "name": base_workflow_name, |
| 75 | + "python_version": python_version, |
| 76 | + } |
| 77 | + |
| 78 | + if os_type in ['linux', 'macos']: |
| 79 | + d['requires'] = ['download_third_parties_nix'] |
| 80 | + |
| 81 | + if filter_branch: |
| 82 | + d["filters"] = gen_filter_branch_tree(filter_branch) |
| 83 | + |
| 84 | + return {"binary_{os_type}_{btype}".format(os_type=os_type, btype=btype): d} |
| 85 | + |
| 86 | + |
| 87 | +def gen_filter_branch_tree(*branches): |
| 88 | + return { |
| 89 | + "branches": { |
| 90 | + "only": list(branches), |
| 91 | + }, |
| 92 | + "tags": { |
| 93 | + # Using a raw string here to avoid having to escape |
| 94 | + # anything |
| 95 | + "only": r"/v[0-9]+(\.[0-9]+)*-rc[0-9]+/" |
| 96 | + } |
| 97 | + } |
| 98 | + |
| 99 | + |
| 100 | +def generate_upload_workflow(base_workflow_name, filter_branch, btype): |
| 101 | + d = { |
| 102 | + "name": "{base_workflow_name}_upload".format(base_workflow_name=base_workflow_name), |
| 103 | + "context": "org-member", |
| 104 | + "requires": [base_workflow_name], |
| 105 | + } |
| 106 | + |
| 107 | + if filter_branch: |
| 108 | + d["filters"] = gen_filter_branch_tree(filter_branch) |
| 109 | + |
| 110 | + return {"binary_{btype}_upload".format(btype=btype): d} |
| 111 | + |
| 112 | + |
| 113 | +def generate_smoketest_workflow(pydistro, base_workflow_name, filter_branch, python_version, os_type): |
| 114 | + |
| 115 | + required_build_suffix = "_upload" |
| 116 | + required_build_name = base_workflow_name + required_build_suffix |
| 117 | + |
| 118 | + smoke_suffix = "smoke_test_{pydistro}".format(pydistro=pydistro) |
| 119 | + d = { |
| 120 | + "name": "{base_workflow_name}_{smoke_suffix}".format( |
| 121 | + base_workflow_name=base_workflow_name, smoke_suffix=smoke_suffix), |
| 122 | + "requires": [required_build_name], |
| 123 | + "python_version": python_version, |
| 124 | + } |
| 125 | + |
| 126 | + if filter_branch: |
| 127 | + d["filters"] = gen_filter_branch_tree(filter_branch) |
| 128 | + |
| 129 | + return {"smoke_test_{os_type}_{pydistro}".format(os_type=os_type, pydistro=pydistro): d} |
| 130 | + |
| 131 | + |
| 132 | +def indent(indentation, data_list): |
| 133 | + return ("\n" + " " * indentation).join(yaml.dump(data_list).splitlines()) |
| 134 | + |
| 135 | + |
| 136 | +def unittest_workflows(indentation=6): |
| 137 | + jobs = [] |
| 138 | + jobs += build_download_job(None) |
| 139 | + for os_type in ["linux", "windows", "macos"]: |
| 140 | + for device_type in ["cpu", "gpu"]: |
| 141 | + if os_type == "macos" and device_type == "gpu": |
| 142 | + continue |
| 143 | + |
| 144 | + for i, python_version in enumerate(PYTHON_VERSIONS): |
| 145 | + job = { |
| 146 | + "name": f"unittest_{os_type}_{device_type}_py{python_version}", |
| 147 | + "python_version": python_version, |
| 148 | + } |
| 149 | + |
| 150 | + if device_type == 'gpu': |
| 151 | + job['filters'] = gen_filter_branch_tree('master', 'nightly') |
| 152 | + |
| 153 | + if os_type != "windows": |
| 154 | + job['requires'] = ['download_third_parties_nix'] |
| 155 | + |
| 156 | + jobs.append({f"unittest_{os_type}_{device_type}": job}) |
| 157 | + |
| 158 | + if i == 0 and os_type == "linux" and device_type == "cpu": |
| 159 | + jobs.append({ |
| 160 | + f"stylecheck": { |
| 161 | + "name": f"stylecheck_py{python_version}", |
| 162 | + "python_version": python_version, |
| 163 | + } |
| 164 | + }) |
| 165 | + return indent(indentation, jobs) |
| 166 | + |
| 167 | + |
| 168 | +if __name__ == "__main__": |
| 169 | + d = os.path.dirname(__file__) |
| 170 | + env = jinja2.Environment( |
| 171 | + loader=jinja2.FileSystemLoader(d), |
| 172 | + lstrip_blocks=True, |
| 173 | + autoescape=False, |
| 174 | + ) |
| 175 | + |
| 176 | + with open(os.path.join(d, 'config.yml'), 'w') as f: |
| 177 | + f.write(env.get_template('config.yml.in').render( |
| 178 | + build_workflows=build_workflows, |
| 179 | + unittest_workflows=unittest_workflows, |
| 180 | + )) |
0 commit comments