🎉(build) bootstrap library folders

Add initial project structure with tests and configuration files:
 - ruff for format and linting
 - uv with hatch for packaging
This commit is contained in:
Quentin BEY
2025-03-28 23:40:37 +01:00
parent 61df4414f7
commit f049940ddb
21 changed files with 750 additions and 0 deletions

28
.github/ISSUE_TEMPLATE/Bug_report.md vendored Normal file
View File

@@ -0,0 +1,28 @@
---
name: 🐛 Bug Report
about: If something is not working as expected 🤔.
---
## Bug Report
**Problematic behavior**
A clear and concise description of the behavior.
**Expected behavior/code**
A clear and concise description of what you expected to happen (or code).
**Steps to Reproduce**
1. Do this...
2. Then this...
3. And then the bug happens!
**Environment**
- Library version:
- Platform:
**Possible Solution**
<!--- Only if you have suggestions on a fix for the bug -->
**Additional context/Screenshots**
Add any other context about the problem here. If applicable, add screenshots to help explain.

View File

@@ -0,0 +1,23 @@
---
name: ✨ Feature Request
about: I have a suggestion (and may want to build it 💪)!
---
## Feature Request
**Is your feature request related to a problem or unsupported use case? Please describe.**
A clear and concise description of what the problem is. For example: I need to do some task and I have an issue...
**Describe the solution you'd like**
A clear and concise description of what you want to happen. Add any considered drawbacks.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Discovery, Documentation, Adoption, Migration Strategy**
If you can, explain how users will be able to use this and possibly write out a version the docs (if applicable).
Maybe a screenshot or design?
**Do you want to work on it through a Pull Request?**
<!-- Make sure to coordinate with us before you spend too much time working on an implementation! -->

View File

