Skip to content
Draft
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
2 changes: 1 addition & 1 deletion .copier-answers.resonant.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
_commit: v0.48.1
_commit: v0.50.2
_src_path: https://github.com/kitware-resonant/cookiecutter-resonant
core_app_name: core
include_example_code: false
Expand Down
92 changes: 92 additions & 0 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
// README at: https://github.com/devcontainers/templates/tree/main/src/docker-existing-docker-compose
{
"name": "bats-ai",
"dockerComposeFile": [
"../docker-compose.yml",
"../docker-compose.override.yml",
"./docker-compose.devcontainer.yml"
],
"service": "django",
"overrideCommand": true,
// The "vscode" user and remoteUser are set by the base image label (devcontainers/base).
"workspaceFolder": "/home/vscode/bats-ai",
"features": {
"ghcr.io/devcontainers/features/node:1": {},
"ghcr.io/rails/devcontainer/features/postgres-client:1": {
"version": 18
},
"ghcr.io/devcontainers/features/terraform:1": {},
"ghcr.io/devcontainers/features/aws-cli:1": {},
"ghcr.io/devcontainers/features/github-cli:1": {},
"ghcr.io/devcontainers-extra/features/heroku-cli:1": {}
},
"customizations": {
"vscode": {
"extensions": [
// Python
"ms-python.python",
"ms-python.vscode-pylance",
"ms-python.debugpy",
"ms-python.mypy-type-checker",
"charliermarsh.ruff",
// Django
"batisteo.vscode-django",
"augustocdias.tasks-shell-input",
// Other file formats
"editorconfig.editorconfig",
"mikestead.dotenv",
"tamasfe.even-better-toml",
"timonwong.shellcheck",
// Infrastructure
"ms-azuretools.vscode-containers",
"hashicorp.terraform",
"github.vscode-github-actions",
// Remove AWS extension, as only the CLI is wanted; see: https://github.com/devcontainers/features/issues/1228
"-AmazonWebServices.aws-toolkit-vscode"
],
"settings": {
"containers.containerClient": "com.microsoft.visualstudio.containers.docker",
// Container-specific Python paths
"python.defaultInterpreterPath": "/home/vscode/venv/bin/python",
// Ensure that `envFile` from any user settings is ignored; Docker Compose provides it.
"python.envFile": "",
// Reduce file watcher overhead for generated/cache directories.
"files.watcherExclude": {
"**/__pycache__/**": true,
"**/.pytest_cache/**": true,
"**/node_modules/**": true
}
}
}
},
// Prevent a prompt every time the debugger opens a port or Django auto-restarts.
"otherPortsAttributes": {
"onAutoForward": "silent"
},
"portsAttributes": {
"8000": {
"label": "Django",
// Show a dialog if the port isn't free.
"requireLocalPort": true,
"onAutoForward": "silent"
},
"8080": {
"label": "Vite",
"requireLocalPort": true,
"onAutoForward": "silent"
}
},
// Install a global Python and create a venv before VSCode extensions start,
// to prevent prompts and ensure test discovery works on first load.
"onCreateCommand": {
"python": ["uv", "python", "install", "--default"],
"venv": ["uv", "sync", "--all-extras", "--all-groups"],
"npm": ["npm", "--prefix", "client", "install"]
},
// Ensure it is re-synced on restarts.
"updateContentCommand": {
"venv": ["uv", "sync", "--all-extras", "--all-groups"],
"npm": ["npm", "--prefix", "client", "install"]
}
}
14 changes: 14 additions & 0 deletions .devcontainer/docker-compose.devcontainer.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
services:
django:
# Don't expose ports, devcontainer forwarding is superior, since we can just bind to localhost.
ports: !reset []
# Don't auto-run the default command, launch.json or the terminal will be used.
command: !reset []

celery:
# Celery will be started via launch.json or the terminal.
profiles: ["celery"]

