Skip to content

Commit

Permalink
Switch project management from poetry to uv
Browse files Browse the repository at this point in the history
No breaking changes anticipated.

Incidental changes:
* simplification of the docker image
* bump Python version to 3.13
  • Loading branch information
cu committed Dec 26, 2024
1 parent 6f3a116 commit f9d8218
Show file tree
Hide file tree
Showing 10 changed files with 538 additions and 618 deletions.
4 changes: 4 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -1 +1,5 @@
instance/
**/node_modules/
Dockerfile*
*.md
deploy/
9 changes: 4 additions & 5 deletions .github/workflows/tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
integration-tests:
strategy:
matrix:
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"]
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
Expand All @@ -26,7 +26,6 @@ jobs:

- name: Install poetry deps and run tests
run: |
pipx install poetry
poetry install
poetry run flake8 --show-source
poetry run pytest
pipx install uv
uv run flake8 --show-source --exclude .venv
uv run pytest
1 change: 1 addition & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@
# https://github.com/prettier/prettier/issues/5019
# https://github.com/prettier/prettier/issues/5308
**/*.md
**/*.yaml
85 changes: 35 additions & 50 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,73 +1,58 @@
ARG APP_NAME=silicon
ARG PYTHON_IMAGE=docker.io/library/python:3.11-slim
ARG PYTHON_IMAGE=docker.io/library/python:3.13-slim
ARG NODE_IMAGE=docker.io/library/node:lts-alpine


# Staging:
# * copy the project dir into /staging
# * install the javascript dependencies
FROM $NODE_IMAGE AS staging
### Stage 1: Install JS dependencies
FROM $NODE_IMAGE AS npm-install

COPY ./ /staging/
WORKDIR /staging/silicon/static

RUN npm ci


# Build:
# * run tests
# * build a wheel and constraints file
# * create a venv in /$APP_NAME
# * install wheel and dependencies into the venv
### Stage 2: Install Silicon and dependencies
FROM $PYTHON_IMAGE AS build
ARG APP_NAME
ENV PYTHONUNBUFFERED=1

COPY --from=staging /staging /staging
WORKDIR /staging
RUN mkdir instance
# uv settings
ARG UV_LINK_MODE=copy
ARG UV_COMPILE_BYTECODE=1
ARG UV_PYTHON_DOWNLOADS=never
ARG UV_PROJECT_ENVIRONMENT=/silicon/.venv

RUN pip install --no-cache-dir poetry
RUN poetry install
RUN TMP=/dev/shm poetry run pytest
RUN poetry build --format wheel
RUN poetry export --format requirements.txt --output constraints.txt --without-hashes