@@ -0,0 +1,22 @@
---
name: 🤗 Support Question
about: If you have a question 💬, or something was not clear from the docs!
---
<!-- ^ Click "Preview" for a nicer view! ^
We primarily use GitHub as an issue tracker. If however you're encountering an issue not covered in the docs, we may be able to help! -->
---
Please make sure you have read our [main Readme](https://github.com/suitenumerique/django-lasuite).
Also make sure it was not already answered in [an open or close issue](https://github.com/suitenumerique/django-lasuite/issues).
If your question was not covered, and you feel like it should be, fire away! We'd love to improve our docs! 👌
**Topic**
What's the general area of your question: for example, setup, database schema, search functionality,...
**Question**
Try to be as specific as possible so we can help you as best we can. Please be patient 🙏

11
.github/PULL_REQUEST_TEMPLATE.md vendored Normal file
View File

@@ -0,0 +1,11 @@
## Purpose
Description...
## Proposal
Description...
- [] item 1...
- [] item 2...

90
.github/workflows/tests.yml vendored Normal file
View File

@@ -0,0 +1,90 @@
name: Test Workflow
on:
push:
branches:
- main
pull_request:
branches:
- '*'
jobs:
lint-git:
runs-on: ubuntu-latest
if: github.event_name == 'pull_request' # Makes sense only for pull requests
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: show
run: git log
- name: Check absence of fixup commits
run: |
! git log | grep 'fixup!'
- name: Install gitlint
run: pip install --user requests gitlint
- name: Lint commit messages added to main
run: ~/.local/bin/gitlint --commits origin/${{ github.event.pull_request.base.ref }}..HEAD
check-changelog:
runs-on: ubuntu-latest
if: |
contains(github.event.pull_request.labels.*.name, 'noChangeLog') == false &&
github.event_name == 'pull_request'
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Check that the CHANGELOG has been modified in the current branch
run: git whatchanged --name-only --pretty="" origin/${{ github.event.pull_request.base.ref }}..HEAD | grep CHANGELOG
lint-changelog:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Check CHANGELOG max line length
run: |
max_line_length=$(cat CHANGELOG.md | grep -Ev "^\[.*\]: https://github.com" | wc -L)
if [ $max_line_length -ge 80 ]; then
echo "ERROR: CHANGELOG has lines longer than 80 characters."
exit 1
fi
lint-back:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Install Python
uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Install development dependencies
run: pip install --user .[dev]
- name: Check code formatting with ruff
run: ~/.local/bin/ruff format . --diff
- name: Lint code with ruff
run: ~/.local/bin/ruff check .
test-pytest:
runs-on: ubuntu-latest
env:
DJANGO_SETTINGS_MODULE: test_project.settings
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Install Python
uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Install development dependencies
run: pip install --user .[dev]
- name: Run tests
run: ~/.local/bin/pytest -vvv tests/

53
.gitignore vendored Normal file
View File

@@ -0,0 +1,53 @@
# Python
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg
# Django
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
media/
staticfiles/
# Virtual Environment
venv/
env/
ENV/
# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.coverage.*
.cache
coverage.xml
*.cover
# Editor/IDE specific
.idea/
.vscode/
*.swp
*.swo
# OS specific files
.DS_Store
Thumbs.db

78
.gitlint Normal file
View File

@@ -0,0 +1,78 @@
# All these sections are optional, edit this file as you like.
[general]
# Ignore certain rules, you can reference them by their id or by their full name
# ignore=title-trailing-punctuation, T3
# verbosity should be a value between 1 and 3, the commandline -v flags take precedence over this
# verbosity = 2
# By default gitlint will ignore merge commits. Set to 'false' to disable.
# ignore-merge-commits=true
# By default gitlint will ignore fixup commits. Set to 'false' to disable.
# ignore-fixup-commits=true
# By default gitlint will ignore squash commits. Set to 'false' to disable.
# ignore-squash-commits=true
# Enable debug mode (prints more output). Disabled by default.
# debug=true
# Set the extra-path where gitlint will search for user defined rules
# See http://jorisroovers.github.io/gitlint/user_defined_rules for details
extra-path=gitlint/
# [title-max-length]
# line-length=80
[title-must-not-contain-word]
# Comma-separated list of words that should not occur in the title. Matching is case
# insensitive. It's fine if the keyword occurs as part of a larger word (so "WIPING"
# will not cause a violation, but "WIP: my title" will.
words=wip
#[title-match-regex]
# python like regex (https://docs.python.org/2/library/re.html) that the
# commit-msg title must be matched to.
# Note that the regex can contradict with other rules if not used correctly
# (e.g. title-must-not-contain-word).
#regex=
# [B1]
# B1 = body-max-line-length
# line-length=120
# [body-min-length]
# min-length=5
# [body-is-missing]
# Whether to ignore this rule on merge commits (which typically only have a title)
# default = True
# ignore-merge-commits=false
# [body-changed-file-mention]
# List of files that need to be explicitly mentioned in the body when they are changed
# This is useful for when developers often erroneously edit certain files or git submodules.
# By specifying this rule, developers can only change the file when they explicitly reference
# it in the commit message.
# files=gitlint/rules.py,README.md
# [author-valid-email]
# python like regex (https://docs.python.org/2/library/re.html) that the
# commit author email address should be matched to
# For example, use the following regex if you only want to allow email addresses from foo.com
# regex=[^@]+@foo.com
[ignore-by-title]
# Allow empty body & wrong title pattern only when bots (pyup/greenkeeper)
# upgrade dependencies
regex=^(⬆️.*|Update (.*) from (.*) to (.*)|(chore|fix)\(package\): update .*)$
ignore=B6,UC1
# [ignore-by-body]
# Ignore certain rules for commits of which the body has a line that matches a regex
# E.g. Match bodies that have a line that that contain "release"
# regex=(.*)release(.*)
#
# Ignore certain rules, you can reference them by their id or by their full name
# Use 'all' to ignore all rules
# ignore=T1,body-min-length

11
CHANGELOG.md Normal file
View File

@@ -0,0 +1,11 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0),
and this project adheres to
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
[unreleased]: https://github.com/suitenumerique/django-lasuite/commits/main/

91
Makefile Normal file
View File

@@ -0,0 +1,91 @@
# /!\ /!\ /!\ /!\ /!\ /!\ /!\ DISCLAIMER /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\
#
# This Makefile is only meant to be used for DEVELOPMENT purpose as we are
# changing the user id that will run in the container.
#
# PLEASE DO NOT USE IT FOR YOUR CI/PRODUCTION/WHATEVER...
#
# /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\
#
# Note to developers:
#
# While editing this file, please respect the following statements:
#
# 1. Every variable should be defined in the ad hoc VARIABLES section with a
# relevant subsection
# 2. Every new rule should be defined in the ad hoc RULES section with a
# relevant subsection depending on the targeted service
# 3. Rules should be sorted alphabetically within their section
# 4. When a rule has multiple dependencies, you should:
# - duplicate the rule name to add the help string (if required)
# - write one dependency per line to increase readability and diffs
# 5. .PHONY rule statement should be written after the corresponding rule
# ==============================================================================
# VARIABLES
BOLD := \033[1m
RESET := \033[0m
GREEN := \033[1;32m
# Use uv for package management
UV = uv
# ==============================================================================
# RULES
default: help
help: ## Display this help message
@echo "$(BOLD)Django LaSuite Makefile"
@echo "Please use 'make $(BOLD)target$(RESET)' where $(BOLD)target$(RESET) is one of:"
@grep -E '^[a-zA-Z0-9_-]+:.*?## .*$$' $(firstword $(MAKEFILE_LIST)) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "$(GREEN)%-30s$(RESET) %s\n", $$1, $$2}'
.PHONY: help
install: ## Install the project
@$(UV) sync
.PHONY: install
install-dev: ## Install the project with dev dependencies
@$(UV) sync --extra dev
.PHONY: install-dev
install-build: ## Install the project with build dependencies
@$(UV) sync --extra build
clean: ## Clean the project folder
@rm -rf build/
@rm -rf dist/
@rm -rf *.egg-info
@find . -type d -name __pycache__ -exec rm -rf {} +
@find . -type f -name "*.pyc" -delete
.PHONY: clean
format: ## Run the formatter
@ruff format
.PHONY: format
lint: format ## Run the linter
@ruff check .
.PHONY: lint
test: ## Run the tests
@pytest tests/ -v
.PHONY: test
build: install-build ## Build the project
@$(UV) build
.PHONY: build
migrate: ## Run the test project migrations
@cd tests && python -m test_project.manage migrate
.PHONY: migrate
runserver: ## Run the test project server
@cd tests && python -m test_project.manage runserver
.PHONY: runserver
shell: ## Run the test project Django shell
@cd tests && python -m test_project.manage shell
.PHONY: shell

47
README.md Normal file
View File

@@ -0,0 +1,47 @@
# Django La Suite
A Django library for the common requirement for the "La Suite numérique" projects.
## Installation
```bash
pip install django-lasuite
```
## Quick Start
To be documented with the first implementations.
## Development
### Requirements
For this project, we use `uv` Python package manager.
Please follow the installation guidelines on the [uv documentation](https://docs.astral.sh/uv/getting-started/installation/).
### Setup
```bash
# Clone the repository
git clone https://github.com/suitenumerique/django-lasuite.git
cd django-lasuite
# Install development dependencies
make install-dev
```
### Testing
```bash
make test
```
### Code Quality
```bash
make lint
```
## License
MIT License

35
gitlint/gitlint_emoji.py Normal file
View File

@@ -0,0 +1,35 @@
"""Gitlint extra rule to validate the message title."""
import re
import requests
from gitlint.rules import CommitMessageTitle, LineRule, RuleViolation
class GitmojiTitle(LineRule):
"""
Enforce that each commit title is of the form "<gitmoji>(<scope>) <subject>"
where gitmoji is an emoji from the list defined in https://gitmoji.carloscuesta.me and
subject should be all lowercase.
"""
id = "UC1"
name = "title-should-have-gitmoji-and-scope"
target = CommitMessageTitle
def validate(self, title, _commit):
"""
Download the list possible gitmojis from the project's GitHub repository and check that
title contains one of them.
"""
gitmojis = requests.get(
"https://raw.githubusercontent.com/carloscuesta/gitmoji/master/packages/gitmojis/src/gitmojis.json",
timeout=10,
).json()["gitmojis"]
emojis = [item["emoji"] for item in gitmojis]
pattern = r"^({:s})\(.*\)\s[a-z].*$".format("|".join(emojis))
if not re.search(pattern, title):
violation_msg = 'Title does not match regex "<gitmoji>(<scope>) <subject>"'
return [RuleViolation(self.id, violation_msg, title)]
return None

118
pyproject.toml Normal file
View File

@@ -0,0 +1,118 @@
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[project]
name = "django-lasuite"
version = "0.0.1"
description = "Django La Suite - A Django library"
readme = "README.md"
requires-python = ">=3.10"
license = {file = "LICENSE"}
authors = [
{name = "DINUM", email = "dev@mail.numerique.gouv.fr"},
]
classifiers = [
"Development Status :: 3 - Alpha",
"Framework :: Django",
"Framework :: Django :: 5.0",
"Intended Audience :: Developers",
"License :: OSI Approved :: MIT License",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
]
dependencies = [
"django>=5.0",
]
[project.urls]
"Homepage" = "https://github.com/suitenumerique/django-lasuite"
"Bug Tracker" = "https://github.com/suitenumerique/django-lasuite/issues"
[project.optional-dependencies]
build = [
"setuptools",
"wheel",
]
dev = [
"pytest",
"ruff",
]
[tool.hatch.build.targets.sdist]
only-include = ["src"]
[tool.hatch.build.targets.wheel]
packages = ["src/lasuite"]
[tool.pytest.ini_options]
python_files = "test_*.py"
testpaths = ["tests"]
[tool.ruff]
line-length = 120
target-version = "py310"
lint.select = [
# pycodestyle
"E", "W",
# Pyflakes
"F",
# pyupgrade
"UP",
# flake8-bugbear
"B",
# flake8-simplify
"SIM",
# isort
"I",
# flake8-logging-format
"G",
# flake8-pie
"PIE",
# flake8-comprehensions
"C4",
# flake8-django
"DJ",
# flake8-bandit
"S",
# flake8-builtins
"A",
# flake8-datetimez
"DTZ",
# flake8-gettext
"INT",
# Pylint
"PL",
# flake8-fixme
"FIX",
# flake8-self
"SLF",
# flake8-return
"RET",
# pep8-naming (N)
"N",
# pydocstyle
"D",
# flake8-pytest-style (PT)
"PT",
]
lint.ignore = [
# incorrect-blank-line-before-class
"D203",
# missing-blank-line-after-summary
"D205",
# multi-line-summary-first-line
"D212",
]
lint.per-file-ignores = {"**/tests/*"= [
# flake8-bandit
"S",
# flake8-self
"SLF",
]}
[tool.ruff.lint.isort]
known-first-party = ["lasuite"]

7
setup.py Normal file
View File

@@ -0,0 +1,7 @@
"""Setup file for the package."""
from setuptools import setup
# This file is kept for compatibility with older tools
# The build configuration is primarily in pyproject.toml
setup()

1
src/lasuite/__init__.py Normal file
View File

@@ -0,0 +1 @@
"""Django La Suite library."""

1
tests/__init__.py Normal file
View File

@@ -0,0 +1 @@
"""Test suite for the library."""

6
tests/test_dummy.py Normal file
View File

@@ -0,0 +1,6 @@
"""A dummy test to check if the test suite is working."""
def test_success():
"""A dummy test to check if the test suite is working."""
assert True

View File

@@ -0,0 +1 @@
"""Django test project."""

View File

@@ -0,0 +1,12 @@
#!/usr/bin/env python
"""Test project management script."""
import os
import sys
from django.core.management import execute_from_command_line
if __name__ == "__main__":
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "test_project.settings")
execute_from_command_line(sys.argv)

View File

@@ -0,0 +1,96 @@
"""Django settings for test project."""
from pathlib import Path
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent.parent
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = "django-insecure-test-key-for-development-only" # noqa: S105
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = ["*"]
# Application definition
INSTALLED_APPS = [
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
]
MIDDLEWARE = [
"django.middleware.security.SecurityMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware",
"django.middleware.common.CommonMiddleware",
"django.middleware.csrf.CsrfViewMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
"django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware",
]
ROOT_URLCONF = "test_project.urls"
TEMPLATES = [
{
"BACKEND": "django.template.backends.django.DjangoTemplates",
"DIRS": [],
"APP_DIRS": True,
"OPTIONS": {
"context_processors": [
"django.template.context_processors.debug",
"django.template.context_processors.request",
"django.contrib.auth.context_processors.auth",
"django.contrib.messages.context_processors.messages",
],
},
},
]
WSGI_APPLICATION = "test_project.wsgi.application"
# Database
DATABASES = {
"default": {
"ENGINE": "django.db.backends.sqlite3",
"NAME": BASE_DIR / "db.sqlite3",
}
}
# Internationalization
LANGUAGE_CODE = "en-us"
TIME_ZONE = "UTC"
USE_I18N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
STATIC_URL = "/static/"
# Default primary key field type
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
# Logging Configuration
LOGGING = {
"version": 1,
"disable_existing_loggers": False,
"formatters": {
"verbose": {
"format": "{levelname} {asctime} {module} {message}",
"style": "{",
},
},
"handlers": {
"console": {
"class": "logging.StreamHandler",
"formatter": "verbose",
},
},
"root": {
"handlers": ["console"],
"level": "INFO",
},
}

View File

@@ -0,0 +1,3 @@
"""Test project URL configuration."""
urlpatterns = []

View File

@@ -0,0 +1,16 @@
"""
WSGI config for the test project.
It exposes the WSGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/3.1/howto/deployment/wsgi/
"""
import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "test_project.settings")
application = get_wsgi_application()