Skip to content

Commit

Permalink
Add CI workflow columns in README
Browse files Browse the repository at this point in the history
  • Loading branch information
sip-21 authored and vincenzopalazzo committed Jun 1, 2024
1 parent 5ea35a4 commit e3b4546
Show file tree
Hide file tree
Showing 5 changed files with 348 additions and 70 deletions.
128 changes: 106 additions & 22 deletions .ci/test.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
from pathlib import Path
import subprocess
from pprint import pprint
from collections import namedtuple
from typing import Generator

import json
import logging
import os
import shlex
import shutil
import subprocess
import sys
import tempfile
import shlex
import os
from collections import namedtuple
from pathlib import Path, PosixPath
from typing import Generator, List
from itertools import chain

logging.basicConfig(stream=sys.stdout, level=logging.DEBUG)

Expand All @@ -18,8 +18,8 @@
'.ci',
'.git',
'.github',
'archived',
'lightning',
'feeadjuster'
]

global_dependencies = [
Expand All @@ -42,6 +42,10 @@
)


def list_plugins(plugins):
return ", ".join([p.name for p in sorted(plugins)])


def enumerate_plugins(basedir: Path) -> Generator[Plugin, None, None]:
plugins = list([
x for x in basedir.iterdir() \
Expand All @@ -50,13 +54,17 @@ def enumerate_plugins(basedir: Path) -> Generator[Plugin, None, None]:
pip_pytest = [
x for x in plugins if (x / Path('requirements.txt')).exists()
]
print(f"Pip plugins: {list_plugins(pip_pytest)}")

poetry_pytest = [
x for x in plugins if (x / Path("pyproject.toml")).exists()
]
print(poetry_pytest)
print(f"Poetry plugins: {list_plugins(poetry_pytest)}")

other_plugins = [x for x in plugins if x not in pip_pytest and x not in poetry_pytest]
other_plugins = [
x for x in plugins if x not in pip_pytest and x not in poetry_pytest
]
print(f"Other plugins: {list_plugins(other_plugins)}")

for p in sorted(pip_pytest):
yield Plugin(
Expand Down Expand Up @@ -227,20 +235,24 @@ def install_pyln_testing(pip_path):
stderr=subprocess.STDOUT,
)

def run_one(p: Plugin) -> bool:
print("Running tests on plugin {p.name}".format(p=p))

testfiles = [
def get_testfiles(p: Plugin) -> List[PosixPath]:
return [
x for x in p.path.iterdir()
if (x.is_dir() and x.name == 'tests')
or (x.name.startswith("test_") and x.name.endswith('.py'))
]

if len(testfiles) == 0:
def has_testfiles(p: Plugin) -> bool:
return len(get_testfiles(p)) > 0

def run_one(p: Plugin) -> bool:
print("Running tests on plugin {p.name}".format(p=p))

if not has_testfiles(p):
print("No test files found, skipping plugin {p.name}".format(p=p))
return True

print("Found {ctestfiles} test files, creating virtualenv and running tests".format(ctestfiles=len(testfiles)))
print("Found {ctestfiles} test files, creating virtualenv and running tests".format(ctestfiles=len(get_testfiles(p))))
print("##[group]{p.name}".format(p=p))

# Create a virtual env
Expand Down Expand Up @@ -295,8 +307,65 @@ def run_one(p: Plugin) -> bool:
finally:
print("##[endgroup]")


def run_all(args):
def configure_git():
# Git requires some user and email to be configured in order to work in the context of GitHub Actions.
subprocess.run(
["git", "config", "--global", "user.email", '"[email protected]"']
)
subprocess.run(["git", "config", "--global", "user.name", '"lightningd"'])


# gather data
def collect_gather_data(results, success):
gather_data = {}
for t in results:
p = t[0]
if has_testfiles(p):
if success or t[1]:
gather_data[p.name] = "passed"
else:
gather_data[p.name] = "failed"
return gather_data


def push_gather_data(data, workflow, python_version):
print("Pushing gather data...")
configure_git()
subprocess.run(["git", "fetch"])
subprocess.run(["git", "checkout", "badges"])
filenames_to_add = []
for plugin_name, result in data.items():
filenames_to_add.append(git_add_gather_data(
plugin_name, result, workflow, python_version
))
output = subprocess.check_output(list(chain(["git", "add", "-v"], filenames_to_add))).decode("utf-8")
print(f"output from git add: {output}")
if output != "":
output = subprocess.check_output(
[
"git",
"commit",
"-m",
f"Update test result for Python{python_version} to ({workflow} workflow)",
]
).decode("utf-8")
print(f"output from git commit: {output}")
subprocess.run(["git", "push", "origin", "badges"])
print("Done.")


def git_add_gather_data(plugin_name, result, workflow, python_version):
_dir = f".badges/gather_data/{workflow}/{plugin_name}"
filename = os.path.join(_dir, f"python{python_version}.txt")
os.makedirs(_dir, exist_ok=True)
with open(filename, "w") as file:
print(f"Writing {filename}")
file.write(result)

return filename


def run_all(workflow, python_version, update_badges, plugin_names):
root_path = subprocess.check_output([
'git',
'rev-parse',
Expand All @@ -306,20 +375,35 @@ def run_all(args):
root = Path(root_path)

plugins = list(enumerate_plugins(root))
if args != []:
plugins = [p for p in plugins if p.name in args]
if plugin_names != []:
plugins = [p for p in plugins if p.name in plugin_names]
print("Testing the following plugins: {names}".format(names=[p.name for p in plugins]))
else:
print("Testing all plugins in {root}".format(root=root))

results = [(p, run_one(p)) for p in plugins]
success = all([t[1] for t in results])

if update_badges:
push_gather_data(collect_gather_data(results, success), workflow, python_version)

if not success:
print("The following tests failed:")
for t in filter(lambda t: not t[1], results):
print(" - {p.name} ({p.path})".format(p=t[0]))
sys.exit(1)
else:
print("All tests passed.")


if __name__ == "__main__":
run_all(sys.argv[1:])
import argparse

parser = argparse.ArgumentParser(description='Plugins test script')
parser.add_argument("workflow", type=str, help="Name of the GitHub workflow")
parser.add_argument("python_version", type=str, help="Python version")
parser.add_argument("--update-badges", action='store_true', help="Whether badges data should be updated")
parser.add_argument("plugins", nargs="*", default=[], help="List of plugins")
args = parser.parse_args()

run_all(args.workflow, args.python_version, args.update_badges, args.plugins)
168 changes: 168 additions & 0 deletions .ci/update_badges.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
import json
import os
import subprocess
from collections import namedtuple
from pathlib import Path, PosixPath
from typing import Generator, List

Plugin = namedtuple(
"Plugin",
[
"name",
"path",
"language",
"framework",
"details",
],
)

exclude = [
".ci",
".git",
".github",
"archived",
"lightning",
]


def configure_git():
# Git needs some user and email to be configured in order to work in the context of GitHub Actions.
subprocess.run(
["git", "config", "--global", "user.email", '"[email protected]"']
)
subprocess.run(["git", "config", "--global", "user.name", '"lightningd"'])


def get_testfiles(p: Plugin) -> List[PosixPath]:
return [
x
for x in p.path.iterdir()
if (x.is_dir() and x.name == "tests")
or (x.name.startswith("test_") and x.name.endswith(".py"))
]


def has_testfiles(p: Plugin) -> bool:
return len(get_testfiles(p)) > 0


def list_plugins(plugins):
return ", ".join([p.name for p in sorted(plugins)])


def enumerate_plugins(basedir: Path) -> Generator[Plugin, None, None]:
plugins = list(
[x for x in basedir.iterdir() if x.is_dir() and x.name not in exclude]
)
pip_pytest = [x for x in plugins if (x / Path("requirements.txt")).exists()]
print(f"Pip plugins: {list_plugins(pip_pytest)}")

poetry_pytest = [x for x in plugins if (x / Path("pyproject.toml")).exists()]
print(f"Poetry plugins: {list_plugins(poetry_pytest)}")

other_plugins = [
x for x in plugins if x not in pip_pytest and x not in poetry_pytest
]
print(f"Other plugins: {list_plugins(other_plugins)}")

for p in sorted(pip_pytest):
yield Plugin(
name=p.name,
path=p,
language="python",
framework="pip",
details={
"requirements": p / Path("requirements.txt"),
"devrequirements": p / Path("requirements-dev.txt"),
},
)

for p in sorted(poetry_pytest):
yield Plugin(
name=p.name,
path=p,
language="python",
framework="poetry",
details={
"pyproject": p / Path("pyproject.toml"),
},
)

for p in sorted(other_plugins):
yield Plugin(
name=p.name,
path=p,
language="other",
framework="generic",
details={
"requirements": p / Path("tests/requirements.txt"),
"setup": p / Path("tests/setup.sh"),
},
)


def update_and_commit_badge(plugin_name, passed, workflow):
json_data = { "schemaVersion": 1, "label": "", "message": " ✔ ", "color": "green" }
if not passed:
json_data.update({"message": "✗", "color": "red"})

filename = os.path.join(".badges", f"{plugin_name}_{workflow}.json")
with open(filename, "w") as file:
file.write(json.dumps(json_data))

output = subprocess.check_output(["git", "add", "-v", filename]).decode("utf-8")
if output != "":
subprocess.run(["git", "commit", "-m", f'Update {plugin_name} badge to {"passed" if passed else "failed"} ({workflow})'])
return True
return False


def push_badges_data(workflow, num_of_python_versions):
print("Pushing badges data...")
configure_git()
subprocess.run(["git", "fetch"])
subprocess.run(["git", "checkout", "badges"])
subprocess.run(["git", "pull"])

root_path = (
subprocess.check_output(["git", "rev-parse", "--show-toplevel"])
.decode("ASCII")
.strip()
)
plugins = list(enumerate_plugins(Path(root_path)))

any_changes = False
for plugin in plugins:
results = []
_dir = f".badges/gather_data/main/{plugin.name}"
if os.path.exists(_dir):
for child in Path(_dir).iterdir():
result = child.read_text().strip()
results.append(result)
print(f"Results for {child}: {result}")

passed = False
if (
len(set(results)) == 1
and results[0] == "passed"
# and len(results) == num_of_python_versions # TODO: Disabled as gather data for python versions is missing sporadingly.
):
passed = True
any_changes |= update_and_commit_badge(plugin.name, passed, workflow)

if any_changes:
subprocess.run(["git", "push", "origin", "badges"])
print("Done.")


if __name__ == "__main__":
import argparse

parser = argparse.ArgumentParser(description="Plugins completion script")
parser.add_argument("workflow", type=str, help="Name of the GitHub workflow")
parser.add_argument(
"num_of_python_versions", type=str, help="Number of Python versions"
)
args = parser.parse_args()

push_badges_data(args.workflow, int(args.num_of_python_versions))
Loading

0 comments on commit e3b4546

Please sign in to comment.