RUN python -m venv /$APP_NAME
RUN /$APP_NAME/bin/pip install dist/*.whl --constraint constraints.txt
COPY --from=npm-install /staging /staging
WORKDIR /staging

RUN --mount=type=cache,target=/root/.cache <<EOS sh -ex
pip install uv
uv sync --locked --no-install-project
TMP=/dev/shm uv run pytest
mv LICENSE scripts silicon /silicon
mv entrypoint.sh /
EOS

# Final stage:
# * create a user named $APP_NAME
# * install curl and set healthcheck
# * create an instance data directory
### Stage 3: Configure final image
FROM $PYTHON_IMAGE AS final

ARG APP_NAME
ENV APP_NAME=$APP_NAME
ENV FLASK_APP=$APP_NAME:create_app()
ENV INSTANCE_PATH=/home/$APP_NAME/instance
ENV FLASK_APP=silicon:create_app()
ENV INSTANCE_PATH=/home/silicon/instance
ENV GUNICORN_CMD_ARGS="--bind :5000 --workers 2 --threads 4"
EXPOSE 5000

COPY --from=build /$APP_NAME /$APP_NAME
COPY ./entrypoint.sh /
COPY --from=build /entrypoint.sh /
RUN chmod +x /entrypoint.sh
COPY --from=build /silicon /silicon

ARG DEBIAN_FRONTEND=noninteractive
RUN apt-get update && \
apt-get install -yq --no-install-recommends curl tini && \
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
--mount=type=cache,target=/var/lib/apt,sharing=locked <<EOS sh -ex
apt-get update
apt-get install -yq --no-install-recommends curl tini
apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
groupadd --gid 5000 silicon
useradd --create-home --uid 5000 --gid 5000 silicon
EOS
USER silicon
WORKDIR /silicon

EXPOSE 5000
HEALTHCHECK --start-period=10s --timeout=5s \
CMD curl http://localhost:5000/view/home || exit 1

RUN groupadd --gid 5000 $APP_NAME
RUN useradd --create-home --uid 5000 --gid 5000 $APP_NAME

USER $APP_NAME
WORKDIR /home/$APP_NAME

RUN mkdir instance

ENTRYPOINT ["/usr/bin/tini", "--", "/entrypoint.sh"]
CMD ["gunicorn", "--worker-tmp-dir /dev/shm", "$FLASK_APP"]
CMD ["/silicon/.venv/bin/gunicorn", "--worker-tmp-dir /dev/shm", "'silicon:create_app()'"]
50 changes: 17 additions & 33 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ certain design decisions, see [this blog article](https://blog.bityard.net/artic
Projects we rely on and appreciate!

* [Python](https://www.python.org/), of course.
* [Poetry](https://python-poetry.org/) for project management.
* [uv](https://github.com/astral-sh/uv) for project management.
* [Flask](https://flask.palletsprojects.com/), the micro-framework.
* [Mistune](https://github.com/lepture/mistune) to render Markdown into HTML.
* [Pygments](https://pygments.org/) for syntax highlighting of code blocks.
Expand All @@ -53,7 +53,6 @@ Projects we rely on and appreciate!
* [CodeMirror](https://codemirror.net/) (optional) for editor syntax
highlighting


# Quickstart

This project is configured and deployed much like any other Flask project.
Expand Down Expand Up @@ -106,32 +105,19 @@ data in `/home/silicon/instance`.

## Prerequisites

This project requires Python 3.9 or greater. On a Debian/Ubuntu system, that
means the following packages:
This repo tries to be somewhat flexible about its tools and workflow. However,
along the happy path you will find some combination of Python 3.9 (or better),
`uv`, Docker/Podman, and `npm`.

* `python3`
* `python3-pip` (unless installed via other means)
* `python3-dev`
* `python3-venv`
* `npm` (or `docker`) (optional to enable CodeMirror editor)
Install [uv](https://github.com/astral-sh/uv) if necessary. If you are not a fan
of curlpipes, there are many other ways to install it:

Install [Poetry](https://python-poetry.org/) if necessary. Everyone has their
own way of setting up their Python tooling but I'm a fan of
[pipx](https://github.com/pypa/pipx):

```sh
pip3 install --user pipx
pipx install poetry
```
* `pip install --user uv`
* `pipx install uv`
* download binaries from the [latest release](https://github.com/astral-sh/uv)

## Setup

Use poetry to create a virtual environment and install the dependencies:

```sh
poetry install
```

Some settings can either be set as environment variables or written to a
file named `.env` in the project root. For development, this will suffice:

Expand All @@ -152,51 +138,49 @@ docs, but these are some you might care to know about:
of random characters. Setting this is optional as the app will create one
(and write it to a file in `INSTANCE_PATH`) if one doesn't exist.
* `SILICON_EDITOR`: When set to `textarea`, this disables the CodeMirror text
editor when editing pages and uses a standard textarea element instead.
editor when editing pages and uses a standard textarea element instead.

To initialize the database after the configuration settings have been set,
run the following command. It will create an `instance` directory in the root
of the project and initialize the SQLite database from `schema.sql`.

```sh
poetry run flask --app silicon init-db
uv run flask --app silicon init-db
```

## Running Silicon

Run the project via the `flask` development server:

```sh
poetry run flask --app silicon run --debug
uv run flask --app silicon run --debug
```

Unless you changed the defaults, you should be able to access the UI on
http://localhost:5000/

## Running tests and flake8

To run the tests, install the dev dependencies and run `pytest`:
To run the tests:

```sh
poetry install --with dev
poetry run pytest
uv run pytest
```

If you have a tmpfs filesystem, you can set the `TMP` environment variable to
have test databases created there (which is faster and results in less
wear-and-tear on your disk):

```sh
TMP=/dev/shm poetry run pytest
TMP=/dev/shm uv run pytest
```

To make sure all code is formatted to flake8 standards, run `flake8`:

```sh
poetry run flake8
uv run flake8 --exclude .venv
```


# Production Deployment

Silicon Notes is a fairly simple web application which contains no built-in
Expand Down Expand Up @@ -259,7 +243,7 @@ In the event that a database migration is needed, follow these steps:
To import the data:

4. Move or rename the old `instance/silicon.sqlite`, if it exists.
5. Run `poetry run flask --app silicon init-db`.
5. Run `uv run flask --app silicon init-db`.
6. Run `sqlite3 instance/silicon.sqlite < silicon_data.sql`.
7. Start the Silicon instance.

Expand Down
4 changes: 2 additions & 2 deletions entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

set -e

export PATH="/$APP_NAME/bin:$PATH"
export PATH="/silicon/.venv/bin:$PATH"

flask --app $APP_NAME init-db
flask --app silicon init-db

eval "exec $@"
Loading

0 comments on commit f9d8218

Please sign in to comment.