Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
988f9eb
Adding PR verification action
bergsalex Aug 14, 2025
d4ba198
Testing docker image build
bergsalex Aug 14, 2025
15f78a8
Testing node cleanup
bergsalex Aug 14, 2025
34aaff3
Debug image size
bergsalex Aug 14, 2025
11ae9b6
Testing GCP Cloud Build
bergsalex Aug 14, 2025
abf0670
Docker build in github with custom base image
bergsalex Aug 15, 2025
14791c4
Fix uv python location in Dockerfile
bergsalex Aug 15, 2025
1113b3c
Refactor github actions and split test and lint dev dependencies
bergsalex Aug 15, 2025
b753929
Fix optional workflow call inputs
bergsalex Aug 15, 2025
a7b096e
Limit dependency install to lint only on format-lint action
bergsalex Aug 15, 2025
f0b8141
Merge remote-tracking branch 'origin/repository-reorganization' into …
bergsalex Aug 19, 2025
36c90ca
Merge remote-tracking branch 'origin/YODA-217-update-mouse-tracking-r…
bergsalex Aug 19, 2025
d096b61
Temporarily enable PR action on merges to repository-reorganizatiom b…
bergsalex Aug 19, 2025
627585e
Adding cpu group option to add pytorch tf in tests
bergsalex Aug 19, 2025
c3d9b18
Fixing macos torch index mismatch bug and fixing extra cpu flag in te…
bergsalex Aug 19, 2025
b50b9ea
Debugging missing pytorch
bergsalex Aug 19, 2025
976ebe8
Specifying pytorch-cpu index in GH Actions test step
bergsalex Aug 19, 2025
0ccf604
Removing pytorch-cpu repository
bergsalex Aug 19, 2025
1add278
Explicitly specify pypi index
bergsalex Aug 19, 2025
e049320
Explicitly set pypi index
bergsalex Aug 19, 2025
3e9c91f
Force pytorch install with uv pip
bergsalex Aug 19, 2025
cea427e
Fix locally failing tests
bergsalex Aug 19, 2025
19f2a07
Testing if NO_COLOR=1 env fixes formatting issue in Github Actions Tests
bergsalex Aug 20, 2025
a90b3a7
Fixing auto formatting
bergsalex Aug 20, 2025
f4ccf11
Testing TTY_COMPATIBLE and TERM env vars to get CLI tests to pass in …
bergsalex Aug 20, 2025
6b0952f
Fixing terminal issues in GitHub Actions unit tests, and mocking csv …
bergsalex Aug 20, 2025
dd6e544
Fix additional terminal errors in GitHub Actions tests
bergsalex Aug 20, 2025
4bc4843
Merge remote-tracking branch 'origin/repository-reorganization' into …
bergsalex Aug 20, 2025
87a151c
Switch from GitHub Artifacts to Docker Hub
bergsalex Aug 21, 2025
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: 50 additions & 0 deletions .github/workflows/_build-docker-action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
name: 'Build Docker Image'
on:
workflow_call:
env:
REGISTRY: docker.io
IMAGE_NAME: aberger4/mouse-tracking
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write

steps:

- name: Checkout repository
uses: actions/checkout@v4

- name: Log in to Container Registry
if: github.event_name != 'pull_request'
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: aberger4
password: ${{ secrets.DOCKER_SECRET }}

- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=raw,value=latest,enable={{is_default_branch}}

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
33 changes: 33 additions & 0 deletions .github/workflows/_format-lint-action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
name: 'Lint Code Definition'
on:
workflow_call:
inputs:
python-version:
description: 'Python version to set up'
required: false
default: '3.10'
type: string
jobs:
format-lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3

- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: ${{ inputs.python-version }}

- name: Install uv
uses: astral-sh/setup-uv@v3
with:
version: "latest"

- name: Install dependencies with uv
run: uv sync --only-group lint

- name: Run Ruff Linter
run: uv run --only-group lint ruff check src/ tests/

- name: Run Ruff Formatter
run: uv run --only-group lint ruff format --check src/ tests/
38 changes: 38 additions & 0 deletions .github/workflows/_run-tests-action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
name: 'Python Tests Definition'
on:
workflow_call:
inputs:
python-version:
description: Python version to set up'
required: false
default: '3.10'
type: string
runner-os:
description: 'Runner OS'
required: false
default: 'ubuntu-latest'
type: string
jobs:
run-tests:
runs-on: ${{ inputs.runner-os }}
steps:
- uses: actions/checkout@v3

- name: Set up Python ${{ inputs.python-version }}
uses: actions/setup-python@v4
with:
python-version: ${{ inputs.python-version }}

- name: Install uv
uses: astral-sh/setup-uv@v3
with:
version: "latest"

- name: Install dependencies with uv
run: uv sync --extra cpu

