Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
41fd5db
chore(process_updates): move away from docker check to celery
crankynetman Jan 27, 2026
26db980
fix(tasks): docstring typos
crankynetman Jan 27, 2026
9f9204e
feat(settings): make timeout configurable and provide better validation
crankynetman Jan 27, 2026
44cb819
fix(pyproject): forgot a line when I refactored images.
crankynetman Jan 27, 2026
d5e906a
chore(settings): slap some variables and make names clearer.
crankynetman Jan 27, 2026
d21eb19
chore(tasks): cleanup var name
crankynetman Jan 27, 2026
eb96e9a
tests(scheduler): add tests for scheduler and combine with django oth…
crankynetman Jan 27, 2026
d5d27c9
chore(coverage): listen to the robot; do what it says
crankynetman Jan 27, 2026
f2e4f93
chore(ruff): I got my swim trunks and my ruff flippy-floppies
crankynetman Jan 27, 2026
1aacb69
chore(uv): I think I got the workspace stuff done right now
crankynetman Jan 27, 2026
f0d7991
chore(pytest): ignore scheduler in django
crankynetman Jan 27, 2026
a04d935
ci(coverage): wait to run after coverage is collected please.
crankynetman Jan 27, 2026
7dce3eb
ci(coverage): use the right coverage settings this time
crankynetman Jan 27, 2026
861935a
ci(coverage): Im floundering here
crankynetman Jan 27, 2026
b671661
ci(coverage): Im floundering here still but maybe closer
crankynetman Jan 27, 2026
f953ffb
ci(coverage): Im floundering here still but maybe closer undo some st…
crankynetman Jan 27, 2026
9aa75bf
chore(pytest.yml): rename cobertura step to be clearer
crankynetman Jan 27, 2026
8c03233
ci(pytest): we don't need that pypi hack now?
crankynetman Jan 28, 2026
6655ec4
ci(pytest): we don't need that pypi hack but need the package
crankynetman Jan 28, 2026
53064e7
ci(pytest): use pip tho
crankynetman Jan 28, 2026
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
3 changes: 3 additions & 0 deletions .envs/.local/.celery
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
CELERY_BROKER_URL=redis://scram-redis-1:6379/2
CELERY_RESULT_BACKEND=redis://scram-redis-1:6379/2
SCRAM_API_URL=http://django:8000/
3 changes: 3 additions & 0 deletions .envs/.local/.celery-secondary
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
CELERY_BROKER_URL=redis://scram-redis-2:6379/2
CELERY_RESULT_BACKEND=redis://scram-redis-2:6379/2
SCRAM_API_URL=http://django-secondary:8000/
5 changes: 5 additions & 0 deletions .github/workflows/behave.yml
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ jobs:
- name: Upload Coverage to Coveralls
if: matrix.python-version == '3.12'
uses: coverallsapp/github-action@v2
with:
parallel: true
flag-name: behave

- name: Upload Coverage to GitHub
if: matrix.python-version == '3.12'
Expand All @@ -83,6 +86,8 @@ jobs:
uses: 5monkeys/cobertura-action@v14
with:
minimum_coverage: "50"
report_name: "Django Pytest/Behave Coverage"


- name: Check Docker state (post-test)
if: always()
Expand Down
20 changes: 20 additions & 0 deletions .github/workflows/coverage-finish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
---
name: Finish Coverage

on:
workflow_run:
workflows: ["Run pytest", "Run behave"]
types:
- completed

jobs:
finish:
runs-on: ubuntu-latest
permissions:
contents: read
needs: [behave, pytest]
steps:
- name: Finish Coveralls
uses: coverallsapp/github-action@v2
with:
parallel-finished: true
30 changes: 27 additions & 3 deletions .github/workflows/pytest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,8 @@ jobs:
- name: Install dependencies
run: |
python -m pip install --upgrade pip
python -m pip install uv
python -m pip install uv pytest-github-actions-annotate-failures
uv pip install --system -r requirements/local.txt --prerelease=allow
# https://github.com/pytest-dev/pytest-github-actions-annotate-failures/pull/68 isn't yet in a release
uv pip install --system git+https://github.com/pytest-dev/pytest-github-actions-annotate-failures.git@6e66cd895fe05cd09be8bad58f5d79110a20385f

- name: Apply migrations
env:
Expand All @@ -82,3 +80,29 @@ jobs:
DATABASE_URL: "postgres://scram:@localhost:5432/test_scram_${{ matrix.python-version }}"
REDIS_HOST: "localhost"
run: pytest

