diff --git a/tools/yunopackage/YunohostAppGenerator.py b/tools/yunopackage/YunohostAppGenerator.py
index cdd8536f..09dd8be4 100644
--- a/tools/yunopackage/YunohostAppGenerator.py
+++ b/tools/yunopackage/YunohostAppGenerator.py
@@ -1,13 +1,39 @@
#### Imports
import jinja2 as j2
-from flask import Flask, render_template, render_template_string, request, redirect, flash, send_file
+from flask import (
+ Flask,
+ render_template,
+ render_template_string,
+ request,
+ redirect,
+ flash,
+ send_file,
+)
from markupsafe import Markup # No longer imported from Flask
+
# Form libraries
from flask_wtf import FlaskForm
-from wtforms import StringField, RadioField, SelectField, SubmitField, TextAreaField, BooleanField, SelectMultipleField
-from wtforms.validators import DataRequired, InputRequired, Optional, Regexp, URL, Length
+from wtforms import (
+ StringField,
+ RadioField,
+ SelectField,
+ SubmitField,
+ TextAreaField,
+ BooleanField,
+ SelectMultipleField,
+)
+from wtforms.validators import (
+ DataRequired,
+ InputRequired,
+ Optional,
+ Regexp,
+ URL,
+ Length,
+)
+
# Markdown to HTML - for debugging purposes
from misaka import Markdown, HtmlRenderer
+
# Managing zipfiles
import zipfile
from flask_cors import CORS
@@ -15,16 +41,16 @@ from urllib import parse
from secrets import token_urlsafe
#### GLOBAL VARIABLES
-YOLOGEN_VERSION = '0.7.5'
-GENERATOR_DICT = {'GENERATOR_VERSION': YOLOGEN_VERSION}
+YOLOGEN_VERSION = "0.7.5"
+GENERATOR_DICT = {"GENERATOR_VERSION": YOLOGEN_VERSION}
#### Create FLASK and Jinja Environments
-url_prefix = ''
+url_prefix = ""
# url_prefix = '/yunohost-app-generator'
# app = Flask(__name__)
app = Flask(__name__) # Blueprint('main', __name__, url_prefix=url_prefix)
-app.config['SECRET_KEY'] = token_urlsafe(16) # Necessary for the form CORS
+app.config["SECRET_KEY"] = token_urlsafe(16) # Necessary for the form CORS
cors = CORS(app)
environment = j2.Environment(loader=j2.FileSystemLoader("templates/"))
@@ -32,8 +58,9 @@ environment = j2.Environment(loader=j2.FileSystemLoader("templates/"))
#### Custom functions
+
# Define custom filter
-@app.template_filter('render_markdown')
+@app.template_filter("render_markdown")
def render_markdown(text):
renderer = HtmlRenderer()
markdown = Markdown(renderer)
@@ -41,12 +68,12 @@ def render_markdown(text):
# Add custom filter
-j2.filters.FILTERS['render_markdown'] = render_markdown
+j2.filters.FILTERS["render_markdown"] = render_markdown
# Converting markdown to html
def markdown_file_to_html_string(file):
- with open(file, 'r') as file:
+ with open(file, "r") as file:
markdown_content = file.read()
# Convert content from Markdown to HTML
html_content = render_markdown(markdown_content)
@@ -56,65 +83,113 @@ def markdown_file_to_html_string(file):
### Forms
+
## PHP forms
class Form_PHP_Config(FlaskForm):
- php_config_file = SelectField('Type de fichier PHP :', choices=[
- ('php-fpm.conf', Markup('Fichier de configuration PHP complet (php-fpm.conf)')),
- ('php_extra-fpm.conf',
- Markup('Fichier de configuration PHP particulier (extra_php-fpm.conf)'))],
- default='php_extra-fpm.conf', validators=[DataRequired()])
+ php_config_file = SelectField(
+ "Type de fichier PHP :",
+ choices=[
+ (
+ "php-fpm.conf",
+ Markup(
+ 'Fichier de configuration PHP complet (php-fpm.conf)'
+ ),
+ ),
+ (
+ "php_extra-fpm.conf",
+ Markup(
+ 'Fichier de configuration PHP particulier (extra_php-fpm.conf)'
+ ),
+ ),
+ ],
+ default="php_extra-fpm.conf",
+ validators=[DataRequired()],
+ )
## TODO : figure out how to include these comments/title values
# 'title': 'Remplace la configuration générée par défaut par un fichier de configuration complet. À éviter si possible.
# 'title': "Choisir un fichier permettant un paramétrage d'options complémentaires. C'est généralement recommandé."
- php_config_file_content = TextAreaField("Saisissez le contenu du fichier de configuration PHP :",
- validators=[Optional()],
- render_kw={"class": "form-control",
- "style": "width: 50%;height:11em;min-height: 5.5em; max-height: 55em;flex-grow: 1;box-sizing: border-box;",
- "title": "TODO",
- "placeholder": "; Additional php.ini defines, specific to this pool of workers. \n\nphp_admin_value[upload_max_filesize] = 100M \nphp_admin_value[post_max_size] = 100M"})
+ php_config_file_content = TextAreaField(
+ "Saisissez le contenu du fichier de configuration PHP :",
+ validators=[Optional()],
+ render_kw={
+ "class": "form-control",
+ "style": "width: 50%;height:11em;min-height: 5.5em; max-height: 55em;flex-grow: 1;box-sizing: border-box;",
+ "title": "TODO",
+ "placeholder": "; Additional php.ini defines, specific to this pool of workers. \n\nphp_admin_value[upload_max_filesize] = 100M \nphp_admin_value[post_max_size] = 100M",
+ },
+ )
class Form_PHP(Form_PHP_Config):
- use_php = BooleanField('Nécessite PHP', default=False)
+ use_php = BooleanField("Nécessite PHP", default=False)
## NodeJS forms
class Form_NodeJS(FlaskForm):
- use_nodejs = BooleanField('Nécessite NodeJS', default=False)
- use_nodejs_version = StringField("Version de NodeJS :",
- render_kw={"placeholder": "20", "class": "form-control",
- "title": "Saisissez la version de NodeJS à installer. Cela peut-être une version majeure (ex: 20) ou plus précise (ex: 20.1)."}) # TODO : this should be validated using a regex, should be only numbers and any (≥0) number of dots in between
- use_nodejs_needs_yarn = BooleanField('Nécessite Yarn', default=False, render_kw={
- "title": "Faut-il installer automatiquement Yarn ? Cela configurera les dépôts spécifiques à Yarn."})
+ use_nodejs = BooleanField("Nécessite NodeJS", default=False)
+ use_nodejs_version = StringField(
+ "Version de NodeJS :",
+ render_kw={
+ "placeholder": "20",
+ "class": "form-control",
+ "title": "Saisissez la version de NodeJS à installer. Cela peut-être une version majeure (ex: 20) ou plus précise (ex: 20.1).",
+ },
+ ) # TODO : this should be validated using a regex, should be only numbers and any (≥0) number of dots in between
+ use_nodejs_needs_yarn = BooleanField(
+ "Nécessite Yarn",
+ default=False,
+ render_kw={
+ "title": "Faut-il installer automatiquement Yarn ? Cela configurera les dépôts spécifiques à Yarn."
+ },
+ )
## Python forms
+
class Form_Python(FlaskForm):
- use_python = BooleanField('Nécessite Python',
- default=False) ## TODO -> python3, python3-pip, python3-ven dependencies by default
- python_dependencies_type = SelectField('Configuration des dépendances Python :', choices=[
- ('requirements.txt', Markup('Fichier requirements.txt')),
- ('manual_list', 'Liste manuelle')],
- default='requirements.txt', validators=[DataRequired(), Optional()])
- python_requirements = TextAreaField("La liste de dépendances inclue dans le fichier requirements.txt :",
- render_kw={"class": "form-control",
- "style": "width: 50%;height:5.5em;min-height: 5.5em; max-height: 55em;flex-grow: 1;box-sizing: border-box;",
- "title": "Lister les dépendances à installer, une par ligne, avec un numéro de version derrière.\nEx: 'dépendance==1.0'.",
- "placeholder": "tensorflow==2.3.1 \nuvicorn==0.12.2 \nfastapi==0.63.0"})
- python_dependencies_list = StringField("Liste de dépendances python :",
- render_kw={"placeholder": "tensorflow uvicorn fastapi",
- "class": "form-control",
- "title": "Lister les dépendances à installer, séparées d'un espace."})
+ use_python = BooleanField(
+ "Nécessite Python", default=False
+ ) ## TODO -> python3, python3-pip, python3-ven dependencies by default
+ python_dependencies_type = SelectField(
+ "Configuration des dépendances Python :",
+ choices=[
+ ("requirements.txt", Markup("Fichier requirements.txt")),
+ ("manual_list", "Liste manuelle"),
+ ],
+ default="requirements.txt",
+ validators=[DataRequired(), Optional()],
+ )
+ python_requirements = TextAreaField(
+ "La liste de dépendances inclue dans le fichier requirements.txt :",
+ render_kw={
+ "class": "form-control",
+ "style": "width: 50%;height:5.5em;min-height: 5.5em; max-height: 55em;flex-grow: 1;box-sizing: border-box;",
+ "title": "Lister les dépendances à installer, une par ligne, avec un numéro de version derrière.\nEx: 'dépendance==1.0'.",
+ "placeholder": "tensorflow==2.3.1 \nuvicorn==0.12.2 \nfastapi==0.63.0",
+ },
+ )
+ python_dependencies_list = StringField(
+ "Liste de dépendances python :",
+ render_kw={
+ "placeholder": "tensorflow uvicorn fastapi",
+ "class": "form-control",
+ "title": "Lister les dépendances à installer, séparées d'un espace.",
+ },
+ )
## Manifest form
# Dependencies form
class DependenciesForm(FlaskForm):
- auto_update = BooleanField("Activer le robot de mise à jour automatiques :", default=False,
- render_kw={
- "title": "Si le logiciel est disponible sur github et publie des releases ou des tags pour ses nouvelles versions, un robot proposera automatiquement des mises à jours."})
+ auto_update = BooleanField(
+ "Activer le robot de mise à jour automatiques :",
+ default=False,
+ render_kw={
+ "title": "Si le logiciel est disponible sur github et publie des releases ou des tags pour ses nouvelles versions, un robot proposera automatiquement des mises à jours."
+ },
+ )
## TODO
# These infos are used by https://github.com/YunoHost/apps/blob/master/tools/autoupdate_app_sources/autoupdate_app_sources.py
@@ -124,230 +199,429 @@ class DependenciesForm(FlaskForm):
# autoupdate.strategy = "latest_github_tag"
- dependencies = StringField("Dépendances de l'application (liste des paquets apt) à installer :",
- render_kw={"placeholder": "foo foo2.1-ext somerandomdep", "class": "form-control",
- "title": "Lister les paquets dont dépend l'application, séparés par un espace."})
+ dependencies = StringField(
+ "Dépendances de l'application (liste des paquets apt) à installer :",
+ render_kw={
+ "placeholder": "foo foo2.1-ext somerandomdep",
+ "class": "form-control",
+ "title": "Lister les paquets dont dépend l'application, séparés par un espace.",
+ },
+ )
- use_db = SelectField("Configurer une base de données :", choices=[
- ('false', "Non"),
- ('mysql', "MySQL/MariaDB"),
- ('postgresql', "PostgreSQL")],
- default='false',
- render_kw={"title": "L'application nécessite-t-elle une base de données ?"})
+ use_db = SelectField(
+ "Configurer une base de données :",
+ choices=[
+ ("false", "Non"),
+ ("mysql", "MySQL/MariaDB"),
+ ("postgresql", "PostgreSQL"),
+ ],
+ default="false",
+ render_kw={"title": "L'application nécessite-t-elle une base de données ?"},
+ )
# manifest
class manifestForm(DependenciesForm):
- version = StringField('Version', validators=[Regexp('\d{1,4}.\d{1,4}(.\d{1,4})?(.\d{1,4})?~ynh\d+')],
- render_kw={"class": "form-control",
- "placeholder": "1.0~ynh1"})
- description_en = TextAreaField("Description en quelques lignes de l'application, en anglais :",
- validators=[DataRequired()],
- render_kw={"class": "form-control", "style": "resize: none;",
- "title": "Explain in *a few (10~15) words* the purpose of the app \\"
- "or what it actually does (it is meant to give a rough idea to users browsing a catalog of 100+ apps)"})
- description_fr = TextAreaField("Description en quelques lignes de l'application :", validators=[DataRequired()],
- render_kw={"class": "form-control", "style": "resize: none;",
- "title": "Expliquez en *quelques* (10~15) mots l'utilité de l'app \\"
- "ou ce qu'elle fait (l'objectif est de donner une idée grossière pour des utilisateurs qui naviguent dans un catalogue de 100+ apps)"})
+ version = StringField(
+ "Version",
+ validators=[Regexp("\d{1,4}.\d{1,4}(.\d{1,4})?(.\d{1,4})?~ynh\d+")],
+ render_kw={"class": "form-control", "placeholder": "1.0~ynh1"},
+ )
+ description_en = TextAreaField(
+ "Description en quelques lignes de l'application, en anglais :",
+ validators=[DataRequired()],
+ render_kw={
+ "class": "form-control",
+ "style": "resize: none;",
+ "title": "Explain in *a few (10~15) words* the purpose of the app \\"
+ "or what it actually does (it is meant to give a rough idea to users browsing a catalog of 100+ apps)",
+ },
+ )
+ description_fr = TextAreaField(
+ "Description en quelques lignes de l'application :",
+ validators=[DataRequired()],
+ render_kw={
+ "class": "form-control",
+ "style": "resize: none;",
+ "title": "Expliquez en *quelques* (10~15) mots l'utilité de l'app \\"
+ "ou ce qu'elle fait (l'objectif est de donner une idée grossière pour des utilisateurs qui naviguent dans un catalogue de 100+ apps)",
+ },
+ )
# TODO : handle multiple names separated by commas (.split(',') ?
- maintainers = StringField('Mainteneurs et mainteneuses', render_kw={"class": "form-control",
- "placeholder": "Généralement vous mettez votre nom ici… Si vous êtes d'accord ;)"}) # TODO : Usually you put your name here… if you like ;)
- architectures = SelectMultipleField('Architectures supportées :', choices=[
- ('all', 'Toutes les architectures'),
- ('amd64', 'amd64'),
- ('arm64', 'arm64'),
- ('i386', 'i386'),
- ('todo', 'TODO : list more architectures')],
- default=['all'], validators=[DataRequired()])
- yunohost_required_version = StringField('Mainteneurs et mainteneuses', render_kw={"class": "form-control",
- "placeholder": "11.1.21",
- "title": "Version minimale de Yunohost pour que l'application fonctionne."})
+ maintainers = StringField(
+ "Mainteneurs et mainteneuses",
+ render_kw={
+ "class": "form-control",
+ "placeholder": "Généralement vous mettez votre nom ici… Si vous êtes d'accord ;)",
+ },
+ ) # TODO : Usually you put your name here… if you like ;)
+ architectures = SelectMultipleField(
+ "Architectures supportées :",
+ choices=[
+ ("all", "Toutes les architectures"),
+ ("amd64", "amd64"),
+ ("arm64", "arm64"),
+ ("i386", "i386"),
+ ("todo", "TODO : list more architectures"),
+ ],
+ default=["all"],
+ validators=[DataRequired()],
+ )
+ yunohost_required_version = StringField(
+ "Mainteneurs et mainteneuses",
+ render_kw={
+ "class": "form-control",
+ "placeholder": "11.1.21",
+ "title": "Version minimale de Yunohost pour que l'application fonctionne.",
+ },
+ )
- multi_instance = BooleanField("Application multi-instance", default=False,
- render_kw={"class": "",
- "title": "Peux-t-on installer simultannément plusieurs fois l'application sur un même serveur ?"})
+ multi_instance = BooleanField(
+ "Application multi-instance",
+ default=False,
+ render_kw={
+ "class": "",
+ "title": "Peux-t-on installer simultannément plusieurs fois l'application sur un même serveur ?",
+ },
+ )
- ldap = SelectField('Integrate with LDAP (user can login using Yunohost credentials :', choices=[
- ('false', 'False'),
- ('true', 'True'),
- ('not_relevant', 'Not relevant')], default='not_relevant', validators=[DataRequired()], render_kw={
- "title": """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."""})
- sso = SelectField('Integrate with Yunohost SingleSignOn (SSO) :', choices=[
- ('false', 'False'),
- ('true', 'True'),
- ('not_relevant', 'Not relevant')], default='not_relevant', validators=[DataRequired()], render_kw={
- "title": """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."""})
+ ldap = SelectField(
+ "Integrate with LDAP (user can login using Yunohost credentials :",
+ choices=[
+ ("false", "False"),
+ ("true", "True"),
+ ("not_relevant", "Not relevant"),
+ ],
+ default="not_relevant",
+ validators=[DataRequired()],
+ render_kw={
+ "title": """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."""
+ },
+ )
+ sso = SelectField(
+ "Integrate with Yunohost SingleSignOn (SSO) :",
+ choices=[
+ ("false", "False"),
+ ("true", "True"),
+ ("not_relevant", "Not relevant"),
+ ],
+ default="not_relevant",
+ validators=[DataRequired()],
+ render_kw={
+ "title": """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."""
+ },
+ )
- license = StringField('Licence', validators=[DataRequired()], render_kw={"class": "form-control",
- "placeholder": "GPL"})
+ license = StringField(
+ "Licence",
+ validators=[DataRequired()],
+ render_kw={"class": "form-control", "placeholder": "GPL"},
+ )
- website = StringField('Site web', validators=[URL(), Optional()], render_kw={"class": "form-control",
- "placeholder": "https://awesome-app-website.com"})
- demo = StringField('Site de démonstration', validators=[URL(), Optional()], render_kw={"class": "form-control",
- "placeholder": "https://awesome-app-website.com/demo"})
- admindoc = StringField("Documentation d'aministration", validators=[URL(), Optional()],
- render_kw={"class": "form-control",
- "placeholder": "https://awesome-app-website.com/doc/admin"})
- userdoc = StringField("Documentation d'utilisation", validators=[URL(), Optional()],
- render_kw={"class": "form-control",
- "placeholder": "https://awesome-app-website.com/doc/user"})
- code = StringField('Dépôt de code', validators=[URL(), Optional()], render_kw={"class": "form-control",
- "placeholder": "https://awesome-app-website.com/get-the-code"})
+ website = StringField(
+ "Site web",
+ validators=[URL(), Optional()],
+ render_kw={
+ "class": "form-control",
+ "placeholder": "https://awesome-app-website.com",
+ },
+ )
+ demo = StringField(
+ "Site de démonstration",
+ validators=[URL(), Optional()],
+ render_kw={
+ "class": "form-control",
+ "placeholder": "https://awesome-app-website.com/demo",
+ },
+ )
+ admindoc = StringField(
+ "Documentation d'aministration",
+ validators=[URL(), Optional()],
+ render_kw={
+ "class": "form-control",
+ "placeholder": "https://awesome-app-website.com/doc/admin",
+ },
+ )
+ userdoc = StringField(
+ "Documentation d'utilisation",
+ validators=[URL(), Optional()],
+ render_kw={
+ "class": "form-control",
+ "placeholder": "https://awesome-app-website.com/doc/user",
+ },
+ )
+ code = StringField(
+ "Dépôt de code",
+ validators=[URL(), Optional()],
+ render_kw={
+ "class": "form-control",
+ "placeholder": "https://awesome-app-website.com/get-the-code",
+ },
+ )
- data_dir = BooleanField("L'application nécessite un répertoire dédié pour ses données", default=False,
- render_kw={"title": "Faut-il créer un répertoire /home/yunohost.app/votreApplication ?"})
- data_subdirs = StringField('Si nécessaire, lister les sous-répertoires à configurer :',
- validators=[Optional()], render_kw={"class": "form-control",
- "placeholder": "data, uploads, themes"})
- use_whole_domain = BooleanField("L'application nécessite d'utiliser tout un domaine (installation à la racine) :",
- default=False,
- render_kw={
- "title": "Doit-on installer l'application à la racine du domaine ? Sinon, on pourra l'installer dans un sous-dossier, par exemple /mon_app."})
+ data_dir = BooleanField(
+ "L'application nécessite un répertoire dédié pour ses données",
+ default=False,
+ render_kw={
+ "title": "Faut-il créer un répertoire /home/yunohost.app/votreApplication ?"
+ },
+ )
+ data_subdirs = StringField(
+ "Si nécessaire, lister les sous-répertoires à configurer :",
+ validators=[Optional()],
+ render_kw={"class": "form-control", "placeholder": "data, uploads, themes"},
+ )
+ use_whole_domain = BooleanField(
+ "L'application nécessite d'utiliser tout un domaine (installation à la racine) :",
+ default=False,
+ render_kw={
+ "title": "Doit-on installer l'application à la racine du domaine ? Sinon, on pourra l'installer dans un sous-dossier, par exemple /mon_app."
+ },
+ )
supports_change_url = BooleanField(
- "L'application autorise le changement d'adresse (changement de domaine ou de chemin)", default=True,
- render_kw={"title": "Faut-il permettre le changement d'URL pour l'application ? (fichier change_url)"})
+ "L'application autorise le changement d'adresse (changement de domaine ou de chemin)",
+ default=True,
+ render_kw={
+ "title": "Faut-il permettre le changement d'URL pour l'application ? (fichier change_url)"
+ },
+ )
- needs_admin = BooleanField("L'application nécessite de configurer un compte d'administration :", default=False,
- render_kw={"class": "",
- "title": "Faut-il configurer un compte admin à l'installation ?"})
+ needs_admin = BooleanField(
+ "L'application nécessite de configurer un compte d'administration :",
+ default=False,
+ render_kw={
+ "class": "",
+ "title": "Faut-il configurer un compte admin à l'installation ?",
+ },
+ )
# admin_password_help_message = BooleanField("TODO :", default=False,
# render_kw={"class": "",
# "title": "TODO"})
- language = SelectMultipleField('Langues supportées :', choices=[
- ('en', 'English'),
- ('fr', 'Français'),
- ('en', 'Spanish'),
- ('it', 'Italian'),
- ('de', 'German'),
- ('zh', 'Chinese'),
- ('jp', 'Japanese'),
- ('da', 'Danish'),
- ('pt', 'Portugese'),
- ('nl', 'Dutch'),
- ('ru', 'Russian')],
- default=['en'], validators=[DataRequired()])
+ language = SelectMultipleField(
+ "Langues supportées :",
+ choices=[
+ ("en", "English"),
+ ("fr", "Français"),
+ ("en", "Spanish"),
+ ("it", "Italian"),
+ ("de", "German"),
+ ("zh", "Chinese"),
+ ("jp", "Japanese"),
+ ("da", "Danish"),
+ ("pt", "Portugese"),
+ ("nl", "Dutch"),
+ ("ru", "Russian"),
+ ],
+ default=["en"],
+ validators=[DataRequired()],
+ )
- default_language = SelectField('Langues par défaut :', choices=[
- ('en', 'English'),
- ('fr', 'Français'),
- ('en', 'Spanish'),
- ('it', 'Italian'),
- ('zh', 'Chinese'),
- ('jp', 'Japanese'),
- ('da', 'Danish'),
- ('pt', 'Portugese'),
- ('nl', 'Dutch'),
- ('ru', 'Russian')],
- default=['en'])
+ default_language = SelectField(
+ "Langues par défaut :",
+ choices=[
+ ("en", "English"),
+ ("fr", "Français"),
+ ("en", "Spanish"),
+ ("it", "Italian"),
+ ("zh", "Chinese"),
+ ("jp", "Japanese"),
+ ("da", "Danish"),
+ ("pt", "Portugese"),
+ ("nl", "Dutch"),
+ ("ru", "Russian"),
+ ],
+ default=["en"],
+ )
- visibility = RadioField("Visibilité de l'application :", choices=[
- ('admin', "Administrateur/administratrice uniquement"),
- ('all_users', "Personnes connectées"),
- ('visitors', "Publique")],
- default='all_users', validators=[DataRequired()])
+ visibility = RadioField(
+ "Visibilité de l'application :",
+ choices=[
+ ("admin", "Administrateur/administratrice uniquement"),
+ ("all_users", "Personnes connectées"),
+ ("visitors", "Publique"),
+ ],
+ default="all_users",
+ validators=[DataRequired()],
+ )
- source_url = StringField("Code source ou exécutable de l'application", validators=[DataRequired(), URL()],
- render_kw={"class": "form-control",
- "placeholder": "https://github.com/foo/bar/archive/refs/tags/v1.2.3.tar.gz"}) # Application source code URL
- sha256sum = StringField('Empreinte du code source (format sha256sum)',
- validators=[DataRequired(), Length(min=64, max=64)],
- render_kw={"class": "form-control",
- "placeholder": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
- "title": "Sha256sum of the archive. Should be 64 characters-long."}) # Source code hash (sha256sum format)
+ source_url = StringField(
+ "Code source ou exécutable de l'application",
+ validators=[DataRequired(), URL()],
+ render_kw={
+ "class": "form-control",
+ "placeholder": "https://github.com/foo/bar/archive/refs/tags/v1.2.3.tar.gz",
+ },
+ ) # Application source code URL
+ sha256sum = StringField(
+ "Empreinte du code source (format sha256sum)",
+ validators=[DataRequired(), Length(min=64, max=64)],
+ render_kw={
+ "class": "form-control",
+ "placeholder": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
+ "title": "Sha256sum of the archive. Should be 64 characters-long.",
+ },
+ ) # Source code hash (sha256sum format)
## Main form
-class appGeneratorForm(manifestForm, DependenciesForm, Form_PHP, Form_NodeJS, Form_Python):
- app_name = StringField("Nom de l'application :", validators=[DataRequired()],
- render_kw={"placeholder": "My Great App", "class": "form-control",
- "title": "Définir le nom de l'application, affiché dans l'interface"})
+
+class appGeneratorForm(
+ manifestForm, DependenciesForm, Form_PHP, Form_NodeJS, Form_Python
+):
+ app_name = StringField(
+ "Nom de l'application :",
+ validators=[DataRequired()],
+ render_kw={
+ "placeholder": "My Great App",
+ "class": "form-control",
+ "title": "Définir le nom de l'application, affiché dans l'interface",
+ },
+ )
app_id = StringField(
- Markup(
- """Identifiant (id) de l'application (en minuscule et sans espaces) :"""),
- validators=[DataRequired(), Regexp("[a-z_1-9]+.*(?(en minuscule et sans espaces) :"""
+ ),
+ validators=[DataRequired(), Regexp("[a-z_1-9]+.*(?