client:
# npm will be started via launch.json or the terminal.
profiles: ["client"]
17 changes: 7 additions & 10 deletions .github/workflows/ci.yaml → .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
name: CI
on:
pull_request:
# types: [opened, synchronize]
# TODO: why is this here?
push:
branches:
- "main"
- main
permissions:
contents: read
jobs:
Expand All @@ -15,7 +13,7 @@ jobs:
matrix:
linter: [eslint, typescript]
name: Lint [${{ matrix.linter }}]
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
steps:
- name: Checkout repository
uses: actions/checkout@v6
Expand All @@ -32,7 +30,7 @@ jobs:
working-directory: client
test-python:
name: Test Python
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
services:
postgres:
image: postgis/postgis:latest
Expand All @@ -55,7 +53,7 @@ jobs:
- 5672:5672
minio:
# This image does not require any command arguments (which GitHub Actions don't support)
image: bitnamilegacy/minio:2025.7.23
image: bitnamilegacy/minio:latest
env:
MINIO_ROOT_USER: minioAccessKey
MINIO_ROOT_PASSWORD: minioSecretKey
Expand All @@ -66,16 +64,15 @@ jobs:
--health-start-interval 2s
ports:
- 9000:9000

steps:
- name: Checkout repository
uses: actions/checkout@v6
- name: Install uv
uses: astral-sh/setup-uv@v7

- name: Run tests
run: uv run tox
run: |
uv run tox
env:
DJANGO_DATABASE_URL: postgres://postgres:postgres@localhost:5432/django
DJANGO_CELERY_BROKER_URL: amqp://localhost:5672/
DJANGO_MINIO_STORAGE_URL: http://minioAccessKey:minioSecretKey@localhost:9000/django-storage-testing
DJANGO_MINIO_STORAGE_URL: http://minioAccessKey:minioSecretKey@localhost:9000/django-storage
90 changes: 90 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "Django: Server",
"type": "debugpy",
"request": "launch",
"program": "${workspaceFolder}/manage.py",
"args": ["runserver_plus", "--print-sql", "localhost:8000"],
"django": true,
"console": "integratedTerminal"
},
{
"name": "Django: Server (eager Celery)",
"type": "debugpy",
"request": "launch",
"program": "${workspaceFolder}/manage.py",
"args": ["runserver_plus", "--print-sql", "localhost:8000"],
"env": {
"DJANGO_CELERY_TASK_ALWAYS_EAGER": "true"
},
"django": true,
"console": "integratedTerminal"
},
{
"name": "Django: Management Command",
"type": "debugpy",
"request": "launch",
"program": "${workspaceFolder}/manage.py",
"args": ["${input:managementCommand}"],
"django": true,
"console": "integratedTerminal"
},
{
"name": "Celery: Worker",
"type": "debugpy",
"request": "launch",
"module": "celery",
"args": [
"--app",
"bats_ai.celery",
"worker",
"--loglevel",
"INFO",
"--pool",
"solo",
"--without-heartbeat"
],
"console": "integratedTerminal",
"justMyCode": false
},
{
"name": "Pytest: Debug",
"type": "debugpy",
"request": "launch",
"purpose": ["debug-test"],
"console": "integratedTerminal",
"django": true,
"justMyCode": false
},
{
"name": "Vite: Dev Server",
"type": "node",
"request": "launch",
"cwd": "${workspaceFolder}/client",
"runtimeExecutable": "npm",
"runtimeArgs": ["run", "dev"],
"console": "integratedTerminal"
}
],
"compounds": [
{
"name": "Django + Celery",
"configurations": ["Django: Server", "Celery: Worker"],
"stopAll": true
}
],
"inputs": [
{
"id": "managementCommand",
"type": "command",
"command": "shellCommand.execute",
"args": {
"command": "./manage.py help --commands",
"description": "Django management command",
"allowCustomValues": true
}
}
]
}
37 changes: 37 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
{
// File cleanup
"files.trimTrailingWhitespace": true,
"files.insertFinalNewline": true,
"files.trimFinalNewlines": true,

// Python
"python.analysis.autoFormatStrings": true,
"python.testing.pytestEnabled": true,
"python.analysis.autoImportCompletions": true,
"python.analysis.gotoDefinitionInStringLiteral": true,
// Allow auto-importing from deeper symbols inside of Django.
"python.analysis.packageIndexDepths": [
{
"name": "django",
"depth": 6
}
],
"python.analysis.inlayHints.pytestParameters": true,

// Django templates
"emmet.includeLanguages": {
"django-html": "html"
},

// Type checking: Use Mypy and disable Pylance.
"mypy-type-checker.importStrategy": "fromEnvironment",
// Mypy daemon seems better, but is buggy in practice.
"mypy-type-checker.preferDaemon": false,
"mypy-type-checker.reportingScope": "file",
"python.analysis.typeCheckingMode": "off",

// Ruff
"[python]": {
"editor.defaultFormatter": "charliermarsh.ruff"
}
}
5 changes: 1 addition & 4 deletions bats_ai/core/views/export_annotation.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from __future__ import annotations

