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
100 changes: 100 additions & 0 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
name: Docs

# Copy to .github/workflows/docs.yml. Before the first run, set the repository's
# Pages source to "GitHub Actions": Settings -> Pages -> Source, or
# `gh api -X POST repos/GeiserX/<repo>/pages -f build_type=workflow` for a repo
# with no Pages yet. For a repo already publishing from a branch the API is a
# PUT, and its docs list `source.branch` and `source.path` as required, so the
# bare `-f build_type=workflow` PUT may 422: try it on CashPilot first and
# record which form worked. A deploy while the source is still a branch is
# expected to fail; that is the test that the switch happened.
#
# Never for a repo whose gh-pages branch holds a Helm chart index (index.yaml,
# artifacthub-repo.yml): Pumperly, LynxPrompt, genieacs-container,
# duplicacy-container. Those use docs-pages-helm.yml instead.
#
# Pull requests that touch the docs run the strict build, so a broken link, a
# missing page or an unreachable page fails the check instead of reaching the
# site. A push to main deploys, and only after the same build has passed.
#
# The site is published as a Pages artifact, never with `mkdocs gh-deploy`:
# no gh-pages branch, no `contents: write`, and one artifact is the whole site.
on:
push:
branches:
- main
paths:
- 'docs/**'
- 'mkdocs.yml'
- '.github/workflows/docs.yml'
# docs/index.md includes README.md: every merged entry rebuilds the site.
- 'README.md'
# docs/hooks/site.py counts DELETED.md at build time: a retirement rebuilds the site too.
- 'DELETED.md'
# Add every file a page includes with pymdownx.snippets (for example
# docker-compose.yml, .env.example): a change to it must rebuild the
# site, and a rename must fail the build.
# No paths filter on pull requests, on purpose: a check skipped by a path
# filter stays Pending and blocks the merge once it is a required check
# (repos.md step 5 makes green checks required). The build takes seconds.
pull_request:
workflow_dispatch:

# One run at a time per ref, in push order, never cancelled. Locking only the
# deploy job is not enough: an older push whose build finishes later would
# deploy last and publish a stale site. Pull requests get their own group.
concurrency:
group: pages-${{ github.ref }}
cancel-in-progress: false

permissions:
contents: read

jobs:
build:
name: build (strict)
# Public repo: GitHub-hosted runners are free and unlimited here, and the
# job runs on pull requests, so it must never touch a self-hosted runner.
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
# This job never pushes; a persisted token is readable by pip and every MkDocs plugin.
persist-credentials: false

- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: '3.13'
cache: pip
cache-dependency-path: docs/requirements-docs.txt

- name: Install dependencies
run: pip install -r docs/requirements-docs.txt

# mkdocs.yml sets `strict: true`; the flag here keeps the build strict
# even if someone removes that line.
- name: Build
run: mkdocs build --strict

- name: Upload the site
if: github.event_name != 'pull_request'
uses: actions/upload-pages-artifact@fc324d3547104276b827a68afc52ff2a11cc49c9 # v5.0.0
with:
path: site

deploy:
# Runs on push to main only, after the build job has passed.
if: github.event_name != 'pull_request'
needs: build
runs-on: ubuntu-latest
permissions:
pages: write
id-token: write
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- uses: actions/configure-pages@45bfe0192ca1faeb007ade9deae92b16b8254a0d # v6.0.0

- id: deployment
uses: actions/deploy-pages@368f82528645a54fb793d4d04e342629a3f51346 # v5.0.1
4 changes: 3 additions & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@ jobs:
- uses: actions/checkout@v7
with:
persist-credentials: false
# En un PR se salta el enlace a la web de la lista: se publica al fusionar,
# así que el PR que la crea o cambia no puede comprobarla. En main sí.
- uses: lycheeverse/lychee-action@v2
with:
args: --no-progress --insecure --accept 200..=403 --timeout 30 --max-retries 3 --exclude img.shields.io README.md
args: --no-progress --insecure --accept 200..=403 --timeout 30 --max-retries 3 --exclude img.shields.io ${{ github.event_name == 'pull_request' && '--exclude ^https://geiserx\.github\.io/awesome-asturies/' || '' }} README.md
fail: true
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@
<a href="https://awesome.re"><img src="https://awesome.re/badge-flat.svg" alt="Awesome"></a>
<br><br>
<p>Una selección de software open source que da soporte específico al Principado de Asturias, sus concejos, universidad e instituciones.</p>
<p>Búscalos en <a href="https://geiserx.github.io/awesome-asturies/">geiserx.github.io/awesome-asturies</a>.</p>
</div>

