feat: use uv
All checks were successful
continuous-integration/drone/pr Build is passing

its faster, and has some nicer features
This commit is contained in:
Darragh Elliott
2025-06-17 21:48:18 +00:00
parent 940813aad0
commit c79f5ef415
11 changed files with 139 additions and 113 deletions

1
.dockerignore Symbolic link
View File

@@ -0,0 +1 @@
.gitignore

3
.gitignore vendored
View File

@@ -5,9 +5,10 @@ global/data/topbanner/.topbanner.??.xml
.*.xmllist
# Local build stuff
output
# Python venv
# Python stuff
.venv
__pycache__
uv.lock
#Nltk
.nltk_data

View File

@@ -1,33 +1,30 @@
FROM debian:latest
COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/
# Install deps
RUN apt update
RUN apt install --yes --no-install-recommends \
RUN apt-get update && apt-get install --yes --no-install-recommends \
rsync \
libxslt1.1 \
libxml2 \
golang \
python3 \
python3-venv \
python3-pip \
git \
node-less \
openssh-client \
ca-certificates \
expect
# Set uv project env, to persist stuff moving dirs
ENV UV_PROJECT_ENVIRONMENT=/root/.cache/uv/venv
# Set the workdir
WORKDIR /website-source
# Setup venv
ENV VIRTUAL_ENV=/opt/venv
RUN python3 -m venv $VIRTUAL_ENV
ENV PATH="$VIRTUAL_ENV/bin:$PATH"
# Copy the requirements
# Copy the pyproject and build deps
# Done in a seperate step for optimal docker caching
COPY ./requirements.txt /website-source/requirements.txt
RUN pip install -r /website-source/requirements.txt
COPY ./pyproject.toml .
RUN uv sync --no-install-package build
# Copy everything else
COPY . /website-source/
WORKDIR /website-source
COPY . .
ENTRYPOINT [ "bash", "./entrypoint.sh" ]

View File