- name: Make Sure PyTorch is Installed
run: uv pip install torch==2.6.0 torchvision==0.21.0 torchaudio==2.6.0 --index-url https://download.pytorch.org/whl/cpu

- name: Test with pytest
run: uv run pytest tests
20 changes: 20 additions & 0 deletions .github/workflows/pull-request.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
name: Pull Request Checks

on:
pull_request:
branches: [ main, repository-reorganization ]

jobs:
format-lint:
name: "Format and Lint"
uses: ./.github/workflows/_format-lint-action.yml

test:
name: "Run Tests"
needs: format-lint
uses: ./.github/workflows/_run-tests-action.yml

build:
name: "Build Docker Image"
needs: [format-lint, test]
uses: ./.github/workflows/_build-docker-action.yml
33 changes: 28 additions & 5 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ dependencies = [
"scipy==1.11.4",
"pandas==2.0.3",
"opencv-python-headless==4.8.0.76",
"imageio==2.31.6",
"imageio[ffmpeg]==2.31.6",
"pillow==9.4.0",
"matplotlib==3.7.1",
"typer>=0.12.4",
Expand Down Expand Up @@ -40,16 +40,33 @@ cpu = [


# ---- uv configuration: point Torch family at cu126 index ----
# ---- uv indexes ----
[[tool.uv.index]]
name = "pypi"
url = "https://pypi.org/simple"
explicit = false

[[tool.uv.index]]
name = "pytorch-cu126"
url = "https://download.pytorch.org/whl/cu126"
explicit = true

# ---- uv per-package sources with markers ----
# Use CUDA wheels only when installing the `gpu` extra AND on platforms that can use them.
# Fall back to CPU wheels when installing the `cpu` extra.
[tool.uv.sources]
torch = { index = "pytorch-cu126" }
torchvision = { index = "pytorch-cu126" }
torchaudio = { index = "pytorch-cu126" }

torch = [
{ index = "pytorch-cu126", marker = "sys_platform == 'linux' and extra == 'gpu'" },
{ index = "pypi", marker = "sys_platform == 'linux' and extra != 'gpu'" },
]
torchvision = [
{ index = "pytorch-cu126", marker = "sys_platform == 'linux' and extra == 'gpu'" },
{ index = "pypi", marker = "sys_platform == 'linux' and extra != 'gpu'" },
]
torchaudio = [
{ index = "pytorch-cu126", marker = "sys_platform == 'linux' and extra == 'gpu'" },
{ index = "pypi", marker = "sys_platform == 'linux' and extra != 'gpu'" },
]

[project.scripts]
mouse-tracking-runtime = "mouse_tracking.cli.main:app"
Expand Down Expand Up @@ -98,8 +115,14 @@ addopts = "--benchmark-skip"

[dependency-groups]
dev = [
{include-group = "lint"},
{include-group = "test"}
]
test = [
"pytest>=8.3.5",
"pytest-benchmark>=5.1.0",
"pytest-cov>=6.1.1",
]
lint = [
"ruff>=0.11.2",
]
4 changes: 3 additions & 1 deletion tests/cli/infer/test_arena_corner.py
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,9 @@ def test_arena_corner_frame_options(self, mock_infer, num_frames, frame_interval
def test_arena_corner_help_text(self):
"""Test that the command has proper help text."""
# Arrange & Act
result = self.runner.invoke(app, ["arena-corner", "--help"])
result = self.runner.invoke(
app, ["arena-corner", "--help"], env={"TERM": "dumb"}
)

# Assert
assert result.exit_code == 0
Expand Down
4 changes: 2 additions & 2 deletions tests/cli/infer/test_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ def test_infer_commands_help_structure():

# Act & Assert
for command in commands:
result = runner.invoke(app, [command, "--help"])
result = runner.invoke(app, [command, "--help"], env={"TERM": "dumb"})
assert result.exit_code == 0
assert "Usage:" in result.stdout
assert "--help" in result.stdout
Expand Down Expand Up @@ -252,7 +252,7 @@ def test_infer_command_help_format(command_name):
runner = CliRunner()

# Act
result = runner.invoke(app, [command_name, "--help"])
result = runner.invoke(app, [command_name, "--help"], env={"TERM": "dumb"})

# Assert
assert result.exit_code == 0
Expand Down
2 changes: 1 addition & 1 deletion tests/cli/infer/test_fecal_boli.py
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,7 @@ def test_fecal_boli_default_values(self, mock_infer):
def test_fecal_boli_help_text(self):
"""Test that the fecal boli command has proper help text."""
# Arrange & Act
result = self.runner.invoke(app, ["fecal-boli", "--help"])
result = self.runner.invoke(app, ["fecal-boli", "--help"], env={"TERM": "dumb"})

# Assert
assert result.exit_code == 0
Expand Down
4 changes: 3 additions & 1 deletion tests/cli/infer/test_food_hopper.py
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,9 @@ def test_food_hopper_default_values(self, mock_infer):
def test_food_hopper_help_text(self):
"""Test that the food hopper command has proper help text."""
# Arrange & Act
result = self.runner.invoke(app, ["food-hopper", "--help"])
result = self.runner.invoke(
app, ["food-hopper", "--help"], env={"TERM": "dumb"}
)

# Assert
assert result.exit_code == 0
Expand Down
2 changes: 1 addition & 1 deletion tests/cli/infer/test_lixit.py
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ def test_lixit_default_values(self, mock_infer):
def test_lixit_help_text(self):
"""Test that the lixit command has proper help text."""
# Arrange & Act
result = self.runner.invoke(app, ["lixit", "--help"])
result = self.runner.invoke(app, ["lixit", "--help"], env={"TERM": "dumb"})

# Assert
assert result.exit_code == 0
Expand Down
4 changes: 3 additions & 1 deletion tests/cli/infer/test_multi_identity.py
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,9 @@ def test_multi_identity_default_values(self, mock_infer):
def test_multi_identity_help_text(self):
"""Test that the multi-identity command has proper help text."""
# Arrange & Act
result = self.runner.invoke(app, ["multi-identity", "--help"])
result = self.runner.invoke(
app, ["multi-identity", "--help"], env={"TERM": "dumb"}
)

# Assert
assert result.exit_code == 0
Expand Down
2 changes: 1 addition & 1 deletion tests/cli/infer/test_multi_pose.py
Original file line number Diff line number Diff line change
Expand Up @@ -319,7 +319,7 @@ def test_multi_pose_default_values(self, mock_infer):
def test_multi_pose_help_text(self):
"""Test that the multi-pose command has proper help text."""
# Arrange & Act
result = self.runner.invoke(app, ["multi-pose", "--help"])
result = self.runner.invoke(app, ["multi-pose", "--help"], env={"TERM": "dumb"})

# Assert
assert result.exit_code == 0
Expand Down
4 changes: 3 additions & 1 deletion tests/cli/infer/test_multi_segmentation.py
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,9 @@ def test_multi_segmentation_default_values(self, mock_infer):
def test_multi_segmentation_help_text(self):
"""Test that the multi-segmentation command has proper help text."""
# Arrange & Act
result = self.runner.invoke(app, ["multi-segmentation", "--help"])
result = self.runner.invoke(
app, ["multi-segmentation", "--help"], env={"TERM": "dumb"}
)

# Assert
assert result.exit_code == 0
Expand Down
4 changes: 3 additions & 1 deletion tests/cli/infer/test_single_pose.py
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,9 @@ def test_single_pose_default_values(self, mock_infer):
def test_single_pose_help_text(self):
"""Test that the single-pose command has proper help text."""
# Arrange & Act
result = self.runner.invoke(app, ["single-pose", "--help"])
result = self.runner.invoke(
app, ["single-pose", "--help"], env={"TERM": "dumb"}
)

# Assert
assert result.exit_code == 0
Expand Down
4 changes: 3 additions & 1 deletion tests/cli/infer/test_single_segmentation.py
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,9 @@ def test_single_segmentation_default_values(self, mock_infer):
def test_single_segmentation_help_text(self):
"""Test that the single-segmentation command has proper help text."""
# Arrange & Act
result = self.runner.invoke(app, ["single-segmentation", "--help"])
result = self.runner.invoke(
app, ["single-segmentation", "--help"], env={"TERM": "dumb"}
)

# Assert
assert result.exit_code == 0
Expand Down
9 changes: 7 additions & 2 deletions tests/cli/qa/test_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,11 @@ def test_qa_single_pose_execution_with_mock_file():
pose_file = Path(tmp_file.name)

# Mock the inspect_pose_v6 function to avoid actual file processing
with patch("mouse_tracking.cli.qa.inspect_pose_v6") as mock_inspect:
# and mock to_csv to avoid creating real CSV files
with (
patch("mouse_tracking.cli.qa.inspect_pose_v6") as mock_inspect,
patch("pandas.DataFrame.to_csv") as mock_to_csv,
):
mock_inspect.return_value = {"metric1": 0.5, "metric2": 0.8}

# Act
Expand All @@ -120,6 +124,7 @@ def test_qa_single_pose_execution_with_mock_file():
# Assert
assert result.exit_code == 0
mock_inspect.assert_called_once()
mock_to_csv.assert_called_once()

# Cleanup
if pose_file.exists():
Expand Down Expand Up @@ -277,7 +282,7 @@ def test_qa_command_help_format(command_name):
runner = CliRunner()

# Act
result = runner.invoke(app, [command_name, "--help"])
result = runner.invoke(app, [command_name, "--help"], env={"TERM": "dumb"})

# Assert
assert result.exit_code == 0
Expand Down
Loading