<!-- --8<-- [start:lista] -->
## Contenido

<!--lint disable awesome-list-item-->
Expand Down Expand Up @@ -122,7 +124,7 @@ For the badge (grande):

## Contribuir

Las contribuciones son bienvenidas. Lee las [directrices de contribución](contributing.md) antes de enviar un pull request.
Las contribuciones son bienvenidas. Lee las [directrices de contribución](https://github.com/GeiserX/awesome-asturies/blob/main/contributing.md) antes de enviar un pull request.

## Nota

Expand All @@ -131,3 +133,4 @@ Esta lista se centra en software open source que da **soporte específico al Pri
## Descargo de responsabilidad

No se aceptan proyectos relacionados con pornografía, contenido NSFW, loterías o apuestas, religión, política partidista ni cualquier otro tema controvertido. Esta lista pretende ser un recurso técnico neutral y útil para la comunidad de desarrolladores.
<!-- --8<-- [end:lista] -->
92 changes: 92 additions & 0 deletions docs/hooks/site.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
"""MkDocs hooks for the one-page site.

on_page_markdown replaces the `<!-- n:... -->` tokens in docs/index.md with
numbers counted from README.md and DELETED.md at build time, so the home page
never states a stale figure. Entries are counted inside category sections
only: a `## Mantenedores` list of GitHub profiles is not a project.

on_page_content adds loading="lazy" to every image except the banner, so a
page with hundreds of shields.io badges paints before they all arrive, and
fails the build if the list did not come through the include. A missing
section marker already fails the build in pymdownx.snippets (check_paths:
true raises SnippetMissingError); this check is for markers that exist but
enclose the wrong lines.
"""

import re
from pathlib import Path

ROOT = Path(__file__).resolve().parents[2]
ENTRY = re.compile(r"^- \[[^\]]+\]\(https?://")
# DELETED.md also lists repos that no longer exist as "- `owner/repo` - reason", with no link:
# they are retired projects too and count as such.
RETIRED = re.compile(r"^- (?:\[[^\]]+\]\(https?://|`[^`]+` - )")
H2 = re.compile(r"^## (.+)$")
NOT_CATEGORIES = {
"Contenido",
"Insignia",
"Mantenedores",
"Contribuir",
"Nota",
"Descargo de responsabilidad",
}
IMG = re.compile(r"<img\b[^>]*>")
LISTED = re.compile(r"<li>\s*<a href=\"https?://")


def _entries_by_section(text):
"""Yield (section title or None, is_entry) for every line."""
section = None
for line in text.splitlines():
heading = H2.match(line)
if heading:
section = heading.group(1).strip()
continue
yield section, bool(ENTRY.match(line))


def _counts():
readme = (ROOT / "README.md").read_text(encoding="utf-8")
deleted = (ROOT / "DELETED.md").read_text(encoding="utf-8")
categories = set()
proyectos = 0
for section, is_entry in _entries_by_section(readme):
if section is None or section in NOT_CATEGORIES:
continue
categories.add(section)
proyectos += is_entry
return {
"proyectos": proyectos,
"categorias": len(categories),
"retirados": sum(1 for line in deleted.splitlines() if RETIRED.match(line)),
}


def on_page_markdown(markdown, page, config, files):
if page.file.src_uri != "index.md":
return markdown
for key, value in _counts().items():
markdown = markdown.replace(f"<!-- n:{key} -->", str(value))
if "<!-- n:" in markdown:
raise RuntimeError("docs/index.md has a count token the hook does not know")
return markdown


def on_page_content(html, page, config, files):
if page.file.src_uri != "index.md":
return html
listed = len(LISTED.findall(html))
expected = _counts()["proyectos"]
if 'id="contenido"' not in html or 'id="insignia"' not in html or listed < expected:
raise RuntimeError(
f"docs/index.md shows {listed} of the README's {expected} projects: "
"check the `--8<-- [start:lista]` and `[end:lista]` markers in README.md"
)

def lazy(match):
tag = match.group(0)
if "banner.svg" in tag or "loading=" in tag:
return tag
return tag.replace("<img", '<img loading="lazy" decoding="async"', 1)

return IMG.sub(lazy, html)
41 changes: 41 additions & 0 deletions docs/images/icon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
33 changes: 33 additions & 0 deletions docs/images/logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
59 changes: 59 additions & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
---
hide:
- navigation
---

# Awesome Asturies { .aw-visually-hidden }

<p align="center">
<img src="images/banner.svg" alt="Awesome Asturies" width="100%">
</p>

<p align="center">
<a href="https://awesome.re"><img alt="Awesome" src="https://awesome.re/badge-flat.svg"></a>
<a href="https://github.com/GeiserX/awesome-asturies/stargazers"><img alt="Estrellas en GitHub" src="https://img.shields.io/github/stars/GeiserX/awesome-asturies?style=flat-square&logo=github"></a>
<a href="#contenido"><img alt="Proyectos en la lista" src="https://img.shields.io/badge/proyectos-<!-- n:proyectos -->-c60b1e?style=flat-square"></a>
<a href="https://github.com/GeiserX/awesome-asturies/commits/main"><img alt="Último commit" src="https://img.shields.io/github/last-commit/GeiserX/awesome-asturies?style=flat-square"></a>
<a href="https://github.com/GeiserX/awesome-asturies/blob/main/LICENSE"><img alt="Licencia CC0-1.0" src="https://img.shields.io/github/license/GeiserX/awesome-asturies?style=flat-square"></a>
</p>

---

**Awesome Asturies** es una selección de <!-- n:proyectos --> proyectos open source, en <!-- n:categorias --> categorías, que dan soporte específico al Principado de Asturias, sus concejos, universidad e instituciones. Cada entrada enlaza al repositorio y a la institución o servicio al que da soporte, y muestra sus estrellas, último commit, lenguaje y licencia. Se retiran los proyectos archivados, borrados, abandonados, sustituidos, fuera de criterio o que han dejado de funcionar; cada uno queda en [DELETED.md](https://github.com/GeiserX/awesome-asturies/blob/main/DELETED.md) con su motivo (<!-- n:retirados --> hasta hoy).

<div class="grid cards" markdown>

- :material-format-list-bulleted: **[Explorar por categoría](#contenido)**

---

De Administración y Transparencia a Vivienda y Urbanismo, pasando por Asturies, Softastur, SADEI, Gijón. El índice lateral sigue la categoría que estás leyendo.

- :material-magnify: **[Buscar un proyecto](https://geiserx.github.io/awesome-asturies/?q=Asturies)**

---

Pulsa `/` y escribe lo que necesitas: Asturies, Softastur, SADEI, Gijón. La búsqueda cubre el nombre y la descripción de todas las entradas.

- :material-plus-box-outline: **[Proponer un proyecto](https://github.com/GeiserX/awesome-asturies/issues/new?template=anadir-proyecto.md)**

---

Abre un issue con la plantilla o envía un pull request siguiendo las [directrices](https://github.com/GeiserX/awesome-asturies/blob/main/contributing.md). Solo hace falta el enlace, la descripción y la institución o servicio al que da soporte.

- :material-shield-check-outline: **[Añade la insignia a tu proyecto](#insignia)**

---

Si tu proyecto está en la lista, una insignia en tu README lo dice. Cuatro estilos, una línea de Markdown.

</div>

## Cómo se mantiene la lista

- Entra el software que da soporte al Principado de Asturias, no el software hecho por alguien de allí. Los criterios completos están en [contributing.md](https://github.com/GeiserX/awesome-asturies/blob/main/contributing.md).
- Un proyecto archivado se retira sin más. Uno que no lo está se retira cuando deja de funcionar, y hay que poder enseñar el fallo; la falta de commits recientes no lo retira. También salen los repos borrados o renombrados, los que su autor da por abandonados, los sustituidos por otro proyecto y los que no cumplen los criterios.
- Los proyectos retirados quedan en [DELETED.md](https://github.com/GeiserX/awesome-asturies/blob/main/DELETED.md) con su motivo, para no volver a añadirlos por error.
- La integración continua comprueba el formato, el orden alfabético y las insignias en cada cambio ([awesome-lint-extra](https://github.com/GeiserX/awesome-lint-extra)). Cada mes se revisan los enlaces rotos que encuentra lychee y cada trimestre se buscan repos recién archivados. La guía de los mantenedores es [MAINTENANCE.md](https://github.com/GeiserX/awesome-asturies/blob/main/MAINTENANCE.md).

--8<-- "README.md:lista"
1 change: 1 addition & 0 deletions docs/requirements-docs.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
mkdocs-material==9.7.7
Loading
Loading