@@ -82,12 +82,12 @@ Alterations to build scripts or the files used site-wide will result in near ful
### Native
We can either install the required dependencies manually using our preferred package manager. If you are a nix use one can run `nix-shell` to enter a shell with the required build dependencies, with the python `virtualenv` already installed and activated.
We can either install the required dependencies manually using our preferred package manager. If you are a nix use one can run `nix-shell` to enter a shell with the required build dependencies.
If installing manually, the required binary names are
```
python3 pip
uv
```
Also needed are the libraries
@@ -96,17 +96,9 @@ Also needed are the libraries
libxml2 libxslt
```
Then, we must activate a Python virtual env and install the python dependencies.
As we are using [UV](https://docs.astral.sh/uv/) we can just run the build process directly, and let it handle deps.
```
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
```
After getting the dependencies one way or another we can actually build and serve the pages.
The pages can be built and served by running `./build.py`. Try `--help` for more information. The simple web server used lacks the features of `apache` which used on the FSFE web servers. This is why no index is automatically selected for each directory and other behaviours.
The pages can be built and served by running `uv run build`. Try `--help` for more information. The simple web server used lacks the features of `apache` which used on the FSFE web servers. This is why no index is automatically selected for each directory and other behaviours.
### Docker
@@ -129,7 +121,7 @@ KEY_PRIVATE=none KEY_PASSWORD=none GIT_TOKEN=none docker compose
Once your preferred method has been chosen, simply running `docker compose run --service-ports build --serve` should build the webpages and make them available over localhost.
Some more explanation: we are essentially just using docker as a way to provide dependencies and then running the build script. All flags after `build` are passed to `build.py`. The `service-ports` flag is required to open ports from the container for serving the output, not needed if not using the `--serve` flag of the build script.
Some more explanation: we are essentially just using docker as a way to provide dependencies and then running the build script. All flags after `build` are passed to the `build` cli. The `service-ports` flag is required to open ports from the container for serving the output, not needed if not using the `--serve` flag of the build script.
## Githooks

View File

@@ -2,5 +2,6 @@
#
# SPDX-License-Identifier: GPL-3.0-or-later
# __init__.py is a special Python file that allows a directory to become
# a Python package so it can be accessed using the 'import' statement.
from .build import main
__all__ = ["main"]

View File

@@ -7,21 +7,20 @@
import argparse
import logging
import multiprocessing
import os
import sys
from pathlib import Path
from build.lib.misc import lang_from_filename
from .lib.misc import lang_from_filename
from build.phase0.full import full
from build.phase0.global_symlinks import global_symlinks
from build.phase0.prepare_early_subdirectories import prepare_early_subdirectories
from .phase0.full import full
from .phase0.global_symlinks import global_symlinks
from .phase0.prepare_early_subdirectories import prepare_early_subdirectories
from build.phase1.run import phase1_run
from build.phase2.run import phase2_run
from .phase1.run import phase1_run
from .phase2.run import phase2_run
from build.phase3.serve_websites import serve_websites
from build.phase3.stage_to_target import stage_to_target
from .phase3.serve_websites import serve_websites
from .phase3.stage_to_target import stage_to_target
logger = logging.getLogger(__name__)
@@ -84,7 +83,11 @@ def parse_arguments() -> argparse.Namespace:
return args
def main(args: argparse.Namespace):
def main():
"""
Main process of the website builder
"""
args = parse_arguments()
logging.basicConfig(
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
datefmt="%Y-%m-%d %H:%M:%S",
@@ -158,13 +161,3 @@ def main(args: argparse.Namespace):
if args.serve:
serve_websites(working_target, 2000, 100)
if __name__ == "__main__":
"""
Main process of the website builder
"""
# Change to the dir the script is in.
os.chdir(os.path.dirname(__file__))
args = parse_arguments()
main(args)

View File

@@ -1,21 +1,35 @@
# Contributing
## Build Process Code
### Tooling
We use [UV](https://docs.astral.sh/uv/) for managing python versions, and project dependencies.
Please install it in your prefered package manager, and then use
```
uv run build
```
To run the build process.
We check the validity of python code in the repo using [ruff](https://astral.sh/ruff). We use it for both checking and formatting, with `ruff check` enabled in CI.
To run it in the project using the project config, please use `uv run ruff`.
### Overview Stuff
We try to keep to some design patterns to keep things manageable.
Firstly, each phase as described in [the overview](./overview.md) should handle a meaningfully different kind of interaction. Each phase should be structured, to the greatest degree possible, as a sequence of steps. We consider that each phase should have a `run.py` file that exposes a `ipahse_*run` function that takes the arguments needed for its phase.
Each run function then calls a sequence of functions that are defined in the other files in the `phase*` folder. Each other file in the folder should expose one function, with the same name as the file, minus file extension. For example, `create_files.py` should expose the function `create_files`. It is a common pattern for the first expose function to generate a list of files or things to act on, and then multithread this using another function.
Each run function then calls a sequence of functions that are defined in the other files in the `phase*` folder. Each other file in the folder should expose one function, with the same name as the file, minus file extension. For example, `create_files.py` should expose the function `create_files`. It is a common pattern for the first expose function to generate a list of files or things to act on, and then multithread this using another function.
Each step function should use `logger.info` at the top of its function to declare what it is doing.
### Best Practices
This is a little bit of a mesys list of things we have found that are not perhaps entirely obvious.
This is a little bit of a messy list of things we have found that are not perhaps entirely obvious.
- When doing manipulation of stuff, have a look in the lib functions to see if it is already present. If you find a common pattern, perhaps functionise it.
- In phase 1, only update files using the `update_if_changed` function. This function will, as expected, take a file path and a string, and only update the file with the string if there is a difference. Not doing this means a file will always be updated, and hence anything depending on it will always be rebuild, even if the file has not actually changed.
@@ -23,7 +37,5 @@ This is a little bit of a mesys list of things we have found that are not perhap
- All steps are largely considered to be synchronous, and must be finished before the next step can start. Therefore, async must unfortunately be avoided. There are some steps where performance benefits could be achieved by allowing the next step to run concurrently, but the design complications make this unattractive.
- We use a single process pool to multithread with. This gives a small performance benefit over making and deleting pools continuously.
- All paths are to be handled with `pathlib`, not as strings.
- XML code should be generated with LXML instead of string templating. This is to ensure that we generate valid XML every time, and prevents issues with escaping, etc.
- Where possibly, type hint stuff. We try and keep the codebase reasonably typed to make it comprehensible
- XML code should be generated with LXML instead of string templating. This is to ensure that we generate valid XML every time, and prevents issues with escaping, etc.
- Where possibly, type hint stuff. We try and keep the codebase reasonably typed to make it comprehensible

View File

@@ -44,4 +44,4 @@ rsync -rlpgoDz --delete --checksum --filter=':- .gitignore' ./ /website-cached/s
cd /website-cached/source
# run build script expaning all args passed to this script
python3 ./build.py "$@"
uv run --reinstall-package build build "$@"

35
pyproject.toml Normal file
View File

@@ -0,0 +1,35 @@
[project]
name = "build"
version = "0.0.0"
description = "Python tooling to build the fsfe websites"
readme = "README.md"
# Pinned to 3.12 as a known working version, that also has a prebuild wheel for tdewolff-minify
requires-python = "==3.12.*"
dependencies = [
# XML parser
"lxml==5.3.2",
# For getting english language names of languages from two letter codes.
"python-iso639==2025.2.18",
# For stopwords for the search index
"nltk==3.9.1",
# For minification html css and js
"tdewolff-minify==2.20.37",
# For HTTP requests
"requests==2.32.3",
]
[project.scripts]
build = "build:main"
[dependency-groups]
dev = [
"ruff"
]
[build-system]
requires = ["uv_build"]
build-backend = "uv_build"
[tool.uv.build-backend]
module-name = "build"
module-root = ""

View File

@@ -1,10 +0,0 @@
# XML parser
lxml==5.3.2
# For getting english language names of languages from two letter codes.
python-iso639==2025.2.18
# For stopwords for the search index
nltk==3.9.1
# For minification html css and js
tdewolff-minify==2.20.37
# For HTTP requests
requests==2.32.3

View File

@@ -8,49 +8,53 @@ let
treefmt-nixSrc = builtins.fetchTarball "https://github.com/numtide/treefmt-nix/archive/refs/heads/master.tar.gz";
treefmt-nix = import treefmt-nixSrc;
in
pkgs.mkShell {
nativeBuildInputs = with pkgs; [
# The main required tool python
python3
# needed by lxml
libxslt
libxml2
# For less compilation
lessc
# Needed for git clean in full rebuilds
git
# Needed for compiling minifiers
libffi
go
# Formatter
(treefmt-nix.mkWrapper pkgs {
# Used to find the project root
projectRootFile = "shell.nix";
enableDefaultExcludes = true;
programs = {
ruff-check.enable = true;
ruff-format.enable = true;
nixfmt.enable = true;
};
settings = {
global = {
on-unmatched = "debug";
excludes = [
".nltk_data"
".venv"
];
(pkgs.buildFHSEnv {
name = "simple-env";
# Installed for host pc only
targetPkgs =
pkgs:
(with pkgs; [
# For getting python deps
uv
# needed by lxml
libxslt
libxml2
# For less compilation
lessc
# Needed for git clean in full rebuilds
git
# Needed for compiling minifiers
libffi
go
# Formatter
(treefmt-nix.mkWrapper pkgs {
# Used to find the project root
projectRootFile = "shell.nix";
enableDefaultExcludes = true;
programs = {
ruff-check.enable = true;
ruff-format.enable = true;
nixfmt.enable = true;
};
};
})
# Packages for git hooks
mediainfo
perl
file
];
shellHook = ''
export PIP_DISABLE_PIP_VERSION_CHECK=1;
python -m venv .venv;
source .venv/bin/activate;
pip install -r requirements.txt;
'';
}
settings = {
global = {
on-unmatched = "debug";
excludes = [
".nltk_data"
".venv"
];
};
};
})
# Packages for git hooks
mediainfo
perl
file
]);
# Installed for every architecture: only install the lib outputs
multiPkgs =
pkgs:
(with pkgs; [
]);
# runScript = '''';
}).env