from datetime import datetime
import logging
from typing import TYPE_CHECKING

from django.http import JsonResponse
from django.shortcuts import get_object_or_404
Expand All @@ -10,9 +10,6 @@

from bats_ai.core.models import ExportedAnnotationFile

if TYPE_CHECKING:
from datetime import datetime

logger = logging.getLogger(__name__)

router = Router()
Expand Down
46 changes: 33 additions & 13 deletions dev/django.Dockerfile
Original file line number Diff line number Diff line change
@@ -1,13 +1,33 @@
FROM ghcr.io/astral-sh/uv:debian

# Make Python more friendly to running in containers
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1

# Make uv install content in well-known locations
ENV UV_PROJECT_ENVIRONMENT=/var/lib/venv \
UV_CACHE_DIR=/var/cache/uv/cache \
UV_PYTHON_INSTALL_DIR=/var/cache/uv/bin \
# The uv cache and environment are expected to be mounted on different volumes,
# so hardlinks won't work
UV_LINK_MODE=symlink
FROM mcr.microsoft.com/devcontainers/base:ubuntu-24.04

COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /usr/local/bin/

# Ensure Python output appears immediately in container logs.
ENV PYTHONUNBUFFERED=1

# Put the uv and npm caches in a separate location,
# where they can persist and be shared across containers.
# The uv cache and virtual environment are on different volumes, so hardlinks won't work.
ENV UV_CACHE_DIR=/home/vscode/pkg-cache/uv \
UV_PYTHON_INSTALL_DIR=/home/vscode/pkg-cache/uv-python \
UV_LINK_MODE=symlink \
NPM_CONFIG_CACHE=/home/vscode/pkg-cache/npm

# Put the virtual environment outside the project directory,
# to improve performance on macOS and prevent accidental usage from the host machine.
# Activate it, so `uv run` doesn't need to be prefixed.
ENV UV_PROJECT_ENVIRONMENT=/home/vscode/venv \
PATH="/home/vscode/venv/bin:$PATH"

# Put tool scratch files outside the project directory too.
ENV TOX_WORK_DIR=/home/vscode/tox \
RUFF_CACHE_DIR=/home/vscode/.cache/ruff \
MYPY_CACHE_DIR=/home/vscode/.cache/mypy

RUN ["chsh", "-s", "/usr/bin/zsh", "vscode"]

USER vscode

# Pre-create named volume mount points, so the new volume inherits `vscode` user ownership:
# https://docs.docker.com/engine/storage/volumes/#populate-a-volume-using-a-container
RUN ["mkdir", "/home/vscode/pkg-cache"]
23 changes: 23 additions & 0 deletions dev/docker-development.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Docker Compose Development (without VS Code)

An alternative to the recommended [dev container](../README.md) workflow.

## Setup
1. `docker compose run --rm django ./manage.py migrate`
1. `docker compose run --rm django ./manage.py createsuperuser`

## Run
1. `docker compose up`
1. Access http://localhost:8000/
1. `Ctrl+C` to stop

To include the Celery worker: `docker compose --profile celery up`

## Update
1. `docker compose down`
1. `docker compose pull`
1. `docker compose build --pull`
1. `docker compose run --rm django ./manage.py migrate`

## Reset
Remove all data and volumes: `docker compose down -v`
Loading