From 761fa06ff37f89a2305c0f325dee9a7b2e48a3a9 Mon Sep 17 00:00:00 2001 From: capJavert Date: Tue, 14 Mar 2017 00:25:57 +0100 Subject: [PATCH 01/23] moved to latest versions of requirements --- requirements.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/requirements.txt b/requirements.txt index eab2e2d..0ce32c8 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -Flask==0.10 -Flask-WTF==0.8.3 -Jinja2==2.7 -Flask-DebugToolbar==0.8.0 +Flask +Flask-WTF +Jinja2 +Flask-DebugToolbar From 74e665ea4b7287ed1330ca4cb14d4a99a2f0973f Mon Sep 17 00:00:00 2001 From: capJavert Date: Tue, 14 Mar 2017 00:26:24 +0100 Subject: [PATCH 02/23] changed Flask-wtf imports and basic implementation --- project/controllers/printer.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/project/controllers/printer.py b/project/controllers/printer.py index ac3c472..0da47df 100644 --- a/project/controllers/printer.py +++ b/project/controllers/printer.py @@ -1,11 +1,13 @@ # -*- coding: utf-8 -*- from project import app from flask import render_template, request -from flask.ext.wtf import Form, TextField, validators +from flask_wtf import FlaskForm +from wtforms import StringField +from wtforms.validators import DataRequired -class CreateForm(Form): - text = TextField(u'Text:', [validators.Length(min=1, max=20)]) +class CreateForm(FlaskForm): + text = StringField('name', validators=[DataRequired()]) @app.route('/') From 9b3fab7cf79c93f7d3c932630e8249484179718e Mon Sep 17 00:00:00 2001 From: capJavert Date: Tue, 14 Mar 2017 00:26:46 +0100 Subject: [PATCH 03/23] added standard dev .gitignore --- .gitignore | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6156991 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +.idea +*.pyc +*/__pycache__ +*.DS_STORE From d3c26185fcbd0e473e3ccddedd1d284f32965918 Mon Sep 17 00:00:00 2001 From: capJavert Date: Tue, 14 Mar 2017 00:27:03 +0100 Subject: [PATCH 04/23] updated README with upgrade instructions --- README.markdown | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.markdown b/README.markdown index 2a423a3..f5d40e0 100644 --- a/README.markdown +++ b/README.markdown @@ -4,10 +4,11 @@ It basically here to be my base skeleton for new python web applications Demo : http://flask-mvc-salimane.herokuapp.com/ Dependencies : - + git clone git://github.com/salimane/flask-mvc.git cd flask-mvc - pip install -r requirements.txt + if you have old version of project: pip uninstall -r requirements.txt + pip install -r requirements.txt To run: From b07683505991746b8d802712f77661e3b1d7795e Mon Sep 17 00:00:00 2001 From: Salimane Adjao Moustapha Date: Tue, 25 Apr 2023 21:24:09 +0200 Subject: [PATCH 05/23] update outdated packages Signed-off-by: Salimane Adjao Moustapha --- .dockerignore | 22 ++++++++++++ .editorconfig | 24 +++++++++++++ .github/workflows/main.yml | 29 ++++++++++++++++ .gitignore | 5 +++ .python-version | 1 + .tool-versions | 1 + Dockerfile | 42 +++++++++++++++++++++++ LICENSE | 21 ++++++++++++ Makefile | 57 ++++++++++++++++++++++++++++++ README.markdown | 19 ---------- README.md | 61 +++++++++++++++++++++++++++++++++ codecov.yml | 4 +++ docker-compose.yml | 10 ++++++ project/controllers/__init__.py | 2 +- requirements-test.txt | 7 ++++ requirements.txt | 36 ++++++++++++++++--- script/setup | 11 ++++++ script/test | 20 +++++++++++ setup.cfg | 5 +++ 19 files changed, 353 insertions(+), 24 deletions(-) create mode 100644 .dockerignore create mode 100644 .editorconfig create mode 100644 .github/workflows/main.yml create mode 100644 .python-version create mode 100644 .tool-versions create mode 100644 Dockerfile create mode 100644 LICENSE create mode 100644 Makefile delete mode 100644 README.markdown create mode 100644 README.md create mode 100644 codecov.yml create mode 100644 docker-compose.yml create mode 100644 requirements-test.txt create mode 100755 script/setup create mode 100755 script/test create mode 100644 setup.cfg diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..34becff --- /dev/null +++ b/.dockerignore @@ -0,0 +1,22 @@ +__pycache__ +.cache +.coverage +.coverage.* +.dockerignore +.drone.yml +.git +.gitignore +.Python +.tox +*,cover +*.log +*.pyc +*.pyd +*.pyo +coverage.xml +db.sqlite3 +docker-compose.yml +Dockerfile +env +pip-delete-this-directory.txt +pip-log.txt diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..34c40d2 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,24 @@ +# EditorConfig is awesome: http://EditorConfig.org + +root = true + +[*] +end_of_line = lf +charset = utf-8 +#trim_trailing_whitespace = true +insert_final_newline = true + +[*.sh] +indent_style = tabs +indent_size = 4 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.js,*.json] +indent_style = space +indent_size = 2 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.md] +trim_trailing_whitespace = false diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..7a035ef --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,29 @@ +name: Flask-mvc + +on: + push: + branches: [ $default-branch ] + pull_request: + branches: [ $default-branch ] + +permissions: + contents: read + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Set up Python 3.10 + uses: actions/setup-python@v3 + with: + python-version: "3.10" + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install flake8 pytest + if [ -f requirements.txt ]; then pip install -r requirements.txt; fi + - name: Lint with flake8 + run: | + # stop the build if there are Python syntax errors or undefined names + flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics diff --git a/.gitignore b/.gitignore index 6156991..d210770 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,8 @@ *.pyc */__pycache__ *.DS_STORE +config/config.py +!config/config.py.example +.*-venv*/ +data/* +temp/ diff --git a/.python-version b/.python-version new file mode 100644 index 0000000..09dcc78 --- /dev/null +++ b/.python-version @@ -0,0 +1 @@ +3.10.11 diff --git a/.tool-versions b/.tool-versions new file mode 100644 index 0000000..28b9ff6 --- /dev/null +++ b/.tool-versions @@ -0,0 +1 @@ +python 3.10.11 diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..8105ef3 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,42 @@ +FROM python:3.10-alpine +MAINTAINER me@salimane.com + +# Set locale to UTF-8 +ENV LANG en_US.UTF-8 + +RUN apk --no-cache add g++ gcc git jpeg-dev libffi-dev libjpeg libxml2-dev \ + libxslt-dev linux-headers musl-dev openldap-dev openssl postgresql-dev zlib zlib-dev && \ + rm -rf /var/cache/apk/* + +ENV LIBRARY_PATH=/lib:/usr/lib +WORKDIR /opt/flask +RUN pip install -U pip setuptools +RUN pip install -r /opt/flask/requirements.txt +RUN pip install uwsgi + +EXPOSE 16000 +CMD ["sh", "-c", "sleep 5 ; uwsgi --http 0.0.0.0:16000 --wsgi-file runserver.py --callable app --processes 8 --threads 2"] + +ARG BUILD_DATE +ARG VCS_REF +ARG VCS_REF_MSG +ARG VCS_URL +ARG VERSION + +LABEL vendor="salimane" \ + name="salimane/flask-mvc" \ + maintainer="me@salimane.com" \ + description="python boilerplate application following MVC pattern using flask micro framework." \ + com.salimane.component.name="flask-mvc" \ + com.salimane.component.build-date="$BUILD_DATE" \ + com.salimane.component.vcs-url="$VCS_URL" \ + com.salimane.component.vcs-ref="$VCS_REF" \ + com.salimane.component.vcs-ref-msg="$VCS_REF_MSG" \ + com.salimane.component.version="$VERSION" \ + com.salimane.component.distribution-scope="public" \ + com.salimane.component.changelog-url="https://github.com/salimane/flask-mvc/releases" \ + com.salimane.component.url="https://github.com/salimane/flask-mvc" \ + com.salimane.component.run="docker run -e ENV_NAME=ENV_VALUE -p 16000:16000/tcp IMAGE" \ + com.salimane.component.environment.required="" \ + com.salimane.component.environment.optional="" \ + com.salimane.component.dockerfile="/usr/src/app/Dockerfile" diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..4c4f5b0 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 Salimane Adjao Moustapha + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..c7585ea --- /dev/null +++ b/Makefile @@ -0,0 +1,57 @@ +.PHONY: all check-version docker-build docker-push docker-run run set-revision clean setup +VERSION := $(strip $(shell [ -d .git ] && git describe --always --tags --dirty)) +BUILD_DATE := $(shell date -u +"%Y-%m-%dT%H:%M:%S%Z") +VCS_URL := $(shell [ -d .git ] && git config --get remote.origin.url) +VCS_REF := $(strip $(shell [ -d .git ] && git rev-parse --short HEAD)) +# IS_TAG := $(shell [ -d .git ] && git describe --exact-match HEAD || echo 'no tags') +VCS_REF_MSG := $(shell if [ "$(IS_TAG)" != "" ]; then git tag -l -n1 $(IS_TAG) | awk '{$$1 = ""; print $$0;}'; else git log --format='%s' -n 1 $(VCS_REF); fi) + +all: docker-build; + +check-version: + $(info $(VERSION)) +ifneq (,$(findstring dirty,$(VERSION))) + $(error Working copy dirty, aborting) +endif + +docker-build: + docker build \ + --build-arg BUILD_DATE="$(BUILD_DATE)" \ + --build-arg VERSION="$(VERSION)" \ + --build-arg VCS_URL="$(VCS_URL)" \ + --build-arg VCS_REF="$(VCS_REF)" \ + --build-arg VCS_REF_MSG="$(VCS_REF_MSG)" \ + --compress \ + -t salimane/flask-mvc:latest . + +docker-push: #check-version + if [ "$(IS_TAG)" != "" ]; then \ + docker tag salimane/flask-mvc:latest salimane/flask-mvc:$(VERSION);\ + fi + docker push salimane/flask-mvc + +docker-run: + docker-compose up -d + +run: + python runserver.py + +set-revision: + echo $(BUILD_DATE) > BUILD_TIME + if [ -d ".git" ]; then echo "$(VERSION)"; fi > BUILD_REVISION + +clean: + find . -name '*venv*' | xargs rm -rf + rm -rf "htmlcov" ".cache" ".coverage" + +setup: + script/setup + +test: + script/test + +flake: + flake8 --config=setup.cfg --statistics --count . + +autopep8: + autopep8 -ira . diff --git a/README.markdown b/README.markdown deleted file mode 100644 index f5d40e0..0000000 --- a/README.markdown +++ /dev/null @@ -1,19 +0,0 @@ -A simple boilerplate application following the MVC pattern using Flask micro python framework. -It basically here to be my base skeleton for new python web applications - -Demo : http://flask-mvc-salimane.herokuapp.com/ - -Dependencies : - - git clone git://github.com/salimane/flask-mvc.git - cd flask-mvc - if you have old version of project: pip uninstall -r requirements.txt - pip install -r requirements.txt - -To run: - - python runserver.py - - -[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/salimane/flask-mvc/trend.png)](https://bitdeli.com/free "Bitdeli Badge") - diff --git a/README.md b/README.md new file mode 100644 index 0000000..d7bb7af --- /dev/null +++ b/README.md @@ -0,0 +1,61 @@ +# Flask-mvc + +![Build Status](https://github.com/salimane/flask-mvc/actions/workflows/main.yml/badge.svg) +[![Maintenance](https://img.shields.io/maintenance/yes/2023.svg)](https://github.com/salimane/flask-mvc/commits/master) + + +A simple boilerplate application following the MVC pattern using Flask micro python framework. +It basically here to be a base skeleton for new python web applications + +It includes: + +| environment | url | +| --- | --- | +| Demo | http://flask-mvc-salimane.herokuapp.com/ | + +This repository is maintained by [Salimane Adjao Moustapha](https://github.com/salimane). +If you need to make changes to or have any ideas for improvement at this, please send a PR + +## Prerequisites + +* Git +* A working [Python](https://www.python.org/) 3.10 installation with [virtualenv](https://virtualenv.pypa.io/en/stable/) and [pip](https://pypi.python.org/pypi/pip). + ** + ```shell + # Mac OS X + brew update + brew install asdf pipx + asdf plugin add python + asdf install python 3.10.11 + pipx ensurepath + pipx install virtualenv + ``` + +## Setup + + ```Bash + $ git clone git@github.com:salimane/flask-mvc.git + $ cd flask-mvc + $ asdf local python 3.10.11 + $ pipx install virtualenv + $ Run ``make setup`` + ``` + +## Running + +* Run in console with +``` +$ make run +``` + +## Contributing + +It basically here to be a base skeleton for new python web applications, so we'd love your input! Got a question or an idea? Create an issue or a pull-request. + +## Maintainers + +* [Salimane Adjao Moustapha - @salimane](https://github.com/salimane) + +## Copyright Notice + +Copyright (C) 2023 Salimane Adjao Moustapha, authors, and contributors. Licensed under the [MIT License](/LICENSE). diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 0000000..3f2d27d --- /dev/null +++ b/codecov.yml @@ -0,0 +1,4 @@ +coverage: + ignore: # files and folders for processing + - .*test/.* + - .*test.py diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..53ca791 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,10 @@ +version: '3' +services: + postgres: + image: postgres:15.2-alpine + ports: + - 5432:5432 + environment: + POSTGRES_DB: flask_dev + POSTGRES_USER: flask + POSTGRES_PASSWORD: flask diff --git a/project/controllers/__init__.py b/project/controllers/__init__.py index 844f750..fef5ecb 100644 --- a/project/controllers/__init__.py +++ b/project/controllers/__init__.py @@ -1,4 +1,4 @@ import os import glob __all__ = [os.path.basename( - f)[:-3] for f in glob.glob(os.path.dirname(__file__) + "/*.py")] + f)[:-3] for f in glob.glob(os.path.dirname(__file__) + "/*.py") if not f.endswith('__init__.py')] diff --git a/requirements-test.txt b/requirements-test.txt new file mode 100644 index 0000000..195687c --- /dev/null +++ b/requirements-test.txt @@ -0,0 +1,7 @@ +-r requirements.txt +flake8==6.0.0 +mock==5.0.2 +pytest==7.3.1 +pytest-cov==4.0.0 +virtualenv==20.22.0 +mccabe==0.7.0 diff --git a/requirements.txt b/requirements.txt index 0ce32c8..529fc5e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,32 @@ -Flask -Flask-WTF -Jinja2 -Flask-DebugToolbar +Flask-Admin==1.6.1 +Flask-Assets==2.0 +Flask-Babel==3.1.0 +Flask-BabelEx==0.9.4 +Flask-Bcrypt==1.0.1 +Flask-Cors==3.0.10 +Flask-DebugToolbar==0.13.1 +Flask-Limiter==3.3.0 +Flask-Login==0.6.2 +Flask-Mail==0.9.1 +Flask-Migrate==4.0.4 +Flask-Principal==0.4.0 +Flask-QueryInspect==0.1.2 +Flask-RESTful==0.3.9 +Flask-Security==3.0.0 +Flask-SQLAlchemy==3.0.3 +Flask-Testing==0.8.1 +Flask-Uploads==0.2.1 +Flask-WTF==1.1.1 +Flask==2.2.3 +Jinja2==3.1.2 +tornado==6.3.1 +setuptools==67.7.2 + +# jobs +Celery==5.2.7 +flower==1.2.0 + +# Other +itsdangerous==2.1.2 +cssmin==0.2.0 +jsmin==3.0.1 diff --git a/script/setup b/script/setup new file mode 100755 index 0000000..dd05fc9 --- /dev/null +++ b/script/setup @@ -0,0 +1,11 @@ +#!/bin/sh +set -e -x + +export REPO_DIR=$( cd $(dirname "`dirname $0`") && pwd ) +if [ -z "$PYTHON_VIRTUALENV" ]; then + export PYTHON_VIRTUALENV=".flask-mvc-venv-`uname -s`-`uname -m`" +fi +export VIRTUAL_ENV_DIR="$REPO_DIR/$PYTHON_VIRTUALENV" +python -m venv $PYTHON_VIRTUALENV +echo "Using virtualenv located in : $VIRTUAL_ENV_DIR" +pip install -r $REPO_DIR/requirements.txt diff --git a/script/test b/script/test new file mode 100755 index 0000000..6432238 --- /dev/null +++ b/script/test @@ -0,0 +1,20 @@ +#!/bin/sh +set -e -x + +export REPO_DIR=$( cd $(dirname "`dirname $0`") && pwd ) +if [ -z "$PYTHON_VIRTUALENV" ]; then + export PYTHON_VIRTUALENV=".test-venv-`uname -s`-`uname -m`" +fi +export VIRTUAL_ENV_DIR="$REPO_DIR/$PYTHON_VIRTUALENV" +python -m venv $PYTHON_VIRTUALENV +echo "Using virtualenv located in : $VIRTUAL_ENV_DIR" +pip install -r $REPO_DIR/requirements-test.txt + +cd $REPO_DIR +export PYTHONPATH=".:`pwd`" + +echo "==> Static Code Analysis for PEP8 style guide and various errors" +# flake8 --config=setup.cfg --statistics --count . +flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics +# echo "==> Executing tests with code coverage collection" +# py.test --cov=./ --cov-report=html test/test.py diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..e56ce94 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,5 @@ +[flake8] +max-line-length = 220 +exclude = .svn,CVS,.bzr,.hg,.git,__pycache__,.tox,.eggs,*.egg,.vscode,.*venv* +[pep8] +max-line-length = 220 From d463a36ffe89b1f31b272db847cf774e289269b5 Mon Sep 17 00:00:00 2001 From: Salimane Adjao Moustapha Date: Tue, 25 Apr 2023 21:39:26 +0200 Subject: [PATCH 06/23] add actions page (#4) --- .github/workflows/{main.yml => master.yml} | 4 ++-- README.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) rename .github/workflows/{main.yml => master.yml} (90%) diff --git a/.github/workflows/main.yml b/.github/workflows/master.yml similarity index 90% rename from .github/workflows/main.yml rename to .github/workflows/master.yml index 7a035ef..e577e75 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/master.yml @@ -2,9 +2,9 @@ name: Flask-mvc on: push: - branches: [ $default-branch ] + branches: [ master ] pull_request: - branches: [ $default-branch ] + branches: [ master ] permissions: contents: read diff --git a/README.md b/README.md index d7bb7af..251f193 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Flask-mvc -![Build Status](https://github.com/salimane/flask-mvc/actions/workflows/main.yml/badge.svg) +[![Build Status](https://github.com/salimane/flask-mvc/actions/workflows/main.yml/badge.svg)](https://github.com/salimane/flask-mvc/actions) [![Maintenance](https://img.shields.io/maintenance/yes/2023.svg)](https://github.com/salimane/flask-mvc/commits/master) From 86d0a3ca14b3cd92a9f0b9810d69677ca064089b Mon Sep 17 00:00:00 2001 From: Salimane Adjao Moustapha Date: Tue, 25 Apr 2023 21:42:56 +0200 Subject: [PATCH 07/23] add build status badge Signed-off-by: Salimane Adjao Moustapha --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 251f193..b62892e 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Flask-mvc -[![Build Status](https://github.com/salimane/flask-mvc/actions/workflows/main.yml/badge.svg)](https://github.com/salimane/flask-mvc/actions) +[![Build Status](https://github.com/salimane/flask-mvc/actions/workflows/master.yml/badge.svg)](https://github.com/salimane/flask-mvc/actions) [![Maintenance](https://img.shields.io/maintenance/yes/2023.svg)](https://github.com/salimane/flask-mvc/commits/master) From 24bdd66538ba7e999087eefffff7638c5294ba0f Mon Sep 17 00:00:00 2001 From: Salimane Adjao Moustapha Date: Sat, 11 Apr 2026 22:00:04 +0200 Subject: [PATCH 08/23] Update packages dependencies and documentation (#6) * Update packages dependencies and documentation Signed-off-by: Salimane Adjao Moustapha * rename file Signed-off-by: Salimane Adjao Moustapha * fix test Signed-off-by: Salimane Adjao Moustapha * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --------- Signed-off-by: Salimane Adjao Moustapha Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .env.example | 10 ++ .github/PULL_REQUEST_TEMPLATE.md | 11 ++ .github/copilot-instructions.md | 168 +++++++++++++++++++++++ .github/dependabot.yml | 36 +++++ .github/workflows/master.yml | 41 ++++-- Dockerfile | 62 ++++----- Procfile | 2 +- README.md | 193 ++++++++++++++++++++++----- docker-compose.yml | 21 ++- project/__init__.py | 26 +++- project/config.py | 37 +++++ project/controllers/__init__.py | 5 +- project/controllers/printer.py | 17 +-- project/models/Printer.py | 11 -- project/models/printer.py | 8 ++ project/templates/layout/layout.html | 16 +-- project/templates/layout/macros.html | 5 + project/templates/printer/index.html | 9 +- project/templates/printer/print.html | 4 +- requirements-test.txt | 10 +- requirements.txt | 23 ++-- runserver.py | 6 +- script/test | 11 +- setup.cfg | 7 +- tests/__init__.py | 0 tests/conftest.py | 18 +++ tests/test_printer.py | 43 ++++++ 27 files changed, 651 insertions(+), 149 deletions(-) create mode 100644 .env.example create mode 100644 .github/PULL_REQUEST_TEMPLATE.md create mode 100644 .github/copilot-instructions.md create mode 100644 .github/dependabot.yml create mode 100644 project/config.py delete mode 100644 project/models/Printer.py create mode 100644 project/models/printer.py create mode 100644 project/templates/layout/macros.html create mode 100644 tests/__init__.py create mode 100644 tests/conftest.py create mode 100644 tests/test_printer.py diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..4749e73 --- /dev/null +++ b/.env.example @@ -0,0 +1,10 @@ +# Required environment variables — copy to .env and fill in values before running. + +# REQUIRED: Flask secret key. Generate with: python -c "import secrets; print(secrets.token_hex(32))" +SECRET_KEY= + +# OPTIONAL: Flask environment (development | testing | production). Default: development +FLASK_ENV=development + +# OPTIONAL: Port to bind the dev server to. Default: 8080 +PORT=8080 diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..51d7f23 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,11 @@ +# Issue + + +# How was it fixed ? + + +# How is it being tested ? + + +# Out of Scope + diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 0000000..af03dfd --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,168 @@ +# GitHub Copilot Instructions — flask-mvc + +## Project overview + +A Flask boilerplate following the **MVC pattern** designed as a base skeleton for new Python web +applications. The primary goal is clarity and correctness over complexity. + +--- + +## Architecture + +### Application Factory + +The app is created via `create_app(config_name)` in `project/__init__.py`. Never import a global +`app` object — always go through the factory. The factory: +1. Loads the correct `Config` class from `project/config.py` +2. Initialises Flask extensions (e.g. `DebugToolbarExtension`) +3. Registers all Blueprints + +### MVC layout + +``` +project/ +├── __init__.py # create_app() factory +├── config.py # Config classes (Development / Testing / Production) +├── controllers/ # Flask Blueprints — one file per resource +│ └── printer.py # printer_bp Blueprint +├── models/ # Plain Python classes, no ORM currently +│ └── printer.py +├── static/ +│ └── css/style.css +└── templates/ + ├── layout/ + │ ├── layout.html # base template + │ └── macros.html # reusable Jinja2 macros (render_error, etc.) + └── printer/ + ├── index.html + └── print.html +``` + +### Blueprints + +Every controller is a Blueprint registered in `create_app()`. Do **not** use the old glob +auto-import pattern. To add a new resource: + +1. Create `project/controllers/.py` with a `_bp = Blueprint(...)`. +2. Register it in `create_app()`: + ```python + from project.controllers. import _bp + app.register_blueprint(_bp) + ``` + +--- + +## Conventions + +### Python style + +- **PEP 8** throughout. `flake8` enforces it with `max-line-length = 100`. +- Module filenames are always **`snake_case.py`** — never PascalCase. +- Imports at the **top** of every file — never inside functions or routes. +- Classes use **PascalCase**; functions and variables use **snake_case**. + +### Configuration + +- All runtime config comes from **environment variables**. No secrets in source code. +- `SECRET_KEY` is **required** at startup; the app raises `ValueError` immediately if missing. +- Use `FLASK_ENV` to select the config profile: `development` (default), `testing`, `production`. +- Reference `project/config.py` before adding any new config key. + +### Controllers + +- One Blueprint per resource in `project/controllers/`. +- Always use **POST/Redirect/GET** after a successful form submission to prevent double-posts: + ```python + return redirect(url_for('.')) + ``` +- Form classes live in the same file as the Blueprint that owns them. +- Keep controllers thin — delegate logic to model classes. + +### Models + +- Plain Python classes. No Flask imports except `flash` / `current_app` where unavoidable. +- Models do **not** duplicate validation that the form layer already enforces. + +### Templates + +- All templates extend `layout/layout.html`. +- Reusable Jinja2 macros belong in `layout/macros.html` and are imported with + `{% from "layout/macros.html" import %}`. +- Use `url_for('blueprint_name.view_name')` — never hardcode URLs. +- Write valid HTML5 (``, ``, ``). + +--- + +## Environment variables + +| Variable | Required | Default | Description | +|------------|----------|---------------|--------------------------------------| +| `SECRET_KEY` | Yes | — | Flask session / CSRF signing key | +| `FLASK_ENV` | No | `development` | Config profile to load | +| `PORT` | No | `8080` | Port for the development server | + +Generate a secure key with: +```bash +python -c "import secrets; print(secrets.token_hex(32))" +``` + +--- + +## Development setup + +```bash +cp .env.example .env # fill in SECRET_KEY at minimum +python -m venv .venv +source .venv/bin/activate +pip install -r requirements-test.txt +``` + +Run the dev server: +```bash +SECRET_KEY= python runserver.py +# or: make run (reads from .env if you source it first) +``` + +--- + +## Testing + +```bash +SECRET_KEY=test-secret pytest tests/ -v --cov=project +``` + +- Tests live in `tests/`. Fixtures (`app`, `client`) are in `tests/conftest.py`. +- Use `TestingConfig` which sets `WTF_CSRF_ENABLED = False` and a fallback `SECRET_KEY`. +- Every new controller must have a corresponding test file `tests/test_.py`. +- Cover: GET renders correct template, POST with valid data redirects, POST with invalid data + returns the form with errors. +- The `conftest.py` `app` fixture calls `create_app('testing')` — do not create a new app + instance inside individual test files. + +--- + +## Adding a new resource (checklist) + +- [ ] `project/models/.py` — model class +- [ ] `project/controllers/.py` — Blueprint + form + routes +- [ ] Register Blueprint in `project/__init__.py → create_app()` +- [ ] `project/templates//` — HTML templates +- [ ] `tests/test_.py` — test suite +- [ ] Export any new env vars in `.env.example` + +--- + +## What is intentionally excluded + +This boilerplate is minimal by design. The following are **not wired up yet** but can be added +when needed: + +- Database / SQLAlchemy — postgres is already running in `docker-compose.yml` on port 5432 + (`flask_dev` db, user `flask`, password `flask`). Add Flask-SQLAlchemy + Flask-Migrate and + set `DATABASE_URL` in `.env` to connect. +- Authentication (Flask-Login, Flask-Security) +- REST API layer (Flask-RESTful) +- Task queue (Celery / Flower) +- Frontend asset pipeline (Flask-Assets, cssmin, jsmin) + +If you need one of these, add it in isolation with its own Blueprint and tests. diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..9889861 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,36 @@ +version: 2 +updates: + - package-ecosystem: pip + directory: / + schedule: + interval: "daily" + time: "10:00" + timezone: "Europe/Berlin" + commit-message: + prefix: "deps" + prefix-development: "deps-dev" + include: "scope" + open-pull-requests-limit: 1 + rebase-strategy: "auto" + groups: + batch-updates: + patterns: + - "*" + - package-ecosystem: "docker" + directory: "/" + schedule: + interval: "monthly" + time: "10:00" + timezone: "Europe/Berlin" + commit-message: + prefix: "docker" + open-pull-requests-limit: 1 + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "monthly" + time: "10:00" + timezone: "Europe/Berlin" + commit-message: + prefix: "ci" + open-pull-requests-limit: 1 \ No newline at end of file diff --git a/.github/workflows/master.yml b/.github/workflows/master.yml index e577e75..9ce4bfa 100644 --- a/.github/workflows/master.yml +++ b/.github/workflows/master.yml @@ -10,20 +10,39 @@ permissions: contents: read jobs: - build: + test: runs-on: ubuntu-latest + strategy: + matrix: + python-version: ["3.11", "3.12"] + steps: - - uses: actions/checkout@v3 - - name: Set up Python 3.10 - uses: actions/setup-python@v3 + - uses: actions/checkout@v4 + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 with: - python-version: "3.10" + python-version: ${{ matrix.python-version }} + cache: pip + - name: Install dependencies run: | python -m pip install --upgrade pip - pip install flake8 pytest - if [ -f requirements.txt ]; then pip install -r requirements.txt; fi - - name: Lint with flake8 - run: | - # stop the build if there are Python syntax errors or undefined names - flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics + pip install -r requirements-test.txt + + - name: Lint (fatal errors) + run: flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics + + - name: Lint (full style check) + run: flake8 --config=setup.cfg --statistics --count . + + - name: Run tests with coverage + env: + SECRET_KEY: ${{ secrets.SECRET_KEY || 'ci-test-secret-key-not-for-production' }} + run: pytest tests/ -v --cov=project --cov-report=xml + + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v4 + with: + files: coverage.xml + fail_ci_if_error: false diff --git a/Dockerfile b/Dockerfile index 8105ef3..f53d1aa 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,21 +1,13 @@ FROM python:3.10-alpine -MAINTAINER me@salimane.com -# Set locale to UTF-8 -ENV LANG en_US.UTF-8 - -RUN apk --no-cache add g++ gcc git jpeg-dev libffi-dev libjpeg libxml2-dev \ - libxslt-dev linux-headers musl-dev openldap-dev openssl postgresql-dev zlib zlib-dev && \ - rm -rf /var/cache/apk/* - -ENV LIBRARY_PATH=/lib:/usr/lib -WORKDIR /opt/flask -RUN pip install -U pip setuptools -RUN pip install -r /opt/flask/requirements.txt -RUN pip install uwsgi - -EXPOSE 16000 -CMD ["sh", "-c", "sleep 5 ; uwsgi --http 0.0.0.0:16000 --wsgi-file runserver.py --callable app --processes 8 --threads 2"] +LABEL maintainer="me@salimane.com" \ + vendor="salimane" \ + name="salimane/flask-mvc" \ + description="Python boilerplate application following MVC pattern using Flask." \ + com.salimane.component.name="flask-mvc" \ + com.salimane.component.distribution-scope="public" \ + com.salimane.component.changelog-url="https://github.com/salimane/flask-mvc/releases" \ + com.salimane.component.url="https://github.com/salimane/flask-mvc" ARG BUILD_DATE ARG VCS_REF @@ -23,20 +15,30 @@ ARG VCS_REF_MSG ARG VCS_URL ARG VERSION -LABEL vendor="salimane" \ - name="salimane/flask-mvc" \ - maintainer="me@salimane.com" \ - description="python boilerplate application following MVC pattern using flask micro framework." \ - com.salimane.component.name="flask-mvc" \ - com.salimane.component.build-date="$BUILD_DATE" \ +LABEL com.salimane.component.build-date="$BUILD_DATE" \ com.salimane.component.vcs-url="$VCS_URL" \ com.salimane.component.vcs-ref="$VCS_REF" \ com.salimane.component.vcs-ref-msg="$VCS_REF_MSG" \ - com.salimane.component.version="$VERSION" \ - com.salimane.component.distribution-scope="public" \ - com.salimane.component.changelog-url="https://github.com/salimane/flask-mvc/releases" \ - com.salimane.component.url="https://github.com/salimane/flask-mvc" \ - com.salimane.component.run="docker run -e ENV_NAME=ENV_VALUE -p 16000:16000/tcp IMAGE" \ - com.salimane.component.environment.required="" \ - com.salimane.component.environment.optional="" \ - com.salimane.component.dockerfile="/usr/src/app/Dockerfile" + com.salimane.component.version="$VERSION" + +ENV LANG=en_US.UTF-8 \ + PYTHONDONTWRITEBYTECODE=1 \ + PYTHONUNBUFFERED=1 + +RUN apk --no-cache add gcc musl-dev libffi-dev openssl-dev \ + postgresql-dev libpq && \ + rm -rf /var/cache/apk/* + +WORKDIR /opt/flask + +# Install dependencies first (cached layer unless requirements.txt changes) +COPY requirements.txt . +RUN pip install --no-cache-dir -U pip && \ + pip install --no-cache-dir -r requirements.txt + +# Copy application source +COPY . . + +EXPOSE 16000 + +CMD ["gunicorn", "runserver:app", "--bind", "0.0.0.0:16000", "--workers", "4", "--threads", "2", "--worker-class", "sync"] diff --git a/Procfile b/Procfile index 597f8b6..79ad41d 100644 --- a/Procfile +++ b/Procfile @@ -1 +1 @@ -web: python runserver.py +web: gunicorn "runserver:app" --bind 0.0.0.0:$PORT --workers 2 diff --git a/README.md b/README.md index b62892e..1fa278d 100644 --- a/README.md +++ b/README.md @@ -1,61 +1,184 @@ -# Flask-mvc +# Flask-MVC [![Build Status](https://github.com/salimane/flask-mvc/actions/workflows/master.yml/badge.svg)](https://github.com/salimane/flask-mvc/actions) -[![Maintenance](https://img.shields.io/maintenance/yes/2023.svg)](https://github.com/salimane/flask-mvc/commits/master) +[![codecov](https://codecov.io/gh/salimane/flask-mvc/branch/master/graph/badge.svg)](https://codecov.io/gh/salimane/flask-mvc) +[![Maintenance](https://img.shields.io/maintenance/yes/2026.svg)](https://github.com/salimane/flask-mvc/commits/master) +[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE) +A minimal Flask boilerplate following the **MVC pattern** — designed as a clean starting point +for new Python web applications. -A simple boilerplate application following the MVC pattern using Flask micro python framework. -It basically here to be a base skeleton for new python web applications +--- -It includes: +## Features -| environment | url | -| --- | --- | -| Demo | http://flask-mvc-salimane.herokuapp.com/ | +- **Application Factory** (`create_app()`) — safe for testing and multiple configs +- **Blueprint-based controllers** — one file per resource, explicit registration +- **Environment-based configuration** — `Development`, `Testing`, `Production` config classes +- **POST/Redirect/GET** form handling — prevents double-submit on refresh +- **Jinja2 macro library** — shared `render_error` and helpers in `layout/macros.html` +- **Test suite** — 96% coverage out of the box with pytest + pytest-cov +- **Docker-ready** — optimised multi-layer `Dockerfile` and `docker-compose.yml` -This repository is maintained by [Salimane Adjao Moustapha](https://github.com/salimane). -If you need to make changes to or have any ideas for improvement at this, please send a PR +--- + +## Project structure + +``` +flask-mvc/ +├── project/ +│ ├── __init__.py # create_app() factory +│ ├── config.py # Dev / Testing / Production config classes +│ ├── controllers/ +│ │ └── printer.py # printer Blueprint + form +│ ├── models/ +│ │ └── printer.py # Printer model +│ ├── static/css/ +│ └── templates/ +│ ├── layout/ +│ │ ├── layout.html # base template +│ │ └── macros.html # reusable Jinja2 macros +│ └── printer/ +├── tests/ +│ ├── conftest.py # app / client fixtures +│ └── test_printer.py +├── .env.example # required env vars — copy to .env before running +├── Dockerfile +├── docker-compose.yml +├── requirements.txt +├── requirements-test.txt +└── runserver.py +``` + +--- ## Prerequisites -* Git -* A working [Python](https://www.python.org/) 3.10 installation with [virtualenv](https://virtualenv.pypa.io/en/stable/) and [pip](https://pypi.python.org/pypi/pip). - ** - ```shell - # Mac OS X - brew update - brew install asdf pipx - asdf plugin add python - asdf install python 3.10.11 - pipx ensurepath - pipx install virtualenv - ``` +- Python 3.11+ +- pip + +Optional (for Python version management): + +```bash +brew install asdf +asdf plugin add python +asdf install python 3.12.3 +``` + +--- ## Setup - ```Bash - $ git clone git@github.com:salimane/flask-mvc.git - $ cd flask-mvc - $ asdf local python 3.10.11 - $ pipx install virtualenv - $ Run ``make setup`` - ``` +```bash +# 1. Clone +git clone git@github.com:salimane/flask-mvc.git +cd flask-mvc + +# 2. Create and activate a virtual environment +python -m venv .venv +source .venv/bin/activate + +# 3. Install dependencies +pip install -r requirements-test.txt + +# 4. Configure environment +cp .env.example .env +# Open .env and set SECRET_KEY — generate one with: +# python -c "import secrets; print(secrets.token_hex(32))" +``` + +--- ## Running -* Run in console with +```bash +# Development server (reads PORT from env, default 8080) +source .env && python runserver.py + +# Or via Make +make run ``` -$ make run + +### Docker + +```bash +cp .env.example .env # fill in SECRET_KEY +docker-compose up --build +``` + +The app is served by **gunicorn** on port `16000` inside the container. + +--- + +## Testing + +```bash +SECRET_KEY=test pytest tests/ -v --cov=project --cov-report=term-missing ``` +Or via the helper script (creates an isolated virtualenv automatically): + +```bash +script/test +``` + +### Coverage + +``` +Name Stmts Miss Cover +----------------------------------------------------- +project/__init__.py 15 1 93% +project/config.py 20 1 95% +project/controllers/printer.py 18 0 100% +project/models/printer.py 4 0 100% +----------------------------------------------------- +TOTAL 57 2 96% +``` + +--- + +## Configuration + +All configuration is driven by environment variables. See `.env.example` for the full list. + +| Variable | Required | Default | Description | +|--------------|----------|---------------|----------------------------------| +| `SECRET_KEY` | **Yes** | — | Flask session / CSRF signing key | +| `FLASK_ENV` | No | `development` | Config profile (`development`, `testing`, `production`) | +| `PORT` | No | `8080` | Dev server port | + +Generate a secure key: + +```bash +python -c "import secrets; print(secrets.token_hex(32))" +``` + +--- + +## Extending the boilerplate + +To add a new resource (e.g. `user`): + +1. **Model** — `project/models/user.py` +2. **Controller** — `project/controllers/user.py` with `user_bp = Blueprint('user', __name__)` +3. **Register** — add `app.register_blueprint(user_bp)` in `create_app()` (`project/__init__.py`) +4. **Templates** — `project/templates/user/` +5. **Tests** — `tests/test_user.py` + +See [`.github/copilot-instructions.md`](.github/copilot-instructions.md) for the full development +guide and coding conventions. + +--- + ## Contributing -It basically here to be a base skeleton for new python web applications, so we'd love your input! Got a question or an idea? Create an issue or a pull-request. +Issues and pull requests are welcome. Please keep PRs focused — one concern per PR. ## Maintainers -* [Salimane Adjao Moustapha - @salimane](https://github.com/salimane) +[Salimane Adjao Moustapha](https://github.com/salimane) + +## License -## Copyright Notice +Copyright © 2026 Salimane Adjao Moustapha. Licensed under the [MIT License](LICENSE). -Copyright (C) 2023 Salimane Adjao Moustapha, authors, and contributors. Licensed under the [MIT License](/LICENSE). diff --git a/docker-compose.yml b/docker-compose.yml index 53ca791..99f6398 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,10 +1,27 @@ -version: '3' +version: '3.8' services: + web: + build: . + ports: + - "16000:16000" + environment: + - SECRET_KEY=${SECRET_KEY} + - FLASK_ENV=${FLASK_ENV:-production} + env_file: + - .env + depends_on: + - postgres + postgres: image: postgres:15.2-alpine ports: - - 5432:5432 + - "5432:5432" environment: POSTGRES_DB: flask_dev POSTGRES_USER: flask POSTGRES_PASSWORD: flask + volumes: + - postgres_data:/var/lib/postgresql/data + +volumes: + postgres_data: diff --git a/project/__init__.py b/project/__init__.py index e73a1ec..fbb219a 100644 --- a/project/__init__.py +++ b/project/__init__.py @@ -1,9 +1,25 @@ # -*- coding: utf-8 -*- __version__ = '0.1' + from flask import Flask from flask_debugtoolbar import DebugToolbarExtension -app = Flask('project') -app.config['SECRET_KEY'] = 'random' -app.debug = True -toolbar = DebugToolbarExtension(app) -from project.controllers import * + +from project.config import config + +toolbar = DebugToolbarExtension() + + +def create_app(config_name=None): + import os + if config_name is None: + config_name = os.environ.get('FLASK_ENV', 'default') + + app = Flask('project') + app.config.from_object(config[config_name]) + + toolbar.init_app(app) + + from project.controllers.printer import printer_bp + app.register_blueprint(printer_bp) + + return app diff --git a/project/config.py b/project/config.py new file mode 100644 index 0000000..e435df4 --- /dev/null +++ b/project/config.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +import os + + +class Config: + SECRET_KEY = os.environ.get('SECRET_KEY') + if not SECRET_KEY: + raise ValueError('SECRET_KEY environment variable is not set') + + WTF_CSRF_ENABLED = True + DEBUG_TB_ENABLED = False + DEBUG_TB_INTERCEPT_REDIRECTS = False + + +class DevelopmentConfig(Config): + DEBUG = True + DEBUG_TB_ENABLED = True + + +class TestingConfig(Config): + SECRET_KEY = os.environ.get('SECRET_KEY', 'test-secret-key-not-for-production') + TESTING = True + WTF_CSRF_ENABLED = False + DEBUG_TB_ENABLED = False + + +class ProductionConfig(Config): + DEBUG = False + TESTING = False + + +config = { + 'development': DevelopmentConfig, + 'testing': TestingConfig, + 'production': ProductionConfig, + 'default': DevelopmentConfig, +} diff --git a/project/controllers/__init__.py b/project/controllers/__init__.py index fef5ecb..6651888 100644 --- a/project/controllers/__init__.py +++ b/project/controllers/__init__.py @@ -1,4 +1 @@ -import os -import glob -__all__ = [os.path.basename( - f)[:-3] for f in glob.glob(os.path.dirname(__file__) + "/*.py") if not f.endswith('__init__.py')] +# Blueprints are registered explicitly in project/__init__.py via create_app(). diff --git a/project/controllers/printer.py b/project/controllers/printer.py index 0da47df..80b10e9 100644 --- a/project/controllers/printer.py +++ b/project/controllers/printer.py @@ -1,26 +1,27 @@ # -*- coding: utf-8 -*- -from project import app -from flask import render_template, request +from flask import Blueprint, render_template, request, redirect, url_for from flask_wtf import FlaskForm from wtforms import StringField from wtforms.validators import DataRequired +from project.models.printer import Printer + +printer_bp = Blueprint('printer', __name__) + class CreateForm(FlaskForm): text = StringField('name', validators=[DataRequired()]) -@app.route('/') +@printer_bp.route('/') def start(): return render_template('printer/index.html') -@app.route('/print', methods=['GET', 'POST']) +@printer_bp.route('/print', methods=['GET', 'POST']) def printer(): form = CreateForm(request.form) if request.method == 'POST' and form.validate(): - from project.models.Printer import Printer - printer = Printer() - printer.show_string(form.text.data) - return render_template('printer/index.html') + Printer().show_string(form.text.data) + return redirect(url_for('printer.start')) return render_template('printer/print.html', form=form) diff --git a/project/models/Printer.py b/project/models/Printer.py deleted file mode 100644 index 54ff974..0000000 --- a/project/models/Printer.py +++ /dev/null @@ -1,11 +0,0 @@ -# -*- coding: utf-8 -*- -from flask import flash - - -class Printer(object): - - def show_string(self, text): - if text == '': - flash("You didn't enter any text to flash") - else: - flash(text + "!!!") diff --git a/project/models/printer.py b/project/models/printer.py new file mode 100644 index 0000000..197eddd --- /dev/null +++ b/project/models/printer.py @@ -0,0 +1,8 @@ +# -*- coding: utf-8 -*- +from flask import flash + + +class Printer: + + def show_string(self, text): + flash(text + '!!!') diff --git a/project/templates/layout/layout.html b/project/templates/layout/layout.html index a026d6c..23f68a6 100644 --- a/project/templates/layout/layout.html +++ b/project/templates/layout/layout.html @@ -1,22 +1,22 @@ + + Flask-MVC - - Home - About + +

