Added codeberg
This commit is contained in:
@@ -4,6 +4,7 @@ import traceback
|
||||
from sources import CSInventoryConfig, CSItem, CheatsheetSourceType
|
||||
from sources.plain import process_plain_url
|
||||
from sources.gitea import process_gitea
|
||||
from sources.codeberg import process_codeberg
|
||||
from logger import get_worker_thread_logger
|
||||
|
||||
def load_cheatsheet_inventory(file: str) -> CSInventoryConfig:
|
||||
@@ -38,6 +39,9 @@ def prepare_cheatsheets(config: CSInventoryConfig, outdir: str) -> list[CSItem]:
|
||||
case CheatsheetSourceType.PLAIN_URL:
|
||||
new_items.append(process_plain_url(item, outdir))
|
||||
|
||||
case CheatsheetSourceType.CODEBERG_SOURCE:
|
||||
new_items += process_codeberg(item, outdir)
|
||||
|
||||
case _:
|
||||
logger.warning("Unknown Source Type: %s", item.source.type)
|
||||
except:
|
||||
|
||||
@@ -7,8 +7,10 @@ from uuid import uuid4
|
||||
# Configuration
|
||||
class CheatsheetSourceType(str, Enum):
|
||||
GITEA_SOURCE = "gitea"
|
||||
CODEBERG_SOURCE = "codeberg"
|
||||
PLAIN_URL = "url"
|
||||
|
||||
|
||||
class CSSourceBase(BaseModel):
|
||||
type: CheatsheetSourceType
|
||||
|
||||
@@ -21,12 +23,21 @@ class CSSourceGitea(CSSourceBase):
|
||||
tag: str
|
||||
hide_repo: bool = False
|
||||
|
||||
class CSSourceCodeberg(CSSourceBase):
|
||||
type: Literal[CheatsheetSourceType.CODEBERG_SOURCE]
|
||||
base_url: str
|
||||
fetch_url: str | None = Field(default=None)
|
||||
repo: str
|
||||
owner: str
|
||||
tag: str
|
||||
hide_repo: bool = False
|
||||
|
||||
class CSSourcePlainURL(CSSourceBase):
|
||||
type: Literal[CheatsheetSourceType.PLAIN_URL]
|
||||
url: str
|
||||
repo_url: str
|
||||
|
||||
CSSourceType = CSSourcePlainURL | CSSourceGitea
|
||||
CSSourceType = CSSourcePlainURL | CSSourceGitea | CSSourceCodeberg
|
||||
|
||||
class CSInventoryItem(BaseModel):
|
||||
source: CSSourceType
|
||||
|
||||
146
src/sources/codeberg.py
Normal file
146
src/sources/codeberg.py
Normal file
@@ -0,0 +1,146 @@
|
||||
|
||||
from datetime import datetime
|
||||
from sources import CSSourceGitea, CSItem, CSInventoryItem
|
||||
from sources.util import cache_cheatsheet, get_datestring
|
||||
import httpx
|
||||
from pathlib import Path
|
||||
from logger import get_worker_thread_logger
|
||||
|
||||
|
||||
def process_codeberg(item: CSInventoryItem, outdir: str) -> list[CSItem] | None:
|
||||
logger = get_worker_thread_logger()
|
||||
logger.info(f"Processing Gitea cheatsheet: {item.source.owner}/{item.source.repo}@{item.source.tag}")
|
||||
source: CSSourceGitea = item.source
|
||||
commit_hash, tag_id = get_codeberg_release_commit_sha(source.fetch_url or source.base_url, source.owner, source.repo, source.tag)
|
||||
assets = list_codeberg_release_assets(source.fetch_url or source.base_url, source.owner, source.repo, tag_id)
|
||||
|
||||
assets = list(filter(lambda a: a[1].endswith(".pdf"), assets))
|
||||
print(assets)
|
||||
assets_urls = list(map(lambda a: (
|
||||
f"{source.fetch_url or source.base_url}/{source.owner}/{source.repo}/releases/download/{source.tag}/{a[1]}",
|
||||
f"{source.base_url}/{source.owner}/{source.repo}/releases/download/{source.tag}/{a[1]}"
|
||||
), assets),
|
||||
)
|
||||
|
||||
logger.info(f"Found {len(assets_urls)} PDF assets in Gitea release {source.owner}/{source.repo}@{source.tag}")
|
||||
|
||||
res = []
|
||||
|
||||
for fetch_url, real_url in assets_urls:
|
||||
|
||||
if item.cache:
|
||||
cache_url = cache_cheatsheet(fetch_url, outdir)
|
||||
if cache_url:
|
||||
real_url = cache_url
|
||||
else:
|
||||
continue
|
||||
|
||||
name = Path(real_url).stem
|
||||
|
||||
res.append(CSItem(
|
||||
url = real_url,
|
||||
date=get_datestring(),
|
||||
commit=commit_hash[:10] if commit_hash else "",
|
||||
author=item.author if item.author else source.owner,
|
||||
title=f"{name}",
|
||||
id=item.id,
|
||||
git_repo=f"{source.base_url}/{source.owner}/{source.repo}" if not source.hide_repo else "",
|
||||
git_repo_type="Codeberg"
|
||||
))
|
||||
|
||||
return res
|
||||
|
||||
def get_codeberg_release_commit_sha(base_url, owner, repo, tag_name, token=None):
|
||||
"""
|
||||
Resolve the commit SHA for a Codeberg release tag.
|
||||
|
||||
:param base_url: e.g. "https://codeberg.org"
|
||||
:param owner: repo owner
|
||||
:param repo: repository name
|
||||
:param tag_name: release tag (e.g. "v1.2.3")
|
||||
:param token: optional API token
|
||||
:return: commit SHA (str)
|
||||
"""
|
||||
logger = get_worker_thread_logger()
|
||||
|
||||
|
||||
with httpx.Client() as client:
|
||||
headers = {}
|
||||
if token:
|
||||
headers["Authorization"] = f"token {token}"
|
||||
|
||||
# 1) List tags and find the matching tag
|
||||
tags_url = f"{base_url}/api/v1/repos/{owner}/{repo}/tags"
|
||||
resp = client.get(tags_url, headers=headers)
|
||||
resp.raise_for_status()
|
||||
tags = resp.json()
|
||||
|
||||
|
||||
sorted_tags = list(sorted(tags, key=lambda t: datetime.fromisoformat(t["commit"]["created"]), reverse=True) )
|
||||
|
||||
tag = sorted_tags[0] if sorted_tags else None
|
||||
if not tag:
|
||||
logger.warning(f"Tag '{tag_name}' not found")
|
||||
return None, ""
|
||||
|
||||
# Lightweight tags usually already contain the commit SHA
|
||||
commit_sha = tag.get("commit", {}).get("sha")
|
||||
tag_obj_sha = tag.get("id")
|
||||
|
||||
# If commit.sha looks valid, return it
|
||||
if commit_sha:
|
||||
return commit_sha, tag.get("name")
|
||||
|
||||
# 2) Annotated tag: dereference via /git/tags/{sha}
|
||||
if not tag_obj_sha:
|
||||
raise RuntimeError("Tag object SHA missing; cannot dereference annotated tag")
|
||||
|
||||
git_tag_url = f"{base_url}/api/v1/repos/{owner}/{repo}/git/tags/{tag_obj_sha}"
|
||||
resp = client.get(git_tag_url, headers=headers)
|
||||
resp.raise_for_status()
|
||||
annotated = resp.json()
|
||||
|
||||
# The object pointed to by the tag (usually a commit)
|
||||
target = annotated.get("object", {})
|
||||
if target.get("type") != "commit":
|
||||
raise RuntimeError(f"Tag points to a {target.get('type')} instead of a commit")
|
||||
|
||||
return target.get("sha"), tag.get("name")
|
||||
|
||||
|
||||
def list_codeberg_release_assets(base_url, owner, repo, tag, token=None):
|
||||
"""
|
||||
Return a list of (download_url, filename) for all assets of a Codeberg release.
|
||||
|
||||
:param base_url: Codeberg host URL, e.g. "https://codeberg.org"
|
||||
:param owner: repository owner
|
||||
:param repo: repository name
|
||||
:param tag: release tag name
|
||||
:param token: optional API token
|
||||
:returns: list of (download_url, filename) tuples
|
||||
"""
|
||||
|
||||
with httpx.Client() as client:
|
||||
headers = {}
|
||||
if token:
|
||||
headers["Authorization"] = f"token {token}"
|
||||
|
||||
# 1) Get release by tag
|
||||
rel_url = f"{base_url}/api/v1/repos/{owner}/{repo}/releases/tags/{tag}"
|
||||
rel_resp = client.get(rel_url, headers=headers)
|
||||
rel_resp.raise_for_status()
|
||||
release: dict = rel_resp.json()
|
||||
|
||||
assets = release.get("assets", [])
|
||||
result = []
|
||||
|
||||
for asset in assets:
|
||||
# Codeberg asset info usually contains:
|
||||
# - "browser_download_url" → direct URL
|
||||
# - "name" → filename
|
||||
download_url = asset.get("browser_download_url")
|
||||
filename = asset.get("name")
|
||||
if download_url and filename:
|
||||
result.append((download_url, filename))
|
||||
|
||||
return result
|
||||
@@ -10,8 +10,8 @@ def process_gitea(item: CSInventoryItem, outdir: str) -> list[CSItem] | None:
|
||||
logger = get_worker_thread_logger()
|
||||
logger.info(f"Processing Gitea cheatsheet: {item.source.owner}/{item.source.repo}@{item.source.tag}")
|
||||
source: CSSourceGitea = item.source
|
||||
commit_hash = get_release_commit_sha(source.fetch_url or source.base_url, source.owner, source.repo, source.tag)
|
||||
assets = list_release_assets(source.fetch_url or source.base_url, source.owner, source.repo, source.tag)
|
||||
commit_hash = get_gitea_release_commit_sha(source.fetch_url or source.base_url, source.owner, source.repo, source.tag)
|
||||
assets = list_gitea_release_assets(source.fetch_url or source.base_url, source.owner, source.repo, source.tag)
|
||||
|
||||
assets = list(filter(lambda a: a[1].endswith(".pdf"), assets))
|
||||
print(assets)
|
||||
@@ -49,7 +49,7 @@ def process_gitea(item: CSInventoryItem, outdir: str) -> list[CSItem] | None:
|
||||
|
||||
return res
|
||||
|
||||
def get_release_commit_sha(base_url, owner, repo, tag_name, token=None):
|
||||
def get_gitea_release_commit_sha(base_url, owner, repo, tag_name, token=None):
|
||||
"""
|
||||
Resolve the commit SHA for a Gitea release tag.
|
||||
|
||||
@@ -102,7 +102,7 @@ def get_release_commit_sha(base_url, owner, repo, tag_name, token=None):
|
||||
return target.get("sha")
|
||||
|
||||
|
||||
def list_release_assets(base_url, owner, repo, tag, token=None):
|
||||
def list_gitea_release_assets(base_url, owner, repo, tag, token=None):
|
||||
"""
|
||||
Return a list of (download_url, filename) for all assets of a Gitea release.
|
||||
|
||||
|
||||
@@ -61,6 +61,13 @@ h1 {
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
h3 {
|
||||
color: #333;
|
||||
text-align: center;
|
||||
font-weight: normal;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
table {
|
||||
width: calc(100% - 40px);
|
||||
border-collapse: collapse;
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"version":3,"sourceRoot":"","sources":["../../styles/main.scss"],"names":[],"mappings":"AAAA;AAAA;AAAA;AAAA;AAKA;EACI;EACA;EACA;EACA;EACA;EACA;EAEA;EACA;EAEA;;;AAGJ;AACA;EACI;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA;;;AAGJ;EACI;EACA;;;AAGJ;AACA;AAAA;EAEI;EACA;;;AAGJ;EACI;EACA;EACA;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;;;AAGJ;EACI;;;AAGJ;EACI;EACA;;;AAGJ;EACI;EACA;;;AAGJ;EACI;;;AAGJ;AACA;EACI;EACA;EACA;EACA;EACA;EACA","file":"main.css"}
|
||||
{"version":3,"sourceRoot":"","sources":["../../styles/main.scss"],"names":[],"mappings":"AAAA;AAAA;AAAA;AAAA;AAKA;EACI;EACA;EACA;EACA;EACA;EACA;EAEA;EACA;EAEA;;;AAGJ;AACA;EACI;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA;;;AAGJ;EACI;EACA;;;AAGJ;AACA;AAAA;EAEI;EACA;;;AAGJ;EACI;EACA;EACA;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;;;AAGJ;EACI;;;AAGJ;EACI;EACA;;;AAGJ;EACI;EACA;;;AAGJ;EACI;;;AAGJ;AACA;EACI;EACA;EACA;EACA;EACA;EACA","file":"main.css"}
|
||||
@@ -64,6 +64,13 @@ h1 {
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
h3 {
|
||||
color: #333;
|
||||
text-align: center;
|
||||
font-weight: normal;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
table {
|
||||
width: calc(100% - 40px);
|
||||
border-collapse: collapse;
|
||||
|
||||
@@ -4,12 +4,21 @@
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="stylesheet" href="css/main.css">
|
||||
<title>FS²</title>
|
||||
<title>typst4ei</title>
|
||||
</head>
|
||||
<body>
|
||||
{% include "navbar.j2" %}
|
||||
|
||||
<h1>Formel(sammlung)²</h1>
|
||||
<h1>typst4ei</h1>
|
||||
<h3>Eine Sammlung von Formelsammlung für/von EI Stundenten der TUM</h3>
|
||||
|
||||
<p>
|
||||
Disclaimer: Die Richtigkeit des Materials kann nicht garantiert werden.
|
||||
Wir wissen auch nicht was wir tun. Nutzt die Formelsammlungen auf eigene Gefahr.
|
||||
|
||||
Aber Feedback und Korrekturen sind immer willkommen!
|
||||
</p>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
|
||||
@@ -4,6 +4,6 @@
|
||||
|
||||
<a href="index.html">Home</a>
|
||||
<a href="https://gitea.mintcalc.com/alexander/FSSquared">Gitea</a>
|
||||
<a href="impressum.html">Impressum</a>
|
||||
<!--<a href="impressum.html">Impressum</a>-->
|
||||
<a href="license.html">License</a>
|
||||
</nav>
|
||||
|
||||
Reference in New Issue
Block a user