Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 50 additions & 36 deletions .github/api.py
Original file line number Diff line number Diff line change
@@ -1,27 +1,26 @@
# -*- coding: utf-8 -*-
#
# This script parses the README.md and generates a machine-readable
# data structure from it, for publication as an API.
#
# https://springload.github.io/awesome-wagtail/api/v1/readme.json
#
# It is automatically ran as part of Travis builds, also validating the README
# formatting, and the API endpoint is deployed on successful builds on master.
#
# See also:
# - https://djangopackages.org/api/v3/grids/wagtail-cms/
# - https://github.com/awesomerank/rank

from __future__ import absolute_import, unicode_literals
"""
This script parses the README.md and generates a machine-readable
data structure from it, for publication as an API.

https://springload.github.io/awesome-wagtail/api/v1/readme.json

It is automatically ran as part of Travis builds, also validating the README
formatting, and the API endpoint is deployed on successful builds on master.

See also:
- https://djangopackages.org/api/v3/grids/wagtail-cms/
- https://github.com/awesomerank/rank
"""

import json
import codecs
import datetime
from datetime import datetime, timezone
from pathlib import Path

API_PATH = '/api/v1/readme.json'


def parse_line(line, category):
def parse_line(line: str, category: str) -> dict[str, str]:
"""Parse a single line from the README into a structured dictionary."""
print(line)
name = line.split('](')[0][3:]
url = line.split('](')[1].split(')')[0]
Expand All @@ -35,11 +34,13 @@ def parse_line(line, category):
}


def parse_section(section, category=''):
return [parse_line(l, category) for l in section.split('\n')]
def parse_section(section: str, category: str = '') -> list[dict[str, str]]:
"""Parse a section of lines into a list of structured items."""
return [parse_line(line, category) for line in section.split('\n')]


def parse_subsections(section):
def parse_subsections(section: str) -> list[dict[str, str]]:
"""Parse a section containing multiple subsections."""
subsections = section.split('### ')[1:]

items = []
Expand All @@ -53,33 +54,46 @@ def parse_subsections(section):
return items


def cut_section(start):
return readme.split('## %s\n\n' % start)[1].split('\n\n## ')[0]
def cut_section(readme: str, start: str) -> str:
"""Extract a specific section from the README."""
return readme.split(f'## {start}\n\n')[1].split('\n\n## ')[0]


def parse_readme(readme):
def parse_readme(readme: str) -> dict:
"""Parse the entire README into a structured dictionary."""
return {
'apps': parse_subsections(cut_section('Apps')),
'tools': parse_subsections(cut_section('Tools')),
'resources': parse_subsections(cut_section('Resources')),
'sites': parse_section(cut_section('Open-source sites')),
'apps': parse_subsections(cut_section(readme, 'Apps')),
'tools': parse_subsections(cut_section(readme, 'Tools')),
'resources': parse_subsections(cut_section(readme, 'Resources')),
'sites': parse_section(cut_section(readme, 'Open-source sites')),
'metadata': {
'updated': '%sZ' % datetime.datetime.utcnow().isoformat(),
'updated': datetime.now(timezone.utc).isoformat(),
},
}


if __name__ == '__main__':
readme = open('README.md', 'r').read()
readme_path = Path('README.md')

try:
readme = readme_path.read_text(encoding='utf-8')
parsed_readme = parse_readme(readme)
json_path = './dist%s' % API_PATH

with codecs.open(json_path, mode='w+', encoding='utf8') as f:
readme_payload = json.dumps(parsed_readme, indent=True, ensure_ascii=False)
print(readme_payload)
f.write(readme_payload)
except:
json_path = Path(f'./dist{API_PATH}')
json_path.parent.mkdir(parents=True, exist_ok=True)

readme_payload = json.dumps(parsed_readme, indent=2, ensure_ascii=False)
print(readme_payload)

json_path.write_text(readme_payload, encoding='utf-8')

except FileNotFoundError as e:
print(f'Error: Could not find file - {e}')
raise
except (KeyError, IndexError) as e:
print(f'Error: README formatting issue - {e}')
print('Is the README well formatted?')
raise
except Exception as e:
print(f'Unexpected error: {e}')
raise
47 changes: 28 additions & 19 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
@@ -1,28 +1,37 @@
name: CI

on:
push: {branches: ["*", "*/*"]}
push:
branches: master
pull_request:

jobs:
build:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v1
- uses: actions/setup-ruby@v1
with:
ruby-version: '2.x'
- run: gem install awesome_bot
- run: awesome_bot README.md --request-delay 0.1 --allow-redirect --allow-dupe --allow-ssl
- uses: actions/setup-python@v1
with:
python-version: '3.x'
architecture: 'x64'
- run: mkdir -p dist/api/v1/ && python .github/api.py
- name: deploy
if: github.ref == 'refs/heads/master'
uses: peaceiris/actions-gh-pages@v2.1.0
env:
ACTIONS_DEPLOY_KEY: ${{ secrets.ACTIONS_DEPLOY_KEY }}
PUBLISH_BRANCH: gh-pages
PUBLISH_DIR: ./dist
- uses: actions/checkout@v6
- uses: ruby/setup-ruby@v1
with:
ruby-version: "3.3"
- run: gem install awesome_bot
- run: awesome_bot README.md --request-delay 0.1 --allow-redirect --allow-dupe --allow-ssl
- uses: actions/setup-python@v5
with:
python-version: "3.14"
- run: python .github/api.py
- uses: actions/upload-pages-artifact@v3
if: github.event_name == 'push' && github.ref == 'refs/heads/master'
with:
path: ./dist

deploy:
if: github.event_name == 'push' && github.ref == 'refs/heads/master'
runs-on: ubuntu-latest
needs: build
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- uses: actions/deploy-pages@v4
id: deployment
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ bower_components
.idea
.vagrant
.anaconda
.claude

# -------------------------------------------------
# Generated files
Expand Down
Loading