Flask MVC Boilerplate

{% for message in get_flashed_messages() %}
{{ message }}
{% endfor %} - {% macro render_error(field) %} - {% if field.errors %} - {% for error in field.errors %}{{ error }}{% endfor %} - {% endif %} - {% endmacro %} {% block body %}{% endblock %}
+ diff --git a/project/templates/layout/macros.html b/project/templates/layout/macros.html new file mode 100644 index 0000000..12b6037 --- /dev/null +++ b/project/templates/layout/macros.html @@ -0,0 +1,5 @@ +{% macro render_error(field) %} + {% if field.errors %} + {% for error in field.errors %}{{ error }}{% endfor %} + {% endif %} +{% endmacro %} diff --git a/project/templates/printer/index.html b/project/templates/printer/index.html index 00e9c5e..40293bf 100644 --- a/project/templates/printer/index.html +++ b/project/templates/printer/index.html @@ -1,8 +1,7 @@ {% extends "layout/layout.html" %} {% block body %} - +

+ Click here to print! +

{% endblock %} + diff --git a/project/templates/printer/print.html b/project/templates/printer/print.html index 6053f54..aa8d331 100644 --- a/project/templates/printer/print.html +++ b/project/templates/printer/print.html @@ -1,6 +1,7 @@ {% extends "layout/layout.html" %} +{% from "layout/macros.html" import render_error %} {% block body %} -
+ {{ form.csrf_token }}
{{ form.text.label }} {{ form.text(size=20) }} {{ render_error(form.text) }}
@@ -8,3 +9,4 @@
{% endblock %} + diff --git a/requirements-test.txt b/requirements-test.txt index 195687c..32f6849 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -1,7 +1,5 @@ -r requirements.txt -flake8==6.0.0 -mock==5.0.2 -pytest==7.3.1 -pytest-cov==4.0.0 -virtualenv==20.22.0 -mccabe==0.7.0 +flake8>=7.0.0 +pytest>=8.1.0 +pytest-cov>=5.0.0 +mock>=5.0.0 diff --git a/requirements.txt b/requirements.txt index 529fc5e..59f9b67 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,32 +1,29 @@ +Flask==3.0.3 Flask-Admin==1.6.1 Flask-Assets==2.0 Flask-Babel==3.1.0 -Flask-BabelEx==0.9.4 Flask-Bcrypt==1.0.1 -Flask-Cors==3.0.10 -Flask-DebugToolbar==0.13.1 +Flask-Cors==4.0.0 +Flask-DebugToolbar==0.15.1 Flask-Limiter==3.3.0 Flask-Login==0.6.2 Flask-Mail==0.9.1 Flask-Migrate==4.0.4 Flask-Principal==0.4.0 Flask-QueryInspect==0.1.2 -Flask-RESTful==0.3.9 +Flask-RESTful==0.3.10 Flask-Security==3.0.0 -Flask-SQLAlchemy==3.0.3 -Flask-Testing==0.8.1 +Flask-SQLAlchemy==3.1.1 Flask-Uploads==0.2.1 -Flask-WTF==1.1.1 -Flask==2.2.3 +Flask-WTF==1.2.1 Jinja2==3.1.2 -tornado==6.3.1 -setuptools==67.7.2 +gunicorn==22.0.0 # jobs -Celery==5.2.7 -flower==1.2.0 +Celery==5.3.6 +flower==2.0.1 -# Other +# other itsdangerous==2.1.2 cssmin==0.2.0 jsmin==3.0.1 diff --git a/runserver.py b/runserver.py index 80400ce..1d60fd2 100644 --- a/runserver.py +++ b/runserver.py @@ -1,8 +1,10 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- import os -from project import app +from project import create_app + +app = create_app(os.environ.get('FLASK_ENV', 'development')) if __name__ == '__main__': - port = int(os.environ.get("PORT", 8080)) + port = int(os.environ.get('PORT', 8080)) app.run('0.0.0.0', port=port) diff --git a/script/test b/script/test index 6432238..c3c33d7 100755 --- a/script/test +++ b/script/test @@ -8,13 +8,16 @@ fi export VIRTUAL_ENV_DIR="$REPO_DIR/$PYTHON_VIRTUALENV" python -m venv $PYTHON_VIRTUALENV echo "Using virtualenv located in : $VIRTUAL_ENV_DIR" -pip install -r $REPO_DIR/requirements-test.txt +. "$VIRTUAL_ENV_DIR/bin/activate" +pip install -q -r $REPO_DIR/requirements-test.txt cd $REPO_DIR export PYTHONPATH=".:`pwd`" echo "==> Static Code Analysis for PEP8 style guide and various errors" -# flake8 --config=setup.cfg --statistics --count . flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics -# echo "==> Executing tests with code coverage collection" -# py.test --cov=./ --cov-report=html test/test.py +echo "==> Full flake8 style check" +flake8 --config=setup.cfg --statistics --count . +echo "==> Executing tests with code coverage collection" +pytest --cov=project --cov-report=html --cov-report=term-missing tests/ + diff --git a/setup.cfg b/setup.cfg index e56ce94..6ed12eb 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,6 @@ [flake8] -max-line-length = 220 +max-line-length = 100 exclude = .svn,CVS,.bzr,.hg,.git,__pycache__,.tox,.eggs,*.egg,.vscode,.*venv* -[pep8] -max-line-length = 220 + +[pycodestyle] +max-line-length = 100 diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000..0c8c3e0 --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- +import os +import pytest + +os.environ.setdefault('SECRET_KEY', 'test-secret-key-not-for-production') + +from project import create_app # noqa: E402 + + +@pytest.fixture +def app(): + app = create_app('testing') + yield app + + +@pytest.fixture +def client(app): + return app.test_client() diff --git a/tests/test_printer.py b/tests/test_printer.py new file mode 100644 index 0000000..3f4ffdb --- /dev/null +++ b/tests/test_printer.py @@ -0,0 +1,43 @@ +# -*- coding: utf-8 -*- + + +class TestStart: + def test_get_returns_200(self, client): + response = client.get('/') + assert response.status_code == 200 + + def test_get_contains_link_to_print(self, client): + response = client.get('/') + assert b'/print' in response.data + + +class TestPrinterGet: + def test_get_returns_200(self, client): + response = client.get('/print') + assert response.status_code == 200 + + def test_get_renders_form(self, client): + response = client.get('/print') + assert b' Date: Sat, 11 Apr 2026 22:03:12 +0200 Subject: [PATCH 09/23] docker: bump python from 3.10-alpine to 3.14-alpine (#8) Bumps python from 3.10-alpine to 3.14-alpine. --- updated-dependencies: - dependency-name: python dependency-version: 3.14-alpine dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index f53d1aa..9558880 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3.10-alpine +FROM python:3.14-alpine LABEL maintainer="me@salimane.com" \ vendor="salimane" \ From cc252bb23b7659039f9c84307ae4a81444dd4638 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 11 Apr 2026 22:04:19 +0200 Subject: [PATCH 10/23] deps(deps): bump the batch-updates group with 16 updates (#9) Bumps the batch-updates group with 16 updates: | Package | From | To | | --- | --- | --- | | [flask](https://github.com/pallets/flask) | `3.0.3` | `3.1.3` | | [flask-admin](https://github.com/pallets-eco/flask-admin) | `1.6.1` | `2.0.2` | | [flask-assets](https://github.com/miracle2k/flask-assets) | `2.0` | `2.1.0` | | [flask-babel](https://github.com/python-babel/flask-babel) | `3.1.0` | `4.0.0` | | [flask-cors](https://github.com/corydolphin/flask-cors) | `4.0.0` | `6.0.2` | | [flask-debugtoolbar](https://github.com/pallets-eco/flask-debugtoolbar) | `0.15.1` | `0.16.0` | | [flask-limiter](https://github.com/alisaifee/flask-limiter) | `3.3.0` | `4.1.1` | | [flask-login](https://github.com/maxcountryman/flask-login) | `0.6.2` | `0.6.3` | | [flask-mail](https://github.com/pallets-eco/flask-mail) | `0.9.1` | `0.10.0` | | [flask-migrate](https://github.com/miguelgrinberg/flask-migrate) | `4.0.4` | `4.1.0` | | [flask-security](https://github.com/pallets-eco/flask-security) | `3.0.0` | `5.7.1` | | [flask-wtf](https://github.com/pallets-eco/flask-wtf) | `1.2.1` | `1.2.2` | | [jinja2](https://github.com/pallets/jinja) | `3.1.2` | `3.1.6` | | [gunicorn](https://github.com/benoitc/gunicorn) | `22.0.0` | `25.3.0` | | [celery](https://github.com/celery/celery) | `5.3.6` | `5.6.3` | | [itsdangerous](https://github.com/pallets/itsdangerous) | `2.1.2` | `2.2.0` | Updates `flask` from 3.0.3 to 3.1.3 - [Release notes](https://github.com/pallets/flask/releases) - [Changelog](https://github.com/pallets/flask/blob/main/CHANGES.rst) - [Commits](https://github.com/pallets/flask/compare/3.0.3...3.1.3) Updates `flask-admin` from 1.6.1 to 2.0.2 - [Release notes](https://github.com/pallets-eco/flask-admin/releases) - [Changelog](https://github.com/pallets-eco/flask-admin/blob/master/doc/changelog.rst) - [Commits](https://github.com/pallets-eco/flask-admin/compare/v1.6.1...v2.0.2) Updates `flask-assets` from 2.0 to 2.1.0 - [Release notes](https://github.com/miracle2k/flask-assets/releases) - [Changelog](https://github.com/miracle2k/flask-assets/blob/master/CHANGES) - [Commits](https://github.com/miracle2k/flask-assets/compare/2.0...2.1.0) Updates `flask-babel` from 3.1.0 to 4.0.0 - [Release notes](https://github.com/python-babel/flask-babel/releases) - [Changelog](https://github.com/python-babel/flask-babel/blob/master/CHANGELOG) - [Commits](https://github.com/python-babel/flask-babel/compare/v3.1.0...v4.0.0) Updates `flask-cors` from 4.0.0 to 6.0.2 - [Release notes](https://github.com/corydolphin/flask-cors/releases) - [Changelog](https://github.com/corydolphin/flask-cors/blob/main/CHANGELOG.md) - [Commits](https://github.com/corydolphin/flask-cors/compare/4.0.0...6.0.2) Updates `flask-debugtoolbar` from 0.15.1 to 0.16.0 - [Release notes](https://github.com/pallets-eco/flask-debugtoolbar/releases) - [Commits](https://github.com/pallets-eco/flask-debugtoolbar/compare/0.15.1...0.16.0) Updates `flask-limiter` from 3.3.0 to 4.1.1 - [Release notes](https://github.com/alisaifee/flask-limiter/releases) - [Changelog](https://github.com/alisaifee/flask-limiter/blob/master/HISTORY.rst) - [Commits](https://github.com/alisaifee/flask-limiter/compare/3.3.0...4.1.1) Updates `flask-login` from 0.6.2 to 0.6.3 - [Release notes](https://github.com/maxcountryman/flask-login/releases) - [Changelog](https://github.com/maxcountryman/flask-login/blob/main/CHANGES.md) - [Commits](https://github.com/maxcountryman/flask-login/compare/0.6.2...0.6.3) Updates `flask-mail` from 0.9.1 to 0.10.0 - [Release notes](https://github.com/pallets-eco/flask-mail/releases) - [Changelog](https://github.com/pallets-eco/flask-mail/blob/main/CHANGES.md) - [Commits](https://github.com/pallets-eco/flask-mail/compare/0.9.1...0.10.0) Updates `flask-migrate` from 4.0.4 to 4.1.0 - [Release notes](https://github.com/miguelgrinberg/flask-migrate/releases) - [Changelog](https://github.com/miguelgrinberg/Flask-Migrate/blob/main/CHANGES.md) - [Commits](https://github.com/miguelgrinberg/flask-migrate/compare/v4.0.4...v4.1.0) Updates `flask-security` from 3.0.0 to 5.7.1 - [Release notes](https://github.com/pallets-eco/flask-security/releases) - [Changelog](https://github.com/pallets-eco/flask-security/blob/main/CHANGES.rst) - [Commits](https://github.com/pallets-eco/flask-security/compare/3.0.0...5.7.1) Updates `flask-wtf` from 1.2.1 to 1.2.2 - [Release notes](https://github.com/pallets-eco/flask-wtf/releases) - [Changelog](https://github.com/pallets-eco/flask-wtf/blob/main/docs/changes.rst) - [Commits](https://github.com/pallets-eco/flask-wtf/compare/v1.2.1...v1.2.2) Updates `jinja2` from 3.1.2 to 3.1.6 - [Release notes](https://github.com/pallets/jinja/releases) - [Changelog](https://github.com/pallets/jinja/blob/main/CHANGES.rst) - [Commits](https://github.com/pallets/jinja/compare/3.1.2...3.1.6) Updates `gunicorn` from 22.0.0 to 25.3.0 - [Release notes](https://github.com/benoitc/gunicorn/releases) - [Commits](https://github.com/benoitc/gunicorn/compare/22.0.0...25.3.0) Updates `celery` from 5.3.6 to 5.6.3 - [Release notes](https://github.com/celery/celery/releases) - [Changelog](https://github.com/celery/celery/blob/v5.6.3/Changelog.rst) - [Commits](https://github.com/celery/celery/compare/v5.3.6...v5.6.3) Updates `itsdangerous` from 2.1.2 to 2.2.0 - [Release notes](https://github.com/pallets/itsdangerous/releases) - [Changelog](https://github.com/pallets/itsdangerous/blob/main/CHANGES.rst) - [Commits](https://github.com/pallets/itsdangerous/compare/2.1.2...2.2.0) --- updated-dependencies: - dependency-name: flask dependency-version: 3.1.3 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: batch-updates - dependency-name: flask-admin dependency-version: 2.0.2 dependency-type: direct:production update-type: version-update:semver-major dependency-group: batch-updates - dependency-name: flask-assets dependency-version: 2.1.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: batch-updates - dependency-name: flask-babel dependency-version: 4.0.0 dependency-type: direct:production update-type: version-update:semver-major dependency-group: batch-updates - dependency-name: flask-cors dependency-version: 6.0.2 dependency-type: direct:production update-type: version-update:semver-major dependency-group: batch-updates - dependency-name: flask-debugtoolbar dependency-version: 0.16.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: batch-updates - dependency-name: flask-limiter dependency-version: 4.1.1 dependency-type: direct:production update-type: version-update:semver-major dependency-group: batch-updates - dependency-name: flask-login dependency-version: 0.6.3 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: batch-updates - dependency-name: flask-mail dependency-version: 0.10.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: batch-updates - dependency-name: flask-migrate dependency-version: 4.1.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: batch-updates - dependency-name: flask-security dependency-version: 5.7.1 dependency-type: direct:production update-type: version-update:semver-major dependency-group: batch-updates - dependency-name: flask-wtf dependency-version: 1.2.2 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: batch-updates - dependency-name: jinja2 dependency-version: 3.1.6 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: batch-updates - dependency-name: gunicorn dependency-version: 25.3.0 dependency-type: direct:production update-type: version-update:semver-major dependency-group: batch-updates - dependency-name: celery dependency-version: 5.6.3 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: batch-updates - dependency-name: itsdangerous dependency-version: 2.2.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: batch-updates ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Salimane Adjao Moustapha --- requirements.txt | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/requirements.txt b/requirements.txt index 59f9b67..a2ef34c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,29 +1,29 @@ -Flask==3.0.3 -Flask-Admin==1.6.1 -Flask-Assets==2.0 -Flask-Babel==3.1.0 +Flask==3.1.3 +Flask-Admin==2.0.2 +Flask-Assets==2.1.0 +Flask-Babel==4.0.0 Flask-Bcrypt==1.0.1 -Flask-Cors==4.0.0 -Flask-DebugToolbar==0.15.1 -Flask-Limiter==3.3.0 -Flask-Login==0.6.2 -Flask-Mail==0.9.1 -Flask-Migrate==4.0.4 +Flask-Cors==6.0.2 +Flask-DebugToolbar==0.16.0 +Flask-Limiter==4.1.1 +Flask-Login==0.6.3 +Flask-Mail==0.10.0 +Flask-Migrate==4.1.0 Flask-Principal==0.4.0 Flask-QueryInspect==0.1.2 Flask-RESTful==0.3.10 -Flask-Security==3.0.0 +Flask-Security==5.7.1 Flask-SQLAlchemy==3.1.1 Flask-Uploads==0.2.1 -Flask-WTF==1.2.1 -Jinja2==3.1.2 -gunicorn==22.0.0 +Flask-WTF==1.2.2 +Jinja2==3.1.6 +gunicorn==25.3.0 # jobs -Celery==5.3.6 +Celery==5.6.3 flower==2.0.1 # other -itsdangerous==2.1.2 +itsdangerous==2.2.0 cssmin==0.2.0 jsmin==3.0.1 From e2309ade63d6f7d7f7d6506c32ff7776ff20b3b6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 11 Apr 2026 22:04:43 +0200 Subject: [PATCH 11/23] ci: bump actions/checkout from 4 to 6 (#7) Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 6. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v4...v6) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/master.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/master.yml b/.github/workflows/master.yml index 9ce4bfa..95fc679 100644 --- a/.github/workflows/master.yml +++ b/.github/workflows/master.yml @@ -17,7 +17,7 @@ jobs: python-version: ["3.11", "3.12"] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v5 From 80660cbf4758379e6ebcdec4c9fc817febd2b01e Mon Sep 17 00:00:00 2001 From: Salimane Adjao Moustapha Date: Sat, 11 Apr 2026 22:17:36 +0200 Subject: [PATCH 12/23] Add CodeQL analysis workflow configuration (#13) --- .github/workflows/codeql.yml | 101 +++++++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 .github/workflows/codeql.yml diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 0000000..e23a9d8 --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,101 @@ +# For most projects, this workflow file will not need changing; you simply need +# to commit it to your repository. +# +# You may wish to alter this file to override the set of languages analyzed, +# or to provide custom queries or build logic. +# +# ******** NOTE ******** +# We have attempted to detect the languages in your repository. Please check +# the `language` matrix defined below to confirm you have the correct set of +# supported CodeQL languages. +# +name: "CodeQL Advanced" + +on: + push: + branches: [ "master" ] + pull_request: + branches: [ "master" ] + schedule: + - cron: '24 18 * * 6' + +jobs: + analyze: + name: Analyze (${{ matrix.language }}) + # Runner size impacts CodeQL analysis time. To learn more, please see: + # - https://gh.io/recommended-hardware-resources-for-running-codeql + # - https://gh.io/supported-runners-and-hardware-resources + # - https://gh.io/using-larger-runners (GitHub.com only) + # Consider using larger runners or machines with greater resources for possible analysis time improvements. + runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }} + permissions: + # required for all workflows + security-events: write + + # required to fetch internal or private CodeQL packs + packages: read + + # only required for workflows in private repositories + actions: read + contents: read + + strategy: + fail-fast: false + matrix: + include: + - language: actions + build-mode: none + - language: python + build-mode: none + # CodeQL supports the following values keywords for 'language': 'actions', 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'rust', 'swift' + # Use `c-cpp` to analyze code written in C, C++ or both + # Use 'java-kotlin' to analyze code written in Java, Kotlin or both + # Use 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both + # To learn more about changing the languages that are analyzed or customizing the build mode for your analysis, + # see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning. + # If you are analyzing a compiled language, you can modify the 'build-mode' for that language to customize how + # your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + # Add any setup steps before running the `github/codeql-action/init` action. + # This includes steps like installing compilers or runtimes (`actions/setup-node` + # or others). This is typically only required for manual builds. + # - name: Setup runtime (example) + # uses: actions/setup-example@v1 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v4 + with: + languages: ${{ matrix.language }} + build-mode: ${{ matrix.build-mode }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + + # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs + # queries: security-extended,security-and-quality + + # If the analyze step fails for one of the languages you are analyzing with + # "We were unable to automatically build your code", modify the matrix above + # to set the build mode to "manual" for that language. Then modify this step + # to build your code. + # ℹ️ Command-line programs to run using the OS shell. + # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun + - name: Run manual build steps + if: matrix.build-mode == 'manual' + shell: bash + run: | + echo 'If you are using a "manual" build mode for one or more of the' \ + 'languages you are analyzing, replace this with the commands to build' \ + 'your code, for example:' + echo ' make bootstrap' + echo ' make release' + exit 1 + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v4 + with: + category: "/language:${{matrix.language}}" From 192725eb6d42129127916cb5c4f2107eba80903b Mon Sep 17 00:00:00 2001 From: Salimane Adjao Moustapha Date: Sat, 11 Apr 2026 22:45:10 +0200 Subject: [PATCH 13/23] update python version and add linting setup (#14) Signed-off-by: Salimane Adjao Moustapha --- .github/workflows/master.yml | 52 ++++++++++++----- .python-version | 2 +- .tool-versions | 2 +- Makefile | 13 +++-- README.md | 4 +- project/__init__.py | 9 +-- project/config.py | 15 +++-- project/controllers/printer.py | 19 +++---- project/models/printer.py | 3 +- pyproject.toml | 100 +++++++++++++++++++++++++++++++++ pytest.ini | 6 ++ requirements-test.txt | 3 +- runserver.py | 10 ++-- script/test | 8 +-- setup.cfg | 6 -- tests/conftest.py | 6 +- tests/test_printer.py | 31 +++++----- 17 files changed, 206 insertions(+), 83 deletions(-) create mode 100644 pyproject.toml create mode 100644 pytest.ini delete mode 100644 setup.cfg diff --git a/.github/workflows/master.yml b/.github/workflows/master.yml index 95fc679..c920349 100644 --- a/.github/workflows/master.yml +++ b/.github/workflows/master.yml @@ -10,39 +10,63 @@ permissions: contents: read jobs: + lint: + name: Lint & format + runs-on: ubuntu-latest + + steps: + # actions/checkout@v5 (93cb6efe18208431cddfb8368fd83d5badbf9bfd) + - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd + + - name: Set up Python + # actions/setup-python@v5 (a26af69be951a213d495a4c3e4e4022e16d87065) + uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 + with: + python-version: "3.14" + cache: pip + cache-dependency-path: requirements-test.txt + + - name: Install lint dependencies + run: | + python -m pip install --upgrade pip + pip install black ruff + + - name: Format check (black) + run: black --check --diff . + + - name: Lint (ruff) + run: ruff check . + test: + name: Tests & coverage runs-on: ubuntu-latest - strategy: - matrix: - python-version: ["3.11", "3.12"] + needs: lint steps: - - uses: actions/checkout@v6 + # actions/checkout@v5 (93cb6efe18208431cddfb8368fd83d5badbf9bfd) + - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v5 + - name: Set up Python + # actions/setup-python@v5 (a26af69be951a213d495a4c3e4e4022e16d87065) + uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 with: - python-version: ${{ matrix.python-version }} + python-version: "3.14" cache: pip + cache-dependency-path: requirements-test.txt - name: Install dependencies run: | python -m pip install --upgrade pip pip install -r requirements-test.txt - - name: Lint (fatal errors) - run: flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics - - - name: Lint (full style check) - run: flake8 --config=setup.cfg --statistics --count . - - name: Run tests with coverage env: SECRET_KEY: ${{ secrets.SECRET_KEY || 'ci-test-secret-key-not-for-production' }} run: pytest tests/ -v --cov=project --cov-report=xml - name: Upload coverage to Codecov - uses: codecov/codecov-action@v4 + # codecov/codecov-action@v6 (57e3a136b779b570ffcdbf80b3bdc90e7fab3de2) + uses: codecov/codecov-action@57e3a136b779b570ffcdbf80b3bdc90e7fab3de2 with: files: coverage.xml fail_ci_if_error: false diff --git a/.python-version b/.python-version index 09dcc78..0104088 100644 --- a/.python-version +++ b/.python-version @@ -1 +1 @@ -3.10.11 +3.14.4 diff --git a/.tool-versions b/.tool-versions index 28b9ff6..628e985 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1 +1 @@ -python 3.10.11 +python 3.14.4 diff --git a/Makefile b/Makefile index c7585ea..b68b190 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,5 @@ -.PHONY: all check-version docker-build docker-push docker-run run set-revision clean setup +.PHONY: all check-version docker-build docker-push docker-run run set-revision clean setup test lint fmt + VERSION := $(strip $(shell [ -d .git ] && git describe --always --tags --dirty)) BUILD_DATE := $(shell date -u +"%Y-%m-%dT%H:%M:%S%Z") VCS_URL := $(shell [ -d .git ] && git config --get remote.origin.url) @@ -50,8 +51,10 @@ setup: test: script/test -flake: - flake8 --config=setup.cfg --statistics --count . +lint: + ruff check . + +fmt: + black . + ruff check --fix . -autopep8: - autopep8 -ira . diff --git a/README.md b/README.md index 1fa278d..37c2c9f 100644 --- a/README.md +++ b/README.md @@ -54,7 +54,7 @@ flask-mvc/ ## Prerequisites -- Python 3.11+ +- Python 3.14+ - pip Optional (for Python version management): @@ -62,7 +62,7 @@ Optional (for Python version management): ```bash brew install asdf asdf plugin add python -asdf install python 3.12.3 +asdf install python 3.14.4 ``` --- diff --git a/project/__init__.py b/project/__init__.py index fbb219a..c1e27c9 100644 --- a/project/__init__.py +++ b/project/__init__.py @@ -1,5 +1,4 @@ -# -*- coding: utf-8 -*- -__version__ = '0.1' +__version__ = "0.1" from flask import Flask from flask_debugtoolbar import DebugToolbarExtension @@ -11,15 +10,17 @@ def create_app(config_name=None): import os + if config_name is None: - config_name = os.environ.get('FLASK_ENV', 'default') + config_name = os.environ.get("FLASK_ENV", "default") - app = Flask('project') + app = Flask("project") app.config.from_object(config[config_name]) toolbar.init_app(app) from project.controllers.printer import printer_bp + app.register_blueprint(printer_bp) return app diff --git a/project/config.py b/project/config.py index e435df4..3dc0203 100644 --- a/project/config.py +++ b/project/config.py @@ -1,11 +1,10 @@ -# -*- coding: utf-8 -*- import os class Config: - SECRET_KEY = os.environ.get('SECRET_KEY') + SECRET_KEY = os.environ.get("SECRET_KEY") if not SECRET_KEY: - raise ValueError('SECRET_KEY environment variable is not set') + raise ValueError("SECRET_KEY environment variable is not set") WTF_CSRF_ENABLED = True DEBUG_TB_ENABLED = False @@ -18,7 +17,7 @@ class DevelopmentConfig(Config): class TestingConfig(Config): - SECRET_KEY = os.environ.get('SECRET_KEY', 'test-secret-key-not-for-production') + SECRET_KEY = os.environ.get("SECRET_KEY", "test-secret-key-not-for-production") TESTING = True WTF_CSRF_ENABLED = False DEBUG_TB_ENABLED = False @@ -30,8 +29,8 @@ class ProductionConfig(Config): config = { - 'development': DevelopmentConfig, - 'testing': TestingConfig, - 'production': ProductionConfig, - 'default': DevelopmentConfig, + "development": DevelopmentConfig, + "testing": TestingConfig, + "production": ProductionConfig, + "default": DevelopmentConfig, } diff --git a/project/controllers/printer.py b/project/controllers/printer.py index 80b10e9..ca707d1 100644 --- a/project/controllers/printer.py +++ b/project/controllers/printer.py @@ -1,27 +1,26 @@ -# -*- coding: utf-8 -*- -from flask import Blueprint, render_template, request, redirect, url_for +from flask import Blueprint, redirect, render_template, request, url_for from flask_wtf import FlaskForm from wtforms import StringField from wtforms.validators import DataRequired from project.models.printer import Printer -printer_bp = Blueprint('printer', __name__) +printer_bp = Blueprint("printer", __name__) class CreateForm(FlaskForm): - text = StringField('name', validators=[DataRequired()]) + text = StringField("name", validators=[DataRequired()]) -@printer_bp.route('/') +@printer_bp.route("/") def start(): - return render_template('printer/index.html') + return render_template("printer/index.html") -@printer_bp.route('/print', methods=['GET', 'POST']) +@printer_bp.route("/print", methods=["GET", "POST"]) def printer(): form = CreateForm(request.form) - if request.method == 'POST' and form.validate(): + if request.method == "POST" and form.validate(): Printer().show_string(form.text.data) - return redirect(url_for('printer.start')) - return render_template('printer/print.html', form=form) + return redirect(url_for("printer.start")) + return render_template("printer/print.html", form=form) diff --git a/project/models/printer.py b/project/models/printer.py index 197eddd..ff2486b 100644 --- a/project/models/printer.py +++ b/project/models/printer.py @@ -1,8 +1,7 @@ -# -*- coding: utf-8 -*- from flask import flash class Printer: def show_string(self, text): - flash(text + '!!!') + flash(text + "!!!") diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..e25623e --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,100 @@ +[project] +name = "flask-mvc" +version = "0.1.0" +description = "Flask MVC boilerplate following the MVC pattern" +readme = "README.md" +license = { file = "LICENSE" } +authors = [{ name = "Salimane Adjao Moustapha", email = "me@salimane.com" }] +requires-python = ">=3.14" +keywords = ["flask", "mvc", "boilerplate", "python", "web"] +dependencies = [ + "Flask==3.0.3", + "Flask-WTF==1.2.1", + "gunicorn==22.0.0", +] + +[project.urls] +Homepage = "https://github.com/salimane/flask-mvc" +Repository = "https://github.com/salimane/flask-mvc" +Changelog = "https://github.com/salimane/flask-mvc/releases" + +# --------------------------------------------------------------------------- +# Black +# --------------------------------------------------------------------------- +[tool.black] +line-length = 100 +target-version = ["py314"] +include = '\.pyi?$' +exclude = ''' +/( + \.git + | \.venv + | .*venv.* + | __pycache__ + | \.eggs + | dist + | build +)/ +''' + +# --------------------------------------------------------------------------- +# Ruff (replaces flake8 + isort + pyupgrade) +# --------------------------------------------------------------------------- +[tool.ruff] +line-length = 100 +target-version = "py314" +indent-width = 4 +fix = true +exclude = [ + ".git", + ".venv", + ".*venv.*", + "__pycache__", + ".eggs", + ".pytest_cache", + ".ruff_cache", + "dist", + "build", +] + +[tool.ruff.lint] +# E/W = pycodestyle F = pyflakes I = isort B = flake8-bugbear +# C4 = flake8-comprehensions UP = pyupgrade SIM = flake8-simplify +select = ["E", "W", "F", "I", "B", "C4", "UP", "SIM"] +# E501 line-length is Black's job; avoid double-reporting +ignore = [ + "E501", + # Magic values in assertions/views are self-explanatory in a boilerplate + "SIM108", +] +fixable = ["ALL"] +unfixable = [] +dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$" + +[tool.ruff.lint.isort] +known-first-party = ["project"] + +[tool.ruff.lint.per-file-ignores] +"tests/**" = [ + "S101", # assert statements are fine in tests + "B011", # assert False is fine in tests +] + +[tool.ruff.format] +quote-style = "double" +indent-style = "space" +skip-magic-trailing-comma = false +line-ending = "auto" + +# --------------------------------------------------------------------------- +# Coverage +# --------------------------------------------------------------------------- +[tool.coverage.run] +source = ["project"] +omit = ["tests/*", ".*venv*/*"] + +[tool.coverage.report] +show_missing = true +skip_covered = false +fail_under = 90 + diff --git a/pytest.ini b/pytest.ini new file mode 100644 index 0000000..9855d94 --- /dev/null +++ b/pytest.ini @@ -0,0 +1,6 @@ +[pytest] +testpaths = tests +python_files = test_*.py +python_classes = Test* +python_functions = test_* +addopts = -v --tb=short diff --git a/requirements-test.txt b/requirements-test.txt index 32f6849..04635da 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -1,5 +1,6 @@ -r requirements.txt -flake8>=7.0.0 +black>=24.0.0 +ruff>=0.4.0 pytest>=8.1.0 pytest-cov>=5.0.0 mock>=5.0.0 diff --git a/runserver.py b/runserver.py index 1d60fd2..987e2ed 100644 --- a/runserver.py +++ b/runserver.py @@ -1,10 +1,10 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- import os + from project import create_app -app = create_app(os.environ.get('FLASK_ENV', 'development')) +app = create_app(os.environ.get("FLASK_ENV", "development")) -if __name__ == '__main__': - port = int(os.environ.get('PORT', 8080)) - app.run('0.0.0.0', port=port) +if __name__ == "__main__": + port = int(os.environ.get("PORT", 8080)) + app.run("0.0.0.0", port=port) diff --git a/script/test b/script/test index c3c33d7..41af9ed 100755 --- a/script/test +++ b/script/test @@ -14,10 +14,10 @@ pip install -q -r $REPO_DIR/requirements-test.txt cd $REPO_DIR export PYTHONPATH=".:`pwd`" -echo "==> Static Code Analysis for PEP8 style guide and various errors" -flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics -echo "==> Full flake8 style check" -flake8 --config=setup.cfg --statistics --count . +echo "==> Format check (black)" +black --check --diff . +echo "==> Lint (ruff)" +ruff check . echo "==> Executing tests with code coverage collection" pytest --cov=project --cov-report=html --cov-report=term-missing tests/ diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index 6ed12eb..0000000 --- a/setup.cfg +++ /dev/null @@ -1,6 +0,0 @@ -[flake8] -max-line-length = 100 -exclude = .svn,CVS,.bzr,.hg,.git,__pycache__,.tox,.eggs,*.egg,.vscode,.*venv* - -[pycodestyle] -max-line-length = 100 diff --git a/tests/conftest.py b/tests/conftest.py index 0c8c3e0..3d20729 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,15 +1,15 @@ -# -*- coding: utf-8 -*- import os + import pytest -os.environ.setdefault('SECRET_KEY', 'test-secret-key-not-for-production') +os.environ.setdefault("SECRET_KEY", "test-secret-key-not-for-production") from project import create_app # noqa: E402 @pytest.fixture def app(): - app = create_app('testing') + app = create_app("testing") yield app diff --git a/tests/test_printer.py b/tests/test_printer.py index 3f4ffdb..85fac57 100644 --- a/tests/test_printer.py +++ b/tests/test_printer.py @@ -1,43 +1,40 @@ -# -*- coding: utf-8 -*- - - class TestStart: def test_get_returns_200(self, client): - response = client.get('/') + response = client.get("/") assert response.status_code == 200 def test_get_contains_link_to_print(self, client): - response = client.get('/') - assert b'/print' in response.data + response = client.get("/") + assert b"/print" in response.data class TestPrinterGet: def test_get_returns_200(self, client): - response = client.get('/print') + response = client.get("/print") assert response.status_code == 200 def test_get_renders_form(self, client): - response = client.get('/print') - assert b' Date: Sat, 11 Apr 2026 22:58:16 +0200 Subject: [PATCH 14/23] Add codecov integration (#15) Signed-off-by: Salimane Adjao Moustapha --- .github/workflows/master.yml | 3 ++- pyproject.toml | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/master.yml b/.github/workflows/master.yml index c920349..a6b3163 100644 --- a/.github/workflows/master.yml +++ b/.github/workflows/master.yml @@ -62,11 +62,12 @@ jobs: - name: Run tests with coverage env: SECRET_KEY: ${{ secrets.SECRET_KEY || 'ci-test-secret-key-not-for-production' }} - run: pytest tests/ -v --cov=project --cov-report=xml + run: pytest tests/ -v --cov=project --cov-branch --cov-report=xml - name: Upload coverage to Codecov # codecov/codecov-action@v6 (57e3a136b779b570ffcdbf80b3bdc90e7fab3de2) uses: codecov/codecov-action@57e3a136b779b570ffcdbf80b3bdc90e7fab3de2 with: + token: ${{ secrets.CODECOV_TOKEN }} files: coverage.xml fail_ci_if_error: false diff --git a/pyproject.toml b/pyproject.toml index e25623e..e8d7e2f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -91,6 +91,7 @@ line-ending = "auto" # --------------------------------------------------------------------------- [tool.coverage.run] source = ["project"] +branch = true omit = ["tests/*", ".*venv*/*"] [tool.coverage.report] From 75f1087c7a816f842a59ddd00dc56efb750e0c1b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 Apr 2026 12:56:43 +0200 Subject: [PATCH 15/23] deps(deps): bump the batch-updates group with 3 updates (#16) Bumps the batch-updates group with 3 updates: [flask](https://github.com/pallets/flask), [flask-wtf](https://github.com/pallets-eco/flask-wtf) and [gunicorn](https://github.com/benoitc/gunicorn). Updates `flask` from 3.0.3 to 3.1.3 - [Release notes](https://github.com/pallets/flask/releases) - [Changelog](https://github.com/pallets/flask/blob/main/CHANGES.rst) - [Commits](https://github.com/pallets/flask/compare/3.0.3...3.1.3) Updates `flask-wtf` from 1.2.1 to 1.2.2 - [Release notes](https://github.com/pallets-eco/flask-wtf/releases) - [Changelog](https://github.com/pallets-eco/flask-wtf/blob/main/docs/changes.rst) - [Commits](https://github.com/pallets-eco/flask-wtf/compare/v1.2.1...v1.2.2) Updates `gunicorn` from 22.0.0 to 25.3.0 - [Release notes](https://github.com/benoitc/gunicorn/releases) - [Commits](https://github.com/benoitc/gunicorn/compare/22.0.0...25.3.0) --- updated-dependencies: - dependency-name: flask dependency-version: 3.1.3 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: batch-updates - dependency-name: flask-wtf dependency-version: 1.2.2 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: batch-updates - dependency-name: gunicorn dependency-version: 25.3.0 dependency-type: direct:production update-type: version-update:semver-major dependency-group: batch-updates ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pyproject.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index e8d7e2f..9443be9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,9 +8,9 @@ authors = [{ name = "Salimane Adjao Moustapha", email = "me@salimane.com" }] requires-python = ">=3.14" keywords = ["flask", "mvc", "boilerplate", "python", "web"] dependencies = [ - "Flask==3.0.3", - "Flask-WTF==1.2.1", - "gunicorn==22.0.0", + "Flask==3.1.3", + "Flask-WTF==1.2.2", + "gunicorn==25.3.0", ] [project.urls] From 6fcef7dd3e710ddacd9260e42a8fff3eb6d883dc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 Apr 2026 20:37:34 +0200 Subject: [PATCH 16/23] deps(deps): bump flask-security in the batch-updates group (#17) Bumps the batch-updates group with 1 update: [flask-security](https://github.com/pallets-eco/flask-security). Updates `flask-security` from 5.7.1 to 5.8.0 - [Release notes](https://github.com/pallets-eco/flask-security/releases) - [Changelog](https://github.com/pallets-eco/flask-security/blob/main/CHANGES.rst) - [Commits](https://github.com/pallets-eco/flask-security/compare/5.7.1...5.8.0) --- updated-dependencies: - dependency-name: flask-security dependency-version: 5.8.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: batch-updates ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index a2ef34c..5483fbe 100644 --- a/requirements.txt +++ b/requirements.txt @@ -12,7 +12,7 @@ Flask-Migrate==4.1.0 Flask-Principal==0.4.0 Flask-QueryInspect==0.1.2 Flask-RESTful==0.3.10 -Flask-Security==5.7.1 +Flask-Security==5.8.0 Flask-SQLAlchemy==3.1.1 Flask-Uploads==0.2.1 Flask-WTF==1.2.2 From 39616183ea803b007d73bbb107803f3ac6bee474 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 23 Apr 2026 12:12:01 +0200 Subject: [PATCH 17/23] deps(deps): bump the batch-updates group across 1 directory with 6 updates (#19) Updates the requirements on [flask-wtf](https://github.com/pallets-eco/flask-wtf), [black](https://github.com/psf/black), [ruff](https://github.com/astral-sh/ruff), [pytest](https://github.com/pytest-dev/pytest), [pytest-cov](https://github.com/pytest-dev/pytest-cov) and [mock](https://github.com/testing-cabal/mock) to permit the latest version. Updates `flask-wtf` from 1.2.2 to 1.3.0 - [Release notes](https://github.com/pallets-eco/flask-wtf/releases) - [Changelog](https://github.com/pallets-eco/flask-wtf/blob/main/docs/changes.rst) - [Commits](https://github.com/pallets-eco/flask-wtf/compare/v1.2.2...v1.3.0) Updates `black` to 26.3.1 - [Release notes](https://github.com/psf/black/releases) - [Changelog](https://github.com/psf/black/blob/main/CHANGES.md) - [Commits](https://github.com/psf/black/compare/24.1a1...26.3.1) Updates `ruff` to 0.15.11 - [Release notes](https://github.com/astral-sh/ruff/releases) - [Changelog](https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md) - [Commits](https://github.com/astral-sh/ruff/compare/v0.4.0...0.15.11) Updates `pytest` to 9.0.3 - [Release notes](https://github.com/pytest-dev/pytest/releases) - [Changelog](https://github.com/pytest-dev/pytest/blob/main/CHANGELOG.rst) - [Commits](https://github.com/pytest-dev/pytest/compare/8.1.0...9.0.3) Updates `pytest-cov` to 7.1.0 - [Changelog](https://github.com/pytest-dev/pytest-cov/blob/master/CHANGELOG.rst) - [Commits](https://github.com/pytest-dev/pytest-cov/compare/v5.0.0...v7.1.0) Updates `mock` to 5.2.0 - [Changelog](https://github.com/testing-cabal/mock/blob/master/CHANGELOG.rst) - [Commits](https://github.com/testing-cabal/mock/compare/5.0.0...5.2.0) --- updated-dependencies: - dependency-name: flask-wtf dependency-version: 1.3.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: batch-updates - dependency-name: black dependency-version: 26.3.1 dependency-type: direct:production dependency-group: batch-updates - dependency-name: ruff dependency-version: 0.15.11 dependency-type: direct:production dependency-group: batch-updates - dependency-name: pytest dependency-version: 9.0.3 dependency-type: direct:production dependency-group: batch-updates - dependency-name: pytest-cov dependency-version: 7.1.0 dependency-type: direct:production dependency-group: batch-updates - dependency-name: mock dependency-version: 5.2.0 dependency-type: direct:production dependency-group: batch-updates ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pyproject.toml | 2 +- requirements-test.txt | 10 +++++----- requirements.txt | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 9443be9..5e8250a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,7 +9,7 @@ requires-python = ">=3.14" keywords = ["flask", "mvc", "boilerplate", "python", "web"] dependencies = [ "Flask==3.1.3", - "Flask-WTF==1.2.2", + "Flask-WTF==1.3.0", "gunicorn==25.3.0", ] diff --git a/requirements-test.txt b/requirements-test.txt index 04635da..4dc0669 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -1,6 +1,6 @@ -r requirements.txt -black>=24.0.0 -ruff>=0.4.0 -pytest>=8.1.0 -pytest-cov>=5.0.0 -mock>=5.0.0 +black>=26.3.1 +ruff>=0.15.11 +pytest>=9.0.3 +pytest-cov>=7.1.0 +mock>=5.2.0 diff --git a/requirements.txt b/requirements.txt index 5483fbe..7a2932e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -15,7 +15,7 @@ Flask-RESTful==0.3.10 Flask-Security==5.8.0 Flask-SQLAlchemy==3.1.1 Flask-Uploads==0.2.1 -Flask-WTF==1.2.2 +Flask-WTF==1.3.0 Jinja2==3.1.6 gunicorn==25.3.0 From 540f4ee8ed246478bfe9539c743d65bd9bf1f585 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 28 Apr 2026 01:03:08 +0200 Subject: [PATCH 18/23] deps(deps): update ruff requirement in the batch-updates group (#20) Updates the requirements on [ruff](https://github.com/astral-sh/ruff) to permit the latest version. Updates `ruff` to 0.15.12 - [Release notes](https://github.com/astral-sh/ruff/releases) - [Changelog](https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md) - [Commits](https://github.com/astral-sh/ruff/compare/0.15.11...0.15.12) --- updated-dependencies: - dependency-name: ruff dependency-version: 0.15.12 dependency-type: direct:production dependency-group: batch-updates ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements-test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-test.txt b/requirements-test.txt index 4dc0669..3a54e11 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -1,6 +1,6 @@ -r requirements.txt black>=26.3.1 -ruff>=0.15.11 +ruff>=0.15.12 pytest>=9.0.3 pytest-cov>=7.1.0 mock>=5.2.0 From 433c88d1612cbed26d23878bba7b78d7dd3545cb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 May 2026 13:41:07 +0200 Subject: [PATCH 19/23] ci: bump actions/checkout from 4 to 6 (#22) * ci: bump actions/checkout from 4 to 6 Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 6. - [Release notes](https://github.com/actions/checkout/releases) - [Commits](https://github.com/actions/checkout/compare/v4...v6) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] * Update CodeQL action versions in workflow --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Salimane Adjao Moustapha --- .github/workflows/codeql.yml | 6 +++--- .github/workflows/master.yml | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index e23a9d8..d0a6d0e 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -57,7 +57,7 @@ jobs: # your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # Add any setup steps before running the `github/codeql-action/init` action. # This includes steps like installing compilers or runtimes (`actions/setup-node` @@ -67,7 +67,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v4 + uses: github/codeql-action/init@65216971a11ded447a6b76263d5a144519e5eee1 with: languages: ${{ matrix.language }} build-mode: ${{ matrix.build-mode }} @@ -96,6 +96,6 @@ jobs: exit 1 - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v4 + uses: github/codeql-action/analyze@65216971a11ded447a6b76263d5a144519e5eee1 with: category: "/language:${{matrix.language}}" diff --git a/.github/workflows/master.yml b/.github/workflows/master.yml index a6b3163..e3fc893 100644 --- a/.github/workflows/master.yml +++ b/.github/workflows/master.yml @@ -16,7 +16,7 @@ jobs: steps: # actions/checkout@v5 (93cb6efe18208431cddfb8368fd83d5badbf9bfd) - - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd - name: Set up Python # actions/setup-python@v5 (a26af69be951a213d495a4c3e4e4022e16d87065) @@ -44,7 +44,7 @@ jobs: steps: # actions/checkout@v5 (93cb6efe18208431cddfb8368fd83d5badbf9bfd) - - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd - name: Set up Python # actions/setup-python@v5 (a26af69be951a213d495a4c3e4e4022e16d87065) From a025ae778a49f10de85eac573ca7d3ff356b8171 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 May 2026 13:41:29 +0200 Subject: [PATCH 20/23] deps(deps): bump flask-admin in the batch-updates group (#21) Bumps the batch-updates group with 1 update: [flask-admin](https://github.com/pallets-eco/flask-admin). Updates `flask-admin` from 2.0.2 to 2.1.0 - [Release notes](https://github.com/pallets-eco/flask-admin/releases) - [Changelog](https://github.com/pallets-eco/flask-admin/blob/master/doc/changelog.rst) - [Commits](https://github.com/pallets-eco/flask-admin/compare/v2.0.2...v2.1.0) --- updated-dependencies: - dependency-name: flask-admin dependency-version: 2.1.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: batch-updates ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 7a2932e..5be233a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ Flask==3.1.3 -Flask-Admin==2.0.2 +Flask-Admin==2.1.0 Flask-Assets==2.1.0 Flask-Babel==4.0.0 Flask-Bcrypt==1.0.1 From 6706eed1433b24b0c88142ebb634caefc1777c91 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 May 2026 15:59:47 +0200 Subject: [PATCH 21/23] deps(deps): bump the batch-updates group across 1 directory with 4 updates (#26) Updates the requirements on [gunicorn](https://github.com/benoitc/gunicorn), [flask-admin](https://github.com/pallets-eco/flask-admin), [black](https://github.com/psf/black) and [ruff](https://github.com/astral-sh/ruff) to permit the latest version. Updates `gunicorn` from 25.3.0 to 26.0.0 - [Release notes](https://github.com/benoitc/gunicorn/releases) - [Commits](https://github.com/benoitc/gunicorn/compare/25.3.0...26.0.0) Updates `flask-admin` from 2.1.0 to 2.2.0 - [Release notes](https://github.com/pallets-eco/flask-admin/releases) - [Changelog](https://github.com/pallets-eco/flask-admin/blob/master/doc/changelog.rst) - [Commits](https://github.com/pallets-eco/flask-admin/compare/v2.1.0...v2.2.0) Updates `black` to 26.5.0 - [Release notes](https://github.com/psf/black/releases) - [Changelog](https://github.com/psf/black/blob/main/CHANGES.md) - [Commits](https://github.com/psf/black/compare/26.3.1...26.5.0) Updates `ruff` to 0.15.13 - [Release notes](https://github.com/astral-sh/ruff/releases) - [Changelog](https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md) - [Commits](https://github.com/astral-sh/ruff/compare/0.15.12...0.15.13) --- updated-dependencies: - dependency-name: gunicorn dependency-version: 26.0.0 dependency-type: direct:production update-type: version-update:semver-major dependency-group: batch-updates - dependency-name: flask-admin dependency-version: 2.2.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: batch-updates - dependency-name: black dependency-version: 26.5.0 dependency-type: direct:production dependency-group: batch-updates - dependency-name: ruff dependency-version: 0.15.13 dependency-type: direct:production dependency-group: batch-updates ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pyproject.toml | 2 +- requirements-test.txt | 4 ++-- requirements.txt | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 5e8250a..322978b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,7 +10,7 @@ keywords = ["flask", "mvc", "boilerplate", "python", "web"] dependencies = [ "Flask==3.1.3", "Flask-WTF==1.3.0", - "gunicorn==25.3.0", + "gunicorn==26.0.0", ] [project.urls] diff --git a/requirements-test.txt b/requirements-test.txt index 3a54e11..14a650b 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -1,6 +1,6 @@ -r requirements.txt -black>=26.3.1 -ruff>=0.15.12 +black>=26.5.0 +ruff>=0.15.13 pytest>=9.0.3 pytest-cov>=7.1.0 mock>=5.2.0 diff --git a/requirements.txt b/requirements.txt index 5be233a..6c2c9bc 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ Flask==3.1.3 -Flask-Admin==2.1.0 +Flask-Admin==2.2.0 Flask-Assets==2.1.0 Flask-Babel==4.0.0 Flask-Bcrypt==1.0.1 @@ -17,7 +17,7 @@ Flask-SQLAlchemy==3.1.1 Flask-Uploads==0.2.1 Flask-WTF==1.3.0 Jinja2==3.1.6 -gunicorn==25.3.0 +gunicorn==26.0.0 # jobs Celery==5.6.3 From 865a8d714ed1ab67732a2495f4933b43b709d4dc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Jun 2026 12:36:57 +0200 Subject: [PATCH 22/23] deps(deps): bump the batch-updates group across 1 directory with 5 updates (#31) Updates the requirements on [flask-cors](https://github.com/corydolphin/flask-cors), [flask-security](https://github.com/pallets-eco/flask-security), [black](https://github.com/psf/black), [ruff](https://github.com/astral-sh/ruff) and [pytest](https://github.com/pytest-dev/pytest) to permit the latest version. Updates `flask-cors` from 6.0.2 to 6.0.5 - [Release notes](https://github.com/corydolphin/flask-cors/releases) - [Changelog](https://github.com/corydolphin/flask-cors/blob/main/CHANGELOG.md) - [Commits](https://github.com/corydolphin/flask-cors/compare/6.0.2...6.0.5) Updates `flask-security` from 5.8.0 to 5.8.1 - [Release notes](https://github.com/pallets-eco/flask-security/releases) - [Changelog](https://github.com/pallets-eco/flask-security/blob/main/CHANGES.rst) - [Commits](https://github.com/pallets-eco/flask-security/compare/5.8.0...5.8.1) Updates `black` to 26.5.1 - [Release notes](https://github.com/psf/black/releases) - [Changelog](https://github.com/psf/black/blob/main/CHANGES.md) - [Commits](https://github.com/psf/black/compare/26.5.0...26.5.1) Updates `ruff` to 0.15.17 - [Release notes](https://github.com/astral-sh/ruff/releases) - [Changelog](https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md) - [Commits](https://github.com/astral-sh/ruff/compare/0.15.13...0.15.17) Updates `pytest` to 9.1.0 - [Release notes](https://github.com/pytest-dev/pytest/releases) - [Changelog](https://github.com/pytest-dev/pytest/blob/main/CHANGELOG.rst) - [Commits](https://github.com/pytest-dev/pytest/compare/9.0.3...9.1.0) --- updated-dependencies: - dependency-name: flask-cors dependency-version: 6.0.5 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: batch-updates - dependency-name: flask-security dependency-version: 5.8.1 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: batch-updates - dependency-name: black dependency-version: 26.5.1 dependency-type: direct:production dependency-group: batch-updates - dependency-name: ruff dependency-version: 0.15.17 dependency-type: direct:production dependency-group: batch-updates - dependency-name: pytest dependency-version: 9.1.0 dependency-type: direct:production dependency-group: batch-updates ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements-test.txt | 6 +++--- requirements.txt | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/requirements-test.txt b/requirements-test.txt index 14a650b..7c5ba26 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -1,6 +1,6 @@ -r requirements.txt -black>=26.5.0 -ruff>=0.15.13 -pytest>=9.0.3 +black>=26.5.1 +ruff>=0.15.17 +pytest>=9.1.0 pytest-cov>=7.1.0 mock>=5.2.0 diff --git a/requirements.txt b/requirements.txt index 6c2c9bc..eb978db 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,7 +3,7 @@ Flask-Admin==2.2.0 Flask-Assets==2.1.0 Flask-Babel==4.0.0 Flask-Bcrypt==1.0.1 -Flask-Cors==6.0.2 +Flask-Cors==6.0.5 Flask-DebugToolbar==0.16.0 Flask-Limiter==4.1.1 Flask-Login==0.6.3 @@ -12,7 +12,7 @@ Flask-Migrate==4.1.0 Flask-Principal==0.4.0 Flask-QueryInspect==0.1.2 Flask-RESTful==0.3.10 -Flask-Security==5.8.0 +Flask-Security==5.8.1 Flask-SQLAlchemy==3.1.1 Flask-Uploads==0.2.1 Flask-WTF==1.3.0 From a9bff1880d883bde9552487e3e9e560d6d40ed03 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Jun 2026 12:38:39 +0200 Subject: [PATCH 23/23] ci: bump actions/setup-python from 5.6.0 to 6.2.0 (#29) Bumps [actions/setup-python](https://github.com/actions/setup-python) from 5.6.0 to 6.2.0. - [Release notes](https://github.com/actions/setup-python/releases) - [Commits](https://github.com/actions/setup-python/compare/a26af69be951a213d495a4c3e4e4022e16d87065...a309ff8b426b58ec0e2a45f0f869d46889d02405) --- updated-dependencies: - dependency-name: actions/setup-python dependency-version: 6.2.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Salimane Adjao Moustapha --- .github/workflows/master.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/master.yml b/.github/workflows/master.yml index e3fc893..ba64ccb 100644 --- a/.github/workflows/master.yml +++ b/.github/workflows/master.yml @@ -20,7 +20,7 @@ jobs: - name: Set up Python # actions/setup-python@v5 (a26af69be951a213d495a4c3e4e4022e16d87065) - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 with: python-version: "3.14" cache: pip @@ -48,7 +48,7 @@ jobs: - name: Set up Python # actions/setup-python@v5 (a26af69be951a213d495a4c3e4e4022e16d87065) - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 with: python-version: "3.14" cache: pip