- name: Install Scheduler Dependencies
run: |
cd scheduler
uv sync

- name: Run Scheduler Tests
run: |
cd scheduler
uv run pytest

- name: Upload Coverage to Coveralls
if: matrix.python-version == '3.12'
uses: coverallsapp/github-action@v2
with:
file: ./scheduler/coverage.xml
parallel: true
flag-name: scheduler

- name: Display Scheduler Coverage Metrics for scheduler tests
if: matrix.python-version == '3.12'
uses: 5monkeys/cobertura-action@v14
with:
report_name: "Pytest Celery Scheduler Coverage"
path: ./scheduler/coverage.xml
minimum_coverage: "80"
5 changes: 5 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,11 @@ pass-reset: compose.override.yml
pytest: compose.override.yml
@docker compose run --rm django coverage run -m pytest

## pytest-scheduler: runs scheduler package tests with coverage
.Phony: pytest-scheduler
pytest-scheduler:
@cd scheduler && uv run pytest

## run: brings up the containers as described in compose.override.yml
.Phony: run
run: compose.override.yml
Expand Down
51 changes: 51 additions & 0 deletions compose.override.local.yml
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,57 @@ services:
deploy:
replicas: 1

celery-worker:
volumes:
- ./scheduler/src/scheduler/:/app/scheduler:ro
env_file:
- ./.envs/.local/.celery

celery-worker-secondary:
volumes:
- ./scheduler/src/scheduler/:/app/scheduler:ro
env_file:
- ./.envs/.local/.celery-secondary
deploy:
replicas: 1

celery-beat:
command:
["celery", "-A", "scheduler.app:scram_api_scheduler", "beat", "--loglevel=info"]
env_file:
- ./.envs/.local/.celery
environment:
- DISABLE_PROCESS_UPDATES=False

celery-beat-secondary:
command:
["celery", "-A", "scheduler.app:scram_api_scheduler", "beat", "--loglevel=info"]
env_file:
- ./.envs/.local/.celery-secondary
environment:
- DISABLE_PROCESS_UPDATES=False
deploy:
replicas: 1

flower:
env_file:
- ./.envs/.local/.celery
command:
["celery", "-A", "scheduler.app:scram_api_scheduler", "flower", "--port=5555"]
ports:
- "5555:5555"

flower-secondary:
env_file:
- ./.envs/.local/.celery-secondary
command:
["celery", "-A", "scheduler.app:scram_api_scheduler", "flower", "--port=5555"]
ports:
- "5556:5555"
deploy:
replicas: 1


networks:
default:
ipam:
Expand Down
23 changes: 23 additions & 0 deletions compose.override.production.yml
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,29 @@ services:
env_file:
- ./.envs/.production/.translator


celery-worker:
volumes:
- ./scheduler/src/scheduler/:/app/scheduler:ro
env_file:
- ./.envs/.production/.celery

celery-beat:
command:
["celery", "-A", "scheduler.app:scram_api_scheduler", "beat", "--loglevel=info"]
env_file:
- ./.envs/.production/.celery


flower:
env_file:
- ./.envs/.production/.celery
command:
["celery", "-A", "scheduler.app:scram_api_scheduler", "flower", "--port=5555"]
ports:
- "5555:5555"


networks:
default:
enable_ipv6: true
Expand Down
102 changes: 98 additions & 4 deletions compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,17 @@ services:
condition: service_healthy
redis:
condition: service_healthy
celery-worker:
condition: service_healthy
networks:
default: {}
sysctls:
- net.ipv6.conf.all.disable_ipv6=0
command: /start
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000/process_updates/"]
test: ["CMD", "curl", "-f", "http://localhost:8000/health/"]
interval: 30s
timeout: 30s
timeout: 2s
start_period: 30s
retries: 5
deploy:
Expand All @@ -35,15 +37,17 @@ services:
condition: service_healthy
django:
condition: service_healthy
celery-worker-secondary:
condition: service_healthy
networks:
default: {}
sysctls:
- net.ipv6.conf.all.disable_ipv6=0
command: /start
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000/process_updates/"]
test: ["CMD", "curl", "-f", "http://localhost:8000/health/"]
interval: 30s
timeout: 30s
timeout: 2s
start_period: 30s
retries: 5
deploy:
Expand Down Expand Up @@ -129,3 +133,93 @@ services:
- net.ipv6.conf.all.disable_ipv6=0
deploy:
replicas: ${TRANSLATOR_REPLICAS:-0}

