Skip to content

Commit

Permalink
Generate workflow files with a Jinja template (#2687)
Browse files Browse the repository at this point in the history
* Generate workflow files with a Jinja template

Fixes #2686

* Remove sudo from allowlist_externals

* Update workflows

* Add condition to skip generate-workflows

* Update workflows

* Update .github/workflows/generate_workflows_lib/src/generate_workflows_lib/test.yml.j2

Co-authored-by: Emídio Neto <[email protected]>

* Update .github/workflows/generate_workflows_lib/src/generate_workflows_lib/misc.yml.j2

Co-authored-by: Emídio Neto <[email protected]>

* Update .github/workflows/generate_workflows_lib/src/generate_workflows_lib/lint.yml.j2

Co-authored-by: Emídio Neto <[email protected]>

* Update .github/workflows/generate_workflows_lib/src/generate_workflows_lib/contrib.yml.j2

Co-authored-by: Emídio Neto <[email protected]>

* Update workflows

* Update workflows

---------

Co-authored-by: Emídio Neto <[email protected]>
  • Loading branch information
ocelotl and emdneto authored Aug 6, 2024
1 parent 2a707bc commit f0d8cb3
Show file tree
Hide file tree
Showing 19 changed files with 10,051 additions and 284 deletions.
14 changes: 14 additions & 0 deletions .github/workflows/generate_workflows.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
from pathlib import Path

from generate_workflows_lib import (
generate_test_workflow,
generate_lint_workflow,
generate_misc_workflow
)

tox_ini_path = Path(__file__).parent.parent.parent.joinpath("tox.ini")
workflows_directory_path = Path(__file__).parent

generate_test_workflow(tox_ini_path, workflows_directory_path, "ubuntu-latest")
generate_lint_workflow(tox_ini_path, workflows_directory_path)
generate_misc_workflow(tox_ini_path, workflows_directory_path)
15 changes: 15 additions & 0 deletions .github/workflows/generate_workflows_lib/hatch_build.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from hatchling.builders.hooks.plugin.interface import BuildHookInterface
from pathlib import Path


class CustomBuildHook(BuildHookInterface):

def initialize(self, version, build_data):

with open(
Path(__file__).parent.parent.parent.parent.joinpath("tox.ini")
) as tox_ini_file_0:
with open(
Path(__file__).parent.joinpath("src/generate_workflows_lib/tox.ini"), "w"
) as tox_ini_file_1:
tox_ini_file_1.write(tox_ini_file_0.read())
32 changes: 32 additions & 0 deletions .github/workflows/generate_workflows_lib/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

[project]
name = "generate-workflows-lib"
dynamic = ["version"]
description = "A library to generate workflows"
license = "Apache-2.0"
requires-python = ">=3.8"
authors = [
{ name = "OpenTelemetry Authors", email = "[email protected]" },
]
classifiers = [
"Development Status :: 4 - Beta",
"Intended Audience :: Developers",
"License :: OSI Approved :: Apache Software License",
"Programming Language :: Python",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Typing :: Typed",
]
dependencies = ["Jinja2", "tox"]

[tool.hatch.version]
path = "src/generate_workflows_lib/version.py"

[tool.hatch.build.targets.wheel.hooks.custom]
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
tox.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,267 @@
from re import compile as re_compile
from jinja2 import Environment, FileSystemLoader
from pathlib import Path
from tox.config.cli.parse import get_options
from tox.session.state import State
from tox.config.sets import CoreConfigSet
from tox.config.source.tox_ini import ToxIni
from collections import defaultdict


_tox_test_env_regex = re_compile(
r"(?P<python_version>py\w+)-test-"
r"(?P<name>[-\w]+\w)-?(?P<test_requirements>\d+)?"
)
_tox_lint_env_regex = re_compile(r"lint-(?P<name>[-\w]+)")
_tox_contrib_env_regex = re_compile(
r"py38-test-(?P<name>[-\w]+\w)-?(?P<contrib_requirements>\d+)?"
)


def get_tox_envs(tox_ini_path: Path) -> list:

tox_ini = ToxIni(tox_ini_path)

conf = State(get_options(), []).conf

tox_section = next(tox_ini.sections())

core_config_set = (
CoreConfigSet(conf, tox_section, tox_ini_path.parent, tox_ini_path)
)

(
core_config_set.
loaders.
extend(
tox_ini.
get_loaders(
tox_section,
base=[],
override_map=defaultdict(list, {}),
conf=core_config_set
)
)
)

return core_config_set.load("env_list")


def get_test_job_datas(tox_envs: list, operating_systems: list) -> list:

os_alias = {
"ubuntu-latest": "Ubuntu",
"windows-latest": "Windows"
}

python_version_alias = {
"pypy3": "pypy-3.8",
"py38": "3.8",
"py39": "3.9",
"py310": "3.10",
"py311": "3.11",
"py312": "3.12",
}

test_job_datas = []

for operating_system in operating_systems:
for tox_env in tox_envs:

tox_test_env_match = _tox_test_env_regex.match(tox_env)

if tox_test_env_match is None:
continue

groups = tox_test_env_match.groupdict()

aliased_python_version = (
python_version_alias[groups["python_version"]]
)
tox_env = tox_test_env_match.string

test_requirements = groups["test_requirements"]

if test_requirements is None:
test_requirements = " "

else:
test_requirements = f"-{test_requirements} "

test_job_datas.append(
{
"name": f"{tox_env}_{operating_system}",
"ui_name": (
f"{groups['name']}"
f"{test_requirements}"
f"{aliased_python_version} "
f"{os_alias[operating_system]}"
),
"python_version": aliased_python_version,
"tox_env": tox_env,
"os": operating_system
}

)

return test_job_datas


def get_lint_job_datas(tox_envs: list) -> list:

lint_job_datas = []

for tox_env in tox_envs:

tox_lint_env_match = _tox_lint_env_regex.match(tox_env)

if tox_lint_env_match is None:
continue

tox_env = tox_lint_env_match.string

lint_job_datas.append(
{
"name": f"{tox_env}",
"ui_name": f"{tox_lint_env_match.groupdict()['name']}",
"tox_env": tox_env,
}

)

return lint_job_datas


def get_contrib_job_datas(tox_envs: list) -> list:

contrib_job_datas = []

for tox_env in tox_envs:

tox_contrib_env_match = _tox_contrib_env_regex.match(tox_env)

if tox_contrib_env_match is None:
continue

groups = tox_contrib_env_match.groupdict()

tox_env = tox_contrib_env_match.string

contrib_requirements = groups["contrib_requirements"]

if contrib_requirements is None:
contrib_requirements = " "

else:
contrib_requirements = f"-{contrib_requirements} "

contrib_job_datas.append(
{
"ui_name": (
f"{groups['name']}"
f"{contrib_requirements}"
),
"tox_env": tox_env,
}

)

return contrib_job_datas


def get_misc_job_datas(tox_envs: list) -> list:

misc_job_datas = []

_tox_benchmark_env_regex = re_compile(r"benchmark.+")

for tox_env in tox_envs:
if (
_tox_test_env_regex.match(tox_env) is not None or
_tox_lint_env_regex.match(tox_env) is not None or
_tox_contrib_env_regex.match(tox_env) is not None or
_tox_benchmark_env_regex.match(tox_env) is not None
):
continue

misc_job_datas.append(tox_env)

return misc_job_datas


def _generate_workflow(
job_datas: list, name: str, workflow_directory_path: Path
):

# Github seems to limit the amount of jobs in a workflow file, that is why
# they are split in groups of 250 per workflow file.
for file_number, job_datas in enumerate(
[
job_datas[index:index + 250]
for index in range(0, len(job_datas), 250)
]
):

with open(
workflow_directory_path.joinpath(f"{name}_{file_number}.yml"),
"w"
) as test_yml_file:

test_yml_file.write(
Environment(
loader=FileSystemLoader(Path(__file__).parent)
).get_template(f"{name}.yml.j2").render(
job_datas=job_datas, file_number=file_number
)
)
test_yml_file.write("\n")


def generate_test_workflow(
tox_ini_path: Path,
workflow_directory_path: Path,
*operating_systems
) -> None:

_generate_workflow(
get_test_job_datas(get_tox_envs(tox_ini_path), operating_systems),
"test",
workflow_directory_path
)


def generate_lint_workflow(
tox_ini_path: Path,
workflow_directory_path: Path,
) -> None:

_generate_workflow(
get_lint_job_datas(get_tox_envs(tox_ini_path)),
"lint",
workflow_directory_path
)


def generate_contrib_workflow(
workflow_directory_path: Path,
) -> None:

_generate_workflow(
get_contrib_job_datas(
get_tox_envs(Path(__file__).parent.joinpath("tox.ini"))
),
"contrib",
workflow_directory_path
)


def generate_misc_workflow(
tox_ini_path: Path,
workflow_directory_path: Path,
) -> None:

_generate_workflow(
get_misc_job_datas(get_tox_envs(tox_ini_path)),
"misc",
workflow_directory_path
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Do not edit this file.
# This file is generated automatically by executing tox -e generate-workflows

name: Contrib {{ file_number }}

on:
push:
branches-ignore:
- 'release/*'
pull_request:

env:
CORE_REPO_SHA: ${% raw %}{{ github.sha }}{% endraw %}
CONTRIB_REPO_SHA: main
PIP_EXISTS_ACTION: w

jobs:
{%- for job_data in job_datas %}

{{ job_data.tox_env }}:
name: {{ job_data.ui_name }}
runs-on: ubuntu-latest
steps:
- name: Checkout contrib repo @ SHA - ${% raw %}{{ env.CONTRIB_REPO_SHA }}{% endraw %}
uses: actions/checkout@v4
with:
repository: open-telemetry/opentelemetry-python-contrib
ref: ${% raw %}{{ env.CONTRIB_REPO_SHA }}{% endraw %}

- name: Checkout core repo @ SHA - ${% raw %}{{ github.sha }}{% endraw %}
uses: actions/checkout@v4
with:
repository: open-telemetry/opentelemetry-python
path: opentelemetry-python-core

- name: Set up Python 3.8
uses: actions/setup-python@v5
with:
python-version: "3.8"
architecture: "x64"

- name: Install tox
run: pip install tox

- name: Run tests
run: tox -e {{ job_data.tox_env }} -- -ra
{%- endfor %}
Loading

0 comments on commit f0d8cb3

Please sign in to comment.