Skip to content

Migrate to uv and gha#223

Merged
csikb merged 40 commits into
mainfrom
migrate-to-uv-and-gha
Oct 28, 2025
Merged

Migrate to uv and gha#223
csikb merged 40 commits into
mainfrom
migrate-to-uv-and-gha

Conversation

@csikb
Copy link
Copy Markdown
Collaborator

@csikb csikb commented Oct 27, 2025

BREAKING CHANGES

csikb added 3 commits October 27, 2025 22:29
- Updated Poetry from version 1.8.4 to 2.2.1
- Updated isort from version 5.13.2 to 7.0.0
- Updated black from version 24.10.0 to 25.9.0
- Removed poetry.lock file
- Refactored pyproject.toml to use the new project structure and updated dependencies
- Updated CircleCI configuration to use newer versions of docker, github-cli, and codecov
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Oct 27, 2025

Walkthrough

Adds a Docker Buildx bake config and a multi-arch build target; rewrites Dockerfile to use uv-based dependency sync and a pre-built virtualenv with non-root execution and Python 3.12, changes runtime port to 8080; appends Node.js ignore entries to .gitignore and .dockerignore; removes an explicit pillow_avif import; minor docstring whitespace edits; sets Sonar Python version to 3.12; updates integration test waits/ports to 8080.

Changes

Cohort / File(s) Summary
Docker Buildx configuration
docker-bake.hcl
New file declaring group default (targets = ["build"]), a placeholder target docker-metadata-action, and a build target that inherits docker-metadata-action, with context = "./", dockerfile = "Dockerfile", and platforms = ["linux/amd64","linux/arm64/v8"].
Dockerfile / build changes
Dockerfile
Major rewrite: switched to Python 3.12-alpine base images, introduced uv-based dependency sync to produce a pre-built virtualenv, changed app assembly to copy prebuilt /app from builder with non-root user and adjusted ownership, updated PATH and CMD to run bss_web_file_server.main:app on port 8080, and changed final EXPOSE to 8080.
Ignore files
/.gitignore, /.dockerignore
Appended a "### Node.js template" block including node_modules/ to both ignore files.
Image service change
src/bss_web_file_server/services/image.py
Removed explicit pillow_avif import (comment removed); no other code changes.
Docstring formatting tweaks
src/bss_web_file_server/models/member.py, src/bss_web_file_server/models/video.py
Minor whitespace edits to module docstrings only (non-functional).
Sonar configuration
sonar-project.properties
Added sonar.python.version=3.12.
Integration tests update
tests-int/test_health.py
Updated Docker compose wait strategy to use LogMessageWaitStrategy("8080"), removed wait=True, and updated port references from 80 to 8080 in tests.

Sequence Diagram(s)

sequenceDiagram
  participant CI as CI/Developer
  participant Bake as docker buildx bake
  participant Target as build target
  participant Docker as Dockerfile
  participant Builder as Builder stage (uv sync)
  participant App as Final app image

  CI->>Bake: trigger bake using `docker-bake.hcl` (group: default -> build)
  Bake->>Target: run `build` (inherits docker-metadata-action)
  Target->>Docker: provide context (`./`) and Dockerfile
  Docker->>Builder: run uv sync to prepare dependencies and build prebuilt /app
  Builder->>App: COPY --from=builder /app into final image (chown to non-root)
  App-->>CI: produce multi-platform images (linux/amd64, linux/arm64/v8)
  note right of App #E8F6FF: Final image runs non-root and exposes port 8080
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Pay attention to the Dockerfile changes: uv sync flow, cache/bind usage, prebuilt virtualenv, chown/COPY semantics, PATH and non-root user setup.
  • Verify the bake target inheritance and platforms in docker-bake.hcl match CI expectations.
  • Confirm test changes and Docker CMD/EXPOSE port consistency (8080) across Dockerfile and tests.
  • Check removal of explicit pillow_avif import for any runtime AVIF dependency implications.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Description Check ❓ Inconclusive The PR description contains only "BREAKING CHANGES" without any detail about what the breaking changes are or why they were made. While this label is nominally related to the changeset (the modifications do include breaking changes like port migration from 80 to 8080), the description uses vague, non-descriptive terminology that fails to convey meaningful information about the actual changes. A reader would not understand what was modified or why without reviewing the changeset directly.
