mirror of
https://github.com/YunoHost/apps.git
synced 2024-09-03 20:06:07 +02:00
Implement an app rejection list (#2515)
Co-authored-by: Alexandre Aubin <alex.aubin@mailoo.org> Co-authored-by: OniriCorpe <oniricorpe@yunohost.org>
This commit is contained in:
parent
6b6ded9f0f
commit
71800d21b6
3 changed files with 332 additions and 43 deletions
212
rejectedlist.toml
Normal file
212
rejectedlist.toml
Normal file
|
@ -0,0 +1,212 @@
|
|||
[sillytavern]
|
||||
name = "SillyTavern"
|
||||
description = "LLM Frontend for power users"
|
||||
upstream = "https://github.com/SillyTavern/SillyTavern"
|
||||
website = "https://sillytavern.app/"
|
||||
reason = "Based on a LLM"
|
||||
|
||||
[e621-image-hosting]
|
||||
name = "e621 image hosting"
|
||||
description = "An Imageboard with a heavy focus on tagging and searchability"
|
||||
upstream = "https://github.com/e621ng/e621ng"
|
||||
website = ""
|
||||
reason = "Docker-compose mess"
|
||||
|
||||
[photo-sphere-viewer]
|
||||
name = "Photo Sphere Viewer"
|
||||
description = "A JavaScript library to display 360° sphere panoramas. Very customizable."
|
||||
upstream = "https://github.com/mistic100/Photo-Sphere-Viewer"
|
||||
website = "https://photo-sphere-viewer.js.org/"
|
||||
reason = "Just a library, can be integrated in a My Webapp"
|
||||
|
||||
[minecraft-bedrock-server]
|
||||
name = "Minecraft Bedrock Server"
|
||||
description = "This allows you to host your own world server on Minecraft Bedrock Edition."
|
||||
upstream = "https://github.com/TheRemote/MinecraftBedrockServer"
|
||||
website = "https://jamesachambers.com/minecraft-bedrock-edition-ubuntu-dedicated-server-guide/"
|
||||
reason = "MineCraft is not free, whatever its flavour"
|
||||
|
||||
[kirbycms]
|
||||
name = "KirbyCMS"
|
||||
description = "Kirby: the CMS that adapts to any project, loved by developers and editors alike."
|
||||
upstream = "https://github.com/getkirby/kirby"
|
||||
website = "https://getkirby.com/"
|
||||
reason = "Not free"
|
||||
|
||||
[coolify]
|
||||
name = "Coolify"
|
||||
description = "Alternative à Vercel"
|
||||
upstream = "https://github.com/coollabsio/coolify"
|
||||
website = "https://coolify.io/"
|
||||
reason = "Does not make sense to integrate in YunoHost, and most likely incompatible anyways"
|
||||
|
||||
[chatmail]
|
||||
name = "chatmail"
|
||||
description = "Deltachat server"
|
||||
upstream = "https://github.com/deltachat/chatmail"
|
||||
website = "https://delta.chat/en/2023-12-13-chatmail"
|
||||
reason = "YunoHost already integrates a mail server"
|
||||
|
||||
[stalwart-mail-server]
|
||||
name = "Stalwart Mail Server"
|
||||
description = "Modern All-in-One Mail Server (IMAP, JMAP, SMTP) written in Rust."
|
||||
upstream = "https://github.com/stalwartlabs/mail-server"
|
||||
website = "https://stalw.art"
|
||||
reason = "YunoHost already integrates a mail server"
|
||||
|
||||
[retronas]
|
||||
name = "RetroNAS"
|
||||
description = "It allows you to store ROMs and sync to different consoles with networking connecting."
|
||||
upstream = "https://github.com/retronas/retronas"
|
||||
website = ""
|
||||
reason = "Big scary security warning in the app's README"
|
||||
|
||||
[quakejs]
|
||||
name = "QuakeJS"
|
||||
description = "QuakeJS allows you to play Quake III Arena Multiplayer on Web Browser using JavaScript."
|
||||
upstream = "https://github.com/inolen/quakejs"
|
||||
website = "http://www.quakejs.com/"
|
||||
reason = "Unmaintained project"
|
||||
|
||||
[speckle-server]
|
||||
name = "Speckle Server"
|
||||
description = "Data infrastructure for the AEC industry."
|
||||
upstream = "https://github.com/specklesystems/speckle-server"
|
||||
website = "https://speckle.systems/"
|
||||
reason = "Too niche of a use case, and no Docker-less install options"
|
||||
|
||||
[graphite]
|
||||
name = "Graphite"
|
||||
description = "Création non destructive d'image"
|
||||
upstream = "https://github.com/GraphiteEditor/Graphite"
|
||||
website = "https://graphite.rs/"
|
||||
reason = "No self-hosting documentation"
|
||||
|
||||
[affine]
|
||||
name = "Affine"
|
||||
description = "Wiki, écriture de document, Mindmap, Moodboard, un melting polt d'outil pour créer divers document."
|
||||
upstream = "https://github.com/toeverything/AFFiNE"
|
||||
website = "https://affine.pro/"
|
||||
reason = "Unclear license and too big of a framework"
|
||||
|
||||
|
||||
[authentik]
|
||||
name = "authentik"
|
||||
description = "Replace Active Directory, Okta and Auth0. Supports OIDC, SAML, LDAP, SCIM, Radius, and Proxy."
|
||||
upstream = "https://github.com/goauthentik/authentik"
|
||||
website = "https://goauthentik.io/"
|
||||
reason = "YunoHost already integrates a SSO, it will conflict."
|
||||
|
||||
[mirlo]
|
||||
name = "Mirlo"
|
||||
description = "Mirlo provides a user-friendly space to help musicians sell music, manage subscriptions, and share w"
|
||||
upstream = "https://github.com/funmusicplace/mirlo/"
|
||||
website = ""
|
||||
reason = "Too big of a Docker-compose mess"
|
||||
|
||||
[h5p]
|
||||
name = "H5P"
|
||||
description = "This collection of tools aim to creates interactive exercices for learning purposes."
|
||||
upstream = "https://github.com/h5p"
|
||||
website = "https://h5p.org/"
|
||||
reason = "The tools are spread across too many repos, making packaging difficult"
|
||||
|
||||
[gophish]
|
||||
name = "Gophish"
|
||||
description = "Phishing toolkit for security awareness training and penetration testers"
|
||||
upstream = "https://github.com/gophish/gophish"
|
||||
website = "https://getgophish.com"
|
||||
reason = "Too niche of a use case"
|
||||
|
||||
[pdftochat]
|
||||
name = "PDFTOCHAT"
|
||||
description = "Chat with your PDFs in seconds. Powered by Together AI and Pinecone."
|
||||
upstream = "https://github.com/Nutlope/pdftochat"
|
||||
website = "https://www.pdftochat.com/"
|
||||
reason = "whatthefuck.gif, also AI"
|
||||
|
||||
[jam]
|
||||
name = "Jam"
|
||||
description = "With Jam you can create audio rooms that can be used for panel discussions, jam sessions, ..."
|
||||
upstream = "https://github.com/jam-systems/jam"
|
||||
website = "https://jam.systems/"
|
||||
reason = "Unmaintained"
|
||||
|
||||
[bluesky-pds]
|
||||
name = "Bluesky PDS"
|
||||
description = "Personal Data Server for Bluesky, federated social network based on the AT protocol."
|
||||
upstream = "https://github.com/bluesky-social/pds"
|
||||
website = "https://bsky.social/about"
|
||||
reason = "One of the principles at the heart of the YunoHost project is to be a modest counterweight to GAFAM and surveillance capitalism, [...] [not to] provide a ready-to-use tool to our users to be part of it directly"
|
||||
|
||||
[anysync]
|
||||
name = "AnySync"
|
||||
description = "Allows sync between devices for Aytype."
|
||||
upstream = "https://github.com/anyproto/any-sync"
|
||||
website = "https://anytype.io/"
|
||||
reason = "Clients are not totally free"
|
||||
|
||||
[dub]
|
||||
name = "dub"
|
||||
description = "Open-source link management infrastructure."
|
||||
upstream = "https://github.com/dubinc/dub"
|
||||
website = "https://dub.co"
|
||||
reason = "Tracking system, against YunoHost core values"
|
||||
|
||||
[maybe]
|
||||
name = "Maybe"
|
||||
description = "The OS for your personal finances"
|
||||
upstream = "https://github.com/maybe-finance/maybe"
|
||||
website = "https://hello.maybe.co"
|
||||
reason = "Surely not ready for self-hosting"
|
||||
|
||||
[osclass]
|
||||
name = "Osclass"
|
||||
description = "Classifieds website"
|
||||
upstream = "https://github.com/osclass/Osclass"
|
||||
website = "https://osclass-classifieds.com"
|
||||
reason = "Unmaintained"
|
||||
|
||||
[opentalk]
|
||||
name = "OpenTalk"
|
||||
description = "German developed, modern, conferencing tool running in browser alone."
|
||||
upstream = "https://gitlab.opencode.de/opentalk"
|
||||
website = "https://opentalk.eu/en"
|
||||
reason = "Looks unpackageable"
|
||||
|
||||
[verdaccio]
|
||||
name = "Verdaccio"
|
||||
description = "Locally managed backup & proxy for npm package registry."
|
||||
upstream = "https://github.com/verdaccio/verdaccio"
|
||||
website = "https://verdaccio.org/"
|
||||
reason = "Too niche of a use case"
|
||||
|
||||
[tabula]
|
||||
name = "Tabula"
|
||||
description = "Extract tables from PDF; single executable .jar on 127.0.0.1.\r\rComparable to Excalibur, I think."
|
||||
upstream = "https://github.com/tabulapdf/tabula"
|
||||
website = "https://tabula.technology/"
|
||||
reason = "Unmaintained"
|
||||
|
||||
[llamafile]
|
||||
name = "Llamafile"
|
||||
description = "Single file LLMs from Mozilla Innovation Projects, that will run locally on any available processors"
|
||||
upstream = "https://github.com/Mozilla-Ocho/llamafile"
|
||||
website = "https://hacks.mozilla.org/2023/11/introducing-llamafile/"
|
||||
reason = "Not packageable as is, it is a binary tool for other apps"
|
||||
|
||||
|
||||
[fastchat]
|
||||
name = "FastChat"
|
||||
description = "- open platform for LLM chatbots incl training, evaluating, & serving via openai-compatible APIs."
|
||||
upstream = "https://github.com/lm-sys/FastChat"
|
||||
website = "https://chat.lmsys.org/"
|
||||
reason = "YunoHost packagers do not wish to promote AI, and training AIs is not lean"
|
||||
|
||||
[slidge]
|
||||
name = "Slidge"
|
||||
description = "XMPP (puppeteer) gateway library to other instant messenger platforms/protocols."
|
||||
upstream = "https://sr.ht/~nicoco/slidge/sources"
|
||||
website = "https://sr.ht/~nicoco/slidge/"
|
||||
reason = "This is only a library, so not packageable as is"
|
||||
|
18
store/app.py
18
store/app.py
|
@ -364,6 +364,24 @@ def add_to_wishlist():
|
|||
),
|
||||
)
|
||||
|
||||
rejectedlist_rawtoml = repo.get_contents(
|
||||
"rejectedlist.toml", ref=repo.default_branch
|
||||
)
|
||||
rejectedlist_rawtoml = rejectedlist_rawtoml.decoded_content.decode()
|
||||
rejectedlist = tomlkit.loads(rejectedlist_rawtoml)
|
||||
|
||||
for rejectedslug, rejectedinfo in rejectedlist.items():
|
||||
if upstream.strip("/ ") in rejectedinfo["upstream"]:
|
||||
return render_template(
|
||||
"wishlist_add.html",
|
||||
csrf_token=csrf_token,
|
||||
successmsg=None,
|
||||
errormsg=_(
|
||||
"We're sorry, but this app is listed among the already declined apps. The specified reason is:<br /><q>%(reason)s</q>",
|
||||
reason=rejectedinfo["reason"],
|
||||
),
|
||||
)
|
||||
|
||||
app_catalog = get_catalog()["apps"]
|
||||
|
||||
if slug in app_catalog:
|
||||
|
|
|
@ -10,6 +10,7 @@ import tempfile
|
|||
import aiohttp
|
||||
import logging
|
||||
from pathlib import Path
|
||||
import re
|
||||
|
||||
from typing import Optional
|
||||
from git import Actor, Repo, GitCommandError
|
||||
|
@ -116,44 +117,58 @@ def on_push(request: Request) -> HTTPResponse:
|
|||
repository = data["repository"]["full_name"]
|
||||
branch = data["ref"].split("/", 2)[2]
|
||||
|
||||
logging.info(f"{repository} -> branch '{branch}'")
|
||||
if repository.startswith("YunoHost-Apps/"):
|
||||
|
||||
need_push = False
|
||||
with tempfile.TemporaryDirectory() as folder_str:
|
||||
folder = Path(folder_str)
|
||||
repo = Repo.clone_from(
|
||||
f"https://{github_login()}:{github_token()}@github.com/{repository}",
|
||||
to_path=folder,
|
||||
)
|
||||
logging.info(f"{repository} -> branch '{branch}'")
|
||||
|
||||
# First rebase the testing branch if possible
|
||||
if branch in ["master", "testing"]:
|
||||
result = git_repo_rebase_testing_fast_forward(repo)
|
||||
need_push = False
|
||||
with tempfile.TemporaryDirectory() as folder_str:
|
||||
folder = Path(folder_str)
|
||||
repo = Repo.clone_from(
|
||||
f"https://{github_login()}:{github_token()}@github.com/{repository}",
|
||||
to_path=folder,
|
||||
)
|
||||
|
||||
# First rebase the testing branch if possible
|
||||
if branch in ["master", "testing"]:
|
||||
result = git_repo_rebase_testing_fast_forward(repo)
|
||||
need_push = need_push or result
|
||||
|
||||
repo.git.checkout(branch)
|
||||
result = generate_and_commit_readmes(repo)
|
||||
need_push = need_push or result
|
||||
|
||||
repo.git.checkout(branch)
|
||||
result = generate_and_commit_readmes(repo)
|
||||
need_push = need_push or result
|
||||
if not need_push:
|
||||
logging.debug("nothing to do")
|
||||
return response.text("nothing to do")
|
||||
|
||||
if not need_push:
|
||||
logging.debug("nothing to do")
|
||||
return response.text("nothing to do")
|
||||
logging.debug(f"Pushing {repository}")
|
||||
repo.remote().push(quiet=False, all=True)
|
||||
|
||||
logging.debug(f"Pushing {repository}")
|
||||
repo.remote().push(quiet=False, all=True)
|
||||
|
||||
return response.text("ok")
|
||||
return response.text("ok")
|
||||
|
||||
|
||||
def on_pr_comment(request: Request, pr_infos: dict) -> HTTPResponse:
|
||||
body = request.json["comment"]["body"].strip()[:100].lower()
|
||||
|
||||
# Check the comment contains proper keyword trigger
|
||||
|
||||
BUMP_REV_COMMANDS = ["!bump", "!new_revision", "!newrevision"]
|
||||
if any(trigger.lower() in body for trigger in BUMP_REV_COMMANDS):
|
||||
bump_revision(request, pr_infos)
|
||||
return response.text("ok")
|
||||
|
||||
REJECT_WISHLIST_COMMANDS = ["!reject", "!nope"]
|
||||
if any(trigger.lower() in body for trigger in REJECT_WISHLIST_COMMANDS):
|
||||
reason = ""
|
||||
for command in REJECT_WISHLIST_COMMANDS:
|
||||
try:
|
||||
reason = re.search(f"{command} (.*)", body).group(1).rstrip()
|
||||
except:
|
||||
pass
|
||||
reject_wishlist(request, pr_infos, reason)
|
||||
return response.text("ok")
|
||||
|
||||
return response.empty()
|
||||
|
||||
|
||||
|
@ -162,32 +177,76 @@ def bump_revision(request: Request, pr_infos: dict) -> HTTPResponse:
|
|||
repository = data["repository"]["full_name"]
|
||||
branch = pr_infos["head"]["ref"]
|
||||
|
||||
logging.info(f"Will bump revision on {repository} branch {branch}...")
|
||||
with tempfile.TemporaryDirectory() as folder_str:
|
||||
folder = Path(folder_str)
|
||||
repo = Repo.clone_from(
|
||||
f"https://{github_login()}:{github_token()}@github.com/{repository}",
|
||||
to_path=folder,
|
||||
)
|
||||
repo.git.checkout(branch)
|
||||
if repository.startswith("YunoHost-Apps/"):
|
||||
|
||||
manifest_file = folder / "manifest.toml"
|
||||
manifest = tomlkit.load(manifest_file.open("r", encoding="utf-8"))
|
||||
version, revision = manifest["version"].split("~ynh")
|
||||
revision = str(int(revision) + 1)
|
||||
manifest["version"] = "~ynh".join([version, revision])
|
||||
tomlkit.dump(manifest, manifest_file.open("w", encoding="utf-8"))
|
||||
logging.info(f"Will bump revision on {repository} branch {branch}...")
|
||||
with tempfile.TemporaryDirectory() as folder_str:
|
||||
folder = Path(folder_str)
|
||||
repo = Repo.clone_from(
|
||||
f"https://{github_login()}:{github_token()}@github.com/{repository}",
|
||||
to_path=folder,
|
||||
)
|
||||
repo.git.checkout(branch)
|
||||
|
||||
repo.git.add("manifest.toml")
|
||||
repo.index.commit(
|
||||
"Bump package revision",
|
||||
author=Actor("yunohost-bot", "yunohost@yunohost.org"),
|
||||
)
|
||||
manifest_file = folder / "manifest.toml"
|
||||
manifest = tomlkit.load(manifest_file.open("r", encoding="utf-8"))
|
||||
version, revision = manifest["version"].split("~ynh")
|
||||
revision = str(int(revision) + 1)
|
||||
manifest["version"] = "~ynh".join([version, revision])
|
||||
tomlkit.dump(manifest, manifest_file.open("w", encoding="utf-8"))
|
||||
|
||||
logging.debug(f"Pushing {repository}")
|
||||
repo.remote().push(quiet=False, all=True)
|
||||
return response.text("ok")
|
||||
repo.git.add("manifest.toml")
|
||||
repo.index.commit(
|
||||
"Bump package revision",
|
||||
author=Actor("yunohost-bot", "yunohost@yunohost.org"),
|
||||
)
|
||||
|
||||
logging.debug(f"Pushing {repository}")
|
||||
repo.remote().push(quiet=False, all=True)
|
||||
return response.text("ok")
|
||||
|
||||
def reject_wishlist(request: Request, pr_infos: dict, reason=None) -> HTTPResponse:
|
||||
data = request.json
|
||||
repository = data["repository"]["full_name"]
|
||||
branch = pr_infos["head"]["ref"]
|
||||
|
||||
if repository == "YunoHost/apps" and branch.startswith("add-to-wishlist"):
|
||||
|
||||
logging.info(f"Will put the suggested app in the rejected list on {repository} branch {branch}...")
|
||||
with tempfile.TemporaryDirectory() as folder_str:
|
||||
folder = Path(folder_str)
|
||||
repo = Repo.clone_from(
|
||||
f"https://{github_login()}:{github_token()}@github.com/{repository}",
|
||||
to_path=folder,
|
||||
)
|
||||
repo.git.checkout(branch)
|
||||
|
||||
rejectedlist_file = (folder / "rejectedlist.toml")
|
||||
rejectedlist = tomlkit.load(rejectedlist_file.open("r", encoding="utf-8"))
|
||||
|
||||
wishlist_file = (folder / "wishlist.toml")
|
||||
wishlist = tomlkit.load(wishlist_file.open("r", encoding="utf-8"))
|
||||
|
||||
suggestedapp_slug = branch.replace("add-to-wishlist-", "")
|
||||
suggestedapp = {suggestedapp_slug:wishlist[suggestedapp_slug]}
|
||||
suggestedapp[suggestedapp_slug]["rejection_pr"] = pr_infos["html_url"]
|
||||
suggestedapp[suggestedapp_slug]["reason"] = reason
|
||||
|
||||
wishlist.pop(suggestedapp_slug)
|
||||
rejectedlist.update(suggestedapp)
|
||||
|
||||
tomlkit.dump(rejectedlist, rejectedlist_file.open("w", encoding="utf-8"))
|
||||
tomlkit.dump(wishlist, wishlist_file.open("w", encoding="utf-8"))
|
||||
|
||||
repo.git.add("rejectedlist.toml")
|
||||
repo.git.add("wishlist.toml")
|
||||
|
||||
suggestedapp_name = suggestedapp[suggestedapp_slug]["name"]
|
||||
repo.index.commit(f"Reject {suggestedapp_name} from catalog", author=Actor("yunohost-bot", "yunohost@yunohost.org"))
|
||||
|
||||
logging.debug(f"Pushing {repository}")
|
||||
repo.remote().push(quiet=False, all=True, force=True)
|
||||
return response.text("ok")
|
||||
|
||||
def generate_and_commit_readmes(repo: Repo) -> bool:
|
||||
assert repo.working_tree_dir is not None
|
||||
|
|
Loading…
Add table
Reference in a new issue