1 Commits

Author SHA1 Message Date
yunohost-bot
50b8b4be4b Add authentik to wishlist 2024-03-04 22:11:33 +01:00
87 changed files with 620 additions and 1897 deletions

View File

@@ -1,35 +0,0 @@
name: Check / auto apply Black
on:
push:
branches:
- master
jobs:
black:
name: Check / auto apply black
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Check files using the black formatter
uses: psf/black@stable
id: black
with:
options: "."
continue-on-error: true
- shell: pwsh
id: check_files_changed
run: |
# Diff HEAD with the previous commit
$diff = git diff
$HasDiff = $diff.Length -gt 0
Write-Host "::set-output name=files_changed::$HasDiff"
- name: Create Pull Request
if: steps.check_files_changed.outputs.files_changed == 'true'
uses: peter-evans/create-pull-request@v6
with:
token: ${{ secrets.GITHUB_TOKEN }}
title: "Format Python code with Black"
commit-message: ":art: Format Python code with Black"
body: |
This pull request uses the [psf/black](https://github.com/psf/black) formatter.
base: ${{ github.head_ref }} # Creates pull request onto pull request or commit branch
branch: actions/black

View File

@@ -1,6 +1,6 @@
# YunoHost application catalog
<img alt="YunoHost logo" src="https://avatars.githubusercontent.com/u/1519495?s=200&v=4" width=80><img alt="Package logo" src="https://yunohost.org/user/images/yunohost_package.png" width=80>
<img src="https://avatars.githubusercontent.com/u/1519495?s=200&v=4" width=80><img src="https://yunohost.org/user/images/yunohost_package.png" width=80>
This repository contains the default YunoHost app catalog, as well as related
tools that can be run manually or automatically.
@@ -16,7 +16,7 @@ them such as their category or maintenance state. This file is regularly read by
- You can browse [the contributor documentation](https://yunohost.org/contributordoc)
- If you are not familiar with Git/GitHub, you can have a look at our [homemade guide](https://yunohost.org/packaging_apps_git)
- Don't hesitate to reach for help on the dedicated [application packaging chatroom](https://yunohost.org/chat_rooms)... we can even schedule an audio meeting to help you get started!
- Don't hesitate to reach for help on the dedicated [application packaging chatroom](https://yunohost.org/chat_rooms) ... we can even schedule an audio meeting to help you get started!
## How to add your app to the application catalog
@@ -46,9 +46,9 @@ App example addition:
```toml
[your_app]
antifeatures = [ "deprecated-software" ] # Replace with the appropriate category id found in antifeatures.toml, remove if no relevant antifeature applies
antifeatures = [ "deprecated-software" ] # Remove if no relevant antifeature applies
potential_alternative_to = [ "YouTube" ] # Indicate if your app can be thought of as an alternative to popular proprietary services (or remove if none applies)
category = "foobar" # Replace with the appropriate category id found in categories.toml, don't invent a category
category = "foobar" # Replace with the appropriate category id found in categories.toml
state = "working"
url = "https://github.com/YunoHost-Apps/your_app_ynh"
```

View File

@@ -1,151 +1,123 @@
[tracking]
icon = "user-secret"
title.en = "Tracking"
title.eu = "Jarraipena"
title.fr = "Pistage"
title.it = "Tracciamento"
description.en = "Tracks you and/or reports your activity to upstream maintainer or third parties, either without your permission or by default."
description.eu = "Zure jardueraren jarraipena egiten du eta/edo jardueraren berri ematen die aplikazioaren arduradunari edo hirugarrenei, baimenik gabe edo defektuz."
description.fr = "Vous piste et/ou rapporte vos activités au mainteneur source ou à des tiers, sans votre permission ou par défaut."
description.it = "Ti traccia e/o riporta la tua attività a chi mantiene il codice sorgente o a terze parti, facendolo senza il tuo permesso o di default."
[non-free-network]
icon = "sitemap"
title.en = "Non-free Network Services"
title.eu = "Libreak ez diren sareko zerbitzuak"
title.fr = "Services réseau non libres"
title.it = "Servizi di rete non liberi"
description.en = "Promotes or depends entirely on a non-free network service."
description.eu = "Librea ez den sare-zerbitzu bat sustatzen du edo horren mende dago erabat."
description.fr = "Promeut ou utilise des services réseau non libres."
description.it = "Promuove o dipende interamente da servizi di rete non liberi."
[non-free-addons]
icon = "puzzle-piece"
title.en = "Non-free Addons"
title.eu = "Libreak ez diren gehigarriak"
title.fr = "Extensions non libres"
title.it = "Estensioni non libere"
description.en = "Promotes other non-free applications or plugins."
description.eu = "Libreak ez diren beste aplikazio edo gehigarri batzuk sustatzen ditu."
description.fr = "Promeut d'autres applications ou plugins non libres."
description.it = "Promoove altre applicazioni o plugin non liberi"
[non-free-dependencies]
icon = "book"
title.en = "Non-free dependencies"
title.eu = "Libreak ez diren dependentziak"
title.fr = "Dépendances non libres"
title.it = "Dipendenze non libere"
description.en = "Relies on software dependencies that are not free in order to run."
description.eu = "Libreak ez diren dependentzien mende dago exekutatu ahal izateko."
description.fr = "Dépend pour fonctionner de dépendances logicielles non libres."
description.it = "Per funzionare, si basa su dipendenze software non libere."
[non-free-assets]
icon = "file-image-o"
title.en = "Non-free assets"
title.eu = "Libreak ez diren baliabideak"
title.fr = "Ressources non libres"
title.it = "Risorse non libere"
description.en = "Contains and makes use of non-free assets. The most common case is apps using artwork - images, sounds, music, etc. - under a commercial license."
description.eu = "Libreak ez diren baliabideak ditu eta erabiltzen ditu. Kasurik ohikoena artelanak (irudiak, soinuak, musika, etab.) erabiltzen dituzten aplikazioak dira. - jabedun-lizentziapean."
description.fr = "Contient ou utilise des médias non libres. Le cas le plus fréquent concerne des applications utilisant des œuvres (images, sons, musiques, etc.) sous une licence commerciale."
description.it = "Contiene ed utilizza risorse mediatiche non libere. Il caso più comune è lutilizzo da parte dellapp di contenuti artistici (immagini, suoni, musica, ecc.) coperti da licenza commerciale."
[bad-security-reputation]
icon = "bug"
title.en = "Bad security reputation"
title.eu = "Segurtasun txarreko ospea"
title.fr = "Mauvaise réputation en matière de sécurité"
title.it = "Cattiva reputazione di sicurezza"
description.en = "Has a bad security reputation, such as deprecated addons."
description.eu = "Segurtasun ospe txarra du, utzitako gehigarriak esaterako."
description.fr = "A une mauvaise réputation en matière de sécurité, en utilisant des plugins dépréciés par exemple."
description.it = "Ha una cattiva reputazione in termini di sicurezza (per esempio, potrebbe utilizzare addon obsoleti)."
[deprecated-software]
icon = "trash-o"
title.en = "Upstream not maintained"
title.eu = "Jatorrizko garapena utzita"
title.fr = "Application non maintenue"
title.it = "Applicazione non mantenuta"
description.en = "This software is not maintained anymore. Expect it to break down over time, be exposed to unfixed security breaches, etc."
description.eu = "Software honek ez du arduradunik. Denborak aurrera egin ahala funtzionatzeari utziko dio, konpondu gabeko segurtasun arazoak izango ditu, etab."
description.fr = "Ce logiciel n'est plus maintenu. Attendez-vous à ce qu'il ne fonctionne plus avec le temps, et que l'on découvre des failles de sécurité qui ne seront pas corrigées, etc."
description.it = "Questo software non è più mantenuto. Ci si può aspettare che con il passare del tempo smetta di funzionare, sia esposto a falle di sicurezza, ecc."
[package-not-maintained]
icon = "user-times"
title.en = "Package not maintained"
title.eu = "Mantendu gabeko paketea"
title.fr = "Paquet non maintenu"
title.it = "Pacchetto non mantenuto"
description.en = "This YunoHost package is not maintained and needs adoption."
description.eu = "Pakete honek ez du mantenduko duenik, boluntario baten beharra dauka."
description.fr = "Ce package YunoHost n'est plus maintenu et doit être adopté."
description.it = "Questo pacchetto di YunoHost non è più mantenuto e necessita di essere adottato."
[paid-content]
icon = "money"
title.en = "Paid content"
title.eu = "Ordainpeko edukia"
title.fr = "Contenu payant"
title.it = "Contenuti a pagamento"
description.en = "Promotes or depends, entirely or partially, on a paid service."
description.eu = "Ordainpeko zerbitzu bat sustatzen du edo bere mende dago, osorik edo neurri batean."
description.fr = "Promeut ou dépend, entièrement ou partiellement, d'un service payant."
description.it = "Promuove o dipende, interamente o parzialmente, da un servizio a pagamento."
[arbitrary-limitations]
icon = "star-half-empty"
title.en = "Arbitrary limitations"
title.eu = "Muga arbitrarioak"
title.fr = "Limitations arbitraires"
title.it = "Limitazioni arbitrarie"
description.en = "Features arbitrary limitations. Please refer to the README."
description.eu = "Muga arbitrarioak ditu. Irakurri README fitxategia."
description.fr = "Contient des limitations arbitraires. Se référer au fichier README."
description.it = "Contiene limitazioni arbitrarie. Fare riferimento al file “README”."
[replaced-by-another-app]
icon = "repeat"
title.en = "Replaced by another app"
title.eu = "Beste aplikazio batek ordeztu du"
title.fr = "Remplacé par une autre application"
title.it = "Sostituita da unaltra app"
description.en = "Was replaced by another app. Please refer to the README."
description.eu = "Beste aplikazio batek ordeztu du. Irakurri README fitxategia."
description.fr = "A été remplacé par une autre application. Se référer au fichier README."
description.it = "Questapp è stata sostituita da unaltra app. Fare riferimento al file “README”."
[alpha-software]
icon = "flask"
title.en = "Alpha software"
title.eu = "Alfa softwarea"
title.fr = "Logiciel en version alpha"
title.it = "Software in versione alpha"
description.en = "Early development stage. May contain changing or unstable features, bugs, and security vulnerability."
description.eu = "Garapenaren hasierako fasean dago. Ezaugarri aldakor edo ezegonkorrak, erroreak eta segurtasuneko arazoak izan ditzazke."
description.fr = "Le logiciel est au tout début de son développement. Il pourrait contenir des fonctionnalités changeantes ou instables, des bugs, et des failles de sécurité."
description.it = "Questo software è allinizio della sua fase di sviluppo. Potrebbe dunque essere instabile, contenere bug e vulnerabilità di sicurezza."
[not-totally-free-upstream]
icon = "lock"
title.en = "Not totally free upstream"
title.eu = "Jatorrizkoa ez da erabat librea"
title.fr = "Application sous licence libre restreinte"
title.it = "Applicazione con licenza parzialmente libera"
description.en = "The packaged app is under an overall free licence, but with clauses that restrict its use."
description.eu = "Aplikazioak lizentzia librea du orokorrean, baina bere erabilera mugatzen duten klausulekin."
description.fr = "L'application packagée est sous une licence globalement libre, mais avec des clauses qui pourraient restreindre son utilisation."
description.it = "Questapplicazione è protetta da licenza generalmente libera, ma con delle clausole che potrebbero limitare il suo utilizzo."
[not-totally-free-package]
icon = "archive"
title.en = "Not totally free package"
title.eu = "Paketea ez da erabat librea"
title.fr = "Package sous licence libre restreinte"
description.en = "The YunoHost package of this app is under an overall free licence, but with clauses that restrict its use."
description.eu = "Aplikazio honen YunoHost paketeak lizentzia librea du orokorrean, baina bere erabilera mugatzen duten klausulekin."
description.fr = "Le package YunoHost de cette application est sous une licence globalement libre, mais avec des clauses qui pourraient restreindre son utilisation."

View File

@@ -502,13 +502,6 @@ state = "working"
subtags = [ "wiki" ]
url = "https://github.com/YunoHost-Apps/cowyo_ynh"
[crabfit]
category = "productivity_and_management"
potential_alternative_to = [ "Doodle", "OpenSondage" ]
state = "working"
subtags = [ "poll" ]
url = "https://github.com/YunoHost-Apps/crabfit_ynh"
[cryptpad]
category = "office"
level = 8
@@ -1572,7 +1565,6 @@ url = "https://github.com/YunoHost-Apps/kanboard_ynh"
[kavita]
category = "reading"
antifeatures = [ "paid-content" ]
level = 8
state = "working"
subtags = [ "books" ]
@@ -1935,7 +1927,6 @@ url = "https://github.com/YunoHost-Apps/mautrix_discord_ynh"
[mautrix_facebook]
category = "communication"
antifeatures = [ "deprecated-software" ]
level = 6
potential_alternative_to = [ "Facebook Messenger" ]
state = "working"
@@ -2709,7 +2700,7 @@ url = "https://github.com/YunoHost-Apps/privatebin_ynh"
category = "publishing"
level = 8
potential_alternative_to = [ "Blogger", "Blogspot", "Wix" ]
state = "notworking"
state = "working"
subtags = [ "website", "blog" ]
url = "https://github.com/YunoHost-Apps/processwire_ynh"

View File

@@ -2,40 +2,34 @@
icon = "cloud"
title.en = "Synchronization"
title.es = "Sincronización"
title.eu = "Sinkronizazioa"
title.fr = "Synchronisation"
title.it = "Sincronizzazione"
description.en = "Files sync, contact, calendar, password managers..."
description.es = "Sincronización, contactos, calendario, gestor de contraseñas..."
description.eu = "Sinkronizazioa, kontaktuak, egutegia, pasahitzen kudeaketa..."
description.fr = "Fichiers, contacts, calendrier, mots de passe..."
description.it = "Sincronizzazione di file, contatti, calendari, gestione password…"
[synchronization.subtags.files]
title.en = "Files"
title.es = "Archivos"
title.eu = "Fitxategiak"
title.fr = "Fichiers"
title.it = "File"
[synchronization.subtags.calendar]
title.en = "Calendar"
title.es = "Calendario"
title.eu = "Egutegia"
title.fr = "Calendrier"
title.it = "Calendario"
[synchronization.subtags.contacts]
title.en = "Contacts"
title.es = "Contactos"
title.eu = "Kontaktuak"
title.fr = "Contacts"
title.it = "Contatti"
[synchronization.subtags.password]
title.en = "Passwords"
title.es = "Contraseñas"
title.eu = "Pasahitzak"
title.fr = "Mots de passe"
title.it = "Password"
@@ -43,47 +37,40 @@ description.it = "Sincronizzazione di file, contatti, calendari, gestione passwo
icon = "globe"
title.en = "Publishing"
title.es = "Publicaciones"
title.eu = "Argitalpenak"
title.fr = "Publication"
title.it = "Pubblicazione"
description.en = "Websites, blog, wiki, CMS..."
description.es = "Paginas Web, blog, wiki, CMS..."
description.eu = "Web orriak, blogak, wikiak, CMSak..."
description.fr = "Site web, blog, wiki, CMS..."
description.it = "Siti web, blog, wiki, CMS…"
[publishing.subtags.website]
title.en = "Website"
title.es = "Paginas web"
title.eu = "Web orriak"
title.fr = "Site web"
title.it = "Siti web"
[publishing.subtags.blog]
title.en = "Blog"
title.es = "blog"
title.eu = "Blogak"
title.fr = "Blog"
title.it = "Blog"
[publishing.subtags.wiki]
title.en = "Wiki"
title.es = "Wiki"
title.eu = "Wikiak"
title.fr = "Wiki"
title.it = "Wiki"
[publishing.subtags.ecommerce]
title.en = "E-commerce"
title.es = "Comercio eletronico"
title.eu = "Merkataritza elektronikoa"
title.fr = "Vente en ligne"
title.it = "Vendite online"
[publishing.subtags.analytics]
title.en = "Analytics"
title.es = "Estadisticas"
title.eu = "Estatistikak"
title.fr = "Statistiques"
title.it = "Analisi del traffico"
@@ -91,40 +78,34 @@ description.it = "Siti web, blog, wiki, CMS…"
icon = "comments-o"
title.en = "Communication"
title.es = "Comunicacion"
title.eu = "Komunikazioa"
title.fr = "Communication"
title.it = "Comunicazione"
description.en = "Chat, email, forum, meetings..."
description.es = "Chat, email, foro, reuniones en grupo..."
description.eu = "Txata, ePosta, foroak, talde-bilerak..."
description.fr = "Chat, email, forum, meetings..."
description.it = "Messaggistica, posta elettronica, forum, riunioni…"
[communication.subtags.chat]
title.en = "Instant messaging"
title.es = "Mensajeria Instantanea"
title.eu = "Bat-bateko mezularitza"
title.fr = "Messagerie instantannée"
title.it = "Messaggistica istantanea"
[communication.subtags.forum]
title.en = "Forum"
title.es = "Foro"
title.eu = "Foroak"
title.fr = "Forum"
title.it = "Forum"
[communication.subtags.email]
title.en = "Email"
title.es = "Email"
title.eu = "ePosta"
title.fr = "Email"
title.it = "Posta elettronica"
[communication.subtags.meeting]
title.en = "Meetings"
title.es = "Reuniones"
title.eu = "Bilerak"
title.fr = "Meetings"
title.it = "Riunioni"
@@ -132,323 +113,268 @@ description.it = "Messaggistica, posta elettronica, forum, riunioni…"
icon = "file-text-o"
title.en = "Office"
title.es = "Ofimatica"
title.eu = "Bulegoa"
title.fr = "Bureautique"
title.it = "Burocrazia"
description.en = "Collaborative text editing, spreadsheets..."
description.es = "Edición de texto colaborativo, hojas de cálculo..."
description.eu = "Testu eta kalkulu-orrien edizioa..."
description.fr = "Édition de texte collaborative, tableurs..."
description.it = "Modifica di testo collaborativa, tabelle…"
[office.subtags.text]
title.en = "Text"
title.es = "Texto"
title.eu = "Testua"
title.fr = "Texte"
title.it = "Testo"
[office.subtags.spreadsheet]
title.en = "Spreadsheet"
title.es = "Hoja de cálculo"
title.eu = "Kalkulu-orriak"
title.fr = "Tableur"
title.it = "Tabelle"
[office.subtags.impress]
title.en = "Slide show"
title.es = "Diapositivas"
title.eu = "Diapositiba-aurkezpenak"
title.fr = "Diaporama"
title.it = "Diapositive"
[office.subtags.draw]
title.en = "Graphism"
title.es = "Graficos"
title.eu = "Grafikoak"
title.fr = "Graphisme"
title.it = "Grafica"
[office.subtags.mindmap]
title.en = "Mindmap"
title.eu = "Ideia-zuhaitzak"
title.fr = "Cartes mentale"
title.it = "Mappe mentali"
[productivity_and_management]
icon = "area-chart"
title.en = "Productivity & management"
title.eu = "Produktibitatea eta kudeaketa"
title.fr = "Productivité & gestion"
title.it = "Produttività & gestionale"
description.en = "Tasks, polls, accounting, ERP..."
description.eu = "Zereginak, inkestak, kontabilitatea, enpresa-baliabideen plangintza (ERP)..."
description.fr = "Tâches, sondages, comptabilité, ERP..."
description.it = "Gestione attività, sondaggi, contabilità, ERP…"
[productivity_and_management.subtags.task]
title.en = "Task"
title.eu = "Zereginak"
title.fr = "Tâches"
title.it = "Gestione attività"
[productivity_and_management.subtags.poll]
title.en = "Poll"
title.eu = "Inkestak"
title.fr = "Sondage"
title.it = "Sondaggi"
[productivity_and_management.subtags.accounting]
title.en = "Accounting"
title.eu = "Kontabilitatea"
title.fr = "Comptabilité"
title.it = "Contabilità"
[productivity_and_management.subtags.business_and_ngos]
title.en = "Business and NGOs"
title.eu = "Korporazio eta GKEak"
title.fr = "Entreprises et associations"
title.it = "Imprese e associazioni"
[small_utilities]
icon = "umbrella"
title.en = "Small utilities"
title.eu = "Tresna txikiak"
title.fr = "Petits utilitaires"
title.it = "Piccoli strumenti"
description.en = "Pastebins, URL shortener, proxies..."
description.eu = "Pastebinak, URL laburtzaileak, proxyak..."
description.fr = "Pastebins, raccourcisseurs d'URL, proxys..."
description.it = "Pastebin, accorciamento di URL, proxy…"
[small_utilities.subtags.pastebin]
title.en = "Pastebin"
title.eu = "Pastebinak"
title.fr = "Pastebin"
title.it = "Pastebin"
[small_utilities.subtags.url_shortener]
title.en = "URL shortener"
title.eu = "URL laburtzaileak"
title.fr = "Raccourcisseurs d'URL"
title.it = "Accorciatore di URL"
[small_utilities.subtags.proxy]
title.en = "Proxy"
title.eu = "Proxyak"
title.fr = "Proxy (Intermédiaire)"
title.it = "Proxy"
[reading]
icon = "newspaper-o"
title.en = "Reading"
title.eu = "Irakurketa"
title.fr = "Lecture"
title.it = "Lettura"
description.en = "Newsfeed readers, books library..."
description.eu = "Albiste-jarioak, liburutegiak..."
description.fr = "Fils d'actualité, livres..."
description.it = "Notizie, libri…"
[reading.subtags.rssreader]
title.en = "RSS readers"
title.eu = "RSS irakurleak"
title.fr = "Lecteurs RSS"
title.it = "Lettore RSS"
[reading.subtags.books]
title.en = "Books"
title.eu = "Liburuak"
title.fr = "Livres"
title.it = "Libri"
[multimedia]
icon = "music"
title.en = "Multimedia"
title.eu = "Multimedia"
title.fr = "Multimédia"
title.it = "Multimedia"
description.en = "Music library, pictures gallery, P2P, TV shows..."
description.eu = "Musika liburutegiak, argazki-bildumak, P2P, TB saioak..."
description.fr = "Bibliothèque de musique, d'images, P2P, séries..."
description.it = "Librerie musicali, gallerie dimmagini, P2P, serie TV…"
[multimedia.subtags.mediacenter]
title.en = "Media center"
title.eu = "Multimedia"
title.fr = "Centre multimédia"
title.it = "Centro multimediale"
[multimedia.subtags.download]
title.en = "Download"
title.eu = "Deskargak"
title.fr = "Téléchargement"
title.it = "Download"
[multimedia.subtags.music]
title.en = "Music"
title.eu = "Musika"
title.fr = "Musique"
title.it = "Musica"
[multimedia.subtags.pictures]
title.en = "Pictures"
title.eu = "Irudiak"
title.fr = "Images"
title.it = "Immagini"
[multimedia.subtags.videos]
title.en = "Videos"
title.eu = "Bideoak"
title.fr = "Vidéos"
title.it = "Video"
[social_media]
icon = "users"
title.en = "Social media"
title.eu = "Sare sozialak"
title.fr = "Médias sociaux"
title.it = "Social media"
description.en = "Microblogging, federated media"
description.eu = "Mikroblogak, federatutako media"
description.fr = "Microblogging, médias fédérés"
description.it = "Microblogging, media federati"
[social_media.subtags.microblogging]
title.en = "Microblogging"
title.eu = "Mikroblogintza"
title.fr = "Microblogging"
title.it = "Microblogging"
[social_media.subtags.blogging]
title.en = "Blogging"
title.eu = "Blogintza"
title.fr = "Blogging"
title.it = "Blogging"
[social_media.subtags.events]
title.en = "Events"
title.eu = "Gertaerak"
title.fr = "Événements"
title.it = "Eventi"
[social_media.subtags.videos]
title.en = "Videos"
title.eu = "Bideoak"
title.fr = "Vidéos"
title.it = "Video"
[social_media.subtags.pictures]
title.en = "Pictures"
title.eu = "Irudiak"
title.fr = "Images"
title.it = "Immagini"
[social_media.subtags.music]
title.en = "Music"
title.eu = "Musika"
title.fr = "Musique"
title.it = "Musica"
[games]
icon = "gamepad"
title.en = "Games"
title.eu = "Jolasak"
title.fr = "Jeux"
title.it = "Giochi"
description.en = "Wanna have some fun? ;)"
description.eu = "Dibertitu nahi duzu? ;)"
description.fr = "Envie de s'amuser ? ;)"
description.it = "Voglia di divertirti? ;)"
[dev]
icon = "flask"
title.en = "Development"
title.eu = "Garapena"
title.fr = "Développement"
title.it = "Sviluppo"
description.en = "Git forges, apps skeleton, CI, translation..."
description.eu = "Forge Git, aplikazioen eskematizazioa, CI, itzulpengintza..."
description.fr = "Forges Git, squelette d'apps, CI, traduction..."
description.it = "Forge Git, schematizzazione di applicazioni, CI, traduzioni…"
[dev.subtags.forge]
title.en = "Forge"
title.eu = "Forge"
title.fr = "Forge"
title.it = "Forgia"
[dev.subtags.skeleton]
title.en = "Skeleton"
title.eu = "Eskematizazioa"
title.fr = "Squelettes"
title.it = "Schematizzazione"
[dev.subtags.programming]
title.en = "Programming"
title.eu = "Programazioa"
title.fr = "Programmation"
title.it = "Programmazione"
[dev.subtags.design]
title.en = "Design"
title.eu = "Diseinua"
title.fr = "Design"
title.it = "Progettazione"
[system_tools]
icon = "wrench"
title.en = "System tools"
title.eu = "Sistemaren tresnak"
title.fr = "Outils système"
title.it = "Strumenti di sistema"
description.en = "Monitoring, backup, network, DB tools..."
description.eu = "Monitorizazioa, babeskopiak, sarea, datu-baseen tresnak..."
description.fr = "Monitoring, sauvegardes, outils réseau, bases de données..."
description.it = "Monitoraggio, backup, servizi di rete, database…"
[system_tools.subtags.backup]
title.en = "Backup"
title.eu = "Babeskopiak"
title.fr = "Sauvegardes"
title.it = "Backup"
[system_tools.subtags.monitoring]
title.en = "Monitoring"
title.eu = "Monitorizazioa"
title.fr = "Monitoring"
title.it = "Monitoraggio"
[system_tools.subtags.network]
title.en = "Network"
title.eu = "Sarea"
title.fr = "Réseau"
title.it = "Rete"
[system_tools.subtags.db]
title.en = "Databases"
title.eu = "Datu-baseak"
title.fr = "Bases de données"
title.it = "Database"
[iot]
icon = "home"
title.en = "Internet of Things (IoT)"
title.eu = "Gauzen internet (IoT)"
title.fr = "Internet des objets (IoT)"
title.it = "Interned delle Cose (IoT)"
description.en = "Home automation, energy dashboard..."
description.eu = "Domotika, automatizazioa, energiaren kudeaketa..."
description.fr = "Domotique, énergie..."
description.it = "Domotica, controllo energia…"
[wat]
icon = "tree"
title.en = "Wat"
title.eu = "Wat"
title.fr = "Wat"
title.it = "Wat"
description.en = "Weird experimental or very-custom stuff"
description.eu = "Esperimentuak, kontu arraroak edo gauza oso bereziak"
description.fr = "Trucs expérimentaux et autres projets spécifiques"
description.it = "Esperimenti e altri progetti particolari"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 311 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 170 KiB

6
store/.gitignore vendored
View File

@@ -1,8 +1,2 @@
config.toml
.stars
assets/fork-awesome.*
assets/forkawesome-webfont.*
assets/tailwind.css
assets/tailwindcss-linux-x64
assets/ynh_logo_*

View File

@@ -17,9 +17,6 @@ nano config.toml
mkdir -p ../builds/default/v3/
curl https://app.yunohost.org/default/v3/apps.json > ../builds/default/v3/apps.json
# you need to manually download the assets to have access to the css and the javascript files
(cd assets && bash fetch_assets)
# You will also want to run list_builder.py to initialize the .apps_cache (at least for a few apps, you can Ctrl+C after a while)
pip3 install tqdm GitPython
pushd ..

View File

@@ -149,14 +149,7 @@ def star_app(app_id, action):
if app_id not in get_catalog()["apps"] and app_id not in get_wishlist():
return _("App %(app_id) not found", app_id=app_id), 404
if not session.get("user", {}):
return (
_("You must be logged in to be able to star an app")
+ "<br/><br/>"
+ _(
"Note that, due to various abuses, we restricted login on the app store to 'trust level 1' users.<br/><br/>'Trust level 1' is obtained after interacting a minimum with the forum, and more specifically: entering at least 5 topics, reading at least 30 posts, and spending at least 10 minutes reading posts."
),
401,
)
return _("You must be logged in to be able to star an app") + "<br/><br/>" + _("Note that, due to various abuses, we restricted login on the app store to 'trust level 1' users.<br/><br/>'Trust level 1' is obtained after interacting a minimum with the forum, and more specifically: entering at least 5 topics, reading at least 30 posts, and spending at least 10 minutes reading posts."), 401
app_star_folder = os.path.join(".stars", app_id)
app_star_for_this_user = os.path.join(
@@ -199,13 +192,7 @@ def add_to_wishlist():
if request.method == "POST":
user = session.get("user", {})
if not user:
errormsg = (
_("You must be logged in to submit an app to the wishlist")
+ "<br/><br/>"
+ _(
"Note that, due to various abuses, we restricted login on the app store to 'trust level 1' users.<br/><br/>'Trust level 1' is obtained after interacting a minimum with the forum, and more specifically: entering at least 5 topics, reading at least 30 posts, and spending at least 10 minutes reading posts."
)
)
errormsg = _("You must be logged in to submit an app to the wishlist") + "<br/><br/>" + _("Note that, due to various abuses, we restricted login on the app store to 'trust level 1' users.<br/><br/>'Trust level 1' is obtained after interacting a minimum with the forum, and more specifically: entering at least 5 topics, reading at least 30 posts, and spending at least 10 minutes reading posts.")
return render_template(
"wishlist_add.html",
locale=get_locale(),
@@ -233,33 +220,12 @@ def add_to_wishlist():
website = request.form["website"].strip().replace("\n", "")
license = request.form["license"].strip().replace("\n", "")
boring_keywords_to_check_for_people_not_reading_the_instructions = [
"free",
"open source",
"open-source",
"self-hosted",
"simple",
"lightweight",
"light-weight",
"léger",
"best",
"most",
"fast",
"rapide",
"flexible",
"puissante",
"puissant",
"powerful",
"secure",
]
boring_keywords_to_check_for_people_not_reading_the_instructions = ["free", "open source", "open-source", "self-hosted", "simple", "lightweight", "light-weight", "léger", "best", "most", "fast", "rapide", "flexible", "puissante", "puissant", "powerful", "secure"]
checks = [
(
check_wishlist_submit_ratelimit(session["user"]["username"]) is True
and session["user"]["bypass_ratelimit"] is False,
_(
"Proposing wishlist additions is limited to once every 15 days per user. Please try again in a few days."
),
check_wishlist_submit_ratelimit(session['user']['username']) is True and session['user']['bypass_ratelimit'] is False,
_("Proposing wishlist additions is limited to once every 15 days per user. Please try again in a few days.")
),
(len(name) >= 3, _("App name should be at least 3 characters")),
(len(name) <= 30, _("App name should be less than 30 characters")),
@@ -293,22 +259,13 @@ def add_to_wishlist():
_("App name contains special characters"),
),
(
all(
keyword not in description.lower()
for keyword in boring_keywords_to_check_for_people_not_reading_the_instructions
),
_(
"Please focus on what the app does, without using marketing, fuzzy terms, or repeating that the app is 'free' and 'self-hostable'."
),
all(keyword not in description.lower() for keyword in boring_keywords_to_check_for_people_not_reading_the_instructions),
_("Please focus on what the app does, without using marketing, fuzzy terms, or repeating that the app is 'free' and 'self-hostable'.")
),
(
description.lower().split()[0] != name
and (
len(description.split()) == 1
or description.lower().split()[1] not in ["is", "est"]
),
_("No need to repeat the name of the app. Focus on what the app does."),
),
description.lower().split()[0] != name and (len(description.split()) == 1 or description.lower().split()[1] not in ["is", "est"]),
_("No need to repeat the name of the app. Focus on what the app does.")
)
]
for check, errormsg in checks:
@@ -343,8 +300,7 @@ def add_to_wishlist():
successmsg=None,
errormsg=_(
"An entry with the name %(slug)s already exists in the wishlist, instead, you can <a href='%(url)s'>add a star to the app to show your interest</a>.",
slug=slug,
url=url,
slug=slug, url=url,
),
)
@@ -368,7 +324,7 @@ def add_to_wishlist():
url = "https://github.com/YunoHost/apps/pulls?q=is%3Apr+is%3Aopen+wishlist"
errormsg = _(
"Failed to create the pull request to add the app to the wishlist… Maybe there's already <a href='%(url)s'>a waiting PR for this app</a>? Else, please report the issue to the YunoHost team.",
url=url,
url=url
)
return render_template(
"wishlist_add.html",
@@ -422,7 +378,7 @@ Description: {description}
url=url,
)
save_wishlist_submit_for_ratelimit(session["user"]["username"])
save_wishlist_submit_for_ratelimit(session['user']['username'])
return render_template(
"wishlist_add.html",
@@ -489,17 +445,10 @@ def sso_login_callback():
uri_to_redirect_to_after_login = session.get("uri_to_redirect_to_after_login")
if "trust_level_1" not in user_data["groups"][0].split(","):
return (
_("Unfortunately, login was denied.")
+ "<br/><br/>"
+ _(
"Note that, due to various abuses, we restricted login on the app store to 'trust level 1' users.<br/><br/>'Trust level 1' is obtained after interacting a minimum with the forum, and more specifically: entering at least 5 topics, reading at least 30 posts, and spending at least 10 minutes reading posts."
),
403,
)
if "trust_level_1" not in user_data['groups'][0].split(','):
return _("Unfortunately, login was denied.") + "<br/><br/>" + _("Note that, due to various abuses, we restricted login on the app store to 'trust level 1' users.<br/><br/>'Trust level 1' is obtained after interacting a minimum with the forum, and more specifically: entering at least 5 topics, reading at least 30 posts, and spending at least 10 minutes reading posts."), 403
if "staff" in user_data["groups"][0].split(","):
if "staff" in user_data['groups'][0].split(','):
bypass_ratelimit = True
else:
bypass_ratelimit = False

View File

@@ -1,14 +1,13 @@
import os
install_dir = os.path.dirname(__file__)
command = f"{install_dir}/venv/bin/gunicorn"
command = f'{install_dir}/venv/bin/gunicorn'
pythonpath = install_dir
workers = 4
user = "appstore"
bind = f"unix:{install_dir}/sock"
pid = "/run/gunicorn/appstore-pid"
errorlog = "/var/log/appstore/error.log"
accesslog = "/var/log/appstore/access.log"
user = 'appstore'
bind = f'unix:{install_dir}/sock'
pid = '/run/gunicorn/appstore-pid'
errorlog = '/var/log/appstore/error.log'
accesslog = '/var/log/appstore/access.log'
access_log_format = '%({X-Real-IP}i)s %({X-Forwarded-For}i)s %(h)s %(l)s %(u)s %(t)s "%(r)s" %(s)s %(b)s "%(f)s" "%(a)s"'
loglevel = "warning"
loglevel = 'warning'
capture_output = True

View File

@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-03-05 19:36+0100\n"
"POT-Creation-Date: 2024-02-27 19:41+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -139,7 +139,7 @@ msgid ""
msgstr ""
#: templates/app.html:30 templates/app.html:31 templates/catalog.html:41
#: templates/catalog.html:42 templates/catalog.html:170
#: templates/catalog.html:42 templates/catalog.html:169
msgid ""
"This is usually a temporary situation which requires packagers to fix "
"something in the app."
@@ -331,28 +331,14 @@ msgstr ""
msgid "Checkout the wishlist!"
msgstr ""
#: templates/catalog.html:166
#: templates/catalog.html:165
msgid "Applications currently flagged as broken"
msgstr ""
#: templates/catalog.html:169
#: templates/catalog.html:168
msgid "These are apps which failed our automatic tests."
msgstr ""
#: templates/catalog.html:184
msgid "Deprecated applications"
msgstr ""
#: templates/catalog.html:187
msgid "These are apps who are not maintained anymore."
msgstr ""
#: templates/catalog.html:188
msgid ""
"This means that the developer will no longer update them. We strongly "
"advise against their installation and advise users to find alternatives."
msgstr ""
#: templates/index.html:10
msgid "Application Store"
msgstr ""

View File

@@ -143,7 +143,7 @@
<div id="catalogGoodQuality" class="grid grid-cols-1 gap-4 sm:grid-cols-2 md:grid-cols-3 max-w-screen-lg mx-auto pt-10">
{% for app, infos in catalog['apps'].items() %}
{% if infos['level'] and infos['level'] != "?" and infos['level'] > 4 and not "deprecated-software" in infos['antifeatures'] %}
{% if infos['level'] and infos['level'] != "?" and infos['level'] > 4 %}
{{ appCard(app, infos, timestamp_now, catalog) }}
{% endif %}
{% endfor %}
@@ -160,7 +160,6 @@
</p>
</div>
<div id="lowQualityAppTitle" class="text-center pt-10 mx-4 md:mx-0">
<h2 class="text-lg font-bold text-gray-900">
{{ _("Applications currently flagged as broken") }}
@@ -179,24 +178,6 @@
{% endfor %}
</div>
<div id="deprecatedAppTitle" class="text-center pt-10 mx-4 md:mx-0">
<h2 class="text-lg font-bold text-gray-900">
{{ _("Deprecated applications") }}
</h2>
<p class="text-sm">
{{ _("These are apps who are not maintained anymore.") }}<br/>
{{ _("This means that the developer will no longer update them. We strongly advise against their installation and advise users to find alternatives.") }}
</p>
</div>
<div id="catalogDeprecated" class="grid grid-cols-1 gap-4 sm:grid-cols-2 md:grid-cols-3 max-w-screen-lg mx-auto pt-10">
{% for app, infos in catalog['apps'].items() %}
{% if "deprecated-software" in infos['antifeatures'] %}
{{ appCard(app, infos, timestamp_now, catalog) }}
{% endif %}
{% endfor %}
</div>
<script type="text/javascript">
// A little delay

View File

@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-03-05 19:36+0100\n"
"POT-Creation-Date: 2024-02-27 19:41+0100\n"
"PO-Revision-Date: 2024-02-21 06:04+0100\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: ar <LL@li.org>\n"
@@ -140,7 +140,7 @@ msgid ""
msgstr ""
#: templates/app.html:30 templates/app.html:31 templates/catalog.html:41
#: templates/catalog.html:42 templates/catalog.html:170
#: templates/catalog.html:42 templates/catalog.html:169
msgid ""
"This is usually a temporary situation which requires packagers to fix "
"something in the app."
@@ -331,28 +331,14 @@ msgstr ""
msgid "Checkout the wishlist!"
msgstr ""
#: templates/catalog.html:166
#: templates/catalog.html:165
msgid "Applications currently flagged as broken"
msgstr ""
#: templates/catalog.html:169
#: templates/catalog.html:168
msgid "These are apps which failed our automatic tests."
msgstr ""
#: templates/catalog.html:184
msgid "Deprecated applications"
msgstr ""
#: templates/catalog.html:187
msgid "These are apps who are not maintained anymore."
msgstr ""
#: templates/catalog.html:188
msgid ""
"This means that the developer will no longer update them. We strongly "
"advise against their installation and advise users to find alternatives."
msgstr ""
#: templates/index.html:10
msgid "Application Store"
msgstr ""

View File

@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-03-05 19:36+0100\n"
"POT-Creation-Date: 2024-02-27 19:41+0100\n"
"PO-Revision-Date: 2024-02-21 06:05+0100\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: bn_BD <LL@li.org>\n"
@@ -139,7 +139,7 @@ msgid ""
msgstr ""
#: templates/app.html:30 templates/app.html:31 templates/catalog.html:41
#: templates/catalog.html:42 templates/catalog.html:170
#: templates/catalog.html:42 templates/catalog.html:169
msgid ""
"This is usually a temporary situation which requires packagers to fix "
"something in the app."
@@ -330,28 +330,14 @@ msgstr ""
msgid "Checkout the wishlist!"
msgstr ""
#: templates/catalog.html:166
#: templates/catalog.html:165
msgid "Applications currently flagged as broken"
msgstr ""
#: templates/catalog.html:169
#: templates/catalog.html:168
msgid "These are apps which failed our automatic tests."
msgstr ""
#: templates/catalog.html:184
msgid "Deprecated applications"
msgstr ""
#: templates/catalog.html:187
msgid "These are apps who are not maintained anymore."
msgstr ""
#: templates/catalog.html:188
msgid ""
"This means that the developer will no longer update them. We strongly "
"advise against their installation and advise users to find alternatives."
msgstr ""
#: templates/index.html:10
msgid "Application Store"
msgstr ""

View File

@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-03-05 19:36+0100\n"
"POT-Creation-Date: 2024-02-27 19:41+0100\n"
"PO-Revision-Date: 2024-02-21 06:05+0100\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: br <LL@li.org>\n"
@@ -143,7 +143,7 @@ msgid ""
msgstr ""
#: templates/app.html:30 templates/app.html:31 templates/catalog.html:41
#: templates/catalog.html:42 templates/catalog.html:170
#: templates/catalog.html:42 templates/catalog.html:169
msgid ""
"This is usually a temporary situation which requires packagers to fix "
"something in the app."
@@ -334,28 +334,14 @@ msgstr ""
msgid "Checkout the wishlist!"
msgstr ""
#: templates/catalog.html:166
#: templates/catalog.html:165
msgid "Applications currently flagged as broken"
msgstr ""
#: templates/catalog.html:169
#: templates/catalog.html:168
msgid "These are apps which failed our automatic tests."
msgstr ""
#: templates/catalog.html:184
msgid "Deprecated applications"
msgstr ""
#: templates/catalog.html:187
msgid "These are apps who are not maintained anymore."
msgstr ""
#: templates/catalog.html:188
msgid ""
"This means that the developer will no longer update them. We strongly "
"advise against their installation and advise users to find alternatives."
msgstr ""
#: templates/index.html:10
msgid "Application Store"
msgstr ""

View File

@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-03-05 19:36+0100\n"
"POT-Creation-Date: 2024-02-27 19:41+0100\n"
"PO-Revision-Date: 2024-02-21 06:05+0100\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: ca <LL@li.org>\n"
@@ -139,7 +139,7 @@ msgid ""
msgstr ""
#: templates/app.html:30 templates/app.html:31 templates/catalog.html:41
#: templates/catalog.html:42 templates/catalog.html:170
#: templates/catalog.html:42 templates/catalog.html:169
msgid ""
"This is usually a temporary situation which requires packagers to fix "
"something in the app."
@@ -330,28 +330,14 @@ msgstr ""
msgid "Checkout the wishlist!"
msgstr ""
#: templates/catalog.html:166
#: templates/catalog.html:165
msgid "Applications currently flagged as broken"
msgstr ""
#: templates/catalog.html:169
#: templates/catalog.html:168
msgid "These are apps which failed our automatic tests."
msgstr ""
#: templates/catalog.html:184
msgid "Deprecated applications"
msgstr ""
#: templates/catalog.html:187
msgid "These are apps who are not maintained anymore."
msgstr ""
#: templates/catalog.html:188
msgid ""
"This means that the developer will no longer update them. We strongly "
"advise against their installation and advise users to find alternatives."
msgstr ""
#: templates/index.html:10
msgid "Application Store"
msgstr ""

View File

@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-03-05 19:36+0100\n"
"POT-Creation-Date: 2024-02-27 19:41+0100\n"
"PO-Revision-Date: 2024-02-21 06:08+0100\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: ckb <LL@li.org>\n"
@@ -139,7 +139,7 @@ msgid ""
msgstr ""
#: templates/app.html:30 templates/app.html:31 templates/catalog.html:41
#: templates/catalog.html:42 templates/catalog.html:170
#: templates/catalog.html:42 templates/catalog.html:169
msgid ""
"This is usually a temporary situation which requires packagers to fix "
"something in the app."
@@ -330,28 +330,14 @@ msgstr ""
msgid "Checkout the wishlist!"
msgstr ""
#: templates/catalog.html:166
#: templates/catalog.html:165
msgid "Applications currently flagged as broken"
msgstr ""
#: templates/catalog.html:169
#: templates/catalog.html:168
msgid "These are apps which failed our automatic tests."
msgstr ""
#: templates/catalog.html:184
msgid "Deprecated applications"
msgstr ""
#: templates/catalog.html:187
msgid "These are apps who are not maintained anymore."
msgstr ""
#: templates/catalog.html:188
msgid ""
"This means that the developer will no longer update them. We strongly "
"advise against their installation and advise users to find alternatives."
msgstr ""
#: templates/index.html:10
msgid "Application Store"
msgstr ""

View File

@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-03-05 19:36+0100\n"
"POT-Creation-Date: 2024-02-27 19:41+0100\n"
"PO-Revision-Date: 2024-02-21 06:09+0100\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: cs <LL@li.org>\n"
@@ -139,7 +139,7 @@ msgid ""
msgstr ""
#: templates/app.html:30 templates/app.html:31 templates/catalog.html:41
#: templates/catalog.html:42 templates/catalog.html:170
#: templates/catalog.html:42 templates/catalog.html:169
msgid ""
"This is usually a temporary situation which requires packagers to fix "
"something in the app."
@@ -330,28 +330,14 @@ msgstr ""
msgid "Checkout the wishlist!"
msgstr ""
#: templates/catalog.html:166
#: templates/catalog.html:165
msgid "Applications currently flagged as broken"
msgstr ""
#: templates/catalog.html:169
#: templates/catalog.html:168
msgid "These are apps which failed our automatic tests."
msgstr ""
#: templates/catalog.html:184
msgid "Deprecated applications"
msgstr ""
#: templates/catalog.html:187
msgid "These are apps who are not maintained anymore."
msgstr ""
#: templates/catalog.html:188
msgid ""
"This means that the developer will no longer update them. We strongly "
"advise against their installation and advise users to find alternatives."
msgstr ""
#: templates/index.html:10
msgid "Application Store"
msgstr ""

View File

@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-03-05 19:36+0100\n"
"POT-Creation-Date: 2024-02-27 19:41+0100\n"
"PO-Revision-Date: 2024-02-21 06:05+0100\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: da <LL@li.org>\n"
@@ -139,7 +139,7 @@ msgid ""
msgstr ""
#: templates/app.html:30 templates/app.html:31 templates/catalog.html:41
#: templates/catalog.html:42 templates/catalog.html:170
#: templates/catalog.html:42 templates/catalog.html:169
msgid ""
"This is usually a temporary situation which requires packagers to fix "
"something in the app."
@@ -330,28 +330,14 @@ msgstr ""
msgid "Checkout the wishlist!"
msgstr ""
#: templates/catalog.html:166
#: templates/catalog.html:165
msgid "Applications currently flagged as broken"
msgstr ""
#: templates/catalog.html:169
#: templates/catalog.html:168
msgid "These are apps which failed our automatic tests."
msgstr ""
#: templates/catalog.html:184
msgid "Deprecated applications"
msgstr ""
#: templates/catalog.html:187
msgid "These are apps who are not maintained anymore."
msgstr ""
#: templates/catalog.html:188
msgid ""
"This means that the developer will no longer update them. We strongly "
"advise against their installation and advise users to find alternatives."
msgstr ""
#: templates/index.html:10
msgid "Application Store"
msgstr ""

View File

@@ -7,8 +7,8 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-03-05 19:36+0100\n"
"PO-Revision-Date: 2024-03-06 17:37+0000\n"
"POT-Creation-Date: 2024-02-27 19:41+0100\n"
"PO-Revision-Date: 2024-03-04 19:26+0000\n"
"Last-Translator: Christian Wehrli <christian@chw.onl>\n"
"Language-Team: German <https://translate.yunohost.org/projects/yunohost/apps/"
"de/>\n"
@@ -37,8 +37,8 @@ msgid ""
"reading posts."
msgstr ""
"Beachten Sie, dass wir aufgrund verschiedener Missbräuche die Anmeldung im "
"App-Store auf Benutzer der „Vertrauensstufe 1“ beschränkt haben.<br/><br/"
">„Vertrauensstufe 1“ wird erreicht, wenn man mindestens mit dem Forum "
"App-Store auf Benutzer der „Vertrauensstufe 1“ beschränkt haben.<br/><br/>„"
"Vertrauensstufe 1“ wird erreicht, wenn man mindestens mit dem Forum "
"interagiert hat, spezifisch: Mindestens 5 Themen geöffnet, mindestens 30 "
"Beiträge gelesen und mindestens 10 Minuten damit verbracht haben, Beiträge "
"zu lesen."
@@ -80,8 +80,7 @@ msgstr "Die App-Beschreibung sollte weniger als 100 Zeichen umfassen"
#: app.py:242
msgid "Upstream code repo URL should be at least 10 characters"
msgstr ""
"Die Upstream-Code-Repository-URL sollte mindestens 10 Zeichen lang sein"
msgstr "Die Upstream-Code-Repository-URL sollte mindestens 10 Zeichen lang sein"
#: app.py:246
msgid "Upstream code repo URL should be less than 150 characters"
@@ -171,7 +170,7 @@ msgstr ""
"Tests nicht bestanden hat."
#: templates/app.html:30 templates/app.html:31 templates/catalog.html:41
#: templates/catalog.html:42 templates/catalog.html:170
#: templates/catalog.html:42 templates/catalog.html:169
msgid ""
"This is usually a temporary situation which requires packagers to fix "
"something in the app."
@@ -318,8 +317,8 @@ msgid ""
msgstr ""
"Hergestellt mit <i class='text-red-500 fa fa-heart-o' aria-label='love'></i> "
"mit Hilfe von <a class='text-blue-800' href='https://flask.palletsprojects."
"com'>Flask</a> und <a class='text-blue-800' href='https://tailwindcss."
"com/'>TailwindCSS</a> - <a class='text-blue-800' href='https://github.com/"
"com'>Flask</a> und <a class='text-blue-800' href='https://tailwindcss.com/"
"'>TailwindCSS</a> - <a class='text-blue-800' href='https://github.com/"
"YunoHost/apps/tree/master/store'><i class='fa fa-code fa-fw' aria-"
"hidden='true'></i> Source</a>"
@@ -377,35 +376,14 @@ msgstr "Finden Sie nicht, wonach Sie suchen?"
msgid "Checkout the wishlist!"
msgstr "Schauen Sie sich die Wunschliste an!"
#: templates/catalog.html:166
#: templates/catalog.html:165
msgid "Applications currently flagged as broken"
msgstr "Applikationen, die gegenwärtig als defekt markiert sind"
#: templates/catalog.html:169
#: templates/catalog.html:168
msgid "These are apps which failed our automatic tests."
msgstr "Das sind Apps, welche die automatischen Tests nicht bestanden haben."
#: templates/catalog.html:184
#, fuzzy
#| msgid "Browse all applications"
msgid "Deprecated applications"
msgstr "Alle Applikationen"
#: templates/catalog.html:187
#, fuzzy
#| msgid "These are apps which failed our automatic tests."
msgid "These are apps who are not maintained anymore."
msgstr "Das sind Apps, welche die automatischen Tests nicht bestanden haben."
#: templates/catalog.html:188
msgid ""
"This means that the developer will no longer update them. We strongly "
"advise against their installation and advise users to find alternatives."
msgstr ""
"Das bedeutet, dass der Entwickler sie nicht mehr aktualisieren wird. Wir "
"raten dringend von deren Installation ab und empfehlen Benutzern, nach "
"Alternativen zu suchen."
#: templates/index.html:10
msgid "Application Store"
msgstr "Applikations-Store"

View File

@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-03-05 19:36+0100\n"
"POT-Creation-Date: 2024-02-27 19:41+0100\n"
"PO-Revision-Date: 2024-02-21 06:06+0100\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: el <LL@li.org>\n"
@@ -139,7 +139,7 @@ msgid ""
msgstr ""
#: templates/app.html:30 templates/app.html:31 templates/catalog.html:41
#: templates/catalog.html:42 templates/catalog.html:170
#: templates/catalog.html:42 templates/catalog.html:169
msgid ""
"This is usually a temporary situation which requires packagers to fix "
"something in the app."
@@ -330,28 +330,14 @@ msgstr ""
msgid "Checkout the wishlist!"
msgstr ""
#: templates/catalog.html:166
#: templates/catalog.html:165
msgid "Applications currently flagged as broken"
msgstr ""
#: templates/catalog.html:169
#: templates/catalog.html:168
msgid "These are apps which failed our automatic tests."
msgstr ""
#: templates/catalog.html:184
msgid "Deprecated applications"
msgstr ""
#: templates/catalog.html:187
msgid "These are apps who are not maintained anymore."
msgstr ""
#: templates/catalog.html:188
msgid ""
"This means that the developer will no longer update them. We strongly "
"advise against their installation and advise users to find alternatives."
msgstr ""
#: templates/index.html:10
msgid "Application Store"
msgstr ""

View File

@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-03-05 19:36+0100\n"
"POT-Creation-Date: 2024-02-27 19:41+0100\n"
"PO-Revision-Date: 2024-02-21 06:06+0100\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: eo <LL@li.org>\n"
@@ -139,7 +139,7 @@ msgid ""
msgstr ""
#: templates/app.html:30 templates/app.html:31 templates/catalog.html:41
#: templates/catalog.html:42 templates/catalog.html:170
#: templates/catalog.html:42 templates/catalog.html:169
msgid ""
"This is usually a temporary situation which requires packagers to fix "
"something in the app."
@@ -330,28 +330,14 @@ msgstr ""
msgid "Checkout the wishlist!"
msgstr ""
#: templates/catalog.html:166
#: templates/catalog.html:165
msgid "Applications currently flagged as broken"
msgstr ""
#: templates/catalog.html:169
#: templates/catalog.html:168
msgid "These are apps which failed our automatic tests."
msgstr ""
#: templates/catalog.html:184
msgid "Deprecated applications"
msgstr ""
#: templates/catalog.html:187
msgid "These are apps who are not maintained anymore."
msgstr ""
#: templates/catalog.html:188
msgid ""
"This means that the developer will no longer update them. We strongly "
"advise against their installation and advise users to find alternatives."
msgstr ""
#: templates/index.html:10
msgid "Application Store"
msgstr ""

View File

@@ -7,22 +7,20 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-03-05 19:36+0100\n"
"PO-Revision-Date: 2024-03-09 17:32+0000\n"
"Last-Translator: cri <cri@cri.cl>\n"
"Language-Team: Spanish <https://translate.yunohost.org/projects/yunohost/"
"apps/es/>\n"
"POT-Creation-Date: 2024-02-27 19:41+0100\n"
"PO-Revision-Date: 2024-02-21 06:05+0100\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: es <LL@li.org>\n"
"Language: es\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 5.3.1\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"Generated-By: Babel 2.14.0\n"
#: app.py:150
msgid "App %(app_id) not found"
msgstr "App %(app_id) no encontrada"
msgstr ""
#: app.py:152
msgid "You must be logged in to be able to star an app"
@@ -141,7 +139,7 @@ msgid ""
msgstr ""
#: templates/app.html:30 templates/app.html:31 templates/catalog.html:41
#: templates/catalog.html:42 templates/catalog.html:170
#: templates/catalog.html:42 templates/catalog.html:169
msgid ""
"This is usually a temporary situation which requires packagers to fix "
"something in the app."
@@ -332,28 +330,14 @@ msgstr ""
msgid "Checkout the wishlist!"
msgstr ""
#: templates/catalog.html:166
#: templates/catalog.html:165
msgid "Applications currently flagged as broken"
msgstr ""
#: templates/catalog.html:169
#: templates/catalog.html:168
msgid "These are apps which failed our automatic tests."
msgstr ""
#: templates/catalog.html:184
msgid "Deprecated applications"
msgstr ""
#: templates/catalog.html:187
msgid "These are apps who are not maintained anymore."
msgstr ""
#: templates/catalog.html:188
msgid ""
"This means that the developer will no longer update them. We strongly "
"advise against their installation and advise users to find alternatives."
msgstr ""
#: templates/index.html:10
msgid "Application Store"
msgstr ""

View File

@@ -7,8 +7,8 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-03-05 19:36+0100\n"
"PO-Revision-Date: 2024-03-06 18:13+0000\n"
"POT-Creation-Date: 2024-02-27 19:41+0100\n"
"PO-Revision-Date: 2024-03-04 19:26+0000\n"
"Last-Translator: xabirequejo <xabi.rn@gmail.com>\n"
"Language-Team: Basque <https://translate.yunohost.org/projects/yunohost/apps/"
"eu/>\n"
@@ -82,8 +82,7 @@ msgstr ""
#: app.py:246
msgid "Upstream code repo URL should be less than 150 characters"
msgstr ""
"Jatorrizko kode-gordailuaren URLak 150 karaktere izan ditzazke gehienez"
msgstr "Jatorrizko kode-gordailuaren URLak 150 karaktere izan ditzazke gehienez"
#: app.py:250
msgid "License URL should be at least 10 characters"
@@ -145,8 +144,8 @@ msgid ""
"%(url)s</a>"
msgstr ""
"Proposatutako aplikazioa behar bezala bidali da. YunoHosten taldeak "
"baliozkotu behar du orain. Egoera hemen ikusi dezakezu: <a href='%(url)s'>"
"%(url)s</a>"
"baliozkotu behar du orain. Egoera hemen ikusi dezakezu: <a "
"href='%(url)s'>%(url)s</a>"
#: app.py:449
msgid "Unfortunately, login was denied."
@@ -166,7 +165,7 @@ msgstr ""
"Aplikazioa ez dabilela ageri da ez dituelako gure test automatikoak gainditu."
#: templates/app.html:30 templates/app.html:31 templates/catalog.html:41
#: templates/catalog.html:42 templates/catalog.html:170
#: templates/catalog.html:42 templates/catalog.html:169
msgid ""
"This is usually a temporary situation which requires packagers to fix "
"something in the app."
@@ -367,30 +366,14 @@ msgstr "Ez duzu bilatzen ari zarena aurkitzen?"
msgid "Checkout the wishlist!"
msgstr "Begiratu desira-zerrenda!"
#: templates/catalog.html:166
#: templates/catalog.html:165
msgid "Applications currently flagged as broken"
msgstr "Aplikazioa hondatuta gisa ageri da"
#: templates/catalog.html:169
#: templates/catalog.html:168
msgid "These are apps which failed our automatic tests."
msgstr "Gure test automatikoak gainditu ez dituzten aplikazioak dira hauek."
#: templates/catalog.html:184
msgid "Deprecated applications"
msgstr "Utzitako aplikazioak"
#: templates/catalog.html:187
msgid "These are apps who are not maintained anymore."
msgstr "Aplikazio hauek ez dute mantenduko dituenik."
#: templates/catalog.html:188
msgid ""
"This means that the developer will no longer update them. We strongly "
"advise against their installation and advise users to find alternatives."
msgstr ""
"Honek esan nahi du garatzaileek ez dituztela aurrerantzean eguneratuko. Gure "
"gomendioa ez instalatzea da eta beraien ordez alternatibak bilatzea."
#: templates/index.html:10
msgid "Application Store"
msgstr "Aplikazio denda"

View File

@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-03-05 19:36+0100\n"
"POT-Creation-Date: 2024-02-27 19:41+0100\n"
"PO-Revision-Date: 2024-02-21 06:08+0100\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: fa <LL@li.org>\n"
@@ -139,7 +139,7 @@ msgid ""
msgstr ""
#: templates/app.html:30 templates/app.html:31 templates/catalog.html:41
#: templates/catalog.html:42 templates/catalog.html:170
#: templates/catalog.html:42 templates/catalog.html:169
msgid ""
"This is usually a temporary situation which requires packagers to fix "
"something in the app."
@@ -330,28 +330,14 @@ msgstr ""
msgid "Checkout the wishlist!"
msgstr ""
#: templates/catalog.html:166
#: templates/catalog.html:165
msgid "Applications currently flagged as broken"
msgstr ""
#: templates/catalog.html:169
#: templates/catalog.html:168
msgid "These are apps which failed our automatic tests."
msgstr ""
#: templates/catalog.html:184
msgid "Deprecated applications"
msgstr ""
#: templates/catalog.html:187
msgid "These are apps who are not maintained anymore."
msgstr ""
#: templates/catalog.html:188
msgid ""
"This means that the developer will no longer update them. We strongly "
"advise against their installation and advise users to find alternatives."
msgstr ""
#: templates/index.html:10
msgid "Application Store"
msgstr ""

View File

@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-03-05 19:36+0100\n"
"POT-Creation-Date: 2024-02-27 19:41+0100\n"
"PO-Revision-Date: 2024-02-21 06:06+0100\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: fi <LL@li.org>\n"
@@ -139,7 +139,7 @@ msgid ""
msgstr ""
#: templates/app.html:30 templates/app.html:31 templates/catalog.html:41
#: templates/catalog.html:42 templates/catalog.html:170
#: templates/catalog.html:42 templates/catalog.html:169
msgid ""
"This is usually a temporary situation which requires packagers to fix "
"something in the app."
@@ -330,28 +330,14 @@ msgstr ""
msgid "Checkout the wishlist!"
msgstr ""
#: templates/catalog.html:166
#: templates/catalog.html:165
msgid "Applications currently flagged as broken"
msgstr ""
#: templates/catalog.html:169
#: templates/catalog.html:168
msgid "These are apps which failed our automatic tests."
msgstr ""
#: templates/catalog.html:184
msgid "Deprecated applications"
msgstr ""
#: templates/catalog.html:187
msgid "These are apps who are not maintained anymore."
msgstr ""
#: templates/catalog.html:188
msgid ""
"This means that the developer will no longer update them. We strongly "
"advise against their installation and advise users to find alternatives."
msgstr ""
#: templates/index.html:10
msgid "Application Store"
msgstr ""

View File

@@ -7,14 +7,14 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-03-05 19:36+0100\n"
"PO-Revision-Date: 2024-03-09 04:14+0000\n"
"POT-Creation-Date: 2024-02-27 19:41+0100\n"
"PO-Revision-Date: 2024-02-27 19:19+0000\n"
"Last-Translator: OniriCorpe <oniricorpe@disroot.org>\n"
"Language-Team: French <https://translate.yunohost.org/projects/yunohost/apps/"
"fr/>\n"
"Language: fr\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n > 1;\n"
"X-Generator: Weblate 5.3.1\n"
@@ -30,21 +30,22 @@ msgstr "Vous devez être connecté·e pour mettre une app en favoris"
#: app.py:152 app.py:195 app.py:449 templates/wishlist_add.html:33
msgid ""
"Note that, due to various abuses, we restricted login on the app store to"
" 'trust level 1' users.<br/><br/>'Trust level 1' is obtained after "
"Note that, due to various abuses, we restricted login on the app store to "
"'trust level 1' users.<br/><br/>'Trust level 1' is obtained after "
"interacting a minimum with the forum, and more specifically: entering at "
"least 5 topics, reading at least 30 posts, and spending at least 10 "
"minutes reading posts."
"least 5 topics, reading at least 30 posts, and spending at least 10 minutes "
"reading posts."
msgstr ""
"Notez que, suite à divers abus, la connexion nécessite maintenant d'être "
"'trust level 1' sur le forum.<br/><br/>Le 'trust level 1' est obtenu "
"après avoir intéragit un minimum avec le forum, et plus précisémment: "
"ouvrir au moins 5 fils de discussion, lire au moins 30 messages, et "
"passer au moins 10 minutes à lire des messages."
"'trust level 1' sur le forum.<br/><br/>Le 'trust level 1' est obtenu après "
"avoir intéragit un minimum avec le forum, et plus précisémment: ouvrir au "
"moins 5 fils de discussion, lire au moins 30 messages, et passer au moins 10 "
"minutes à lire des messages."
#: app.py:195
msgid "You must be logged in to submit an app to the wishlist"
msgstr "Vous devez être connecté·e pour proposer une app pour la liste de souhaits"
msgstr ""
"Vous devez être connecté·e pour proposer une app pour la liste de souhaits"
#: app.py:207
msgid "Invalid CSRF token, please refresh the page and try again"
@@ -55,8 +56,8 @@ msgid ""
"Proposing wishlist additions is limited to once every 15 days per user. "
"Please try again in a few days."
msgstr ""
"Proposer une app dans la liste de souhaits est limité à une fois tous les"
" 15 jours et par personne. Merci de réessayer dans quelques jours."
"Proposer une app dans la liste de souhaits est limité à une fois tous les 15 "
"jours et par personne. Merci de réessayer dans quelques jours."
#: app.py:230
msgid "App name should be at least 3 characters"
@@ -100,25 +101,23 @@ msgstr "Le nom de l'app contient des caractères spéciaux"
#: app.py:263
msgid ""
"Please focus on what the app does, without using marketing, fuzzy terms, "
"or repeating that the app is 'free' and 'self-hostable'."
"Please focus on what the app does, without using marketing, fuzzy terms, or "
"repeating that the app is 'free' and 'self-hostable'."
msgstr ""
"S'il vous plaît décrivez ce que fait l'application sans utiliser de "
"termes marketing nébuleux ou répéter que l'app est 'libre' ou 'auto-"
"hébergeable'."
"S'il vous plaît décrivez ce que fait l'application sans utiliser de termes "
"marketing nébuleux ou répéter que l'app est 'libre' ou 'auto-hébergeable'."
#: app.py:267
msgid "No need to repeat the name of the app. Focus on what the app does."
msgstr ""
"Pas besoin de répéter le nom de l'application. Prière de rester concis et"
" de se concentrer sur ce que l'app fait."
"Pas besoin de répéter le nom de l'application. Prière de rester concis et de "
"se concentrer sur ce que l'app fait."
#: app.py:301
#, python-format
msgid ""
"An entry with the name %(slug)s already exists in the wishlist, instead, "
"you can <a href='%(url)s'>add a star to the app to show your "
"interest</a>."
"An entry with the name %(slug)s already exists in the wishlist, instead, you "
"can <a href='%(url)s'>add a star to the app to show your interest</a>."
msgstr ""
"Une entrée nommée %(slug)s existe déjà dans la liste de souhaits, vous "
"pouvez <a href='%(url)s'>l'ajouter en favori</a> afin de montrer votre "
@@ -131,21 +130,20 @@ msgid ""
"there's already <a href='%(url)s'>a waiting PR for this app</a>? Else, "
"please report the issue to the YunoHost team."
msgstr ""
"Échec de la création de la demande d'intégration de l'app dans la liste "
"de souhaits… Peut-être qu'il y a <a href='%(url)s'>déjà une PR en attente"
" pour cette app</a>? Sinon, merci de signaler le problème à l'équipe "
"YunoHost."
"Échec de la création de la demande d'intégration de l'app dans la liste de "
"souhaits… Peut-être qu'il y a <a href='%(url)s'>déjà une PR en attente pour "
"cette app</a>? Sinon, merci de signaler le problème à l'équipe YunoHost."
#: app.py:376
#, python-format
msgid ""
"Your proposed app has succesfully been submitted. It must now be "
"validated by the YunoHost team. You can track progress here: <a "
"href='%(url)s'>%(url)s</a>"
"Your proposed app has succesfully been submitted. It must now be validated "
"by the YunoHost team. You can track progress here: <a href='%(url)s'>"
"%(url)s</a>"
msgstr ""
"Un demande d'intégration à la liste de souhaits a bien été créée pour "
"cette app. Elle doit maintenant être validée par l'équipe YunoHost. Vous "
"pouvez suivre cette demande ici: <a href='%(url)s'>%(url)s</a>"
"Un demande d'intégration à la liste de souhaits a bien été créée pour cette "
"app. Elle doit maintenant être validée par l'équipe YunoHost. Vous pouvez "
"suivre cette demande ici: <a href='%(url)s'>%(url)s</a>"
#: app.py:449
msgid "Unfortunately, login was denied."
@@ -162,11 +160,11 @@ msgid ""
"This app is currently flagged as broken because it failed our automatic "
"tests."
msgstr ""
"Cette app est actuellement marquée comme cassée ou de mauvaise qualité "
"car elle ne passe pas nos tests automatisés."
"Cette app est actuellement marquée comme cassée ou de mauvaise qualité car "
"elle ne passe pas nos tests automatisés."
#: templates/app.html:30 templates/app.html:31 templates/catalog.html:41
#: templates/catalog.html:42 templates/catalog.html:170
#: templates/catalog.html:42 templates/catalog.html:169
msgid ""
"This is usually a temporary situation which requires packagers to fix "
"something in the app."
@@ -180,8 +178,8 @@ msgid ""
"This app has been good quality according to our automatic tests over at "
"least one year."
msgstr ""
"Cette app est de bonne qualité d'après nos tests automatisés depuis au "
"moins un an."
"Cette app est de bonne qualité d'après nos tests automatisés depuis au moins "
"un an."
#: templates/app.html:81
msgid "Try the demo"
@@ -212,7 +210,8 @@ msgstr "Capture d'écran pour %(app)s"
#: templates/app.html:106
#, python-format
msgid "This app is only compatible with these specific architectures: %(archs)s"
msgid ""
"This app is only compatible with these specific architectures: %(archs)s"
msgstr ""
"Cette app est uniquement compatible avec les architectures suivantes : "
"%(archs)s"
@@ -221,8 +220,8 @@ msgstr ""
#, python-format
msgid "This app requires an unusual amount of RAM to install: %(ram)s"
msgstr ""
"Cette app requiert une quantité inhabituelle de RAM pour être installée :"
" %(ram)s"
"Cette app requiert une quantité inhabituelle de RAM pour être installée : "
"%(ram)s"
#: templates/app.html:118
msgid "Important infos before installing"
@@ -255,7 +254,7 @@ msgstr "Documentation officielle pour les admins"
#: templates/app.html:142
msgid "Official user documentation"
msgstr "Documentation officielle d'utilisation"
msgstr "Documentation officielle pour les utilisateur·ice·s"
#: templates/app.html:143
msgid "Official code repository"
@@ -303,21 +302,18 @@ msgstr "Afficher le menu"
#: templates/base.html:197
msgid ""
"Made with <i class='text-red-500 fa fa-heart-o' aria-label='love'></i> "
"using <a class='text-blue-800' "
"href='https://flask.palletsprojects.com'>Flask</a> and <a class='text-"
"blue-800' href='https://tailwindcss.com/'>TailwindCSS</a> - <a class"
"='text-blue-800' "
"href='https://github.com/YunoHost/apps/tree/master/store'><i class='fa "
"fa-code fa-fw' aria-hidden='true'></i> Source</a>"
"Made with <i class='text-red-500 fa fa-heart-o' aria-label='love'></i> using "
"<a class='text-blue-800' href='https://flask.palletsprojects.com'>Flask</a> "
"and <a class='text-blue-800' href='https://tailwindcss.com/'>TailwindCSS</a> "
"- <a class='text-blue-800' href='https://github.com/YunoHost/apps/tree/"
"master/store'><i class='fa fa-code fa-fw' aria-hidden='true'></i> Source</a>"
msgstr ""
"Fait avec <i class='text-red-500 fa fa-heart-o' aria-label='amour'></i> à"
" l'aide de <a class='text-blue-800' "
"href='https://flask.palletsprojects.com'>Flask</a> et <a class='text-"
"blue-800' href='https://tailwindcss.com/'>TailwindCSS</a> - <a class"
"='text-blue-800' "
"href='https://github.com/YunoHost/apps/tree/master/store'><i class='fa "
"fa-code fa-fw' aria-hidden='true'></i> Source</a>"
"Fait avec <i class='text-red-500 fa fa-heart-o' aria-label='amour'></i> à "
"l'aide de <a class='text-blue-800' href='https://flask.palletsprojects."
"com'>Flask</a> et <a class='text-blue-800' href='https://tailwindcss."
"com/'>TailwindCSS</a> - <a class='text-blue-800' href='https://github.com/"
"YunoHost/apps/tree/master/store'><i class='fa fa-code fa-fw' aria-"
"hidden='true'></i> Source</a>"
#: templates/catalog.html:75 templates/catalog.html:80
msgid "Application Catalog"
@@ -373,31 +369,14 @@ msgstr "Vous ne trouvez pas ce que vous cherchez ?"
msgid "Checkout the wishlist!"
msgstr "Jetez un oeil à la liste de souhaits !"
#: templates/catalog.html:166
#: templates/catalog.html:165
msgid "Applications currently flagged as broken"
msgstr "Applications actuellement marquées comme cassées"
#: templates/catalog.html:169
#: templates/catalog.html:168
msgid "These are apps which failed our automatic tests."
msgstr "Il s'agit d'apps qui n'ont pas validé nos tests automatisés."
#: templates/catalog.html:184
msgid "Deprecated applications"
msgstr "Applications obsolètes"
#: templates/catalog.html:187
msgid "These are apps who are not maintained anymore."
msgstr "Il s'agit des applications qui ne sont plus maintenues."
#: templates/catalog.html:188
msgid ""
"This means that the developer will no longer update them. We strongly "
"advise against their installation and advise users to find alternatives."
msgstr ""
"Cela signifie que le développeur ne les mettra plus à jour. Nous "
"décourageons fortement leur installation et vous conseillons de vous tourner "
"vers des alternatives."
#: templates/index.html:10
msgid "Application Store"
msgstr "Store d'application"
@@ -412,19 +391,19 @@ msgstr "Liste de souhaits d'applications"
#: templates/wishlist.html:10
msgid ""
"The wishlist is the place where people can collectively suggest and vote "
"for apps that they would like to see packaged and made available in "
"YunoHost's official apps catalog. Nevertheless, the fact that apps are "
"listed here should by no mean be interpreted as a fact that the YunoHost "
"project plans to integrate it, and is merely a source of inspiration for "
"packaging volunteers."
"The wishlist is the place where people can collectively suggest and vote for "
"apps that they would like to see packaged and made available in YunoHost's "
"official apps catalog. Nevertheless, the fact that apps are listed here "
"should by no mean be interpreted as a fact that the YunoHost project plans "
"to integrate it, and is merely a source of inspiration for packaging "
"volunteers."
msgstr ""
"La liste de souhaits est l'endroit où il est possible de collectivement "
"suggérer et voter pour des applications que vous aimeriez voir packagée "
"et intégrée dans le catalogue officiel de YunoHost. Néanmoins, le fait "
"que des apps soient listées ici ne devrait en aucun cas être interprété "
"comme le fait que le projet YunoHost prévoit leur intégration, et est "
"uniquement une source d'inspiration pour les packageur·euse·s bénévoles."
"suggérer et voter pour des applications que vous aimeriez voir packagée et "
"intégrée dans le catalogue officiel de YunoHost. Néanmoins, le fait que des "
"apps soient listées ici ne devrait en aucun cas être interprété comme le "
"fait que le projet YunoHost prévoit leur intégration, et est uniquement une "
"source d'inspiration pour les packageur·euse·s bénévoles."
#: templates/wishlist.html:33 templates/wishlist_add.html:3
msgid "Suggest an app"
@@ -456,22 +435,22 @@ msgstr "Suggérer une application à ajouter dans le catalogue de YunoHost"
#: templates/wishlist_add.html:29
msgid "You must first login to be allowed to submit an app to the wishlist"
msgstr "Vous devez être connecté·e pour proposer une app pour la liste de souhaits"
msgstr ""
"Vous devez être connecté·e pour proposer une app pour la liste de souhaits"
#: templates/wishlist_add.html:40
msgid "Due to abuses, only one proposal every 15 days per user is allowed."
msgstr ""
"En raison d'abus, la proposition d'app est limitée à une tous les 15 jours "
"par personne."
"par utilisateur·ice."
#: templates/wishlist_add.html:43
msgid ""
"Reviewing those proposals is tiring for volunteers, please don't yolo-"
"send every random nerdy stuff you find on the Internet."
"Reviewing those proposals is tiring for volunteers, please don't yolo-send "
"every random nerdy stuff you find on the Internet."
msgstr ""
"La vérification des propositions est éreintante pour les bénévoles, merci"
" de ne pas bêtement proposer n'importe quelle app un peu nerd que vous "
"trouvez."
"La vérification des propositions est éreintante pour les bénévoles, merci de "
"ne pas bêtement proposer n'importe quelle app un peu nerd que vous trouvez."
#: templates/wishlist_add.html:64
msgid "App's description"
@@ -483,15 +462,15 @@ msgstr "Prière de rester concis et de se concentrer sur ce que l'app fait."
#: templates/wishlist_add.html:66
msgid ""
"No need to repeat '[App] is …'. No need to state that it is free/open-"
"source or self-hosted (otherwise it wouldn't be packaged for YunoHost). "
"Avoid marketing stuff like 'the most', or vague properties like 'easy', "
"'simple', 'lightweight'."
"No need to repeat '[App] is …'. No need to state that it is free/open-source "
"or self-hosted (otherwise it wouldn't be packaged for YunoHost). Avoid "
"marketing stuff like 'the most', or vague properties like 'easy', 'simple', "
"'lightweight'."
msgstr ""
"Il n'est pas nécessaire de répéter '[App] est …', ni que l'app est libre"
"/open-source (sinon, elle ne serait pas intégrable au catalogue). Évitez "
"les formulations marketing type 'le meilleur', ou les propriétés vagues "
"telles que 'facile', 'simple', 'léger'."
"Il n'est pas nécessaire de répéter '[App] est …', ni que l'app est libre/"
"open-source (sinon, elle ne serait pas intégrable au catalogue). Évitez les "
"formulations marketing type 'le meilleur', ou les propriétés vagues telles "
"que 'facile', 'simple', 'léger'."
#: templates/wishlist_add.html:68
msgid "Project code repository"
@@ -507,8 +486,8 @@ msgid ""
"possible case-by-case exceptions for apps which are not-totally-free)"
msgstr ""
"Le projet YunoHost intègrera uniquement des logiciels libre/open-source "
"(avec quelques possibles exceptions au cas-par-cas pour des apps qui ne "
"sont pas entièrement libres)"
"(avec quelques possibles exceptions au cas-par-cas pour des apps qui ne sont "
"pas entièrement libres)"
#: templates/wishlist_add.html:75
msgid "Project website"
@@ -516,11 +495,11 @@ msgstr "Site officiel"
#: templates/wishlist_add.html:77
msgid ""
"Please *do not* just copy-paste the code repository URL. If the project "
"has no proper website, then leave the field empty."
"Please *do not* just copy-paste the code repository URL. If the project has "
"no proper website, then leave the field empty."
msgstr ""
"Prière de *ne pas* juste copier-coller l'URL du dépôt de code. Si le "
"projet n'a pas de site web dédié, laissez le champ vide."
"Prière de *ne pas* juste copier-coller l'URL du dépôt de code. Si le projet "
"n'a pas de site web dédié, laissez le champ vide."
#: templates/wishlist_add.html:84
msgid "Submit"

View File

@@ -7,8 +7,8 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-03-05 19:36+0100\n"
"PO-Revision-Date: 2024-03-06 17:37+0000\n"
"POT-Creation-Date: 2024-02-27 19:41+0100\n"
"PO-Revision-Date: 2024-02-29 20:27+0000\n"
"Last-Translator: \"José M.\" <correo@xmgz.eu>\n"
"Language-Team: Galician <https://translate.yunohost.org/projects/yunohost/"
"apps/gl/>\n"
@@ -118,8 +118,8 @@ msgid ""
"An entry with the name %(slug)s already exists in the wishlist, instead, you "
"can <a href='%(url)s'>add a star to the app to show your interest</a>."
msgstr ""
"Xa existe unha entrada co nome %(slug)s, así que podes <a "
"href='%(url)s'>engadirlle unha estrela para mostrar interese</a>."
"Xa existe unha entrada co nome %(slug)s, así que podes <a href='%(url)"
"s'>engadirlle unha estrela para mostrar interese</a>."
#: app.py:325
#, python-format
@@ -140,8 +140,8 @@ msgid ""
"%(url)s</a>"
msgstr ""
"Foi enviada correctamente a solicitude para a app. Agora vai ser validada "
"polo equipo de YunoHost. Podes ver aquí o proceso: <a href='%(url)s'>"
"%(url)s</a>"
"polo equipo de YunoHost. Podes ver aquí o proceso: <a "
"href='%(url)s'>%(url)s</a>"
#: app.py:449
msgid "Unfortunately, login was denied."
@@ -162,7 +162,7 @@ msgstr ""
"probas automatizadas."
#: templates/app.html:30 templates/app.html:31 templates/catalog.html:41
#: templates/catalog.html:42 templates/catalog.html:170
#: templates/catalog.html:42 templates/catalog.html:169
msgid ""
"This is usually a temporary situation which requires packagers to fix "
"something in the app."
@@ -210,8 +210,7 @@ msgstr "Captura de pantalla de %(app)s"
#, python-format
msgid ""
"This app is only compatible with these specific architectures: %(archs)s"
msgstr ""
"Esta app só é compatible con estas arquitecturas específicas: %(archs)s"
msgstr "Esta app só é compatible con estas arquitecturas específicas: %(archs)s"
#: templates/app.html:112
#, python-format
@@ -307,8 +306,8 @@ msgid ""
msgstr ""
"Creada con <i class='text-red-500 fa fa-heart-o' aria-label='love'></i> "
"usando <a class='text-blue-800' href='https://flask.palletsprojects."
"com'>Flask</a> e <a class='text-blue-800' href='https://tailwindcss."
"com/'>TailwindCSS</a> - <a class='text-blue-800' href='https://github.com/"
"com'>Flask</a> e <a class='text-blue-800' href='https://tailwindcss.com/"
"'>TailwindCSS</a> - <a class='text-blue-800' href='https://github.com/"
"YunoHost/apps/tree/master/store'><i class='fa fa-code fa-fw' aria-"
"hidden='true'></i> Fonte</a>"
@@ -366,37 +365,21 @@ msgstr "Non atopas o que buscabas?"
msgid "Checkout the wishlist!"
msgstr "Mira na lista de desexos!"
#: templates/catalog.html:166
#: templates/catalog.html:165
msgid "Applications currently flagged as broken"
msgstr "Aplicacións marcadas actualmente como estragadas"
#: templates/catalog.html:169
#: templates/catalog.html:168
msgid "These are apps which failed our automatic tests."
msgstr "Estas son as apps que non superaron as nosas probas automatizadas."
#: templates/catalog.html:184
msgid "Deprecated applications"
msgstr "Aplicacións abandonadas"
#: templates/catalog.html:187
msgid "These are apps who are not maintained anymore."
msgstr "Estas son as apps que xa non teñen mantemento."
#: templates/catalog.html:188
msgid ""
"This means that the developer will no longer update them. We strongly "
"advise against their installation and advise users to find alternatives."
msgstr ""
"Isto significa que a desenvolvedora non a vai actualizar. Recomendamos "
"vivamente non instalala e aconsellamos ás usuarias a buscar unha alternativa."
#: templates/index.html:10
msgid "Application Store"
msgstr "Tenda de Aplicacións"
#: templates/index.html:21
msgid "Browse all applications"
msgstr "Ver todas as aplicacións"
msgstr "Ve todas as aplicacións"
#: templates/wishlist.html:3 templates/wishlist.html:8
msgid "Application Wishlist"

View File

@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-03-05 19:36+0100\n"
"POT-Creation-Date: 2024-02-27 19:41+0100\n"
"PO-Revision-Date: 2024-02-21 06:06+0100\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: he <LL@li.org>\n"
@@ -139,7 +139,7 @@ msgid ""
msgstr ""
#: templates/app.html:30 templates/app.html:31 templates/catalog.html:41
#: templates/catalog.html:42 templates/catalog.html:170
#: templates/catalog.html:42 templates/catalog.html:169
msgid ""
"This is usually a temporary situation which requires packagers to fix "
"something in the app."
@@ -330,28 +330,14 @@ msgstr ""
msgid "Checkout the wishlist!"
msgstr ""
#: templates/catalog.html:166
#: templates/catalog.html:165
msgid "Applications currently flagged as broken"
msgstr ""
#: templates/catalog.html:169
#: templates/catalog.html:168
msgid "These are apps which failed our automatic tests."
msgstr ""
#: templates/catalog.html:184
msgid "Deprecated applications"
msgstr ""
#: templates/catalog.html:187
msgid "These are apps who are not maintained anymore."
msgstr ""
#: templates/catalog.html:188
msgid ""
"This means that the developer will no longer update them. We strongly "
"advise against their installation and advise users to find alternatives."
msgstr ""
#: templates/index.html:10
msgid "Application Store"
msgstr ""

View File

@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-03-05 19:36+0100\n"
"POT-Creation-Date: 2024-02-27 19:41+0100\n"
"PO-Revision-Date: 2024-02-21 06:06+0100\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: hi <LL@li.org>\n"
@@ -139,7 +139,7 @@ msgid ""
msgstr ""
#: templates/app.html:30 templates/app.html:31 templates/catalog.html:41
#: templates/catalog.html:42 templates/catalog.html:170
#: templates/catalog.html:42 templates/catalog.html:169
msgid ""
"This is usually a temporary situation which requires packagers to fix "
"something in the app."
@@ -330,28 +330,14 @@ msgstr ""
msgid "Checkout the wishlist!"
msgstr ""
#: templates/catalog.html:166
#: templates/catalog.html:165
msgid "Applications currently flagged as broken"
msgstr ""
#: templates/catalog.html:169
#: templates/catalog.html:168
msgid "These are apps which failed our automatic tests."
msgstr ""
#: templates/catalog.html:184
msgid "Deprecated applications"
msgstr ""
#: templates/catalog.html:187
msgid "These are apps who are not maintained anymore."
msgstr ""
#: templates/catalog.html:188
msgid ""
"This means that the developer will no longer update them. We strongly "
"advise against their installation and advise users to find alternatives."
msgstr ""
#: templates/index.html:10
msgid "Application Store"
msgstr ""

View File

@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-03-05 19:36+0100\n"
"POT-Creation-Date: 2024-02-27 19:41+0100\n"
"PO-Revision-Date: 2024-02-21 06:06+0100\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: hu <LL@li.org>\n"
@@ -139,7 +139,7 @@ msgid ""
msgstr ""
#: templates/app.html:30 templates/app.html:31 templates/catalog.html:41
#: templates/catalog.html:42 templates/catalog.html:170
#: templates/catalog.html:42 templates/catalog.html:169
msgid ""
"This is usually a temporary situation which requires packagers to fix "
"something in the app."
@@ -330,28 +330,14 @@ msgstr ""
msgid "Checkout the wishlist!"
msgstr ""
#: templates/catalog.html:166
#: templates/catalog.html:165
msgid "Applications currently flagged as broken"
msgstr ""
#: templates/catalog.html:169
#: templates/catalog.html:168
msgid "These are apps which failed our automatic tests."
msgstr ""
#: templates/catalog.html:184
msgid "Deprecated applications"
msgstr ""
#: templates/catalog.html:187
msgid "These are apps who are not maintained anymore."
msgstr ""
#: templates/catalog.html:188
msgid ""
"This means that the developer will no longer update them. We strongly "
"advise against their installation and advise users to find alternatives."
msgstr ""
#: templates/index.html:10
msgid "Application Store"
msgstr ""

View File

@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-03-05 19:36+0100\n"
"POT-Creation-Date: 2024-02-27 19:41+0100\n"
"PO-Revision-Date: 2024-02-21 06:06+0100\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: id <LL@li.org>\n"
@@ -139,7 +139,7 @@ msgid ""
msgstr ""
#: templates/app.html:30 templates/app.html:31 templates/catalog.html:41
#: templates/catalog.html:42 templates/catalog.html:170
#: templates/catalog.html:42 templates/catalog.html:169
msgid ""
"This is usually a temporary situation which requires packagers to fix "
"something in the app."
@@ -330,28 +330,14 @@ msgstr ""
msgid "Checkout the wishlist!"
msgstr ""
#: templates/catalog.html:166
#: templates/catalog.html:165
msgid "Applications currently flagged as broken"
msgstr ""
#: templates/catalog.html:169
#: templates/catalog.html:168
msgid "These are apps which failed our automatic tests."
msgstr ""
#: templates/catalog.html:184
msgid "Deprecated applications"
msgstr ""
#: templates/catalog.html:187
msgid "These are apps who are not maintained anymore."
msgstr ""
#: templates/catalog.html:188
msgid ""
"This means that the developer will no longer update them. We strongly "
"advise against their installation and advise users to find alternatives."
msgstr ""
#: templates/index.html:10
msgid "Application Store"
msgstr ""

View File

@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-03-05 19:36+0100\n"
"POT-Creation-Date: 2024-02-27 19:41+0100\n"
"PO-Revision-Date: 2024-02-21 06:07+0100\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: it <LL@li.org>\n"
@@ -139,7 +139,7 @@ msgid ""
msgstr ""
#: templates/app.html:30 templates/app.html:31 templates/catalog.html:41
#: templates/catalog.html:42 templates/catalog.html:170
#: templates/catalog.html:42 templates/catalog.html:169
msgid ""
"This is usually a temporary situation which requires packagers to fix "
"something in the app."
@@ -330,28 +330,14 @@ msgstr ""
msgid "Checkout the wishlist!"
msgstr ""
#: templates/catalog.html:166
#: templates/catalog.html:165
msgid "Applications currently flagged as broken"
msgstr ""
#: templates/catalog.html:169
#: templates/catalog.html:168
msgid "These are apps which failed our automatic tests."
msgstr ""
#: templates/catalog.html:184
msgid "Deprecated applications"
msgstr ""
#: templates/catalog.html:187
msgid "These are apps who are not maintained anymore."
msgstr ""
#: templates/catalog.html:188
msgid ""
"This means that the developer will no longer update them. We strongly "
"advise against their installation and advise users to find alternatives."
msgstr ""
#: templates/index.html:10
msgid "Application Store"
msgstr ""

View File

@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-03-05 19:36+0100\n"
"POT-Creation-Date: 2024-02-27 19:41+0100\n"
"PO-Revision-Date: 2024-02-21 06:07+0100\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: ja <LL@li.org>\n"
@@ -139,7 +139,7 @@ msgid ""
msgstr ""
#: templates/app.html:30 templates/app.html:31 templates/catalog.html:41
#: templates/catalog.html:42 templates/catalog.html:170
#: templates/catalog.html:42 templates/catalog.html:169
msgid ""
"This is usually a temporary situation which requires packagers to fix "
"something in the app."
@@ -330,28 +330,14 @@ msgstr ""
msgid "Checkout the wishlist!"
msgstr ""
#: templates/catalog.html:166
#: templates/catalog.html:165
msgid "Applications currently flagged as broken"
msgstr ""
#: templates/catalog.html:169
#: templates/catalog.html:168
msgid "These are apps which failed our automatic tests."
msgstr ""
#: templates/catalog.html:184
msgid "Deprecated applications"
msgstr ""
#: templates/catalog.html:187
msgid "These are apps who are not maintained anymore."
msgstr ""
#: templates/catalog.html:188
msgid ""
"This means that the developer will no longer update them. We strongly "
"advise against their installation and advise users to find alternatives."
msgstr ""
#: templates/index.html:10
msgid "Application Store"
msgstr ""

View File

@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-03-05 19:36+0100\n"
"POT-Creation-Date: 2024-02-27 19:41+0100\n"
"PO-Revision-Date: 2024-02-21 06:07+0100\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: kab <LL@li.org>\n"
@@ -139,7 +139,7 @@ msgid ""
msgstr ""
#: templates/app.html:30 templates/app.html:31 templates/catalog.html:41
#: templates/catalog.html:42 templates/catalog.html:170
#: templates/catalog.html:42 templates/catalog.html:169
msgid ""
"This is usually a temporary situation which requires packagers to fix "
"something in the app."
@@ -330,28 +330,14 @@ msgstr ""
msgid "Checkout the wishlist!"
msgstr ""
#: templates/catalog.html:166
#: templates/catalog.html:165
msgid "Applications currently flagged as broken"
msgstr ""
#: templates/catalog.html:169
#: templates/catalog.html:168
msgid "These are apps which failed our automatic tests."
msgstr ""
#: templates/catalog.html:184
msgid "Deprecated applications"
msgstr ""
#: templates/catalog.html:187
msgid "These are apps who are not maintained anymore."
msgstr ""
#: templates/catalog.html:188
msgid ""
"This means that the developer will no longer update them. We strongly "
"advise against their installation and advise users to find alternatives."
msgstr ""
#: templates/index.html:10
msgid "Application Store"
msgstr ""

View File

@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-03-05 19:36+0100\n"
"POT-Creation-Date: 2024-02-27 19:41+0100\n"
"PO-Revision-Date: 2024-02-21 06:05+0100\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: ko <LL@li.org>\n"
@@ -139,7 +139,7 @@ msgid ""
msgstr ""
#: templates/app.html:30 templates/app.html:31 templates/catalog.html:41
#: templates/catalog.html:42 templates/catalog.html:170
#: templates/catalog.html:42 templates/catalog.html:169
msgid ""
"This is usually a temporary situation which requires packagers to fix "
"something in the app."
@@ -330,28 +330,14 @@ msgstr ""
msgid "Checkout the wishlist!"
msgstr ""
#: templates/catalog.html:166
#: templates/catalog.html:165
msgid "Applications currently flagged as broken"
msgstr ""
#: templates/catalog.html:169
#: templates/catalog.html:168
msgid "These are apps which failed our automatic tests."
msgstr ""
#: templates/catalog.html:184
msgid "Deprecated applications"
msgstr ""
#: templates/catalog.html:187
msgid "These are apps who are not maintained anymore."
msgstr ""
#: templates/catalog.html:188
msgid ""
"This means that the developer will no longer update them. We strongly "
"advise against their installation and advise users to find alternatives."
msgstr ""
#: templates/index.html:10
msgid "Application Store"
msgstr ""

View File

@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-03-05 19:36+0100\n"
"POT-Creation-Date: 2024-02-27 19:41+0100\n"
"PO-Revision-Date: 2024-02-21 06:07+0100\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: lt <LL@li.org>\n"
@@ -140,7 +140,7 @@ msgid ""
msgstr ""
#: templates/app.html:30 templates/app.html:31 templates/catalog.html:41
#: templates/catalog.html:42 templates/catalog.html:170
#: templates/catalog.html:42 templates/catalog.html:169
msgid ""
"This is usually a temporary situation which requires packagers to fix "
"something in the app."
@@ -331,28 +331,14 @@ msgstr ""
msgid "Checkout the wishlist!"
msgstr ""
#: templates/catalog.html:166
#: templates/catalog.html:165
msgid "Applications currently flagged as broken"
msgstr ""
#: templates/catalog.html:169
#: templates/catalog.html:168
msgid "These are apps which failed our automatic tests."
msgstr ""
#: templates/catalog.html:184
msgid "Deprecated applications"
msgstr ""
#: templates/catalog.html:187
msgid "These are apps who are not maintained anymore."
msgstr ""
#: templates/catalog.html:188
msgid ""
"This means that the developer will no longer update them. We strongly "
"advise against their installation and advise users to find alternatives."
msgstr ""
#: templates/index.html:10
msgid "Application Store"
msgstr ""

View File

@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-03-05 19:36+0100\n"
"POT-Creation-Date: 2024-02-27 19:41+0100\n"
"PO-Revision-Date: 2024-02-21 06:07+0100\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: mk <LL@li.org>\n"
@@ -139,7 +139,7 @@ msgid ""
msgstr ""
#: templates/app.html:30 templates/app.html:31 templates/catalog.html:41
#: templates/catalog.html:42 templates/catalog.html:170
#: templates/catalog.html:42 templates/catalog.html:169
msgid ""
"This is usually a temporary situation which requires packagers to fix "
"something in the app."
@@ -330,28 +330,14 @@ msgstr ""
msgid "Checkout the wishlist!"
msgstr ""
#: templates/catalog.html:166
#: templates/catalog.html:165
msgid "Applications currently flagged as broken"
msgstr ""
#: templates/catalog.html:169
#: templates/catalog.html:168
msgid "These are apps which failed our automatic tests."
msgstr ""
#: templates/catalog.html:184
msgid "Deprecated applications"
msgstr ""
#: templates/catalog.html:187
msgid "These are apps who are not maintained anymore."
msgstr ""
#: templates/catalog.html:188
msgid ""
"This means that the developer will no longer update them. We strongly "
"advise against their installation and advise users to find alternatives."
msgstr ""
#: templates/index.html:10
msgid "Application Store"
msgstr ""

View File

@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-03-05 19:36+0100\n"
"POT-Creation-Date: 2024-02-27 19:41+0100\n"
"PO-Revision-Date: 2024-02-21 06:07+0100\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: nb_NO <LL@li.org>\n"
@@ -139,7 +139,7 @@ msgid ""
msgstr ""
#: templates/app.html:30 templates/app.html:31 templates/catalog.html:41
#: templates/catalog.html:42 templates/catalog.html:170
#: templates/catalog.html:42 templates/catalog.html:169
msgid ""
"This is usually a temporary situation which requires packagers to fix "
"something in the app."
@@ -330,28 +330,14 @@ msgstr ""
msgid "Checkout the wishlist!"
msgstr ""
#: templates/catalog.html:166
#: templates/catalog.html:165
msgid "Applications currently flagged as broken"
msgstr ""
#: templates/catalog.html:169
#: templates/catalog.html:168
msgid "These are apps which failed our automatic tests."
msgstr ""
#: templates/catalog.html:184
msgid "Deprecated applications"
msgstr ""
#: templates/catalog.html:187
msgid "These are apps who are not maintained anymore."
msgstr ""
#: templates/catalog.html:188
msgid ""
"This means that the developer will no longer update them. We strongly "
"advise against their installation and advise users to find alternatives."
msgstr ""
#: templates/index.html:10
msgid "Application Store"
msgstr ""

View File

@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-03-05 19:36+0100\n"
"POT-Creation-Date: 2024-02-27 19:41+0100\n"
"PO-Revision-Date: 2024-02-21 06:07+0100\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: ne <LL@li.org>\n"
@@ -139,7 +139,7 @@ msgid ""
msgstr ""
#: templates/app.html:30 templates/app.html:31 templates/catalog.html:41
#: templates/catalog.html:42 templates/catalog.html:170
#: templates/catalog.html:42 templates/catalog.html:169
msgid ""
"This is usually a temporary situation which requires packagers to fix "
"something in the app."
@@ -330,28 +330,14 @@ msgstr ""
msgid "Checkout the wishlist!"
msgstr ""
#: templates/catalog.html:166
#: templates/catalog.html:165
msgid "Applications currently flagged as broken"
msgstr ""
#: templates/catalog.html:169
#: templates/catalog.html:168
msgid "These are apps which failed our automatic tests."
msgstr ""
#: templates/catalog.html:184
msgid "Deprecated applications"
msgstr ""
#: templates/catalog.html:187
msgid "These are apps who are not maintained anymore."
msgstr ""
#: templates/catalog.html:188
msgid ""
"This means that the developer will no longer update them. We strongly "
"advise against their installation and advise users to find alternatives."
msgstr ""
#: templates/index.html:10
msgid "Application Store"
msgstr ""

View File

@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-03-05 19:36+0100\n"
"POT-Creation-Date: 2024-02-27 19:41+0100\n"
"PO-Revision-Date: 2024-02-21 06:07+0100\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: nl <LL@li.org>\n"
@@ -139,7 +139,7 @@ msgid ""
msgstr ""
#: templates/app.html:30 templates/app.html:31 templates/catalog.html:41
#: templates/catalog.html:42 templates/catalog.html:170
#: templates/catalog.html:42 templates/catalog.html:169
msgid ""
"This is usually a temporary situation which requires packagers to fix "
"something in the app."
@@ -330,28 +330,14 @@ msgstr ""
msgid "Checkout the wishlist!"
msgstr ""
#: templates/catalog.html:166
#: templates/catalog.html:165
msgid "Applications currently flagged as broken"
msgstr ""
#: templates/catalog.html:169
#: templates/catalog.html:168
msgid "These are apps which failed our automatic tests."
msgstr ""
#: templates/catalog.html:184
msgid "Deprecated applications"
msgstr ""
#: templates/catalog.html:187
msgid "These are apps who are not maintained anymore."
msgstr ""
#: templates/catalog.html:188
msgid ""
"This means that the developer will no longer update them. We strongly "
"advise against their installation and advise users to find alternatives."
msgstr ""
#: templates/index.html:10
msgid "Application Store"
msgstr ""

View File

@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-03-05 19:36+0100\n"
"POT-Creation-Date: 2024-02-27 19:41+0100\n"
"PO-Revision-Date: 2024-02-21 06:08+0100\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: oc <LL@li.org>\n"
@@ -139,7 +139,7 @@ msgid ""
msgstr ""
#: templates/app.html:30 templates/app.html:31 templates/catalog.html:41
#: templates/catalog.html:42 templates/catalog.html:170
#: templates/catalog.html:42 templates/catalog.html:169
msgid ""
"This is usually a temporary situation which requires packagers to fix "
"something in the app."
@@ -330,28 +330,14 @@ msgstr ""
msgid "Checkout the wishlist!"
msgstr ""
#: templates/catalog.html:166
#: templates/catalog.html:165
msgid "Applications currently flagged as broken"
msgstr ""
#: templates/catalog.html:169
#: templates/catalog.html:168
msgid "These are apps which failed our automatic tests."
msgstr ""
#: templates/catalog.html:184
msgid "Deprecated applications"
msgstr ""
#: templates/catalog.html:187
msgid "These are apps who are not maintained anymore."
msgstr ""
#: templates/catalog.html:188
msgid ""
"This means that the developer will no longer update them. We strongly "
"advise against their installation and advise users to find alternatives."
msgstr ""
#: templates/index.html:10
msgid "Application Store"
msgstr ""

View File

@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-03-05 19:36+0100\n"
"POT-Creation-Date: 2024-02-27 19:41+0100\n"
"PO-Revision-Date: 2024-02-21 06:08+0100\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: pl <LL@li.org>\n"
@@ -140,7 +140,7 @@ msgid ""
msgstr ""
#: templates/app.html:30 templates/app.html:31 templates/catalog.html:41
#: templates/catalog.html:42 templates/catalog.html:170
#: templates/catalog.html:42 templates/catalog.html:169
msgid ""
"This is usually a temporary situation which requires packagers to fix "
"something in the app."
@@ -331,28 +331,14 @@ msgstr ""
msgid "Checkout the wishlist!"
msgstr ""
#: templates/catalog.html:166
#: templates/catalog.html:165
msgid "Applications currently flagged as broken"
msgstr ""
#: templates/catalog.html:169
#: templates/catalog.html:168
msgid "These are apps which failed our automatic tests."
msgstr ""
#: templates/catalog.html:184
msgid "Deprecated applications"
msgstr ""
#: templates/catalog.html:187
msgid "These are apps who are not maintained anymore."
msgstr ""
#: templates/catalog.html:188
msgid ""
"This means that the developer will no longer update them. We strongly "
"advise against their installation and advise users to find alternatives."
msgstr ""
#: templates/index.html:10
msgid "Application Store"
msgstr ""

View File

@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-03-05 19:36+0100\n"
"POT-Creation-Date: 2024-02-27 19:41+0100\n"
"PO-Revision-Date: 2024-02-21 06:08+0100\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: pt <LL@li.org>\n"
@@ -139,7 +139,7 @@ msgid ""
msgstr ""
#: templates/app.html:30 templates/app.html:31 templates/catalog.html:41
#: templates/catalog.html:42 templates/catalog.html:170
#: templates/catalog.html:42 templates/catalog.html:169
msgid ""
"This is usually a temporary situation which requires packagers to fix "
"something in the app."
@@ -330,28 +330,14 @@ msgstr ""
msgid "Checkout the wishlist!"
msgstr ""
#: templates/catalog.html:166
#: templates/catalog.html:165
msgid "Applications currently flagged as broken"
msgstr ""
#: templates/catalog.html:169
#: templates/catalog.html:168
msgid "These are apps which failed our automatic tests."
msgstr ""
#: templates/catalog.html:184
msgid "Deprecated applications"
msgstr ""
#: templates/catalog.html:187
msgid "These are apps who are not maintained anymore."
msgstr ""
#: templates/catalog.html:188
msgid ""
"This means that the developer will no longer update them. We strongly "
"advise against their installation and advise users to find alternatives."
msgstr ""
#: templates/index.html:10
msgid "Application Store"
msgstr ""

View File

@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-03-05 19:36+0100\n"
"POT-Creation-Date: 2024-02-27 19:41+0100\n"
"PO-Revision-Date: 2024-02-21 06:08+0100\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: pt_BR <LL@li.org>\n"
@@ -139,7 +139,7 @@ msgid ""
msgstr ""
#: templates/app.html:30 templates/app.html:31 templates/catalog.html:41
#: templates/catalog.html:42 templates/catalog.html:170
#: templates/catalog.html:42 templates/catalog.html:169
msgid ""
"This is usually a temporary situation which requires packagers to fix "
"something in the app."
@@ -330,28 +330,14 @@ msgstr ""
msgid "Checkout the wishlist!"
msgstr ""
#: templates/catalog.html:166
#: templates/catalog.html:165
msgid "Applications currently flagged as broken"
msgstr ""
#: templates/catalog.html:169
#: templates/catalog.html:168
msgid "These are apps which failed our automatic tests."
msgstr ""
#: templates/catalog.html:184
msgid "Deprecated applications"
msgstr ""
#: templates/catalog.html:187
msgid "These are apps who are not maintained anymore."
msgstr ""
#: templates/catalog.html:188
msgid ""
"This means that the developer will no longer update them. We strongly "
"advise against their installation and advise users to find alternatives."
msgstr ""
#: templates/index.html:10
msgid "Application Store"
msgstr ""

View File

@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-03-05 19:36+0100\n"
"POT-Creation-Date: 2024-02-27 19:41+0100\n"
"PO-Revision-Date: 2024-02-21 06:08+0100\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: ru <LL@li.org>\n"
@@ -140,7 +140,7 @@ msgid ""
msgstr ""
#: templates/app.html:30 templates/app.html:31 templates/catalog.html:41
#: templates/catalog.html:42 templates/catalog.html:170
#: templates/catalog.html:42 templates/catalog.html:169
msgid ""
"This is usually a temporary situation which requires packagers to fix "
"something in the app."
@@ -331,28 +331,14 @@ msgstr ""
msgid "Checkout the wishlist!"
msgstr ""
#: templates/catalog.html:166
#: templates/catalog.html:165
msgid "Applications currently flagged as broken"
msgstr ""
#: templates/catalog.html:169
#: templates/catalog.html:168
msgid "These are apps which failed our automatic tests."
msgstr ""
#: templates/catalog.html:184
msgid "Deprecated applications"
msgstr ""
#: templates/catalog.html:187
msgid "These are apps who are not maintained anymore."
msgstr ""
#: templates/catalog.html:188
msgid ""
"This means that the developer will no longer update them. We strongly "
"advise against their installation and advise users to find alternatives."
msgstr ""
#: templates/index.html:10
msgid "Application Store"
msgstr ""

View File

@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-03-05 19:36+0100\n"
"POT-Creation-Date: 2024-02-27 19:41+0100\n"
"PO-Revision-Date: 2024-02-21 06:08+0100\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: sk <LL@li.org>\n"
@@ -139,7 +139,7 @@ msgid ""
msgstr ""
#: templates/app.html:30 templates/app.html:31 templates/catalog.html:41
#: templates/catalog.html:42 templates/catalog.html:170
#: templates/catalog.html:42 templates/catalog.html:169
msgid ""
"This is usually a temporary situation which requires packagers to fix "
"something in the app."
@@ -330,28 +330,14 @@ msgstr ""
msgid "Checkout the wishlist!"
msgstr ""
#: templates/catalog.html:166
#: templates/catalog.html:165
msgid "Applications currently flagged as broken"
msgstr ""
#: templates/catalog.html:169
#: templates/catalog.html:168
msgid "These are apps which failed our automatic tests."
msgstr ""
#: templates/catalog.html:184
msgid "Deprecated applications"
msgstr ""
#: templates/catalog.html:187
msgid "These are apps who are not maintained anymore."
msgstr ""
#: templates/catalog.html:188
msgid ""
"This means that the developer will no longer update them. We strongly "
"advise against their installation and advise users to find alternatives."
msgstr ""
#: templates/index.html:10
msgid "Application Store"
msgstr ""

View File

@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-03-05 19:36+0100\n"
"POT-Creation-Date: 2024-02-27 19:41+0100\n"
"PO-Revision-Date: 2024-02-21 06:08+0100\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: sl <LL@li.org>\n"
@@ -140,7 +140,7 @@ msgid ""
msgstr ""
#: templates/app.html:30 templates/app.html:31 templates/catalog.html:41
#: templates/catalog.html:42 templates/catalog.html:170
#: templates/catalog.html:42 templates/catalog.html:169
msgid ""
"This is usually a temporary situation which requires packagers to fix "
"something in the app."
@@ -331,28 +331,14 @@ msgstr ""
msgid "Checkout the wishlist!"
msgstr ""
#: templates/catalog.html:166
#: templates/catalog.html:165
msgid "Applications currently flagged as broken"
msgstr ""
#: templates/catalog.html:169
#: templates/catalog.html:168
msgid "These are apps which failed our automatic tests."
msgstr ""
#: templates/catalog.html:184
msgid "Deprecated applications"
msgstr ""
#: templates/catalog.html:187
msgid "These are apps who are not maintained anymore."
msgstr ""
#: templates/catalog.html:188
msgid ""
"This means that the developer will no longer update them. We strongly "
"advise against their installation and advise users to find alternatives."
msgstr ""
#: templates/index.html:10
msgid "Application Store"
msgstr ""

View File

@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-03-05 19:36+0100\n"
"POT-Creation-Date: 2024-02-27 19:41+0100\n"
"PO-Revision-Date: 2024-02-21 06:09+0100\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: sv <LL@li.org>\n"
@@ -139,7 +139,7 @@ msgid ""
msgstr ""
#: templates/app.html:30 templates/app.html:31 templates/catalog.html:41
#: templates/catalog.html:42 templates/catalog.html:170
#: templates/catalog.html:42 templates/catalog.html:169
msgid ""
"This is usually a temporary situation which requires packagers to fix "
"something in the app."
@@ -330,28 +330,14 @@ msgstr ""
msgid "Checkout the wishlist!"
msgstr ""
#: templates/catalog.html:166
#: templates/catalog.html:165
msgid "Applications currently flagged as broken"
msgstr ""
#: templates/catalog.html:169
#: templates/catalog.html:168
msgid "These are apps which failed our automatic tests."
msgstr ""
#: templates/catalog.html:184
msgid "Deprecated applications"
msgstr ""
#: templates/catalog.html:187
msgid "These are apps who are not maintained anymore."
msgstr ""
#: templates/catalog.html:188
msgid ""
"This means that the developer will no longer update them. We strongly "
"advise against their installation and advise users to find alternatives."
msgstr ""
#: templates/index.html:10
msgid "Application Store"
msgstr ""

View File

@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-03-05 19:36+0100\n"
"POT-Creation-Date: 2024-02-27 19:41+0100\n"
"PO-Revision-Date: 2024-02-21 06:09+0100\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: te <LL@li.org>\n"
@@ -139,7 +139,7 @@ msgid ""
msgstr ""
#: templates/app.html:30 templates/app.html:31 templates/catalog.html:41
#: templates/catalog.html:42 templates/catalog.html:170
#: templates/catalog.html:42 templates/catalog.html:169
msgid ""
"This is usually a temporary situation which requires packagers to fix "
"something in the app."
@@ -330,28 +330,14 @@ msgstr ""
msgid "Checkout the wishlist!"
msgstr ""
#: templates/catalog.html:166
#: templates/catalog.html:165
msgid "Applications currently flagged as broken"
msgstr ""
#: templates/catalog.html:169
#: templates/catalog.html:168
msgid "These are apps which failed our automatic tests."
msgstr ""
#: templates/catalog.html:184
msgid "Deprecated applications"
msgstr ""
#: templates/catalog.html:187
msgid "These are apps who are not maintained anymore."
msgstr ""
#: templates/catalog.html:188
msgid ""
"This means that the developer will no longer update them. We strongly "
"advise against their installation and advise users to find alternatives."
msgstr ""
#: templates/index.html:10
msgid "Application Store"
msgstr ""

View File

@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-03-05 19:36+0100\n"
"POT-Creation-Date: 2024-02-27 19:41+0100\n"
"PO-Revision-Date: 2024-02-21 06:09+0100\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: tr <LL@li.org>\n"
@@ -139,7 +139,7 @@ msgid ""
msgstr ""
#: templates/app.html:30 templates/app.html:31 templates/catalog.html:41
#: templates/catalog.html:42 templates/catalog.html:170
#: templates/catalog.html:42 templates/catalog.html:169
msgid ""
"This is usually a temporary situation which requires packagers to fix "
"something in the app."
@@ -330,28 +330,14 @@ msgstr ""
msgid "Checkout the wishlist!"
msgstr ""
#: templates/catalog.html:166
#: templates/catalog.html:165
msgid "Applications currently flagged as broken"
msgstr ""
#: templates/catalog.html:169
#: templates/catalog.html:168
msgid "These are apps which failed our automatic tests."
msgstr ""
#: templates/catalog.html:184
msgid "Deprecated applications"
msgstr ""
#: templates/catalog.html:187
msgid "These are apps who are not maintained anymore."
msgstr ""
#: templates/catalog.html:188
msgid ""
"This means that the developer will no longer update them. We strongly "
"advise against their installation and advise users to find alternatives."
msgstr ""
#: templates/index.html:10
msgid "Application Store"
msgstr ""

View File

@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-03-05 19:36+0100\n"
"POT-Creation-Date: 2024-02-27 19:41+0100\n"
"PO-Revision-Date: 2024-02-21 06:09+0100\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: uk <LL@li.org>\n"
@@ -140,7 +140,7 @@ msgid ""
msgstr ""
#: templates/app.html:30 templates/app.html:31 templates/catalog.html:41
#: templates/catalog.html:42 templates/catalog.html:170
#: templates/catalog.html:42 templates/catalog.html:169
msgid ""
"This is usually a temporary situation which requires packagers to fix "
"something in the app."
@@ -331,28 +331,14 @@ msgstr ""
msgid "Checkout the wishlist!"
msgstr ""
#: templates/catalog.html:166
#: templates/catalog.html:165
msgid "Applications currently flagged as broken"
msgstr ""
#: templates/catalog.html:169
#: templates/catalog.html:168
msgid "These are apps which failed our automatic tests."
msgstr ""
#: templates/catalog.html:184
msgid "Deprecated applications"
msgstr ""
#: templates/catalog.html:187
msgid "These are apps who are not maintained anymore."
msgstr ""
#: templates/catalog.html:188
msgid ""
"This means that the developer will no longer update them. We strongly "
"advise against their installation and advise users to find alternatives."
msgstr ""
#: templates/index.html:10
msgid "Application Store"
msgstr ""

View File

@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-03-05 19:36+0100\n"
"POT-Creation-Date: 2024-02-27 19:41+0100\n"
"PO-Revision-Date: 2024-02-21 06:05+0100\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: zh_Hans <LL@li.org>\n"
@@ -139,7 +139,7 @@ msgid ""
msgstr ""
#: templates/app.html:30 templates/app.html:31 templates/catalog.html:41
#: templates/catalog.html:42 templates/catalog.html:170
#: templates/catalog.html:42 templates/catalog.html:169
msgid ""
"This is usually a temporary situation which requires packagers to fix "
"something in the app."
@@ -330,28 +330,14 @@ msgstr ""
msgid "Checkout the wishlist!"
msgstr ""
#: templates/catalog.html:166
#: templates/catalog.html:165
msgid "Applications currently flagged as broken"
msgstr ""
#: templates/catalog.html:169
#: templates/catalog.html:168
msgid "These are apps which failed our automatic tests."
msgstr ""
#: templates/catalog.html:184
msgid "Deprecated applications"
msgstr ""
#: templates/catalog.html:187
msgid "These are apps who are not maintained anymore."
msgstr ""
#: templates/catalog.html:188
msgid ""
"This means that the developer will no longer update them. We strongly "
"advise against their installation and advise users to find alternatives."
msgstr ""
#: templates/index.html:10
msgid "Application Store"
msgstr ""

View File

@@ -93,7 +93,6 @@ def get_stars():
get_stars.cache_checksum = None
get_stars()
def check_wishlist_submit_ratelimit(user):
dir_ = os.path.join(".wishlist_ratelimit")
@@ -102,10 +101,7 @@ def check_wishlist_submit_ratelimit(user):
f = os.path.join(dir_, md5(user.encode()).hexdigest())
return not os.path.exists(f) or (time.time() - os.path.getmtime(f)) > (
15 * 24 * 3600
) # 15 days
return not os.path.exists(f) or (time.time() - os.path.getmtime(f)) > (15 * 24 * 3600) # 15 days
def save_wishlist_submit_for_ratelimit(user):
@@ -182,9 +178,9 @@ def get_app_md_and_screenshots(app_folder, infos):
if entry.is_file() and ext in ("png", "jpg", "jpeg", "webp", "gif"):
with open(entry.path, "rb") as img_file:
data = base64.b64encode(img_file.read()).decode("utf-8")
infos["screenshot"] = (
f"data:image/{ext};charset=utf-8;base64,{data}"
)
infos[
"screenshot"
] = f"data:image/{ext};charset=utf-8;base64,{data}"
break
ram_build_requirement = infos["manifest"]["integration"]["ram"]["build"]

View File

@@ -9,11 +9,8 @@ from typing import Any
import tqdm
from appslib.utils import (
REPO_APPS_ROOT, # pylint: disable=import-error
get_catalog,
git_repo_age,
)
from appslib.utils import (REPO_APPS_ROOT, # pylint: disable=import-error
get_catalog, git_repo_age)
from git import Repo
@@ -34,8 +31,7 @@ def app_cache_clone(app: str, infos: dict[str, str]) -> None:
infos["url"],
to_path=app_cache_folder(app),
depth=git_depths.get(infos["state"], git_depths["default"]),
single_branch=True,
branch=infos.get("branch", "master"),
single_branch=True, branch=infos.get("branch", "master"),
)

View File

@@ -23,14 +23,10 @@ def git(cmd: list[str], cwd: Optional[Path] = None) -> str:
if cwd:
full_cmd.extend(["-C", str(cwd)])
full_cmd.extend(cmd)
return (
subprocess.check_output(
full_cmd,
# env=my_env,
)
.strip()
.decode("utf-8")
)
return subprocess.check_output(
full_cmd,
# env=my_env,
).strip().decode("utf-8")
def git_repo_age(path: Path) -> Union[bool, int]:
@@ -46,8 +42,7 @@ def get_catalog(working_only: bool = False) -> dict[str, dict[str, Any]]:
catalog = toml.load((REPO_APPS_ROOT / "apps.toml").open("r", encoding="utf-8"))
if working_only:
catalog = {
app: infos
for app, infos in catalog.items()
app: infos for app, infos in catalog.items()
if infos.get("state") != "notworking"
}
return catalog

View File

@@ -7,9 +7,7 @@ import sys
import requests
catalog = requests.get(
"https://raw.githubusercontent.com/YunoHost/apps/master/apps.json"
).json()
catalog = requests.get("https://raw.githubusercontent.com/YunoHost/apps/master/apps.json").json()
my_env = os.environ.copy()
my_env["GIT_TERMINAL_PROMPT"] = "0"
@@ -46,19 +44,15 @@ def git(cmd, in_folder=None):
def progressbar(it, prefix="", size=60, file=sys.stdout):
it = list(it)
count = len(it)
def show(j, name=""):
name += " "
x = int(size * j / count)
file.write(
"%s[%s%s] %i/%i %s\r" % (prefix, "#" * x, "." * (size - x), j, count, name)
)
x = int(size*j/count)
file.write("%s[%s%s] %i/%i %s\r" % (prefix, "#"*x, "."*(size-x), j, count, name))
file.flush()
show(0)
for i, item in enumerate(it):
yield item
show(i + 1, item["id"])
show(i+1, item["id"])
file.write("\n")
file.flush()
@@ -69,10 +63,7 @@ def build_cache():
folder = os.path.join(".apps_cache", app["id"])
reponame = app["url"].rsplit("/", 1)[-1]
git(f"clone --quiet --depth 1 --single-branch {app['url']} {folder}")
git(
f"remote add fork https://{login}:{token}@github.com/{login}/{reponame}",
in_folder=folder,
)
git(f"remote add fork https://{login}:{token}@github.com/{login}/{reponame}", in_folder=folder)
def apply(patch):
@@ -90,11 +81,7 @@ def diff():
for app in apps():
folder = os.path.join(".apps_cache", app["id"])
if bool(
subprocess.check_output(f"cd {folder} && git diff", shell=True)
.strip()
.decode("utf-8")
):
if bool(subprocess.check_output(f"cd {folder} && git diff", shell=True).strip().decode("utf-8")):
print("\n\n\n")
print("=================================")
print("Changes in : " + app["id"])
@@ -105,50 +92,35 @@ def diff():
def push(patch):
title = (
"[autopatch] "
+ open(os.path.join("patches", patch, "pr_title.md")).read().strip()
)
title = "[autopatch] " + open(os.path.join("patches", patch, "pr_title.md")).read().strip()
def diff_not_empty(app):
folder = os.path.join(".apps_cache", app["id"])
return bool(
subprocess.check_output(f"cd {folder} && git diff", shell=True)
.strip()
.decode("utf-8")
)
return bool(subprocess.check_output(f"cd {folder} && git diff", shell=True).strip().decode("utf-8"))
def app_is_on_github(app):
return "github.com" in app["url"]
apps_to_push = [
app for app in apps() if diff_not_empty(app) and app_is_on_github(app)
]
apps_to_push = [app for app in apps() if diff_not_empty(app) and app_is_on_github(app)]
with requests.Session() as s:
s.headers.update({"Authorization": f"token {token}"})
for app in progressbar(apps_to_push, "Forking: ", 40):
app["repo"] = app["url"][len("https://github.com/") :].strip("/")
app["repo"] = app["url"][len("https://github.com/"):].strip("/")
fork_if_needed(app["repo"], s)
for app in progressbar(apps_to_push, "Pushing: ", 40):
app["repo"] = app["url"][len("https://github.com/") :].strip("/")
app["repo"] = app["url"][len("https://github.com/"):].strip("/")
app_repo_name = app["url"].rsplit("/", 1)[-1]
folder = os.path.join(".apps_cache", app["id"])
current_branch = git(f"symbolic-ref --short HEAD", in_folder=folder)
git(f"reset origin/{current_branch}", in_folder=folder)
git(
["commit", "-a", "-m", title, "--author='Yunohost-Bot <>'"],
in_folder=folder,
)
git(["commit", "-a", "-m", title, "--author='Yunohost-Bot <>'"], in_folder=folder)
try:
git(f"remote remove fork", in_folder=folder)
except Exception:
pass
git(
f"remote add fork https://{login}:{token}@github.com/{login}/{app_repo_name}",
in_folder=folder,
)
git(f"remote add fork https://{login}:{token}@github.com/{login}/{app_repo_name}", in_folder=folder)
git(f"push fork {current_branch}:{patch} --quiet --force", in_folder=folder)
create_pull_request(app["repo"], patch, current_branch, s)
@@ -169,15 +141,11 @@ def fork_if_needed(repo, s):
def create_pull_request(repo, patch, base_branch, s):
PR = {
"title": "[autopatch] "
+ open(os.path.join("patches", patch, "pr_title.md")).read().strip(),
"body": "This is an automatic PR\n\n"
+ open(os.path.join("patches", patch, "pr_body.md")).read().strip(),
"head": login + ":" + patch,
"base": base_branch,
"maintainer_can_modify": True,
}
PR = {"title": "[autopatch] " + open(os.path.join("patches", patch, "pr_title.md")).read().strip(),
"body": "This is an automatic PR\n\n" + open(os.path.join("patches", patch, "pr_body.md")).read().strip(),
"head": login + ":" + patch,
"base": base_branch,
"maintainer_can_modify": True}
r = s.post(github_api + f"/repos/{repo}/pulls", json.dumps(PR))
@@ -191,8 +159,7 @@ def main():
action = sys.argv[1]
if action == "--help":
print(
"""
print("""
Example usage:
# Init local git clone for all apps
@@ -206,8 +173,7 @@ def main():
# Push and create pull requests on all apps with non-empty diff
./autopatch.py --push explicit-php-version-in-deps
"""
)
""")
elif action == "--build-cache":
build_cache()

View File

@@ -21,20 +21,10 @@ import github
# add apps/tools to sys.path
sys.path.insert(0, str(Path(__file__).parent.parent))
from rest_api import (
GithubAPI,
GitlabAPI,
GiteaForgejoAPI,
RefType,
) # noqa: E402,E501 pylint: disable=import-error,wrong-import-position
from rest_api import GithubAPI, GitlabAPI, GiteaForgejoAPI, RefType # noqa: E402,E501 pylint: disable=import-error,wrong-import-position
import appslib.logging_sender # noqa: E402 pylint: disable=import-error,wrong-import-position
from appslib.utils import (
REPO_APPS_ROOT,
get_catalog,
) # noqa: E402 pylint: disable=import-error,wrong-import-position
from app_caches import (
app_cache_folder,
) # noqa: E402 pylint: disable=import-error,wrong-import-position
from appslib.utils import REPO_APPS_ROOT, get_catalog # noqa: E402 pylint: disable=import-error,wrong-import-position
from app_caches import app_cache_folder # noqa: E402 pylint: disable=import-error,wrong-import-position
STRATEGIES = [
@@ -54,30 +44,11 @@ STRATEGIES = [
@cache
def get_github() -> tuple[
Optional[tuple[str, str]],
Optional[github.Github],
Optional[github.InputGitAuthor],
]:
def get_github() -> tuple[Optional[tuple[str, str]], Optional[github.Github], Optional[github.InputGitAuthor]]:
try:
github_login = (
(REPO_APPS_ROOT / ".github_login")
.open("r", encoding="utf-8")
.read()
.strip()
)
github_token = (
(REPO_APPS_ROOT / ".github_token")
.open("r", encoding="utf-8")
.read()
.strip()
)
github_email = (
(REPO_APPS_ROOT / ".github_email")
.open("r", encoding="utf-8")
.read()
.strip()
)
github_login = (REPO_APPS_ROOT / ".github_login").open("r", encoding="utf-8").read().strip()
github_token = (REPO_APPS_ROOT / ".github_token").open("r", encoding="utf-8").read().strip()
github_email = (REPO_APPS_ROOT / ".github_email").open("r", encoding="utf-8").read().strip()
auth = (github_login, github_token)
github_api = github.Github(github_token)
@@ -125,9 +96,7 @@ class LocalOrRemoteRepo:
if not self.manifest_path.exists():
raise RuntimeError(f"{app.name}: manifest.toml doesnt exists?")
# app is in fact a path
self.manifest_raw = (
(app / "manifest.toml").open("r", encoding="utf-8").read()
)
self.manifest_raw = (app / "manifest.toml").open("r", encoding="utf-8").read()
elif isinstance(app, str):
# It's remote
@@ -218,9 +187,7 @@ class AppAutoUpdater:
self.main_upstream = self.manifest.get("upstream", {}).get("code")
def run(
self, edit: bool = False, commit: bool = False, pr: bool = False
) -> tuple[State, str, str, str]:
def run(self, edit: bool = False, commit: bool = False, pr: bool = False) -> tuple[State, str, str, str]:
state = State.up_to_date
main_version = ""
pr_url = ""
@@ -245,11 +212,7 @@ class AppAutoUpdater:
commit_msg += f"\n{msg}"
self.repo.manifest_raw = self.replace_version_and_asset_in_manifest(
self.repo.manifest_raw,
version,
assets,
infos,
is_main=source == "main",
self.repo.manifest_raw, version, assets, infos, is_main=source == "main",
)
if state == State.up_to_date:
@@ -283,9 +246,7 @@ class AppAutoUpdater:
return (state, self.current_version, main_version, pr_url)
@staticmethod
def relevant_versions(
tags: list[str], app_id: str, version_regex: Optional[str]
) -> tuple[str, str]:
def relevant_versions(tags: list[str], app_id: str, version_regex: Optional[str]) -> tuple[str, str]:
def apply_version_regex(tag: str) -> Optional[str]:
# First preprocessing according to the manifest version_regex…
@@ -294,9 +255,7 @@ class AppAutoUpdater:
if match is None:
return None
# Basically: either groupdict if named capture gorups, sorted by names, or groups()
tag = ".".join(
dict(sorted(match.groupdict().items())).values() or match.groups()
)
tag = ".".join(dict(sorted(match.groupdict().items())).values() or match.groups())
# Then remove leading v
tag = tag.lstrip("v")
@@ -305,9 +264,7 @@ class AppAutoUpdater:
def version_numbers(tag: str) -> Optional[tuple[int, ...]]:
filter_keywords = ["start", "rc", "beta", "alpha"]
if any(keyword in tag for keyword in filter_keywords):
logging.debug(
f"Tag {tag} contains filtered keyword from {filter_keywords}."
)
logging.debug(f"Tag {tag} contains filtered keyword from {filter_keywords}.")
return None
t_to_check = tag
@@ -317,7 +274,7 @@ class AppAutoUpdater:
elif tag.startswith("release-"):
t_to_check = tag.split("-", 1)[-1].replace("-", ".")
if re.match(r"^v?\d+(\.\d+)*(\-\d+)?$", t_to_check):
if re.match(r"^v?[\d\.]*\-?\d$", t_to_check):
return AppAutoUpdater.tag_to_int_tuple(t_to_check)
print(f"Ignoring tag {t_to_check}, doesn't look like a version number")
return None
@@ -343,11 +300,9 @@ class AppAutoUpdater:
@staticmethod
def tag_to_int_tuple(tag: str) -> tuple[int, ...]:
tag = tag.lstrip("v").replace("-", ".").rstrip(".")
tag = tag.strip("v").replace("-", ".").strip(".")
int_tuple = tag.split(".")
assert all(
i.isdigit() for i in int_tuple
), f"Cant convert {tag} to int tuple :/"
assert all(i.isdigit() for i in int_tuple), f"Cant convert {tag} to int tuple :/"
return tuple(int(i) for i in int_tuple)
@staticmethod
@@ -362,9 +317,8 @@ class AppAutoUpdater:
except Exception as e:
raise RuntimeError(f"Failed to compute sha256 for {url} : {e}") from e
def get_source_update(
self, name: str, infos: dict[str, Any]
) -> Optional[tuple[str, Union[str, dict[str, str]], str]]:
def get_source_update(self, name: str, infos: dict[str, Any]
) -> Optional[tuple[str, Union[str, dict[str, str]], str]]:
autoupdate = infos.get("autoupdate")
if autoupdate is None:
return None
@@ -373,9 +327,7 @@ class AppAutoUpdater:
asset = autoupdate.get("asset", "tarball")
strategy = autoupdate.get("strategy")
if strategy not in STRATEGIES:
raise ValueError(
f"Unknown update strategy '{strategy}' for '{name}', expected one of {STRATEGIES}"
)
raise ValueError(f"Unknown update strategy '{strategy}' for '{name}', expected one of {STRATEGIES}")
result = self.get_latest_version_and_asset(strategy, asset, autoupdate)
if result is None:
@@ -395,22 +347,14 @@ class AppAutoUpdater:
print("Up to date")
return None
try:
if self.tag_to_int_tuple(self.current_version) > self.tag_to_int_tuple(
new_version
):
print(
"Up to date (current version appears more recent than newest version found)"
)
if self.tag_to_int_tuple(self.current_version) > self.tag_to_int_tuple(new_version):
print("Up to date (current version appears more recent than newest version found)")
return None
except (AssertionError, ValueError):
pass
if (
isinstance(assets, dict)
and isinstance(infos.get("url"), str)
or isinstance(assets, str)
and not isinstance(infos.get("url"), str)
):
if isinstance(assets, dict) and isinstance(infos.get("url"), str) or \
isinstance(assets, str) and not isinstance(infos.get("url"), str):
raise RuntimeError(
"It looks like there's an inconsistency between the old asset list and the new ones... "
"One is arch-specific, the other is not... Did you forget to define arch-specific regexes? "
@@ -420,9 +364,7 @@ class AppAutoUpdater:
if isinstance(assets, str) and infos["url"] == assets:
print(f"URL for asset {name} is up to date")
return None
if isinstance(assets, dict) and assets == {
k: infos[k]["url"] for k in assets.keys()
}:
if isinstance(assets, dict) and assets == {k: infos[k]["url"] for k in assets.keys()}:
print(f"URLs for asset {name} are up to date")
return None
print(f"Update needed for {name}")
@@ -434,26 +376,21 @@ class AppAutoUpdater:
name: url for name, url in assets.items() if re.match(regex, name)
}
if not matching_assets:
raise RuntimeError(
f"No assets matching regex '{regex}' in {list(assets.keys())}"
)
raise RuntimeError(f"No assets matching regex '{regex}' in {list(assets.keys())}")
if len(matching_assets) > 1:
raise RuntimeError(
f"Too many assets matching regex '{regex}': {matching_assets}"
)
raise RuntimeError(f"Too many assets matching regex '{regex}': {matching_assets}")
return next(iter(matching_assets.items()))
def get_latest_version_and_asset(
self, strategy: str, asset: Union[str, dict], autoupdate
) -> Optional[tuple[str, Union[str, dict[str, str]], str]]:
def get_latest_version_and_asset(self, strategy: str, asset: Union[str, dict], autoupdate
) -> Optional[tuple[str, Union[str, dict[str, str]], str]]:
upstream = autoupdate.get("upstream", self.main_upstream).strip("/")
version_re = autoupdate.get("version_regex", None)
_, remote_type, revision_type = strategy.split("_")
api: Union[GithubAPI, GitlabAPI, GiteaForgejoAPI]
if remote_type == "github":
assert upstream and upstream.startswith(
"https://github.com/"
assert (
upstream and upstream.startswith("https://github.com/")
), f"When using strategy {strategy}, having a defined upstream code repo on github.com is required"
api = GithubAPI(upstream, auth=get_github()[0])
if remote_type == "gitlab":
@@ -467,9 +404,7 @@ class AppAutoUpdater:
for release in api.releases()
if not release["draft"] and not release["prerelease"]
}
latest_version_orig, latest_version = self.relevant_versions(
list(releases.keys()), self.app_id, version_re
)
latest_version_orig, latest_version = self.relevant_versions(list(releases.keys()), self.app_id, version_re)
latest_release = releases[latest_version_orig]
latest_assets = {
a["name"]: a["browser_download_url"]
@@ -490,9 +425,7 @@ class AppAutoUpdater:
_, url = self.find_matching_asset(latest_assets, asset)
return latest_version, url, latest_release_html_url
except RuntimeError as e:
raise RuntimeError(
f"{e}.\nFull release details on {latest_release_html_url}."
) from e
raise RuntimeError(f"{e}.\nFull release details on {latest_release_html_url}.") from e
if isinstance(asset, dict):
new_assets = {}
@@ -501,50 +434,34 @@ class AppAutoUpdater:
_, url = self.find_matching_asset(latest_assets, asset_regex)
new_assets[asset_name] = url
except RuntimeError as e:
raise RuntimeError(
f"{e}.\nFull release details on {latest_release_html_url}."
) from e
raise RuntimeError(f"{e}.\nFull release details on {latest_release_html_url}.") from e
return latest_version, new_assets, latest_release_html_url
return None
if revision_type == "tag":
if asset != "tarball":
raise ValueError(
"For the latest tag strategies, only asset = 'tarball' is supported"
)
raise ValueError("For the latest tag strategies, only asset = 'tarball' is supported")
tags = [t["name"] for t in api.tags()]
latest_version_orig, latest_version = self.relevant_versions(
tags, self.app_id, version_re
)
latest_version_orig, latest_version = self.relevant_versions(tags, self.app_id, version_re)
latest_tarball = api.url_for_ref(latest_version_orig, RefType.tags)
return latest_version, latest_tarball, ""
if revision_type == "commit":
if asset != "tarball":
raise ValueError(
"For the latest commit strategies, only asset = 'tarball' is supported"
)
raise ValueError("For the latest commit strategies, only asset = 'tarball' is supported")
commits = api.commits()
latest_commit = commits[0]
latest_tarball = api.url_for_ref(latest_commit["sha"], RefType.commits)
# Let's have the version as something like "2023.01.23"
latest_commit_date = datetime.strptime(
latest_commit["commit"]["author"]["date"][:10], "%Y-%m-%d"
)
latest_commit_date = datetime.strptime(latest_commit["commit"]["author"]["date"][:10], "%Y-%m-%d")
version_format = autoupdate.get("force_version", "%Y.%m.%d")
latest_version = latest_commit_date.strftime(version_format)
return latest_version, latest_tarball, ""
return None
def replace_version_and_asset_in_manifest(
self,
content: str,
new_version: str,
new_assets_urls: Union[str, dict],
current_assets: dict,
is_main: bool,
):
def replace_version_and_asset_in_manifest(self, content: str, new_version: str, new_assets_urls: Union[str, dict],
current_assets: dict, is_main: bool):
replacements = []
if isinstance(new_assets_urls, str):
replacements = [
@@ -554,21 +471,16 @@ class AppAutoUpdater:
if isinstance(new_assets_urls, dict):
replacements = [
repl
for key, url in new_assets_urls.items()
for repl in (
for key, url in new_assets_urls.items() for repl in (
(current_assets[key]["url"], url),
(current_assets[key]["sha256"], self.sha256_of_remote_file(url)),
(current_assets[key]["sha256"], self.sha256_of_remote_file(url))
)
]
if is_main:
def repl(m: re.Match) -> str:
return m.group(1) + new_version + '~ynh1"'
content = re.sub(
r"(\s*version\s*=\s*[\"\'])([^~\"\']+)(\~ynh\d+[\"\'])", repl, content
)
content = re.sub(r"(\s*version\s*=\s*[\"\'])([\d\.]+)(\~ynh\d+[\"\'])", repl, content)
for old, new in replacements:
content = content.replace(old, new)
@@ -626,41 +538,22 @@ def run_autoupdate_for_multiprocessing(data) -> tuple[str, tuple[State, str, str
except Exception:
log_str = stdoutswitch.reset()
import traceback
t = traceback.format_exc()
return (app, (State.failure, log_str, str(t), ""))
def main() -> None:
parser = argparse.ArgumentParser()
parser.add_argument(
"apps",
nargs="*",
type=Path,
help="If not passed, the script will run on the catalog. Github keys required.",
)
parser.add_argument(
"--edit",
action=argparse.BooleanOptionalAction,
default=True,
help="Edit the local files",
)
parser.add_argument(
"--commit",
action=argparse.BooleanOptionalAction,
default=False,
help="Create a commit with the changes",
)
parser.add_argument(
"--pr",
action=argparse.BooleanOptionalAction,
default=False,
help="Create a pull request with the changes",
)
parser.add_argument("apps", nargs="*", type=Path,
help="If not passed, the script will run on the catalog. Github keys required.")
parser.add_argument("--edit", action=argparse.BooleanOptionalAction, default=True,
help="Edit the local files")
parser.add_argument("--commit", action=argparse.BooleanOptionalAction, default=False,
help="Create a commit with the changes")
parser.add_argument("--pr", action=argparse.BooleanOptionalAction, default=False,
help="Create a pull request with the changes")
parser.add_argument("--paste", action="store_true")
parser.add_argument(
"-j", "--processes", type=int, default=multiprocessing.cpu_count()
)
parser.add_argument("-j", "--processes", type=int, default=multiprocessing.cpu_count())
args = parser.parse_args()
appslib.logging_sender.enable()
@@ -679,10 +572,8 @@ def main() -> None:
apps_failed = {}
with multiprocessing.Pool(processes=args.processes) as pool:
tasks = pool.imap(
run_autoupdate_for_multiprocessing,
((app, args.edit, args.commit, args.pr) for app in apps),
)
tasks = pool.imap(run_autoupdate_for_multiprocessing,
((app, args.edit, args.commit, args.pr) for app in apps))
for app, result in tqdm.tqdm(tasks, total=len(apps), ascii=" ·#"):
state, current_version, main_version, pr_url = result
if state == State.up_to_date:
@@ -701,9 +592,7 @@ def main() -> None:
matrix_message += f"\n- {len(apps_already)} pending update PRs"
for app, info in apps_already.items():
paste_message += f"\n- {app}"
paste_message += (
f" ({info[0]} -> {info[1]})" if info[1] else " (app version did not change)"
)
paste_message += f" ({info[0]} -> {info[1]})" if info[1] else " (app version did not change)"
if info[2]:
paste_message += f" see {info[2]}"
@@ -712,9 +601,7 @@ def main() -> None:
matrix_message += f"\n- {len(apps_updated)} new apps PRs"
for app, info in apps_updated.items():
paste_message += f"\n- {app}"
paste_message += (
f" ({info[0]} -> {info[1]})" if info[1] else " (app version did not change)"
)
paste_message += f" ({info[0]} -> {info[1]})" if info[1] else " (app version did not change)"
if info[2]:
paste_message += f" see {info[2]}"

View File

@@ -15,10 +15,11 @@ class RefType(Enum):
class GithubAPI:
def __init__(self, upstream: str, auth: Optional[tuple[str, str]] = None):
self.upstream = upstream
self.upstream_repo = upstream.replace("https://github.com/", "").strip("/")
self.upstream_repo = upstream.replace("https://github.com/", "")\
.strip("/")
assert (
len(self.upstream_repo.split("/")) == 2
), f"'{upstream}' doesn't seem to be a github repository ?"
len(self.upstream_repo.split("/")) == 2
), f"'{upstream}' doesn't seem to be a github repository ?"
self.auth = auth
def internal_api(self, uri: str) -> Any:
@@ -73,12 +74,7 @@ class GitlabAPI:
# Second chance for some buggy gitlab instances...
name = self.project_path.split("/")[-1]
projects = self.internal_api(f"projects?search={name}")
project = next(
filter(
lambda x: x.get("path_with_namespace") == self.project_path,
projects,
)
)
project = next(filter(lambda x: x.get("path_with_namespace") == self.project_path, projects))
assert isinstance(project, dict)
project_id = project.get("id", None)
@@ -99,11 +95,13 @@ class GitlabAPI:
return [
{
"sha": commit["id"],
"commit": {"author": {"date": commit["committed_date"]}},
"commit": {
"author": {
"date": commit["committed_date"]
}
}
}
for commit in self.internal_api(
f"projects/{self.project_id}/repository/commits"
)
for commit in self.internal_api(f"projects/{self.project_id}/repository/commits")
]
def releases(self) -> list[dict[str, Any]]:
@@ -116,21 +114,16 @@ class GitlabAPI:
"prerelease": False,
"draft": False,
"html_url": release["_links"]["self"],
"assets": [
{
"name": asset["name"],
"browser_download_url": asset["direct_asset_url"],
}
for asset in release["assets"]["links"]
],
}
"assets": [{
"name": asset["name"],
"browser_download_url": asset["direct_asset_url"]
} for asset in release["assets"]["links"]],
}
for source in release["assets"]["sources"]:
r["assets"].append(
{
"name": f"source.{source['format']}",
"browser_download_url": source["url"],
}
)
r["assets"].append({
"name": f"source.{source['format']}",
"browser_download_url": source['url']
})
retval.append(r)
return retval

View File

@@ -9,7 +9,6 @@ import urllib.request
import github
from github import Github
# Debug
from rich.traceback import install
@@ -25,25 +24,23 @@ install(width=150, show_locals=True, locals_max_length=None, locals_max_string=N
g = Github(open(".github_token").read().strip())
# Path to the file to be updated
path = ".github/workflows/updater.yml"
path=".github/workflows/updater.yml"
# Title of the PR
title = "[autopatch] Upgrade auto-updater"
title="[autopatch] Upgrade auto-updater"
# Body of the PR message
body = """
body="""
Auto-updater actions need upgrading to continue working:
- actions/checkout@v3
- peter-evans/create-pull-request@v4
"""
# Author of the commit
author = github.InputGitAuthor(
open(".github_login").read().strip(), open(".github_email").read().strip()
)
author=github.InputGitAuthor(open(".github_login").read().strip(), open(".github_email").read().strip())
# Name of the branch created for the PR
new_branch = "upgrade-auto-updater"
new_branch="upgrade-auto-updater"
#####
#
@@ -51,7 +48,7 @@ new_branch = "upgrade-auto-updater"
#
#####
with open("processed.txt") as f:
with open('processed.txt') as f:
processed = f.read().splitlines()
#####
@@ -64,7 +61,7 @@ u = g.get_user("yunohost-bot")
org = g.get_organization("yunohost-apps")
# For each repositories belonging to the bot (user `u`)
i = 0
i=0
for repo in org.get_repos():
if repo.full_name not in processed:
@@ -76,64 +73,50 @@ for repo in org.get_repos():
# Make sure the repository has an auto-updater
try:
repo.get_contents(path, ref="refs/heads/" + base_branch)
repo.get_contents(path, ref="refs/heads/"+base_branch)
except:
with open("processed.txt", "a") as pfile:
pfile.write(repo.full_name + "\n")
with open('processed.txt', 'a') as pfile:
pfile.write(repo.full_name+'\n')
time.sleep(1.5)
continue
# Process the repo
print("Processing " + repo.full_name)
print("Processing "+repo.full_name)
try:
# Get the commit base for the new branch, and create it
commit_sha = repo.get_branch(base_branch).commit.sha
new_branch_ref = repo.create_git_ref(
ref="refs/heads/" + new_branch, sha=commit_sha
)
new_branch_ref = repo.create_git_ref(ref="refs/heads/"+new_branch, sha=commit_sha)
except:
new_branch_ref = repo.get_git_ref(ref="heads/" + new_branch)
new_branch_ref = repo.get_git_ref(ref="heads/"+new_branch)
# Get current file contents
contents = repo.get_contents(path, ref=new_branch_ref.ref)
# Update the file
updater_yml = contents.decoded_content.decode("unicode_escape")
updater_yml = re.sub(
r"(?m)uses: actions/checkout@v[\d]+",
"uses: actions/checkout@v3",
updater_yml,
)
updater_yml = re.sub(
r"(?m)uses: peter-evans/create-pull-request@v[\d]+",
"uses: peter-evans/create-pull-request@v4",
updater_yml,
)
updated = repo.update_file(
contents.path,
message=title,
content=updater_yml,
sha=contents.sha,
branch=new_branch,
author=author,
)
updater_yml = re.sub(r'(?m)uses: actions/checkout@v[\d]+', "uses: actions/checkout@v3", updater_yml)
updater_yml = re.sub(r'(?m)uses: peter-evans/create-pull-request@v[\d]+', "uses: peter-evans/create-pull-request@v4", updater_yml)
updated = repo.update_file(contents.path,
message=title,
content=updater_yml,
sha=contents.sha,
branch=new_branch,
author=author)
# Wait a bit to preserve the API rate limit
time.sleep(1.5)
# Open the PR
pr = repo.create_pull(
title="Upgrade auto-updater", body=body, head=new_branch, base=base_branch
)
pr = repo.create_pull(title="Upgrade auto-updater", body=body, head=new_branch, base=base_branch)
print(repo.full_name + " updated with PR #" + str(pr.id))
i = i + 1
print(repo.full_name+" updated with PR #"+ str(pr.id))
i=i+1
# Wait a bit to preserve the API rate limit
time.sleep(1.5)
with open("processed.txt", "a") as pfile:
pfile.write(repo.full_name + "\n")
with open('processed.txt', 'a') as pfile:
pfile.write(repo.full_name+'\n')
print("Done. " + str(i) + " repos processed")
print("Done. "+str(i)+" repos processed")

View File

@@ -10,22 +10,17 @@ u = g.get_user("yunohost-bot")
# Let's build a minimalistic summary table
print("| Repository ".ljust(22) + " | Decision |")
print("| ".ljust(22, "-") + " | -------- |")
print("| ".ljust(22, '-') + " | -------- |")
# For each repositories belonging to the bot (user `u`)
for repo in u.get_repos():
# Proceed iff the repository is a fork (`parent` key is set) of a repository in our apps organization
if repo.parent.full_name.split("/")[0] != "YunoHost-Apps":
print("| " + repo.name.ljust(20) + " | Skipping |")
if repo.parent.full_name.split('/')[0] != "YunoHost-Apps":
print("| "+repo.name.ljust(20) + " | Skipping |")
else:
# If none of the PRs are opened by the bot, delete the repository
if not any(
[
(pr.user == u)
for pr in list(repo.parent.get_pulls(state="open", sort="created"))
]
):
print("| " + repo.name.ljust(20) + " | Deleting |")
if not any([ (pr.user == u) for pr in list(repo.parent.get_pulls(state='open', sort='created')) ]):
print("| "+repo.name.ljust(20) + " | Deleting |")
repo.delete()
else:
print("| " + repo.name.ljust(20) + " | Keeping |")
print("| "+repo.name.ljust(20) + " | Keeping |")

View File

@@ -6,29 +6,20 @@ from difflib import SequenceMatcher
from typing import Any, Dict, Generator, List, Tuple
import jsonschema
from appslib.utils import (
REPO_APPS_ROOT, # pylint: disable=import-error
get_antifeatures,
get_catalog,
get_categories,
get_graveyard,
get_wishlist,
)
from appslib.utils import (REPO_APPS_ROOT, # pylint: disable=import-error
get_antifeatures, get_catalog, get_categories,
get_graveyard, get_wishlist)
def validate_schema() -> Generator[str, None, None]:
with open(
REPO_APPS_ROOT / "schemas" / "apps.toml.schema.json", encoding="utf-8"
) as file:
with open(REPO_APPS_ROOT / "schemas" / "apps.toml.schema.json", encoding="utf-8") as file:
apps_catalog_schema = json.load(file)
validator = jsonschema.Draft202012Validator(apps_catalog_schema)
for error in validator.iter_errors(get_catalog()):
yield f"at .{'.'.join(error.path)}: {error.message}"
def check_app(
app: str, infos: Dict[str, Any]
) -> Generator[Tuple[str, bool], None, None]:
def check_app(app: str, infos: Dict[str, Any]) -> Generator[Tuple[str, bool], None, None]:
if "state" not in infos:
yield "state is missing", True
return

View File

@@ -21,15 +21,10 @@ from git import Repo
import appslib.logging_sender # pylint: disable=import-error
from app_caches import app_cache_folder # pylint: disable=import-error
from app_caches import apps_cache_update_all # pylint: disable=import-error
from appslib.utils import (
REPO_APPS_ROOT, # pylint: disable=import-error
get_antifeatures,
get_catalog,
get_categories,
)
from packaging_v2.convert_v1_manifest_to_v2_for_catalog import (
convert_v1_manifest_to_v2_for_catalog,
) # pylint: disable=import-error
from appslib.utils import (REPO_APPS_ROOT, # pylint: disable=import-error
get_antifeatures, get_catalog, get_categories)
from packaging_v2.convert_v1_manifest_to_v2_for_catalog import \
convert_v1_manifest_to_v2_for_catalog # pylint: disable=import-error
now = time.time()
@@ -42,7 +37,7 @@ def categories_list():
infos["id"] = category_id
for subtag_id, subtag_infos in infos.get("subtags", {}).items():
subtag_infos["id"] = subtag_id
infos["subtags"] = list(infos.get("subtags", {}).values())
infos["subtags"] = list(infos.get('subtags', {}).values())
return list(new_categories.values())
@@ -59,7 +54,6 @@ def antifeatures_list():
# Actual list build management #
################################
def __build_app_dict(data) -> Optional[tuple[str, dict[str, Any]]]:
name, info = data
try:
@@ -99,17 +93,13 @@ def write_catalog_v2(base_catalog, target_dir: Path) -> None:
target_file = target_dir / "apps.json"
target_file.parent.mkdir(parents=True, exist_ok=True)
target_file.open("w", encoding="utf-8").write(
json.dumps(full_catalog, sort_keys=True)
)
target_file.open("w", encoding="utf-8").write(json.dumps(full_catalog, sort_keys=True))
def write_catalog_v3(base_catalog, target_dir: Path) -> None:
result_dict_with_manifest_v2 = copy.deepcopy(base_catalog)
for app in result_dict_with_manifest_v2.values():
packaging_format = float(
str(app["manifest"].get("packaging_format", "")).strip() or "0"
)
packaging_format = float(str(app["manifest"].get("packaging_format", "")).strip() or "0")
if packaging_format < 2:
app["manifest"] = convert_v1_manifest_to_v2_for_catalog(app["manifest"])
@@ -127,12 +117,7 @@ def write_catalog_v3(base_catalog, target_dir: Path) -> None:
appid = appid.lower()
logo_source = REPO_APPS_ROOT / "logos" / f"{appid}.png"
if logo_source.exists():
logo_hash = (
subprocess.check_output(["sha256sum", logo_source])
.strip()
.decode("utf-8")
.split()[0]
)
logo_hash = subprocess.check_output(["sha256sum", logo_source]).strip().decode("utf-8").split()[0]
shutil.copyfile(logo_source, logos_dir / f"{logo_hash}.png")
# FIXME: implement something to cleanup old logo stuf in the builds/.../logos/ folder somehow
else:
@@ -147,9 +132,7 @@ def write_catalog_v3(base_catalog, target_dir: Path) -> None:
target_file = target_dir / "apps.json"
target_file.parent.mkdir(parents=True, exist_ok=True)
target_file.open("w", encoding="utf-8").write(
json.dumps(full_catalog, sort_keys=True)
)
target_file.open("w", encoding="utf-8").write(json.dumps(full_catalog, sort_keys=True))
def write_catalog_doc(base_catalog, target_dir: Path) -> None:
@@ -177,13 +160,14 @@ def write_catalog_doc(base_catalog, target_dir: Path) -> None:
for k, v in base_catalog.items()
if v["state"] == "working"
}
full_catalog = {"apps": result_dict_doc, "categories": categories_list()}
full_catalog = {
"apps": result_dict_doc,
"categories": categories_list()
}
target_file = target_dir / "apps.json"
target_file.parent.mkdir(parents=True, exist_ok=True)
target_file.open("w", encoding="utf-8").write(
json.dumps(full_catalog, sort_keys=True)
)
target_file.open("w", encoding="utf-8").write(json.dumps(full_catalog, sort_keys=True))
def build_app_dict(app, infos):
@@ -193,38 +177,15 @@ def build_app_dict(app, infos):
repo = Repo(this_app_cache)
commits_in_apps_json = (
Repo(REPO_APPS_ROOT)
.git.log(
"-S",
f'"{app}"',
"--first-parent",
"--reverse",
"--date=unix",
"--format=%cd",
"--",
"apps.json",
)
.split("\n")
)
commits_in_apps_json = Repo(REPO_APPS_ROOT).git.log(
"-S", f"\"{app}\"", "--first-parent", "--reverse", "--date=unix",
"--format=%cd", "--", "apps.json").split("\n")
if len(commits_in_apps_json) > 1:
first_commit = commits_in_apps_json[0]
else:
commits_in_apps_toml = (
Repo(REPO_APPS_ROOT)
.git.log(
"-S",
f"[{app}]",
"--first-parent",
"--reverse",
"--date=unix",
"--format=%cd",
"--",
"apps.json",
"apps.toml",
)
.split("\n")
)
commits_in_apps_toml = Repo(REPO_APPS_ROOT).git.log(
"-S", f"[{app}]", "--first-parent", "--reverse", "--date=unix",
"--format=%cd", "--", "apps.json", "apps.toml").split("\n")
first_commit = commits_in_apps_toml[0]
# Assume the first entry we get (= the oldest) is the time the app was added
@@ -243,18 +204,14 @@ def build_app_dict(app, infos):
try:
_ = repo.commit(infos["revision"])
except ValueError as err:
raise RuntimeError(
f"Revision ain't in history ? {infos['revision']}"
) from err
raise RuntimeError(f"Revision ain't in history ? {infos['revision']}") from err
# Find timestamp corresponding to that commit
timestamp = repo.commit(infos["revision"]).committed_date
# Build the dict with all the infos
if (this_app_cache / "manifest.toml").exists():
manifest = toml.load(
(this_app_cache / "manifest.toml").open("r"), _dict=OrderedDict
)
manifest = toml.load((this_app_cache / "manifest.toml").open("r"), _dict=OrderedDict)
else:
manifest = json.load((this_app_cache / "manifest.json").open("r"))
@@ -270,45 +227,27 @@ def build_app_dict(app, infos):
"manifest": manifest,
"state": infos["state"],
"level": infos.get("level", "?"),
"maintained": "package-not-maintained" not in infos.get("antifeatures", []),
"maintained": 'package-not-maintained' not in infos.get('antifeatures', []),
"high_quality": infos.get("high_quality", False),
"featured": infos.get("featured", False),
"category": infos.get("category", None),
"subtags": infos.get("subtags", []),
"potential_alternative_to": infos.get("potential_alternative_to", []),
"antifeatures": list(
set(
list(manifest.get("antifeatures", {}).keys())
+ infos.get("antifeatures", [])
)
set(list(manifest.get("antifeatures", {}).keys()) + infos.get("antifeatures", []))
),
}
def main() -> None:
parser = argparse.ArgumentParser()
parser.add_argument(
"target_dir",
type=Path,
nargs="?",
default=REPO_APPS_ROOT / "builds" / "default",
help="The directory to write the catalogs to",
)
parser.add_argument(
"-j",
"--jobs",
type=int,
default=multiprocessing.cpu_count(),
metavar="N",
help="Allow N threads to run in parallel",
)
parser.add_argument(
"-c",
"--update-cache",
action=argparse.BooleanOptionalAction,
default=True,
help="Update the apps cache",
)
parser.add_argument("target_dir", type=Path, nargs="?",
default=REPO_APPS_ROOT / "builds" / "default",
help="The directory to write the catalogs to")
parser.add_argument("-j", "--jobs", type=int, default=multiprocessing.cpu_count(), metavar="N",
help="Allow N threads to run in parallel")
parser.add_argument("-c", "--update-cache", action=argparse.BooleanOptionalAction, default=True,
help="Update the apps cache")
args = parser.parse_args()
appslib.logging_sender.enable()

View File

@@ -9,7 +9,11 @@ from glob import glob
def check_output(cmd):
return subprocess.check_output(cmd, shell=True).decode("utf-8").strip()
return (
subprocess.check_output(cmd, shell=True)
.decode("utf-8")
.strip()
)
def convert_app_sources(folder):
@@ -31,13 +35,7 @@ def convert_app_sources(folder):
"sha256": D["sum"],
}
if D.get("format", "tar.gz") not in [
"zip",
"tar.gz",
"tar.xz",
"tgz",
"tar.bz2",
]:
if D.get("format", "tar.gz") not in ["zip", "tar.gz", "tar.xz", "tgz", "tar.bz2"]:
new_D["format"] = D["format"]
if "filename" in D:
new_D["rename"] = D["filename"]
@@ -117,12 +115,12 @@ def _convert_v1_manifest_to_v2(app_path):
"sso": "?",
"disk": "50M",
"ram.build": "50M",
"ram.runtime": "50M",
"ram.runtime": "50M"
}
maintainers = manifest.get("maintainer", {})
if isinstance(maintainers, list):
maintainers = [m["name"] for m in maintainers]
maintainers = [m['name'] for m in maintainers]
else:
maintainers = [maintainers["name"]] if maintainers.get("name") else []
@@ -132,30 +130,15 @@ def _convert_v1_manifest_to_v2(app_path):
manifest["install"] = {}
for question in install_questions:
name = question.pop("name")
if "ask" in question and name in [
"domain",
"path",
"admin",
"is_public",
"password",
]:
if "ask" in question and name in ["domain", "path", "admin", "is_public", "password"]:
question.pop("ask")
if question.get("example") and question.get("type") in [
"domain",
"path",
"user",
"boolean",
"password",
]:
if question.get("example") and question.get("type") in ["domain", "path", "user", "boolean", "password"]:
question.pop("example")
manifest["install"][name] = question
# Rename is_public to init_main_permission
manifest["install"] = {
(k if k != "is_public" else "init_main_permission"): v
for k, v in manifest["install"].items()
}
manifest["install"] = {(k if k != "is_public" else "init_main_permission"): v for k, v in manifest["install"].items()}
if "init_main_permission" in manifest["install"]:
manifest["install"]["init_main_permission"]["type"] = "group"
@@ -183,16 +166,12 @@ def _convert_v1_manifest_to_v2(app_path):
# FIXME: Parse ynh_permission_create --permission="admin" --url="/wp-login.php" --additional_urls="/wp-admin.php" --allowed=$admin_wordpress
ports = check_output(
f"sed -nr 's/(\\w+)=.*ynh_find_port[^0-9]*([0-9]+)\\)/\\1,\\2/p' '{app_path}/scripts/install'"
)
ports = check_output(f"sed -nr 's/(\\w+)=.*ynh_find_port[^0-9]*([0-9]+)\\)/\\1,\\2/p' '{app_path}/scripts/install'")
if ports:
manifest["resources"]["ports"] = {}
for port in ports.split("\n"):
name, default = port.split(",")
exposed = check_output(
f"sed -nr 's/.*yunohost firewall allow .*(TCP|UDP|Both).*${name}/\\1/p' '{app_path}/scripts/install'"
)
exposed = check_output(f"sed -nr 's/.*yunohost firewall allow .*(TCP|UDP|Both).*${name}/\\1/p' '{app_path}/scripts/install'")
if exposed == "Both":
exposed = True
@@ -201,9 +180,7 @@ def _convert_v1_manifest_to_v2(app_path):
name = "main"
if not default.isdigit():
print(
f"Failed to parse '{default}' as a port number ... Will use 12345 instead"
)
print(f"Failed to parse '{default}' as a port number ... Will use 12345 instead")
default = 12345
manifest["resources"]["ports"][f"{name}.default"] = int(default)
@@ -211,57 +188,35 @@ def _convert_v1_manifest_to_v2(app_path):
manifest["resources"]["ports"][f"{name}.exposed"] = exposed
maybequote = "[\"'\"'\"']?"
apt_dependencies = check_output(
f"sed -nr 's/.*_dependencies={maybequote}(.*){maybequote}? *$/\\1/p' '{app_path}/scripts/_common.sh' 2>/dev/null | tr -d '\"' | sed 's@ @\\n@g'"
)
php_version = check_output(
f"sed -nr 's/^ *YNH_PHP_VERSION={maybequote}(.*){maybequote}?$/\\1/p' '{app_path}/scripts/_common.sh' 2>/dev/null | tr -d \"\\\"'\""
)
apt_dependencies = check_output(f"sed -nr 's/.*_dependencies={maybequote}(.*){maybequote}? *$/\\1/p' '{app_path}/scripts/_common.sh' 2>/dev/null | tr -d '\"' | sed 's@ @\\n@g'")
php_version = check_output(f"sed -nr 's/^ *YNH_PHP_VERSION={maybequote}(.*){maybequote}?$/\\1/p' '{app_path}/scripts/_common.sh' 2>/dev/null | tr -d \"\\\"'\"")
if apt_dependencies.strip():
if php_version:
apt_dependencies = apt_dependencies.replace(
"${YNH_PHP_VERSION}", php_version
)
apt_dependencies = ", ".join([d for d in apt_dependencies.split("\n") if d])
apt_dependencies = apt_dependencies.replace("${YNH_PHP_VERSION}", php_version)
apt_dependencies = ', '.join([d for d in apt_dependencies.split("\n") if d])
manifest["resources"]["apt"] = {"packages": apt_dependencies}
extra_apt_repos = check_output(
r"sed -nr 's/.*_extra_app_dependencies.*repo=\"(.*)\".*package=\"(.*)\".*key=\"(.*)\"/\1,\2,\3/p' %s/scripts/install"
% app_path
)
extra_apt_repos = check_output(r"sed -nr 's/.*_extra_app_dependencies.*repo=\"(.*)\".*package=\"(.*)\".*key=\"(.*)\"/\1,\2,\3/p' %s/scripts/install" % app_path)
if extra_apt_repos:
for i, extra_apt_repo in enumerate(extra_apt_repos.split("\n")):
repo, packages, key = extra_apt_repo.split(",")
packages = packages.replace("$", "#FIXME#$")
packages = packages.replace('$', '#FIXME#$')
if "apt" not in manifest["resources"]:
manifest["resources"]["apt"] = {}
if "extras" not in manifest["resources"]["apt"]:
manifest["resources"]["apt"]["extras"] = []
manifest["resources"]["apt"]["extras"].append(
{
"repo": repo,
"key": key,
"packages": packages,
}
)
manifest["resources"]["apt"]["extras"].append({
"repo": repo,
"key": key,
"packages": packages,
})
if os.system(f"grep -q 'ynh_mysql_setup_db' {app_path}/scripts/install") == 0:
manifest["resources"]["database"] = {"type": "mysql"}
elif os.system(f"grep -q 'ynh_psql_setup_db' {app_path}/scripts/install") == 0:
manifest["resources"]["database"] = {"type": "postgresql"}
keys_to_keep = [
"packaging_format",
"id",
"name",
"description",
"version",
"maintainers",
"upstream",
"integration",
"install",
"resources",
]
keys_to_keep = ["packaging_format", "id", "name", "description", "version", "maintainers", "upstream", "integration", "install", "resources"]
keys_to_del = [key for key in manifest.keys() if key not in keys_to_keep]
for key in keys_to_del:
@@ -291,35 +246,19 @@ def _dump_v2_manifest_as_toml(manifest):
upstream = table()
for key, value in manifest["upstream"].items():
upstream[key] = value
upstream["cpe"].comment(
"FIXME: optional but recommended if relevant, this is meant to contain the Common Platform Enumeration, which is sort of a standard id for applications defined by the NIST. In particular, Yunohost may use this is in the future to easily track CVE (=security reports) related to apps. The CPE may be obtained by searching here: https://nvd.nist.gov/products/cpe/search. For example, for Nextcloud, the CPE is 'cpe:2.3:a:nextcloud:nextcloud' (no need to include the version number)"
)
upstream["fund"].comment(
"FIXME: optional but recommended (or remove if irrelevant / not applicable). This is meant to be an URL where people can financially support this app, especially when its development is based on volunteers and/or financed by its community. YunoHost may later advertise it in the webadmin."
)
upstream["cpe"].comment("FIXME: optional but recommended if relevant, this is meant to contain the Common Platform Enumeration, which is sort of a standard id for applications defined by the NIST. In particular, Yunohost may use this is in the future to easily track CVE (=security reports) related to apps. The CPE may be obtained by searching here: https://nvd.nist.gov/products/cpe/search. For example, for Nextcloud, the CPE is 'cpe:2.3:a:nextcloud:nextcloud' (no need to include the version number)")
upstream["fund"].comment("FIXME: optional but recommended (or remove if irrelevant / not applicable). This is meant to be an URL where people can financially support this app, especially when its development is based on volunteers and/or financed by its community. YunoHost may later advertise it in the webadmin.")
toml_manifest["upstream"] = upstream
integration = table()
for key, value in manifest["integration"].items():
integration.add(key, value)
integration["architectures"].comment(
'FIXME: can be replaced by a list of supported archs using the dpkg --print-architecture nomenclature (amd64/i386/armhf/arm64), for example: ["amd64", "i386"]'
)
integration["ldap"].comment(
'FIXME: replace with true, false, or "not_relevant". Not to confuse with the "sso" key : the "ldap" key corresponds to wether or not a user *can* login on the app using its YunoHost credentials.'
)
integration["sso"].comment(
'FIXME: replace with true, false, or "not_relevant". Not to confuse with the "ldap" key : the "sso" key corresponds to wether or not a user is *automatically logged-in* on the app when logged-in on the YunoHost portal.'
)
integration["disk"].comment(
"FIXME: replace with an **estimate** minimum disk requirement. e.g. 20M, 400M, 1G, ..."
)
integration["ram.build"].comment(
"FIXME: replace with an **estimate** minimum ram requirement. e.g. 50M, 400M, 1G, ..."
)
integration["ram.runtime"].comment(
"FIXME: replace with an **estimate** minimum ram requirement. e.g. 50M, 400M, 1G, ..."
)
integration["architectures"].comment('FIXME: can be replaced by a list of supported archs using the dpkg --print-architecture nomenclature (amd64/i386/armhf/arm64), for example: ["amd64", "i386"]')
integration["ldap"].comment('FIXME: replace with true, false, or "not_relevant". Not to confuse with the "sso" key : the "ldap" key corresponds to wether or not a user *can* login on the app using its YunoHost credentials.')
integration["sso"].comment('FIXME: replace with true, false, or "not_relevant". Not to confuse with the "ldap" key : the "sso" key corresponds to wether or not a user is *automatically logged-in* on the app when logged-in on the YunoHost portal.')
integration["disk"].comment('FIXME: replace with an **estimate** minimum disk requirement. e.g. 20M, 400M, 1G, ...')
integration["ram.build"].comment('FIXME: replace with an **estimate** minimum ram requirement. e.g. 50M, 400M, 1G, ...')
integration["ram.runtime"].comment('FIXME: replace with an **estimate** minimum ram requirement. e.g. 50M, 400M, 1G, ...')
toml_manifest["integration"] = integration
install = table()
@@ -328,11 +267,7 @@ def _dump_v2_manifest_as_toml(manifest):
install[key].indent(4)
if key in ["domain", "path", "admin", "is_public", "password"]:
install[key].add(
comment(
"this is a generic question - ask strings are automatically handled by Yunohost's core"
)
)
install[key].add(comment("this is a generic question - ask strings are automatically handled by Yunohost's core"))
for lang, value2 in value.get("ask", {}).items():
install[key].add(f"ask.{lang}", value2)
@@ -370,8 +305,8 @@ def _dump_v2_manifest_as_toml(manifest):
toml_manifest_dump = dumps(toml_manifest)
regex = re.compile(r"\"((description|ask|help)\.[a-z]{2})\"")
toml_manifest_dump = regex.sub(r"\1", toml_manifest_dump)
regex = re.compile(r'\"((description|ask|help)\.[a-z]{2})\"')
toml_manifest_dump = regex.sub(r'\1', toml_manifest_dump)
toml_manifest_dump = toml_manifest_dump.replace('"ram.build"', "ram.build")
toml_manifest_dump = toml_manifest_dump.replace('"ram.runtime"', "ram.runtime")
toml_manifest_dump = toml_manifest_dump.replace('"main.url"', "main.url")
@@ -389,9 +324,7 @@ def _dump_v2_manifest_as_toml(manifest):
if "ports" in manifest["resources"]:
for port_thing in manifest["resources"]["ports"].keys():
toml_manifest_dump = toml_manifest_dump.replace(
f'"{port_thing}"', f"{port_thing}"
)
toml_manifest_dump = toml_manifest_dump.replace(f'"{port_thing}"', f"{port_thing}")
return toml_manifest_dump
@@ -462,9 +395,7 @@ def cleanup_scripts_and_conf(folder):
"^.*ynh_script_progression.*Reloading NGINX web server",
"^.*ynh_systemd_action --service_name=nginx --action=reload",
]
patterns_to_remove_in_scripts = [
re.compile(f"({p})", re.MULTILINE) for p in patterns_to_remove_in_scripts
]
patterns_to_remove_in_scripts = [re.compile(f"({p})", re.MULTILINE) for p in patterns_to_remove_in_scripts]
replaces = [
("path_url", "path"),
@@ -473,21 +404,13 @@ def cleanup_scripts_and_conf(folder):
("FINALPATH", "INSTALL_DIR"),
("datadir", "data_dir"),
("DATADIR", "DATA_DIR"),
('--source_id="$architecture"', ""),
('--source_id="$YNH_ARCH"', ""),
("--source_id=app", ""),
('--source_id="app.$architecture"', ""),
('--source_id="$architecture"', ''),
('--source_id="$YNH_ARCH"', ''),
('--source_id=app', ''),
('--source_id="app.$architecture"', ''),
]
for s in [
"_common.sh",
"install",
"remove",
"upgrade",
"backup",
"restore",
"change_url",
]:
for s in ["_common.sh", "install", "remove", "upgrade", "backup", "restore", "change_url"]:
script = f"{folder}/scripts/{s}"
@@ -497,18 +420,10 @@ def cleanup_scripts_and_conf(folder):
content = open(script).read()
for pattern in patterns_to_remove_in_scripts:
if (
"^.*ynh_script_progression.*Reloading NGINX web server"
in pattern.pattern
and s == "restore"
):
if "^.*ynh_script_progression.*Reloading NGINX web server" in pattern.pattern and s == "restore":
# This case is legit
continue
if (
"^.*ynh_systemd_action --service_name=nginx --action=reload"
in pattern.pattern
and s == "restore"
):
if "^.*ynh_systemd_action --service_name=nginx --action=reload" in pattern.pattern and s == "restore":
# This case is legit
continue
content = pattern.sub(r"#REMOVEME? \1", content)
@@ -521,9 +436,7 @@ def cleanup_scripts_and_conf(folder):
pattern = re.compile("(^.*nginx.*$)", re.MULTILINE)
content = pattern.sub(r"#REMOVEME? \1", content)
pattern = re.compile(
"(^.*ynh_script_progress.*Updat.* NGINX.*conf.*$)", re.MULTILINE
)
pattern = re.compile("(^.*ynh_script_progress.*Updat.* NGINX.*conf.*$)", re.MULTILINE)
content = pattern.sub(r"\1\n\nynh_change_url_nginx_config", content)
pattern = re.compile(r"(ynh_clean_check_starting)", re.MULTILINE)
@@ -533,6 +446,7 @@ def cleanup_scripts_and_conf(folder):
pattern = re.compile(r"(^\s+path=.*$)", re.MULTILINE)
content = pattern.sub(r"#REMOVEME? \1", content)
open(script, "w").write(content)
for conf in os.listdir(f"{folder}/conf"):
@@ -556,15 +470,15 @@ if __name__ == "__main__":
parser = argparse.ArgumentParser(
description="Attempt to automatically convert a v1 YunoHost app to v2 (at least as much as possible) : parse the app scripts to auto-generate the manifest.toml, and remove now-useless lines from the app scripts"
)
parser.add_argument("app_path", help="Path to the app to convert")
parser.add_argument(
"app_path", help="Path to the app to convert"
)
args = parser.parse_args()
manifest = _convert_v1_manifest_to_v2(args.app_path)
with open(args.app_path + "/manifest.toml", "w") as manifest_file:
manifest_file.write(
"#:schema https://raw.githubusercontent.com/YunoHost/apps/master/schemas/manifest.v2.schema.json\n\n"
)
manifest_file.write("#:schema https://raw.githubusercontent.com/YunoHost/apps/master/schemas/manifest.v2.schema.json\n\n")
manifest_file.write(_dump_v2_manifest_as_toml(manifest))
cleanup_scripts_and_conf(args.app_path)

View File

@@ -17,22 +17,18 @@ def convert_v1_manifest_to_v2_for_catalog(manifest):
manifest["upstream"]["website"] = manifest["url"]
manifest["integration"] = {
"yunohost": manifest.get("requirements", {})
.get("yunohost", "")
.replace(">", "")
.replace("=", "")
.replace(" ", ""),
"yunohost": manifest.get("requirements", {}).get("yunohost", "").replace(">", "").replace("=", "").replace(" ", ""),
"architectures": "all",
"multi_instance": manifest.get("multi_instance", False),
"ldap": "?",
"sso": "?",
"disk": "50M",
"ram": {"build": "50M", "runtime": "10M"},
"ram": {"build": "50M", "runtime": "10M"}
}
maintainers = manifest.get("maintainer", {})
if isinstance(maintainers, list):
maintainers = [m["name"] for m in maintainers]
maintainers = [m['name'] for m in maintainers]
else:
maintainers = [maintainers["name"]] if maintainers.get("name") else []
@@ -43,39 +39,21 @@ def convert_v1_manifest_to_v2_for_catalog(manifest):
manifest["install"] = {}
for question in install_questions:
name = question.pop("name")
if "ask" in question and name in [
"domain",
"path",
"admin",
"is_public",
"password",
]:
if "ask" in question and name in ["domain", "path", "admin", "is_public", "password"]:
question.pop("ask")
if question.get("example") and question.get("type") in [
"domain",
"path",
"user",
"boolean",
"password",
]:
if question.get("example") and question.get("type") in ["domain", "path", "user", "boolean", "password"]:
question.pop("example")
manifest["install"][name] = question
manifest["resources"] = {"system_user": {}, "install_dir": {"alias": "final_path"}}
manifest["resources"] = {
"system_user": {},
"install_dir": {
"alias": "final_path"
}
}
keys_to_keep = [
"packaging_format",
"id",
"name",
"description",
"version",
"maintainers",
"upstream",
"integration",
"install",
"resources",
]
keys_to_keep = ["packaging_format", "id", "name", "description", "version", "maintainers", "upstream", "integration", "install", "resources"]
keys_to_del = [key for key in manifest.keys() if key not in keys_to_keep]
for key in keys_to_del:

View File

@@ -1,23 +1,23 @@
# Auto-README generation
## Initial install
### Initial install
```bash
```
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt
```
## Use on a single app
### Use on a single app
```bash
```
source venv/bin/activate
./make_readme.py /path/to/app
```
Then the README.md in the app folder will be updated
## Launch webhook service for auto update
### Launch webhook service for auto update
Configure the webhook on github

View File

@@ -4,26 +4,25 @@ import argparse
import json
import os
from pathlib import Path
from copy import deepcopy
from typing import Dict, Optional, List, Tuple
import toml
from jinja2 import Environment, FileSystemLoader
def value_for_lang(values: Dict, lang: str):
if not isinstance(values, dict):
return values
if lang in values:
return values[lang]
elif "en" in values:
return values["en"]
else:
return list(values.values())[0]
def value_for_lang(values, lang):
if not isinstance(values, dict):
return values
if lang in values:
return values[lang]
elif "en" in values:
return values["en"]
else:
return list(values.values())[0]
def generate_READMEs(app_path: str):
app_path = Path(app_path)
def generate_READMEs(app_path: Path):
if not app_path.exists():
raise Exception("App path provided doesn't exists ?!")
@@ -34,14 +33,10 @@ def generate_READMEs(app_path: Path):
upstream = manifest.get("upstream", {})
catalog = toml.load(
open(Path(os.path.abspath(__file__)).parent.parent.parent / "apps.toml")
)
from_catalog = catalog.get(manifest["id"], {})
catalog = toml.load(open(Path(os.path.abspath(__file__)).parent.parent.parent / "apps.toml"))
from_catalog = catalog.get(manifest['id'], {})
antifeatures_list = toml.load(
open(Path(os.path.abspath(__file__)).parent.parent.parent / "antifeatures.toml")
)
antifeatures_list = toml.load(open(Path(os.path.abspath(__file__)).parent.parent.parent / "antifeatures.toml"))
if not upstream and not (app_path / "doc" / "DISCLAIMER.md").exists():
print(
@@ -51,37 +46,18 @@ def generate_READMEs(app_path: Path):
env = Environment(loader=FileSystemLoader(Path(__file__).parent / "templates"))
# parse available README template and generate a list in the form of:
# > [("en", ""), ("fr", "_fr"), ...]
available_langs: List[Tuple[str, str]] = [("en", "")]
for README_template in (Path(__file__).parent / "templates").iterdir():
# we only want README_{lang}.md.j2 files
if README_template.name == "README.md.j2":
continue
for lang, lang_suffix in [("en", ""), ("fr", "_fr")]:
if not README_template.name.endswith(
".j2"
) or not README_template.name.startswith("README_"):
continue
language_code = README_template.name.split("_")[1].split(".")[0]
available_langs.append((language_code, "_" + language_code))
for lang, lang_suffix in available_langs:
template = env.get_template(f"README{lang_suffix}.md.j2")
if (app_path / "doc" / f"DESCRIPTION{lang_suffix}.md").exists():
description = (
app_path / "doc" / f"DESCRIPTION{lang_suffix}.md"
).read_text()
description = (app_path / "doc" / f"DESCRIPTION{lang_suffix}.md").read_text()
# Fallback to english if maintainer too lazy to translate the description
elif (app_path / "doc" / "DESCRIPTION.md").exists():
description = (app_path / "doc" / "DESCRIPTION.md").read_text()
else:
description = None
screenshots: List[str]
if (app_path / "doc" / "screenshots").exists():
screenshots = os.listdir(os.path.join(app_path, "doc", "screenshots"))
if ".gitkeep" in screenshots:
@@ -89,7 +65,6 @@ def generate_READMEs(app_path: Path):
else:
screenshots = []
disclaimer: Optional[str]
if (app_path / "doc" / f"DISCLAIMER{lang_suffix}.md").exists():
disclaimer = (app_path / "doc" / f"DISCLAIMER{lang_suffix}.md").read_text()
# Fallback to english if maintainer too lazy to translate the disclaimer idk
@@ -99,22 +74,15 @@ def generate_READMEs(app_path: Path):
disclaimer = None
# TODO: Add url to the documentation... and actually create that documentation :D
antifeatures = {
a: deepcopy(antifeatures_list[a])
for a in from_catalog.get("antifeatures", [])
}
antifeatures = { a: antifeatures_list[a] for a in from_catalog.get('antifeatures', [])}
for k, v in antifeatures.items():
antifeatures[k]["title"] = value_for_lang(v["title"], lang)
antifeatures[k]['title'] = value_for_lang(v['title'], lang)
if manifest.get("antifeatures", {}).get(k, None):
antifeatures[k]["description"] = value_for_lang(
manifest.get("antifeatures", {}).get(k, None), lang
)
antifeatures[k]['description'] = value_for_lang(manifest.get("antifeatures", {}).get(k, None), lang)
else:
antifeatures[k]["description"] = value_for_lang(
antifeatures[k]["description"], lang
)
antifeatures[k]['description'] = value_for_lang(antifeatures[k]['description'], lang)
out: str = template.render(
out = template.render(
lang=lang,
upstream=upstream,
description=description,
@@ -135,4 +103,4 @@ if __name__ == "__main__":
)
args = parser.parse_args()
generate_READMEs(Path(args.app_path))
generate_READMEs(args.app_path)

View File

@@ -31,11 +31,12 @@ If you don't have YunoHost, please consult [the guide](https://yunohost.org/#/in
## Overview
{% if description %}{{description}}{% else %}{{manifest.description[lang]}}{% endif %}
**Shipped version:** {% if upstream.version %}{{upstream.version}}{% else %}{{manifest.version}}
{% endif -%}
{% if upstream.demo %}
**Demo:** <{{upstream.demo}}>
**Demo:** {{upstream.demo}}
{% endif -%}
{% if screenshots %}
@@ -57,21 +58,22 @@ If you don't have YunoHost, please consult [the guide](https://yunohost.org/#/in
{% for antifeature in antifeatures.values() -%}
- **{{ antifeature.title }}**: {{ antifeature.description }}
{% endfor %}
{% endfor -%}
{% endif -%}
## Documentation and resources
{% if upstream.website -%}- Official app website: <{{ upstream.website }}>
{% if upstream.website -%}* Official app website: <{{ upstream.website }}>
{% endif -%}
{% if upstream.userdoc -%}- Official user documentation: <{{ upstream.userdoc }}>
{% if upstream.userdoc -%}* Official user documentation: <{{ upstream.userdoc }}>
{% endif -%}
{% if upstream.admindoc -%}- Official admin documentation: <{{ upstream.admindoc }}>
{% if upstream.admindoc -%}* Official admin documentation: <{{ upstream.admindoc }}>
{% endif -%}
{% if upstream.code -%}- Upstream app code repository: <{{ upstream.code }}>
{% if upstream.code -%}* Upstream app code repository: <{{ upstream.code }}>
{% endif -%}
- YunoHost Store: <https://apps.yunohost.org/app/{{manifest.id}}>
- Report a bug: <https://github.com/YunoHost-Apps/{{manifest.id}}_ynh/issues>
* YunoHost Store: <https://apps.yunohost.org/app/{{manifest.id}}>
* Report a bug: <https://github.com/YunoHost-Apps/{{manifest.id}}_ynh/issues>
## Developer info
@@ -86,4 +88,3 @@ sudo yunohost app upgrade {{manifest.id}} -u https://github.com/YunoHost-Apps/{{
```
**More info regarding app packaging:** <https://yunohost.org/packaging_apps>

View File

@@ -17,11 +17,12 @@ Si vous navez pas YunoHost, regardez [ici](https://yunohost.org/#/install) po
## Vue densemble
{% if description %}{{description}}{% else %}{{manifest.description[lang]}}{% endif %}
**Version incluse :** {% if upstream.version %}{{upstream.version}}{% else %}{{manifest.version}}
{% endif -%}
{% if upstream.demo %}
**Démo :** <{{upstream.demo}}>
**Démo :** {{upstream.demo}}
{% endif -%}
{% if screenshots %}
@@ -39,25 +40,27 @@ Si vous navez pas YunoHost, regardez [ici](https://yunohost.org/#/install) po
{% endif -%}
{% if antifeatures -%}
## :red_circle: Anti-fonctionnalités
## :red_circle: Fonctions indésirables
{% for antifeature in antifeatures.values() -%}
- **{{ antifeature.title }}** : {{ antifeature.description }}
{% endfor %}
- **{{ antifeature.title }}**: {{ antifeature.description }}
{% endfor -%}
{% endif -%}
## Documentations et ressources
{% if upstream.website -%}- Site officiel de lapp : <{{ upstream.website }}>
{% if upstream.website -%}* Site officiel de lapp : <{{ upstream.website }}>
{% endif -%}
{% if upstream.userdoc -%}- Documentation officielle utilisateur : <{{ upstream.userdoc }}>
{% if upstream.userdoc -%}* Documentation officielle utilisateur : <{{ upstream.userdoc }}>
{% endif -%}
{% if upstream.admindoc -%}- Documentation officielle de ladmin : <{{ upstream.admindoc }}>
{% if upstream.admindoc -%}* Documentation officielle de ladmin : <{{ upstream.admindoc }}>
{% endif -%}
{% if upstream.code -%}- Dépôt de code officiel de lapp : <{{ upstream.code }}>
{% if upstream.code -%}* Dépôt de code officiel de lapp : <{{ upstream.code }}>
{% endif -%}
- YunoHost Store : <https://apps.yunohost.org/app/{{manifest.id}}>
- Signaler un bug : <https://github.com/YunoHost-Apps/{{manifest.id}}_ynh/issues>
* YunoHost Store: <https://apps.yunohost.org/app/{{manifest.id}}>
* Signaler un bug : <https://github.com/YunoHost-Apps/{{manifest.id}}_ynh/issues>
## Informations pour les développeurs
@@ -72,4 +75,3 @@ sudo yunohost app upgrade {{manifest.id}} -u https://github.com/YunoHost-Apps/{{
```
**Plus dinfos sur le packaging dapplications :** <https://yunohost.org/packaging_apps>

View File

@@ -35,21 +35,14 @@ async def git(cmd, in_folder=None):
cmd = ["git"] + cmd
cmd = " ".join(map(shlex.quote, cmd))
print(cmd)
command = await asyncio.create_subprocess_shell(
cmd,
env=my_env,
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.STDOUT,
)
command = await asyncio.create_subprocess_shell(cmd, env=my_env, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.STDOUT)
data = await command.stdout.read()
return data.decode().strip()
@app.route("/github", methods=["GET"])
def main_route(request):
return text(
"You aren't supposed to go on this page using a browser, it's for webhooks push instead."
)
return text("You aren't supposed to go on this page using a browser, it's for webhooks push instead.")
@app.route("/github", methods=["POST"])
@@ -65,9 +58,7 @@ async def on_push(request):
return response.json({"error": "Signing algorightm is not sha1 ?!"}, 501)
# HMAC requires the key to be bytes, but data is string
mac = hmac.new(
github_webhook_secret.encode(), msg=request.body, digestmod=hashlib.sha1
)
mac = hmac.new(github_webhook_secret.encode(), msg=request.body, digestmod=hashlib.sha1)
if not hmac.compare_digest(str(mac.hexdigest()), str(signature)):
return response.json({"error": "Bad signature ?!"}, 403)
@@ -80,42 +71,19 @@ async def on_push(request):
print(f"{repository} -> branch '{branch}'")
with tempfile.TemporaryDirectory() as folder:
await git(
[
"clone",
f"https://{login}:{token}@github.com/{repository}",
"--single-branch",
"--branch",
branch,
folder,
]
)
await git(["clone", f"https://{login}:{token}@github.com/{repository}", "--single-branch", "--branch", branch, folder])
generate_READMEs(folder)
await git(["add", "README*.md"], in_folder=folder)
diff_not_empty = await asyncio.create_subprocess_shell(
" ".join(["git", "diff", "HEAD", "--compact-summary"]),
cwd=folder,
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.STDOUT,
)
diff_not_empty = await asyncio.create_subprocess_shell(" ".join(["git", "diff", "HEAD", "--compact-summary"]), cwd=folder, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.STDOUT)
diff_not_empty = await diff_not_empty.stdout.read()
diff_not_empty = diff_not_empty.decode().strip()
if not diff_not_empty:
print("nothing to do")
return text("nothing to do")
await git(
[
"commit",
"-a",
"-m",
"Auto-update README",
"--author='yunohost-bot <yunohost@yunohost.org>'",
],
in_folder=folder,
)
await git(["commit", "-a", "-m", "Auto-update README", "--author='yunohost-bot <yunohost@yunohost.org>'"], in_folder=folder)
await git(["push", "origin", branch, "--quiet"], in_folder=folder)
return text("ok")

View File

@@ -107,8 +107,7 @@ def list_changes(catalog, ci_results) -> dict[str, list[tuple[str, int, int]]]:
def pretty_changes(changes: dict[str, list[tuple[str, int, int]]]) -> str:
pr_body_template = textwrap.dedent(
"""
pr_body_template = textwrap.dedent("""
{%- if changes["major_regressions"] %}
### Major regressions 😭
{% for app in changes["major_regressions"] %}
@@ -139,8 +138,7 @@ def pretty_changes(changes: dict[str, list[tuple[str, int, int]]]) -> str:
- [ ] [{{app}} (See latest job if it exists)](https://ci-apps.yunohost.org/ci/apps/{{app}}/latestjob)
{%- endfor %}
{% endif %}
"""
)
""")
return jinja2.Environment().from_string(pr_body_template).render(changes=changes)
@@ -150,34 +148,24 @@ def make_pull_request(pr_body: str) -> None:
"title": "Update app levels according to CI results",
"body": pr_body,
"head": "update_app_levels",
"base": "master",
"base": "master"
}
with requests.Session() as s:
s.headers.update({"Authorization": f"token {github_token()}"})
response = s.post(
f"https://api.github.com/repos/{APPS_REPO}/pulls", json=pr_data
)
response = s.post(f"https://api.github.com/repos/{APPS_REPO}/pulls", json=pr_data)
if response.status_code == 422:
response = s.get(
f"https://api.github.com/repos/{APPS_REPO}/pulls",
data={"head": "update_app_levels"},
)
response = s.get(f"https://api.github.com/repos/{APPS_REPO}/pulls", data={"head": "update_app_levels"})
response.raise_for_status()
pr_number = response.json()[0]["number"]
# head can't be updated
del pr_data["head"]
response = s.patch(
f"https://api.github.com/repos/{APPS_REPO}/pulls/{pr_number}",
json=pr_data,
)
response = s.patch(f"https://api.github.com/repos/{APPS_REPO}/pulls/{pr_number}", json=pr_data)
response.raise_for_status()
existing_url = response.json()["html_url"]
logging.warning(
f"An existing Pull Request has been updated at {existing_url} !"
)
logging.warning(f"An existing Pull Request has been updated at {existing_url} !")
else:
response.raise_for_status()

View File

@@ -88,6 +88,12 @@ description = "Organize Your GitHub Stars With Ease"
upstream = "https://github.com/astralapp/astral"
website = "https://astralapp.com/"
[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/"
[autobd]
name = "AutoBD"
description = "Comics creation tools"
@@ -197,12 +203,6 @@ description = "Low code platform for creating internal apps, workflows, and admi
upstream = "https://github.com/Budibase/budibase"
website = "https://budibase.com/"
[bukuserver]
name = "bukuserver"
description = "Bookmark manager and personal textual mini-web with browsable front-end on a web server"
upstream = "https://github.com/jarun/buku"
website = ""
[cactus-comments]
name = "Cactus Comments"
description = "Federated comment system, to embed into your webpages, based on the Matrix protocol."
@@ -874,6 +874,12 @@ description = "Read and manage your e-books on any device."
upstream = "https://github.com/Librum-Reader/Librum"
website = "https://librumreader.com/"
[lichen]
name = "Lichen"
description = "Gemtext to HTML translator"
upstream = "https://git.sensorstation.co/lichen.git"
website = ""
[lila]
name = "Lila"
description = "Online chess game server"
@@ -1570,12 +1576,6 @@ description = "Crypto trading bot, automated bitcoin / cryptocurrency trading so
upstream = "https://github.com/Superalgos/Superalgos"
website = "https://superalgos.org/"
[suwayomi]
name = "Suwayomi"
description = "A manga reader server that runs extensions built for Tachiyomi"
upstream = "https://github.com/Suwayomi/Suwayomi-Server"
website = ""
[sympa]
name = "Sympa"
description = "Mailing List manager"