Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use nox to test different environments #1245

Merged
merged 9 commits into from
Dec 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 49 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ jobs:
- run: poetry install --all-extras

- name: Run tests
run: scripts/tests
run: scripts/tests --cov=procrastinate --cov-branch
env:
COVERAGE_FILE: ".coverage.${{ matrix.python-version }}"
PGHOST: localhost
Expand All @@ -63,6 +63,54 @@ jobs:
path: .coverage.${{ matrix.python-version }}
include-hidden-files: true

acceptance:
strategy:
matrix:
mode:
- "current_version_without_post_migration"
- "stable_version_without_post_migration"

name: "acceptance-test"
runs-on: ubuntu-latest

services:
postgres:
image: postgres:16
# Set health checks to wait until postgres has started
env:
POSTGRES_PASSWORD: postgres
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
ports:
- 5432:5432

steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4

- name: Install poetry
run: pipx install poetry

- uses: actions/setup-python@v5
with:
python-version: "3.12"
cache: "poetry"

- name: Get latest tag
id: get-latest-tag
run: gh release list --limit 1 --json tagName --jq '"latest_tag="+.[0].tagName' >> $GITHUB_OUTPUT
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Run tests
run: pipx run nox -s ${{ matrix.mode }}
env:
PGHOST: localhost
PGUSER: postgres
PGPASSWORD: postgres
LATEST_TAG: ${{ steps.get-latest-tag.outputs.latest_tag }}

static-typing:
name: Run Pyright
runs-on: ubuntu-latest
Expand Down
20 changes: 10 additions & 10 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -34,24 +34,24 @@ repos:
- id: pyright
additional_dependencies:
- aiopg==1.4.0
- anyio==4.5.0
- anyio==4.6.2.post1
- asgiref==3.8.1
- attrs==24.2.0
- contextlib2==21.6.0
- croniter==3.0.3
- django-stubs==5.1.0
- django==5.1.1
- psycopg2-binary==2.9.9
- psycopg[pool]==3.2.2
- croniter==5.0.1
- django-stubs==5.1.1
- django==5.1.3
- psycopg2-binary==2.9.10
- psycopg[pool]==3.2.3
- python-dateutil==2.9.0.post0
- sphinx==7.1.2
- sqlalchemy==2.0.35
- sphinx==7.4.7
- sqlalchemy==2.0.36
- typing-extensions==4.12.2
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.6.7
rev: v0.8.1
hooks:
- id: ruff
args: [--fix, --unsafe-fixes]
args: [--fix, --unsafe-fixes, --show-fixes]
- id: ruff-format
- repo: https://github.com/PyCQA/doc8
rev: v1.1.2
Expand Down
126 changes: 61 additions & 65 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,17 @@ the following assumptions:

- You're using `MacOS` or `Linux`, and `bash` or `zsh`.
- You already have `python3` available
- You have `poetry` [installed](https://python-poetry.org/docs/#installation)
- Either you've already setup a PostgreSQL database and environment variables (`PG*`)
are set or you have `docker compose` available and port 5432 is free.
- Either `psql` and other `libpq` executables are available in the `PATH` or they
are located in `usr/local/opt/libpq/bin` (`Homebrew`).
- Either:
- you already have `poetry`, `pre-commit` and `nox` installed
- or you have `pipx` installed and you're ok installing those 3 tools with `pipx`
- or you don't have `pipx` installed but it's ok if we install it for you
- Either:
- you've already setup a PostgreSQL database and environment variables (`PG*`)
are set
- or you have `docker compose` available and port 5432 is free.
- Either:
- `psql` and other `libpq` executables are available in the `PATH`
- or they are located in `usr/local/opt/libpq/bin` (`Homebrew`).

The `dev-env` script will add the `scripts` folder to your `$PATH` for the current
shell, so in the following documentation, if you see `scripts/foo`, you're welcome
Expand Down Expand Up @@ -172,26 +178,30 @@ ALTER TABLE procrastinate_jobs ADD COLUMN extra TEXT;
The name of migration scripts must follow a specific pattern:

```
xx.yy.zz_ab_very_short_description_of_your_changes.sql
xx.yy.zz_ab_{pre|post}_very_short_description_of_your_changes.sql
```

`xx.yy.zz` is the number of the latest released version of Procrastinate. (The latest
release is the one marked `Latest release` on the [Procrastinate releases] page.)
`xx`, `yy` and `zz` must be 2-digit numbers, with leading zeros if necessary.
`ab` is the 2-digit migration script's serial number, `01` being the first number in
the series. And, finally, `very_short_description_of_your_changes` is a very short
description of the changes (wow). It is important to use underscores between the
different parts, and between words in the short description.
`xx.yy.zz` is the number of the latest released version of Procrastinate. (The
latest release is the one marked `Latest release` on the [Procrastinate
releases] page.) `xx`, `yy` and `zz` must be 2-digit numbers, with leading
zeros if necessary. `ab` is the 2-digit migration script's serial number, the
first number for each release being `01` for pre-migrations and `50` for
post-migrations. `pre` is if the migration should be applied before upgrading
the code, `post` is if the migration should be applied after upgrading the
code. And, finally, `very_short_description_of_your_changes` is a very short
description of the changes (wow). It is important to use underscores between
the different parts, and between words in the short description.

For example, let's say the latest released version of Procrastinate is `1.0.1`, and
that the `migrations` directory already includes a migration script whose serial
number is `01` for that release number. In that case, if you need to add a migration
script, its name will start with `01.00.01_02_`.
For example, let's say the latest released version of Procrastinate is `1.0.1`,
that the `migrations` directory already includes a post-migration script whose
serial number for that release number and your migration should be
applied after deploying the corresponding python code. In that case, if you
need to add a migration script, its name will start with `01.00.01_51_post_`.

### Backward-compatibility

As a Procrastinate developer, the changes that you make to the Procrastinate database
schema must be compatible with the Python code of previous Procrastinate versions.
As a Procrastinate developer, you must ensure you use pre-migrations and post-migrations
to maintain backward compatibility with previous versions of Procrastinate.

For example, let's say that the current Procrastinate database schema includes an SQL
function
Expand All @@ -211,8 +221,8 @@ replace the old function by the new one, and add a migration script that removes
function and adds the new one:

```sql
DROP FUNCTION procrastinate_func(integer, text, timestamp);
CREATE FUNCTION procrastinate_func(arg1 integer, arg2 text)
DROP FUNCTION procrastinate_func_v3(integer, text, timestamp);
CREATE FUNCTION procrastinate_func_v3(arg1 integer, arg2 text)
RETURNS INT
...
```
Expand All @@ -226,57 +236,20 @@ So when you make changes to the Procrastinate database schema you must ensure th
new schema still works with old versions of the Procrastinate Python code.

Going back to our `procrastinate_func` example. Instead of replacing the old function
by the new one in `schema.sql`, you will leave the old function, and just add the new
one. And your migration script will just involve adding the new version of the function:
by the new one in `schema.sql`, you add a new function in pre-migrations and remove the
old function in post-migrations:

```sql
CREATE FUNCTION procrastinate_func(arg1 integer, arg2 text)
-- xx_xx_xx_01_pre_add_new_version_procrastinate_func.sql
CREATE FUNCTION procrastinate_func_v4(arg1 integer, arg2 text)
RETURNS INT
...
```

The question that comes next is: when can the old version of `procrastinate_func` be
removed? Or more generally, when can the SQL compatibility layer be removed?

The answer is some time after the next major version of Procrastinate!

For example, if the current Procrastinate version is 1.5.0, the SQL compatibility layer
will be removed after 2.0.0 is released. The 2.0.0 release will be a pivot release, in
the sense that Procrastinate users who want to upgrade from, say, 1.5.0 to 2.5.0, will
need to upgrade from 1.5.0 to 2.0.0 first, and then from 2.0.0 to 2.5.0. And they will
always migrate the database schema before updating the code.

The task of removing the SQL compatibility layer after the release of a major version
(e.g. 2.0.0) is the responsibility of Procrastinate maintainers. More specifically, for
the 2.1.0 release, Procrastinate maintainers will need to edit `schema.sql` and remove
the SQL compatibility layer.

But, as a standard developer, when you make changes to the Procrastinate database schema
that involves leaving or adding SQL statements for compatibility reasons, it's a good
idea to add a migration script for the removal of the SQL compatibility layer. This will
greatly help the Procrastinate maintainers.

For example, let's say the current released version of Procrastinate is 1.5.0, and you
want to change the signature of `procrastinate_func` as described above. You will add
a `1.5.0` migration script (e.g.
`01.05.00_01_add_new_version_procrastinate_func.sql`) that adds the new version of
the function, as already described above. And you will also add a `2.0.0` migration
script (e.g. `02.00.00_01_remove_old_version_procrastinate_func.sql`) that takes
care of removing the old version of the function:

```sql
-- xx_xx_xx_50_post_remove_old_version_procrastinate_func.sql
DROP FUNCTION procrastinate_func(integer, text, timestamp);
```

In this way, you provide the new SQL code, the compatibility layer, and the migration
for the removal of the compatibility layer.
...

:::{note}
The migration scripts that remove the SQL compatibility code are to be added to the
`future_migrations` directory instead of the `migrations` directory. And it will
be the responsibility of Procrastinate maintainers to move them to the
`migrations` directory after the next major release.
:::
```

### Migration tests

Expand All @@ -288,6 +261,29 @@ included in the normal test suite, but you can run them specifically with:
(venv) $ pytest tests/migration
```

We run the `acceptance` tests on 3 different configurations:

- Without the post-migrations applied and with the last released version of
Procrastinate
- Without the post-migrations applied and with the current checked out code
- With all migrations applied and with the current checked out code (this is
just part of the normal test suite)

This is to ensure that the migrations are backward-compatible and that the database
schema can be upgraded without downtime. We simulate all stages of the upgrade process:

- (the initial situation being that Procrastinate is running with the last
released version of the code and all migrations of the last released
version have been applied)
- First, the user would apply pre-migrations while the old version of the
code is still running.
- Then, the user would upgrade the code to the new version.
- Finally, the user would apply post-migrations.

There are cases where new acceptance tests cannot work on the last released version.
In that case, the tests can be skipped by adding `@pytest.mark.skip_before_version("x.y.z")`,
where `x.y.z` is the version of Procrastinate where the test would start running.

## Try our demos

See the demos page for instructions on how to run the demos ({doc}`demos`).
Expand Down
14 changes: 9 additions & 5 deletions docs/howto/django/migrations.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,18 @@ Procrastinate comes with its own migrations so don't forget to run
`./manage.py migrate`.

Procrastinate provides 2 kinds of migrations:
- The Django equivalent of the `procrastinate` normal migrations, which are
used to create all of the PostgreSQL DDL objects used by Procrastinate.
- Specific noop migrations used for Django to understand the Procrastinate
Models (see {doc}`models`).

- The Django equivalent of the `procrastinate` normal migrations, which are
used to create all of the PostgreSQL DDL objects used by Procrastinate.
- Specific noop migrations used for Django to understand the Procrastinate
Models (see {doc}`models`).

Procrastinate's Django migrations are always kept
in sync with your current version of Procrastinate, it's always a good idea
to check the release notes and read the migrations when upgrading so that you
know what will be happening to the database.

See {doc}`../production/migrations` for more information on migrations.
See {doc}`../production/migrations` for more information on migrations, especially
around `pre` and `post` migrations: if you deploy while the code is running, you'll
want to ensure you run the `pre-` migrations before you deploy the code and the
`post-` migrations after.
Loading
Loading