celery-worker:
build:
context: .
dockerfile: ./compose/production/celery/Dockerfile
depends_on:
redis:
condition: service_healthy
healthcheck:
test: ["CMD", "celery", "-A", "scheduler.app:scram_api_scheduler", "inspect", "ping"]
interval: 30s
timeout: 10s
start_period: 30s
retries: 3
restart: unless-stopped

celery-worker-secondary:
build:
context: .
dockerfile: ./compose/production/celery/Dockerfile
depends_on:
redis:
condition: service_healthy
healthcheck:
test: ["CMD", "celery", "-A", "scheduler.app:scram_api_scheduler", "inspect", "ping"]
interval: 30s
timeout: 10s
start_period: 30s
retries: 3
restart: unless-stopped
deploy:
replicas: ${CELERY_WORKER_REPLICAS:-0}

celery-beat:
build:
context: .
dockerfile: ./compose/production/celery/Dockerfile
depends_on:
redis:
condition: service_healthy
celery-worker:
condition: service_healthy
restart: unless-stopped

celery-beat-secondary:
build:
context: .
dockerfile: ./compose/production/celery/Dockerfile
depends_on:
redis:
condition: service_healthy
celery-worker-secondary:
condition: service_healthy
restart: unless-stopped
deploy:
replicas: ${CELERY_BEAT_REPLICAS:-0}

flower:
build:
context: .
dockerfile: ./compose/production/celery/Dockerfile
depends_on:
redis:
condition: service_healthy
celery-worker:
condition: service_healthy
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:5555/healthcheck"]
interval: 30s
timeout: 5s
retries: 3
restart: unless-stopped

flower-secondary:
build:
context: .
dockerfile: ./compose/production/celery/Dockerfile
depends_on:
redis:
condition: service_healthy
celery-worker-secondary:
condition: service_healthy
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:5555/healthcheck"]
interval: 30s
timeout: 5s
retries: 3
restart: unless-stopped
deploy:
replicas: ${FLOWER_REPLICAS:-0}
47 changes: 47 additions & 0 deletions compose/production/celery/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Use a Python image with uv pre-installed
FROM ghcr.io/astral-sh/uv:python3.14-bookworm-slim

# Setup a non-root user
RUN groupadd --system --gid 999 nonroot \
&& useradd --system --gid 999 --uid 999 --create-home nonroot

# Install the project into `/app`
WORKDIR /app

# Enable bytecode compilation
ENV UV_COMPILE_BYTECODE=1

# Copy from the cache instead of linking since it's a mounted volume
ENV UV_LINK_MODE=copy

# Omit development dependencies
ENV UV_NO_DEV=1

# Ensure installed tools can be executed out of the box
ENV UV_TOOL_BIN_DIR=/usr/local/bin

COPY ./scheduler/pyproject.toml ./scheduler/uv.lock* ./

# Install the project's dependencies using the lockfile and settings
RUN --mount=type=cache,target=/root/.cache/uv \
--mount=type=bind,source=scheduler/uv.lock,target=uv.lock \
--mount=type=bind,source=scheduler/pyproject.toml,target=pyproject.toml \
uv sync --locked --no-install-project

# Then, add the rest of the project source code and install it
# Installing separately from its dependencies allows optimal layer caching
COPY ./scheduler/src /app/

RUN --mount=type=cache,target=/root/.cache/uv \
uv sync --locked

# Place executables in the environment at the front of the path
ENV PATH="/app/.venv/bin:$PATH"

# Reset the entrypoint, don't invoke `uv`
ENTRYPOINT []

RUN chown -R nonroot:nonroot /app
USER nonroot

CMD ["celery", "-A", "scheduler.app:scram_api_scheduler", "worker", "--loglevel=info", "-E"]
14 changes: 13 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
[project]
name = "SCRAM"
version = "1.5.1"

# ==== pytest ====
[tool.pytest.ini_options]
addopts = "--ds=config.settings.test --reuse-db"
addopts = [
"--ds=config.settings.test",
"--reuse-db",
"--ignore=scheduler"
]
minversion = "6.0"
python_files = [
"tests.py",
Expand Down Expand Up @@ -139,3 +147,7 @@ module = "*.migrations.*"

[tool.django-stubs]
django_settings_module = "config.settings.test"


[tool.uv.workspace]
exclude = ["scheduler"]
Loading
Loading