Skip to content
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
17 changes: 0 additions & 17 deletions .github/dependabot.yml

This file was deleted.

40 changes: 2 additions & 38 deletions DEVELOPERS.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,54 +66,18 @@ just docker/build-for-os-cli

## Dependency management
Dependencies are managed with `uv`.

### Overview
See the [uv documentation](https://docs.astral.sh/uv/concepts/projects/dependencies) for details on usage.
Commands for adding, removing or modifying constraints of dependencies will automatically respect the
global timestamp cutoff specified in the `pyproject.toml`:
```toml
[tool.uv]
exclude-newer = "YYYY-MM-DDTHH:MM:SSZ"
```

Changes to dependencies should be made via `uv` commands, or by modifying `pyproject.toml` directly followed by
[locking and syncing](https://docs.astral.sh/uv/concepts/projects/sync/) via `uv` or `just` commands like
`just devenv` or `just upgrade-all`. You should not modify `uv.lock` manually.

Note that `uv.lock` must be reproducible from `pyproject.toml`. Otherwise, `just check` will fail.
If `just check` errors saying that the timestamps must match, you might have modified one file but not the other:
If `just check` errors, you might have modified one file but not the other:
- If you modified `pyproject.toml`, you must update `uv.lock` via `uv lock` / `just upgrade-all` or similar.
- If you did not modify `pyproject.toml` but have changes in `uv.lock`, you should revert the changes to `uv.lock`,
modify `pyproject.toml` as you require, then run `uv lock` to update `uv.lock`.

The timestamp cutoff should usually be set to midnight UTC of a past date.
In general, the date is expected to be between 7 and 14 days ago as a result of automated weekly dependency updates.

If you require a package version that is newer than the cutoff allows, you can either manually bump the global cutoff
date or add a package-specific timestamp cutoff. Both options are described below.

### Manually bumping the cutoff date
The cutoff timestamp can be modified to a more recent date either manually in the `pyproject.toml`
or with `just bump-uv-cutoff <days-ago>`.
For example, to set the cutoff to today's date and upgrade all dependencies, run:
```
just bump-uv-cutoff 0
just upgrade-all
```

### Adding a package-specific timestamp cutoff
It is possible to specify a package-specific timestamp cutoff in addition to the global cutoff.
This should be done in the `pyproject.toml` to ensure reproducible installs;
see the [uv documentation](https://docs.astral.sh/uv/reference/settings/#exclude-newer-package) for details.
If set, the package-specific cutoff will take precedence over the global cutoff regardless of which one is more recent.

You should not set a package-specific cutoff that is older than the global cutoff - use a version
constraint instead.
If there is good reason to set a package-specific cutoff that is more recent than the global cutoff,
**care should be taken to ensure that the package-specific cutoff is manually removed once it is over 7 days old**,
as otherwise future automated updates of that package will be indefinitely blocked.
Currently no automated tooling is in place to enforce removal of stale package-specific cutoffs.


## Tagging a new version

OpenSAFELY SQL Runner follows [Semantic Versioning, v2.0.0][2].
Expand Down
45 changes: 7 additions & 38 deletions justfile
Original file line number Diff line number Diff line change
Expand Up @@ -33,47 +33,20 @@ install-precommit:
upgrade-package package: && uvmirror devenv
uv lock --upgrade-package {{ package }}

# Upgrade all packages to the latest versions as of the cutoff in pyproject.toml
upgrade-all: && uvmirror devenv
uv lock --upgrade
# Upgrade all packages to the latest versions
upgrade-all cooldown="7 days ago": && devenv
uv lock --upgrade --exclude-newer "{{ cooldown }}"

# update the uv mirror requirements file
uvmirror file="requirements.uvmirror.txt":
rm -f {{ file }}
uv export --format requirements-txt --frozen --no-hashes --all-groups --all-extras > {{ file }}

# Move the cutoff date in pyproject.toml to N days ago (default: 7) at midnight UTC
bump-uv-cutoff days="7":
#!/usr/bin/env -S uvx --with tomlkit python3.13
# Note we specify the python version here and we don't care if it's different to
# the .python-version; we need 3.11+ for the datetime code used.

import datetime
import tomlkit

with open("pyproject.toml", "rb") as f:
content = tomlkit.load(f)

new_datetime = (
datetime.datetime.now(datetime.UTC) - datetime.timedelta(days=int("{{ days }}"))
).replace(hour=0, minute=0, second=0, microsecond=0)
new_timestamp = new_datetime.strftime("%Y-%m-%dT%H:%M:%SZ")
if existing_timestamp := content["tool"]["uv"].get("exclude-newer"):
if new_datetime < datetime.datetime.fromisoformat(existing_timestamp):
print(
f"Existing cutoff {existing_timestamp} is more recent than {new_timestamp}, not updating."
)
exit(0)
content["tool"]["uv"]["exclude-newer"] = new_timestamp

with open("pyproject.toml", "w") as f:
tomlkit.dump(content, f)

# This is the default input command to update-dependencies action
# https://github.com/bennettoxford/update-dependencies-action

# Bump the timestamp cutoff to midnight UTC 7 days ago and upgrade all dependencies
update-dependencies: bump-uv-cutoff upgrade-all
# recipe is used for doing lockfileMaintenance via update-dependencies action, until min release age is respected fo uv
update-dependencies: upgrade-all && uvmirror

# *args is variadic, 0 or more. This allows us to do `just test -k match`, for example.

Expand Down Expand Up @@ -128,14 +101,10 @@ check:
check-lockfile:
#!/usr/bin/env bash
set -euo pipefail
# Make sure dates in pyproject.toml and uv.lock are in sync
# Make sure lockfile is reproducible from pyproject.toml
unset UV_EXCLUDE_NEWER
rc=0
uv lock --check || rc=$?
if test "$rc" != "0" ; then
echo "Timestamp cutoffs in uv.lock must match those in pyproject.toml. See DEVELOPERS.md for details and hints." >&2
exit $rc
fi
uv lock --check

# Fix formatting, import sort ordering, and justfile
fix:
Expand Down
22 changes: 8 additions & 14 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
requires-python = ">=3.14"

dependencies = [
"pymssql",
"structlog",
"sqlglot",
"pymssql<=2.3.13",
"sqlglot<=30.6.0",
"structlog<=25.5.0",
]

[tool.coverage.run]
Expand Down Expand Up @@ -56,17 +56,11 @@ extend-ignore = [
]
isort.lines-after-imports = 2

# Note: any `exclude-newer-package` timestamps should be removed if > 7 days old
# See https://github.com/opensafely-core/repo-template/blob/main/DEVELOPERS.md for details
[tool.uv]
exclude-newer = "2026-04-29T00:00:00Z"
exclude-newer-package = {}

[dependency-groups]
dev = [
"coverage",
"docker",
"pre-commit",
"pytest",
"ruff",
"coverage<=7.13.5",
"docker<=7.1.0",
"pre-commit<=4.6.0",
"pytest<=9.0.3",
"ruff<=0.15.12",
]
19 changes: 8 additions & 11 deletions uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.