feat: PyGithub and direct writing
This commit is contained in:
@@ -21,5 +21,5 @@ repos:
|
|||||||
rev: v0.782
|
rev: v0.782
|
||||||
hooks:
|
hooks:
|
||||||
- id: mypy
|
- id: mypy
|
||||||
files: cibuildwheel/|test/
|
files: ^(cibuildwheel/|test/|bin/projects.py)
|
||||||
pass_filenames: false
|
pass_filenames: false
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ For more information, including how to build on GitHub Actions, Appveyor, Azure
|
|||||||
Options
|
Options
|
||||||
-------
|
-------
|
||||||
|
|
||||||
<!-- START bin.project.py -->
|
<!-- START bin/project.py -->
|
||||||
|
|
||||||
| Name | Stars | CI | OS | Notes |
|
| Name | Stars | CI | OS | Notes |
|
||||||
|-------------------------|-------|----|----|:------|
|
|-------------------------|-------|----|----|:------|
|
||||||
@@ -210,7 +210,8 @@ Options
|
|||||||
<!-- xmlstarlet: 7, last pushed 10 days ago -->
|
<!-- xmlstarlet: 7, last pushed 10 days ago -->
|
||||||
<!-- apriltags2-ethz: 1, last pushed 567 days ago -->
|
<!-- apriltags2-ethz: 1, last pushed 567 days ago -->
|
||||||
|
|
||||||
<!-- END bin.project.py -->
|
|
||||||
|
<!-- END bin/project.py -->
|
||||||
|
|
||||||
> Add your repo here! Send a PR, adding your information to `bin/projects.yml`.
|
> Add your repo here! Send a PR, adding your information to `bin/projects.yml`.
|
||||||
>
|
>
|
||||||
|
|||||||
+69
-30
@@ -1,25 +1,42 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
import json
|
"""
|
||||||
from typing import Dict, Any, List
|
Convert a yaml project list into a nice table.
|
||||||
|
|
||||||
|
Suggested usage:
|
||||||
|
|
||||||
|
./bin/projects.py bin/projects.yml --online --auth $GITHUB_API_TOKEN --readme README.md
|
||||||
|
git diff
|
||||||
|
"""
|
||||||
|
|
||||||
|
import builtins
|
||||||
|
import functools
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
from io import StringIO
|
||||||
|
from typing import Dict, Any, List, Optional, TextIO
|
||||||
|
|
||||||
import click
|
import click
|
||||||
import requests
|
|
||||||
import yaml
|
import yaml
|
||||||
|
from github import Github
|
||||||
|
|
||||||
|
|
||||||
def get_info(gh: str) -> Dict[str, Any]:
|
ICONS = (
|
||||||
url = f"https://api.github.com/repos/{gh}"
|
"appveyor",
|
||||||
req = requests.get(url)
|
"github",
|
||||||
return json.loads(req.content)
|
"azure-pipelines",
|
||||||
|
"circleci",
|
||||||
|
"gitlab",
|
||||||
|
"travisci",
|
||||||
|
"windows",
|
||||||
|
"apple",
|
||||||
|
"linux",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class Project:
|
class Project:
|
||||||
NAME: int = 0
|
NAME: int = 0
|
||||||
ONLINE: bool = True
|
|
||||||
|
|
||||||
def __init__(self, config: Dict[str, Any]):
|
def __init__(self, config: Dict[str, Any], github: Optional[Github] = None):
|
||||||
try:
|
try:
|
||||||
self.name: str = config["name"]
|
self.name: str = config["name"]
|
||||||
self.gh: str = config["gh"]
|
self.gh: str = config["gh"]
|
||||||
@@ -32,18 +49,15 @@ class Project:
|
|||||||
self.ci: List[str] = config.get("ci", [])
|
self.ci: List[str] = config.get("ci", [])
|
||||||
self.os: List[str] = config.get("os", [])
|
self.os: List[str] = config.get("os", [])
|
||||||
|
|
||||||
if self.ONLINE:
|
self.online = github is not None
|
||||||
info = get_info(self.stars_repo)
|
if github is not None:
|
||||||
try:
|
repo = github.get_repo(self.stars_repo)
|
||||||
self.num_stars = info["stargazers_count"]
|
self.num_stars = repo.stargazers_count
|
||||||
self.pushed_at = datetime.strptime(info["pushed_at"], "%Y-%m-%dT%H:%M:%SZ")
|
self.pushed_at = repo.pushed_at
|
||||||
if not self.notes:
|
if not self.notes:
|
||||||
notes = info["description"]
|
notes = repo.description
|
||||||
if notes:
|
if notes:
|
||||||
self.notes = f":closed_book: {notes}"
|
self.notes = f":closed_book: {notes}"
|
||||||
except KeyError:
|
|
||||||
print("Invalid response from Github:", info)
|
|
||||||
raise
|
|
||||||
else:
|
else:
|
||||||
self.num_stars = 0
|
self.num_stars = 0
|
||||||
self.pushed_at = datetime.utcnow()
|
self.pushed_at = datetime.utcnow()
|
||||||
@@ -52,13 +66,13 @@ class Project:
|
|||||||
self.__class__.NAME = max(self.__class__.NAME, name_len)
|
self.__class__.NAME = max(self.__class__.NAME, name_len)
|
||||||
|
|
||||||
def __lt__(self, other: "Project") -> bool:
|
def __lt__(self, other: "Project") -> bool:
|
||||||
if self.ONLINE:
|
if self.online:
|
||||||
return self.num_stars < other.num_stars
|
return self.num_stars < other.num_stars
|
||||||
else:
|
else:
|
||||||
return self.name < other.name
|
return self.name < other.name
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def header(cls):
|
def header(cls) -> str:
|
||||||
return (
|
return (
|
||||||
f"| {'Name':{cls.NAME}} | Stars | CI | OS | Notes |\n"
|
f"| {'Name':{cls.NAME}} | Stars | CI | OS | Notes |\n"
|
||||||
f"|{'':-^{cls.NAME+2 }}|-------|----|----|:------|"
|
f"|{'':-^{cls.NAME+2 }}|-------|----|----|:------|"
|
||||||
@@ -99,9 +113,15 @@ class Project:
|
|||||||
return f"<!-- {self.name}: {self.num_stars}, last pushed {days} days ago -->"
|
return f"<!-- {self.name}: {self.num_stars}, last pushed {days} days ago -->"
|
||||||
|
|
||||||
|
|
||||||
def print_projects(config: List[Dict[str, Any]], *, debug: bool = False, online: bool = True) -> None:
|
def str_projects(
|
||||||
Project.ONLINE = online
|
config: List[Dict[str, Any]], *, online: bool = True, auth: Optional[str] = None
|
||||||
projects = sorted((Project(item) for item in config), reverse=online)
|
) -> str:
|
||||||
|
io = StringIO()
|
||||||
|
print = functools.partial(builtins.print, file=io)
|
||||||
|
|
||||||
|
github = Github(auth) if online else None
|
||||||
|
|
||||||
|
projects = sorted((Project(item, github) for item in config), reverse=online)
|
||||||
|
|
||||||
print(Project.header())
|
print(Project.header())
|
||||||
for project in projects:
|
for project in projects:
|
||||||
@@ -112,22 +132,41 @@ def print_projects(config: List[Dict[str, Any]], *, debug: bool = False, online:
|
|||||||
print(project.links())
|
print(project.links())
|
||||||
|
|
||||||
print()
|
print()
|
||||||
for icon in {"appveyor", "github", "azure-pipelines", "circleci", "gitlab", "travisci", "windows", "apple", "linux"}:
|
for icon in ICONS:
|
||||||
print(f"[{icon} icon]: https://cdn.jsdelivr.net/npm/simple-icons@v4/icons/{icon}.svg")
|
print(
|
||||||
|
f"[{icon} icon]: https://cdn.jsdelivr.net/npm/simple-icons@v4/icons/{icon}.svg"
|
||||||
|
)
|
||||||
|
|
||||||
if debug:
|
|
||||||
print()
|
print()
|
||||||
for project in projects:
|
for project in projects:
|
||||||
print(project.info())
|
print(project.info())
|
||||||
|
|
||||||
|
return io.getvalue()
|
||||||
|
|
||||||
|
|
||||||
@click.command()
|
@click.command()
|
||||||
@click.argument("input", type=click.File("r"))
|
@click.argument("input", type=click.File("r"))
|
||||||
@click.option("--debug/--no-debug")
|
@click.option("--online/--no-online", default=True, help="Get info from GitHub")
|
||||||
@click.option("--online/--no-online", default=True)
|
@click.option("--auth", help="GitHub authentication token")
|
||||||
def projects(input: click.File, debug: bool, online: bool) -> None:
|
@click.option("--readme", type=click.File("r+"), help="Modify a readme file if given")
|
||||||
|
def projects(
|
||||||
|
input: TextIO, online: bool, auth: Optional[str], readme: Optional[TextIO]
|
||||||
|
) -> None:
|
||||||
config = yaml.safe_load(input)
|
config = yaml.safe_load(input)
|
||||||
print_projects(config, debug=debug, online=online)
|
output = str_projects(config, online=online, auth=auth)
|
||||||
|
|
||||||
|
if readme is None:
|
||||||
|
print(output)
|
||||||
|
else:
|
||||||
|
text = readme.read()
|
||||||
|
start_str = "<!-- START bin/project.py -->\n"
|
||||||
|
start = text.find(start_str)
|
||||||
|
end = text.find("<!-- END bin/project.py -->\n")
|
||||||
|
new_text = f"{text[:start + len(start_str)]}\n{output}\n{text[end:]}"
|
||||||
|
|
||||||
|
readme.seek(0)
|
||||||
|
readme.write(new_text)
|
||||||
|
readme.truncate()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
+3
-2
@@ -29,8 +29,9 @@
|
|||||||
ci: [github]
|
ci: [github]
|
||||||
os: [windows, apple, linux]
|
os: [windows, apple, linux]
|
||||||
|
|
||||||
- name: python-admesh
|
# Seems to be defunct
|
||||||
gh: admesh/python-admesh
|
# - name: python-admesh
|
||||||
|
# gh: admesh/python-admesh
|
||||||
|
|
||||||
- name: pybase64
|
- name: pybase64
|
||||||
gh: mayeut/pybase64
|
gh: mayeut/pybase64
|
||||||
|
|||||||
@@ -8,4 +8,5 @@ requests
|
|||||||
click
|
click
|
||||||
mypy
|
mypy
|
||||||
pyyaml
|
pyyaml
|
||||||
|
pygithub
|
||||||
typing-extensions
|
typing-extensions
|
||||||
|
|||||||
Reference in New Issue
Block a user