✅ Passed checks (1 passed)
Check name Status Explanation
Title Check ✅ Passed The PR title "Migrate to uv and gha" accurately describes the main changes in the changeset. The Dockerfile shows a significant migration from Poetry to UV for dependency management, and the docker-bake.hcl additions support the Docker/container build process. The title is concise, specific, and clearly communicates the primary transformation to a teammate reviewing the history, even though "gha" (GitHub Actions) references aren't explicitly shown in the file-level summaries provided.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch migrate-to-uv-and-gha

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-advanced-security
Copy link
Copy Markdown

This pull request sets up GitHub code scanning for this repository. Once the scans have completed and the checks have passed, the analysis results for this pull request branch will appear on this overview. Once you merge this pull request, the 'Security' tab will show more code scanning analysis results (for example, for the default branch). Depending on your configuration and choice of analysis tool, future pull requests will be annotated with code scanning analysis results. For more information about GitHub code scanning, check out the documentation.

@codecov
Copy link
Copy Markdown

codecov Bot commented Oct 28, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (a2d79e5) to head (b653d23).
⚠️ Report is 4 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff            @@
##              main      #223   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files           11        11           
  Lines          213       212    -1     
=========================================
- Hits           213       212    -1     
Flag Coverage Δ
unittests ?

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@csikb csikb self-assigned this Oct 28, 2025
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2d596ec and 0eb39b5.

⛔ Files ignored due to path filters (2)
  • pyproject.toml is excluded by !**/*.toml
  • uv.lock is excluded by !**/*.lock, !**/*.lock
📒 Files selected for processing (1)
  • Dockerfile (1 hunks)
🔇 Additional comments (2)
Dockerfile (2)

17-26: Clarify the purpose of two sequential uv sync commands.

Two uv sync calls appear at lines 17–20 and 25–26 in different contexts:

  • First: uv sync --locked --no-dev --no-install-project (dependencies only, cached)
  • Second: uv sync --locked --no-dev --no-editable (includes app, non-cached)

If the second sync includes the full app installation, the first may be redundant. Confirm whether this two-stage approach is necessary (e.g., for layer caching optimization) or if it can be simplified.


1-1: Document base image version downgrade.

The base image was downgraded from python:3.13 to python:3.12.12 with SHA256 pinning. While reproducibility is improved, the reason for the downgrade should be documented (e.g., compatibility, security, stability). Ensure this is intentional and note it in commit messages or comments if not already done.

Comment thread Dockerfile Outdated
Comment thread Dockerfile Outdated
Comment thread Dockerfile Fixed
Comment thread Dockerfile Fixed
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
Dockerfile (1)

47-47: Non-root user cannot bind to port 80 — runtime failure expected.

The CMD at line 47 binds uvicorn to port 80 while the container runs as nonroot:nonroot (line 43). Binding to ports below 1024 requires root privileges, so this will fail at runtime with "Permission denied."

Either switch to a port ≥1024 (e.g., 8080) or run as root:

Option 1 (Recommended): Use unprivileged port

-CMD ["uvicorn", "bss_web_file_server.main:app", "--host", "0.0.0.0", "--port", "80"]
+CMD ["uvicorn", "bss_web_file_server.main:app", "--host", "0.0.0.0", "--port", "8080"]

Then ensure external routing (load balancer, reverse proxy, or Kubernetes ingress) maps host port 80 → container port 8080.

Option 2 (If port 80 required inside container): Run as root

-USER nonroot:nonroot

This degrades security posture; Option 1 is preferred.

Option 3 (Advanced): Grant CAP_NET_BIND_SERVICE
Use Docker/Kubernetes capabilities to allow nonroot to bind to privileged ports (requires orchestration platform support).

🧹 Nitpick comments (1)
Dockerfile (1)

1-1: Pin builder base image by SHA256 hash.

For supply-chain security and reproducibility, pin the Python base image to a specific digest. The uv image at line 3 is correctly pinned; apply the same pattern here.

-FROM python:3.12.12-alpine AS builder
+FROM python:3.12.12-alpine@sha256:d82291d418d5c47f267708393e40599ae836f2260b0519dd38670e9d281657f5 AS builder
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between eaaf040 and adc46c6.

⛔ Files ignored due to path filters (1)
  • .github/workflows/integration.yml is excluded by !**/*.yml
📒 Files selected for processing (2)
  • Dockerfile (1 hunks)
  • docker-bake.hcl (1 hunks)
🧰 Additional context used
🪛 GitHub Check: Scorecard
Dockerfile

[warning] 1-1: Pinned-Dependencies
score is 8: containerImage not pinned by hash
Remediation tip: pin your Docker image by updating python:3.12.12-alpine to python:3.12.12-alpine@sha256:d82291d418d5c47f267708393e40599ae836f2260b0519dd38670e9d281657f5
Click Remediation section below for further remediation help


[warning] 32-32: Pinned-Dependencies
score is 8: containerImage not pinned by hash
Remediation tip: pin your Docker image by updating python:3.12-alpine to python:3.12-alpine@sha256:d82291d418d5c47f267708393e40599ae836f2260b0519dd38670e9d281657f5
Click Remediation section below for further remediation help

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Build and publish
🔇 Additional comments (3)
docker-bake.hcl (1)

1-18: Docker Bake configuration looks good.

This configuration correctly sets up multi-arch builds for both amd64 and arm64. The inheritance from docker-metadata-action enables seamless integration with GitHub Actions for automatic tagging and metadata injection.

Dockerfile (2)

35-44: Non-root setup is now correct.

The PATH configuration (line 35) now correctly points to /app/bin where uv-created venv executables reside. User creation, WORKDIR alignment, and --chown flag are all properly set for non-root execution.


32-32: I found a discrepancy: line 1 is not actually pinned by SHA256 (it uses python:3.12.12-alpine), only line 3 uses SHA256 pinning. Let me get the current digest for the python:3.12-alpine image.

Pin the app base image by SHA256 digest.

Line 1 (python:3.12.12-alpine) is not actually pinned by SHA256, so the reference to consistency with line 1 in the original comment is inaccurate. However, line 3 demonstrates the correct pattern with digest pinning. For supply-chain security and reproducibility, pin line 32 using the manifest-list digest (not platform-specific):

-FROM python:3.12-alpine AS app
+FROM python:3.12-alpine@sha256:<MANIFEST_DIGEST> AS app

Obtain the manifest-list digest for python:3.12-alpine using:

docker manifest inspect python:3.12-alpine | grep '"digest"' | head -1

Or use tools like crane manifest python:3.12-alpine or skopeo inspect --raw docker://python:3.12-alpine.

@sonarqubecloud
Copy link
Copy Markdown

sonarqubecloud Bot commented Oct 28, 2025

Quality Gate Passed Quality Gate passed

Issues
0 New issues
0 Accepted issues

Measures
0 Security Hotspots
No data about Coverage
0.0% Duplication on New Code

See analysis details on SonarQube Cloud

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between adc46c6 and b653d23.

⛔ Files ignored due to path filters (5)
  • .github/workflows/python.yml is excluded by !**/*.yml
  • .github/workflows/release.yml is excluded by !**/*.yml
  • .pre-commit-config.yaml is excluded by !**/*.yaml
  • docker-compose.yml is excluded by !**/*.yml
  • pyproject.toml is excluded by !**/*.toml
📒 Files selected for processing (3)
  • .dockerignore (1 hunks)
  • Dockerfile (1 hunks)
  • tests-int/test_health.py (3 hunks)
🔇 Additional comments (8)
.dockerignore (1)

164-166: LGTM!

The Node.js template addition follows standard conventions and properly ignores node_modules/ from the Docker build context.

tests-int/test_health.py (1)

25-25: Port update correctly aligned with Dockerfile changes.

The port references have been properly updated from 80 to 8080, matching the new EXPOSE directive and non-root user requirements in the Dockerfile.

Also applies to: 34-34

Dockerfile (6)

1-1: Base image properly pinned by digest.

The Python base image now uses SHA256 digest pinning, addressing the security scanner warnings about unpinned container images.


5-10: UV integration configured correctly.

The UV tool integration uses appropriate environment variables for a production Docker build: bytecode pre-compilation for faster startup, copy link mode for isolation, and disabled runtime downloads.


12-22: Dependency synchronization properly optimized.

The two-phase dependency installation (dependencies first, then project) with cache mounts and bind mounts follows UV best practices for Docker builds, ensuring fast rebuilds when only source code changes.


24-31: Package installation approach is standard but verify project configuration.

The build installs the application as a non-editable package into the venv, allowing the final image to exclude source files. Ensure your pyproject.toml correctly defines the package structure so bss_web_file_server.main:app (referenced in line 49) is importable from the installed package.


34-46: Non-root user implementation is secure and correct.

The app stage properly creates a non-root user, transfers venv ownership, and configures PATH to include the venv executables. This follows Docker security best practices.


48-48: Directory creation is handled by the application at startup—verify file system permissions.

The application's startup lifecycle (in src/bss_web_file_server/main.py) calls video_service.create_base_path() and member_service.create_base_path(), which automatically create /home/nonroot/assets and subdirectories with mkdir(parents=True, exist_ok=True) if they don't exist.

However, ensure that:

  • The parent directory /home/nonroot is accessible and writable by the nonroot user
  • The base image or earlier Dockerfile steps create /home/nonroot with proper permissions if needed

Comment thread tests-int/test_health.py
build=True,
wait=True,
)
compose.waiting_for({"app": LogMessageWaitStrategy("8080")})
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Use the actual startup message in LogMessageWaitStrategy.

The wait strategy currently waits for the string "8080" to appear in logs, which is fragile and may match unintended log entries (e.g., port configuration messages). Since line 26 confirms the actual startup message is "Application startup complete.", use that instead for reliable container readiness detection.

Apply this diff:

-    compose.waiting_for({"app": LogMessageWaitStrategy("8080")})
+    compose.waiting_for({"app": LogMessageWaitStrategy("Application startup complete.")})
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
compose.waiting_for({"app": LogMessageWaitStrategy("8080")})
compose.waiting_for({"app": LogMessageWaitStrategy("Application startup complete.")})
🤖 Prompt for AI Agents
In tests-int/test_health.py around line 17, the LogMessageWaitStrategy is
currently waiting for the string "8080" which is brittle; change it to wait for
the actual startup log "Application startup complete." so the container
readiness is detected reliably. Replace the wait strategy argument from "8080"
to "Application startup complete." (matching the exact message used on line 26)
so the test waits for the definitive startup log instead of a port substring.

@csikb csikb added this pull request to the merge queue Oct 28, 2025
@csikb csikb removed this pull request from the merge queue due to the queue being cleared Oct 28, 2025
@csikb csikb added this pull request to the merge queue Oct 28, 2025
Merged via the queue into main with commit 4ea31cb Oct 28, 2025
15 checks passed
@csikb csikb deleted the migrate-to-uv-and-gha branch October 28, 2025 16:16
github-merge-queue Bot pushed a commit that referenced this pull request Oct 28, 2025
BREAKING CHANGE: Move to uv from poetry and update all dependencies.

Removed CircleCI badge from README.

See #223
@bsstudio-pr-release
Copy link
Copy Markdown

🎉 This PR is included in version 2.0.0 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants