Skip to content

monorepo with packages with independent dependencies using uv#3

Open
thromer wants to merge 6 commits into
mainfrom
postmodern
Open

monorepo with packages with independent dependencies using uv#3
thromer wants to merge 6 commits into
mainfrom
postmodern

Conversation

@thromer
Copy link
Copy Markdown
Owner

@thromer thromer commented Jul 3, 2025

Summary by CodeRabbit

  • New Features

    • Introduced new applications and libraries with clear project structures, including sample main functions and entry points.
    • Added configuration files for new apps and libraries, specifying dependencies and build systems.
    • Added deployment script to automate building, pushing, and deploying containers to Google Cloud Run.
    • Provided initial setup instructions in the documentation.
  • Bug Fixes

    • Not applicable.
  • Documentation

    • Updated README with a new bootstrap section for setup guidance.
  • Tests

    • Added basic tests for new libraries to verify core functionality.
  • Chores

    • Updated ignore patterns for Docker builds.
    • Removed obsolete apps, libraries, and configuration files.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jul 3, 2025

Walkthrough

This change restructures the Python monorepo from a previous "lib-new/app-new" model to a new workspace-based layout with separate apps/a1, apps/a2, libs/l1, and libs/l2 projects, each with their own configuration and code. It introduces new Dockerfiles, deployment scripts, and updates documentation to describe the new bootstrapping and deployment processes.

Changes

File(s) Change Summary
.dockerignore Updated to ignore all .dockerignore files recursively.
Dockerfile, app-new/app_new/*, app-new/pyproject.toml, lib-new/* Deleted old build, app, and library files from the previous structure.
README.md Added "Bootstrap" section with setup instructions and a link to an alternative approach.
apps/a1/Dockerfile, apps/a1/Dockerfile.merged Introduced new multi-stage and merged Dockerfiles for app a1.
apps/a1/a1/init.py, apps/a2/a2/init.py Added main entry point modules for new apps a1 and a2.
apps/a1/pyproject.toml, apps/a2/pyproject.toml Added new project configuration files for apps a1 and a2.
libs/l1/pyproject.toml, libs/l2/pyproject.toml Added new project configuration files for libraries l1 and l2.
libs/l1/src/l1/init.py, libs/l2/src/l2/init.py Added new library modules with hello functions for l1 and l2.
libs/l1/tests/test_l1.py, libs/l2/tests/test_l2.py Added initial test files for l1 and l2 libraries.
pyproject.toml Added new workspace-level configuration referencing new apps and libs.
deploy.sh Added a deployment script for building, pushing, and deploying containers to Google Cloud Run.

Sequence Diagram(s)

sequenceDiagram
    participant Dev as Developer
    participant Repo as Monorepo
    participant UV as uv Tool
    participant Docker as Docker Engine
    participant GCR as Google Artifact Registry
    participant CloudRun as Google Cloud Run

    Dev->>Repo: Clone and bootstrap workspace
    Dev->>UV: Run uv init . and uv sync
    UV->>Repo: Set up virtual environments and dependencies
    Dev->>Docker: Build Docker image for app (e.g., a1)
    Docker->>GCR: Push image to Artifact Registry
    Dev->>CloudRun: Deploy image using deploy.sh
    CloudRun->>Dev: Service deployed and available
Loading

Possibly related PRs

  • Dockerfile example #1: Added initial .dockerignore and Dockerfile for Python 3.12; this PR refines the ignore pattern and removes the previously introduced Dockerfile and related files, showing direct evolution of the build setup.

Poem

A bunny hopped from old to new,
With monorepo dreams in view.
Apps and libs now side by side,
In Docker clouds, they smoothly ride.
"Hello, world!" the rabbits cheer—
The future workspace starts right here!
🐇✨

✨ Finishing Touches
  • 📝 Generate Docstrings

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Copy Markdown

@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: 21

🔭 Outside diff range comments (1)
libs/l2/tests/test_l2.py (1)

5-6: Remove trailing blank line.

-
📜 Review details

Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5c7f9bb and f7bf43e.

⛔ Files ignored due to path filters (3)
  • app-new/uv.lock is excluded by !**/*.lock
  • lib-new/uv.lock is excluded by !**/*.lock
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (22)
  • .dockerignore (1 hunks)
  • Dockerfile (0 hunks)
  • README.md (1 hunks)
  • app-new/app_new/__init__.py (0 hunks)
  • app-new/pyproject.toml (0 hunks)
  • apps/a1/Dockerfile (1 hunks)
  • apps/a1/Dockerfile.merged (1 hunks)
  • apps/a1/a1/__init__.py (1 hunks)
  • apps/a1/pyproject.toml (1 hunks)
  • apps/a2/a2/__init__.py (1 hunks)
  • apps/a2/pyproject.toml (1 hunks)
  • lib-new/pyproject.toml (0 hunks)
  • lib-new/src/lib_new/__init__.py (0 hunks)
  • libs/l1/.python-version (1 hunks)
  • libs/l1/pyproject.toml (1 hunks)
  • libs/l1/src/l1/__init__.py (1 hunks)
  • libs/l1/tests/test_l1.py (1 hunks)
  • libs/l2/.python-version (1 hunks)
  • libs/l2/pyproject.toml (1 hunks)
  • libs/l2/src/l2/__init__.py (1 hunks)
  • libs/l2/tests/test_l2.py (1 hunks)
  • pyproject.toml (1 hunks)
💤 Files with no reviewable changes (5)
  • app-new/app_new/init.py
  • lib-new/src/lib_new/init.py
  • lib-new/pyproject.toml
  • app-new/pyproject.toml
  • Dockerfile
🧰 Additional context used
🧠 Learnings (10)
📓 Common learnings
Learnt from: thromer
PR: thromer/uv-monorepo-example#1
File: Dockerfile:20-20
Timestamp: 2025-06-29T18:28:46.875Z
Learning: uv workspaces provide elegant monorepo management in Docker by using a root pyproject.toml with members configuration and uv sync command, eliminating the need for manual export/build/install steps for each package.
Learnt from: thromer
PR: thromer/uv-monorepo-example#1
File: Dockerfile:28-30
Timestamp: 2025-06-29T22:22:29.398Z
Learning: In the uv-monorepo-example project, the main Dockerfile intentionally creates a minimal scratch-based image containing only Python packages (without the Python interpreter). This is deployed using a separate runtime image (like Dockerfile.merged) that provides the OS and Python interpreter, then copies the packages from the built artifact. This two-stage pattern separates package building from runtime concerns and allows flexible deployment targets.
Learnt from: thromer
PR: thromer/uv-monorepo-example#1
File: Dockerfile:0-0
Timestamp: 2025-06-29T19:36:26.385Z
Learning: In Docker builds using uv sync, editable packages are installed as .pth files that reference source locations, not actual distributions. This breaks in containerized environments where the source paths don't exist. Use uv build + pip install for proper package installation in containers.
Learnt from: thromer
PR: thromer/uv-monorepo-example#1
File: Dockerfile:0-0
Timestamp: 2025-06-29T19:45:02.721Z
Learning: uv export --no-editable still includes local package references (like "." and "../package-name") in the requirements.txt output, which breaks pip install in containers. These need to be filtered out with sed or similar tools.
Learnt from: thromer
PR: thromer/uv-monorepo-example#1
File: lib-new/.python-version:1-1
Timestamp: 2025-06-29T18:50:41.165Z
Learning: In the uv-monorepo-example project, Python version differences between local development (.python-version files specifying 3.11) and Docker runtime (3.12) are intentional. The user uses 3.11 for local development consistency and plans to use CI/CD to ensure 3.12 compatibility.
libs/l2/.python-version (1)
Learnt from: thromer
PR: thromer/uv-monorepo-example#1
File: lib-new/.python-version:1-1
Timestamp: 2025-06-29T18:50:41.165Z
Learning: In the uv-monorepo-example project, Python version differences between local development (.python-version files specifying 3.11) and Docker runtime (3.12) are intentional. The user uses 3.11 for local development consistency and plans to use CI/CD to ensure 3.12 compatibility.
libs/l1/.python-version (1)
Learnt from: thromer
PR: thromer/uv-monorepo-example#1
File: lib-new/.python-version:1-1
Timestamp: 2025-06-29T18:50:41.165Z
Learning: In the uv-monorepo-example project, Python version differences between local development (.python-version files specifying 3.11) and Docker runtime (3.12) are intentional. The user uses 3.11 for local development consistency and plans to use CI/CD to ensure 3.12 compatibility.
README.md (3)
Learnt from: thromer
PR: thromer/uv-monorepo-example#1
File: Dockerfile:20-20
Timestamp: 2025-06-29T18:28:46.875Z
Learning: uv workspaces provide elegant monorepo management in Docker by using a root pyproject.toml with members configuration and uv sync command, eliminating the need for manual export/build/install steps for each package.
Learnt from: thromer
PR: thromer/uv-monorepo-example#1
File: Dockerfile:0-0
Timestamp: 2025-06-29T19:36:26.385Z
Learning: In Docker builds using uv sync, editable packages are installed as .pth files that reference source locations, not actual distributions. This breaks in containerized environments where the source paths don't exist. Use uv build + pip install for proper package installation in containers.
Learnt from: thromer
PR: thromer/uv-monorepo-example#1
File: Dockerfile:28-30
Timestamp: 2025-06-29T22:22:29.398Z
Learning: In the uv-monorepo-example project, the main Dockerfile intentionally creates a minimal scratch-based image containing only Python packages (without the Python interpreter). This is deployed using a separate runtime image (like Dockerfile.merged) that provides the OS and Python interpreter, then copies the packages from the built artifact. This two-stage pattern separates package building from runtime concerns and allows flexible deployment targets.
libs/l1/pyproject.toml (1)
Learnt from: thromer
PR: thromer/uv-monorepo-example#1
File: lib-new/.python-version:1-1
Timestamp: 2025-06-29T18:50:41.165Z
Learning: In the uv-monorepo-example project, Python version differences between local development (.python-version files specifying 3.11) and Docker runtime (3.12) are intentional. The user uses 3.11 for local development consistency and plans to use CI/CD to ensure 3.12 compatibility.
apps/a2/pyproject.toml (1)
Learnt from: thromer
PR: thromer/uv-monorepo-example#1
File: lib-new/.python-version:1-1
Timestamp: 2025-06-29T18:50:41.165Z
Learning: In the uv-monorepo-example project, Python version differences between local development (.python-version files specifying 3.11) and Docker runtime (3.12) are intentional. The user uses 3.11 for local development consistency and plans to use CI/CD to ensure 3.12 compatibility.
apps/a1/pyproject.toml (1)
Learnt from: thromer
PR: thromer/uv-monorepo-example#1
File: lib-new/.python-version:1-1
Timestamp: 2025-06-29T18:50:41.165Z
Learning: In the uv-monorepo-example project, Python version differences between local development (.python-version files specifying 3.11) and Docker runtime (3.12) are intentional. The user uses 3.11 for local development consistency and plans to use CI/CD to ensure 3.12 compatibility.
pyproject.toml (4)
Learnt from: thromer
PR: thromer/uv-monorepo-example#1
File: Dockerfile:20-20
Timestamp: 2025-06-29T18:28:46.875Z
Learning: uv workspaces provide elegant monorepo management in Docker by using a root pyproject.toml with members configuration and uv sync command, eliminating the need for manual export/build/install steps for each package.
Learnt from: thromer
PR: thromer/uv-monorepo-example#1
File: lib-new/.python-version:1-1
Timestamp: 2025-06-29T18:50:41.165Z
Learning: In the uv-monorepo-example project, Python version differences between local development (.python-version files specifying 3.11) and Docker runtime (3.12) are intentional. The user uses 3.11 for local development consistency and plans to use CI/CD to ensure 3.12 compatibility.
Learnt from: thromer
PR: thromer/uv-monorepo-example#1
File: Dockerfile:28-30
Timestamp: 2025-06-29T22:22:29.398Z
Learning: In the uv-monorepo-example project, the main Dockerfile intentionally creates a minimal scratch-based image containing only Python packages (without the Python interpreter). This is deployed using a separate runtime image (like Dockerfile.merged) that provides the OS and Python interpreter, then copies the packages from the built artifact. This two-stage pattern separates package building from runtime concerns and allows flexible deployment targets.
Learnt from: thromer
PR: thromer/uv-monorepo-example#1
File: Dockerfile:0-0
Timestamp: 2025-06-29T19:36:26.385Z
Learning: In Docker builds using uv sync, editable packages are installed as .pth files that reference source locations, not actual distributions. This breaks in containerized environments where the source paths don't exist. Use uv build + pip install for proper package installation in containers.
apps/a1/Dockerfile (4)
Learnt from: thromer
PR: thromer/uv-monorepo-example#1
File: Dockerfile:28-30
Timestamp: 2025-06-29T22:22:29.398Z
Learning: In the uv-monorepo-example project, the main Dockerfile intentionally creates a minimal scratch-based image containing only Python packages (without the Python interpreter). This is deployed using a separate runtime image (like Dockerfile.merged) that provides the OS and Python interpreter, then copies the packages from the built artifact. This two-stage pattern separates package building from runtime concerns and allows flexible deployment targets.
Learnt from: thromer
PR: thromer/uv-monorepo-example#1
File: lib-new/.python-version:1-1
Timestamp: 2025-06-29T18:50:41.165Z
Learning: In the uv-monorepo-example project, Python version differences between local development (.python-version files specifying 3.11) and Docker runtime (3.12) are intentional. The user uses 3.11 for local development consistency and plans to use CI/CD to ensure 3.12 compatibility.
Learnt from: thromer
PR: thromer/uv-monorepo-example#1
File: Dockerfile:20-20
Timestamp: 2025-06-29T18:28:46.875Z
Learning: uv workspaces provide elegant monorepo management in Docker by using a root pyproject.toml with members configuration and uv sync command, eliminating the need for manual export/build/install steps for each package.
Learnt from: thromer
PR: thromer/uv-monorepo-example#1
File: Dockerfile:0-0
Timestamp: 2025-06-29T19:36:26.385Z
Learning: In Docker builds using uv sync, editable packages are installed as .pth files that reference source locations, not actual distributions. This breaks in containerized environments where the source paths don't exist. Use uv build + pip install for proper package installation in containers.
apps/a1/Dockerfile.merged (2)
Learnt from: thromer
PR: thromer/uv-monorepo-example#1
File: Dockerfile:28-30
Timestamp: 2025-06-29T22:22:29.398Z
Learning: In the uv-monorepo-example project, the main Dockerfile intentionally creates a minimal scratch-based image containing only Python packages (without the Python interpreter). This is deployed using a separate runtime image (like Dockerfile.merged) that provides the OS and Python interpreter, then copies the packages from the built artifact. This two-stage pattern separates package building from runtime concerns and allows flexible deployment targets.
Learnt from: thromer
PR: thromer/uv-monorepo-example#1
File: lib-new/.python-version:1-1
Timestamp: 2025-06-29T18:50:41.165Z
Learning: In the uv-monorepo-example project, Python version differences between local development (.python-version files specifying 3.11) and Docker runtime (3.12) are intentional. The user uses 3.11 for local development consistency and plans to use CI/CD to ensure 3.12 compatibility.
🧬 Code Graph Analysis (6)
libs/l2/src/l2/__init__.py (1)
libs/l1/src/l1/__init__.py (1)
  • hello (2-3)
apps/a2/a2/__init__.py (2)
libs/l2/src/l2/__init__.py (1)
  • hello (2-3)
apps/a1/a1/__init__.py (1)
  • main (2-3)
libs/l2/tests/test_l2.py (1)
libs/l2/src/l2/__init__.py (1)
  • hello (2-3)
libs/l1/tests/test_l1.py (1)
libs/l1/src/l1/__init__.py (1)
  • hello (2-3)
apps/a1/a1/__init__.py (1)
libs/l1/src/l1/__init__.py (1)
  • hello (2-3)
libs/l1/src/l1/__init__.py (1)
libs/l2/src/l2/__init__.py (1)
  • hello (2-3)
🪛 Flake8 (7.2.0)
libs/l2/src/l2/__init__.py

[error] 1-1: 'requests' imported but unused

(F401)


[error] 2-2: expected 2 blank lines, found 0

(E302)

apps/a2/a2/__init__.py

[error] 2-2: expected 2 blank lines, found 0

(E302)

libs/l2/tests/test_l2.py

[error] 3-3: expected 2 blank lines, found 1

(E302)


[warning] 5-5: blank line at end of file

(W391)

libs/l1/tests/test_l1.py

[error] 3-3: expected 2 blank lines, found 1

(E302)

apps/a1/a1/__init__.py

[error] 2-2: expected 2 blank lines, found 0

(E302)

libs/l1/src/l1/__init__.py

[error] 2-2: expected 2 blank lines, found 0

(E302)

🪛 Ruff (0.11.9)
libs/l2/src/l2/__init__.py

1-1: requests imported but unused

(F401)

🪛 markdownlint-cli2 (0.17.2)
README.md

1-1: First line in a file should be a top-level heading

(MD041, first-line-heading, first-line-h1)


4-4: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


4-4: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


9-9: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


9-9: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🪛 Checkov (3.2.334)
apps/a1/Dockerfile

[LOW] 6-6: Ensure the base image uses a non latest version tag

(CKV_DOCKER_7)


[LOW] 9-9: Ensure the last USER is not root

(CKV_DOCKER_8)


[LOW] 1-33: Ensure that HEALTHCHECK instructions have been added to container images

(CKV_DOCKER_2)

apps/a1/Dockerfile.merged

[LOW] 2-2: Ensure the base image uses a non latest version tag

(CKV_DOCKER_7)


[LOW] 1-9: Ensure that HEALTHCHECK instructions have been added to container images

(CKV_DOCKER_2)


[LOW] 1-9: Ensure that a user for the container has been created

(CKV_DOCKER_3)

🪛 Hadolint (2.12.0)
apps/a1/Dockerfile

[warning] 6-6: Always tag the version of an image explicitly

(DL3006)


[warning] 9-9: Last USER should not be root

(DL3002)


[error] 19-19: COPY with more than 2 arguments requires the last argument to end with /

(DL3021)

apps/a1/Dockerfile.merged

[warning] 2-2: Always tag the version of an image explicitly

(DL3006)


[warning] 4-4: COPY --from should reference a previously defined FROM alias

(DL3022)

🔇 Additional comments (2)
.dockerignore (1)

2-2: LGTM – harmless but useful exclusion
Ignoring nested .dockerignore files reduces context size and has no side-effects.

pyproject.toml (1)

1-17: Workspace Python floor (≥3.13) clashes with dev interpreters (3.11)
Because uv checks requires-python, any contributor still on 3.11 (as encouraged by the new .python-version files) will be blocked. Decide on a single lower bound:

  1. Drop the floor to >=3.11; or
  2. Upgrade every .python-version to 3.13.

Whichever you choose, keep it consistent across root & members to avoid CI surprises.

Comment thread libs/l1/.python-version Outdated
@@ -0,0 +1 @@
3.11
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue

Version declared here conflicts with package & workspace requirements
libs/l1/pyproject.toml (≥3.13) and the root workspace (≥3.13) cannot be satisfied when the local pyenv version is pinned to 3.11. uv sync/pyright will refuse to run under 3.11.

-3.11
+3.13

Either lift the requires-python floor back to 3.11 in all affected pyproject.toml files or bump the local version to 3.13 to keep the tool-chain happy.

📝 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
3.11
3.13
🤖 Prompt for AI Agents
In libs/l1/.python-version at line 1, the Python version is set to 3.11, which
conflicts with the minimum Python version requirement of 3.13 specified in
libs/l1/pyproject.toml and the root workspace. To fix this, update the version
in .python-version to 3.13 to align with the package and workspace requirements,
ensuring compatibility with uv sync and pyright tools.

Comment thread libs/l2/.python-version Outdated
@@ -0,0 +1 @@
3.11
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue

Same version drift as in l1
Pinning 3.11 here while the library advertises requires-python = ">=3.13" will break installs/tests in the workspace. Align the numbers as suggested for l1.

🤖 Prompt for AI Agents
In libs/l2/.python-version at line 1, the Python version is pinned to 3.11,
which conflicts with the library's advertised requirement of Python >=3.13.
Update the version in this file to 3.13 to align with the declared Python
version requirement and ensure consistency across the workspace.

Comment thread README.md
Comment on lines +1 to +15
Bootstrap

Once per machine
```
curl -LsSf https://astral.sh/uv/install.sh | sh
```

Once per repo
```
uv init .
uv sync
```

Maybe a (slightly?) different approach will work, WIP [Claude conversation](https://claude.ai/chat/414e992f-a883-4721-8283-8bdb7e961a01)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

Minor Markdown-lint issues – add heading & fence languages

-Bootstrap
+# Bootstrap

and add language hints / blank lines:

-```
+```bash
 curl -LsSf https://astral.sh/uv/install.sh | sh

Same for the later code block. Improves readability and satisfies MD031 / MD040.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 markdownlint-cli2 (0.17.2)</summary>

1-1: First line in a file should be a top-level heading

(MD041, first-line-heading, first-line-h1)

---

4-4: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)

---

4-4: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

---

9-9: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)

---

9-9: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

</details>

</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

In README.md lines 1 to 15, the code blocks lack language specifiers and proper
spacing, which causes markdown lint issues. Add "bash" after the opening triple
backticks for both code blocks and ensure there is a blank line before and after
each fenced code block to improve readability and comply with markdown lint
rules MD031 and MD040.


</details>

<!-- This is an auto-generated comment by CodeRabbit -->

<!-- fingerprinting:phantom:triton:cougar -->

Comment thread apps/a2/a2/__init__.py
Comment on lines +1 to +3
from l2 import hello
def main() -> None:
print(hello("no moo a2"))
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

Fix PEP 8 formatting: add blank lines before function definition.

The static analysis correctly identifies a missing blank line before the function definition.

 from l2 import hello
+
+
 def main() -> None:
     print(hello("no moo a2"))
📝 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
from l2 import hello
def main() -> None:
print(hello("no moo a2"))
from l2 import hello
def main() -> None:
print(hello("no moo a2"))
🧰 Tools
🪛 Flake8 (7.2.0)

[error] 2-2: expected 2 blank lines, found 0

(E302)

🤖 Prompt for AI Agents
In apps/a2/a2/__init__.py at lines 1 to 3, the function definition for main()
lacks a blank line before it, violating PEP 8 style guidelines. Add a blank line
after the import statement and before the def main() line to separate the import
from the function definition properly.

Comment on lines +2 to +3
def hello(message: str) -> str:
return f'Hello, {message}'
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

Fix PEP 8 formatting: add blank lines before function definition.

+
+
 def hello(message: str) -> str:
     return f'Hello, {message}'
📝 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
def hello(message: str) -> str:
return f'Hello, {message}'
def hello(message: str) -> str:
return f'Hello, {message}'
🧰 Tools
🪛 Flake8 (7.2.0)

[error] 2-2: expected 2 blank lines, found 0

(E302)

🤖 Prompt for AI Agents
In libs/l2/src/l2/__init__.py around lines 2 to 3, the function definition lacks
the required blank lines before it according to PEP 8 style guidelines. Add two
blank lines before the 'def hello' function to comply with PEP 8 formatting
standards.

Comment thread apps/a1/Dockerfile
# Build
# We use the same image as the runtime base image so that Python
# version and location matches. Might save on data transfer costs.
FROM us-west1-docker.pkg.dev/serverless-runtimes/google-22/runtimes/python313 AS builder
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Pin the base image with a digest for reproducibility.

python313 is explicit but mutable; tomorrow’s push could break your build.
Add an immutable digest (or at least a dated tag) to guarantee identical layers.

🧰 Tools
🪛 Checkov (3.2.334)

[LOW] 6-6: Ensure the base image uses a non latest version tag

(CKV_DOCKER_7)

🪛 Hadolint (2.12.0)

[warning] 6-6: Always tag the version of an image explicitly

(DL3006)

🤖 Prompt for AI Agents
In apps/a1/Dockerfile at line 6, the base image is currently referenced by a
mutable tag 'python313', which can lead to inconsistent builds. To fix this,
replace the tag with a specific immutable digest or a dated tag for the base
image to ensure reproducibility and prevent unexpected changes in future builds.

Comment thread apps/a1/Dockerfile
Comment on lines +27 to +33
FROM scratch
COPY --from=builder /workspace/.venv /workspace/.venv

ENV PYTHONPATH=/workspace/.venv/lib/python3.13/site-packages
ENV PATH=/workspace/.venv/bin:$PATH

CMD ["a1"]
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

Missing HEALTHCHECK and non-root runtime user.

A scratch image inherits root; add:

USER 65532  # non-root
HEALTHCHECK CMD a1 --help || exit 1

to satisfy CKV_DOCKER_2 & CKV_DOCKER_8 and to surface liveness issues early.

🧰 Tools
🪛 Checkov (3.2.334)

[LOW] 1-33: Ensure that HEALTHCHECK instructions have been added to container images

(CKV_DOCKER_2)

🤖 Prompt for AI Agents
In apps/a1/Dockerfile around lines 27 to 33, the Dockerfile uses the scratch
base image which runs as root by default and lacks a HEALTHCHECK instruction. To
fix this, add a non-root user directive by including 'USER 65532' and add a
HEALTHCHECK instruction like 'HEALTHCHECK CMD a1 --help || exit 1' to enable
early detection of liveness issues and comply with CKV_DOCKER_2 and
CKV_DOCKER_8.

Comment thread apps/a1/Dockerfile
# Sorta hacky solution to being able to write to /workspace
USER root

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

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

uv:latest tag is volatile.

Same reproducibility concern as above—pin ghcr.io/astral-sh/uv:<version>@<digest> instead of latest.

🤖 Prompt for AI Agents
In apps/a1/Dockerfile at line 11, the COPY command uses the volatile 'latest'
tag for the uv image, which can cause reproducibility issues. Replace 'latest'
with a specific version tag and digest for the uv image, such as
'ghcr.io/astral-sh/uv:<version>@<digest>', to ensure consistent builds.

Comment thread apps/a1/Dockerfile
UV_LINK_MODE=copy \
UV_NO_MANAGED_PYTHON=1

COPY pyproject.toml uv.lock .
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

Fix multi-source COPY to satisfy Hadolint DL3021.

The linter expects the destination to end in /.
Quick fix:

-COPY pyproject.toml uv.lock .
+COPY pyproject.toml uv.lock ./
📝 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
COPY pyproject.toml uv.lock .
COPY pyproject.toml uv.lock ./
🧰 Tools
🪛 Hadolint (2.12.0)

[error] 19-19: COPY with more than 2 arguments requires the last argument to end with /

(DL3021)

🤖 Prompt for AI Agents
In apps/a1/Dockerfile at line 19, the multi-source COPY command lacks a trailing
slash on the destination, which violates Hadolint rule DL3021. Fix this by
appending a slash to the destination path, changing it to COPY pyproject.toml
uv.lock ./ to clearly indicate the destination is a directory.

Comment thread apps/a1/Dockerfile
Comment on lines +9 to +10
USER root

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Builder still runs as root.

Static analysis rightfully warns (DL3002).
Create a non-privileged user after the COPY, switch back with USER builder, and keep root only where absolutely required.


🏁 Script executed:

#!/bin/bash
# Locate the Dockerfile for app a1 and display its contents with line numbers
FILE=$(find . -type f -path "*/apps/a1/Dockerfile")
if [[ -z "$FILE" ]]; then
  echo "apps/a1/Dockerfile not found"
  exit 1
fi
echo "Found Dockerfile at: $FILE"
nl -ba "$FILE"

Length of output: 322


🏁 Script executed:

#!/bin/bash
# Display the Dockerfile contents with line numbers using sed and cat -n
FILE=$(find . -type f -path "*/apps/a1/Dockerfile")
if [[ -z "$FILE" ]]; then
  echo "apps/a1/Dockerfile not found"
  exit 1
fi
echo "Found Dockerfile at: $FILE"
sed -n '1,200p' "$FILE" | cat -n

Length of output: 1424


Run the builder stage as a non-root user

The builder stage is still running as root (USER root on line 9), which triggers DL3002. We should minimize privileged operations by:

• Creating a dedicated, unprivileged user/group in the builder stage
• Chown’ing /workspace (and any other dirs) to that user
• Switching to that user for all build steps
• Only elevating back to root for operations that truly require it, then reverting to the unprivileged user

Modify apps/a1/Dockerfile around lines 6–13:

 FROM us-west1-docker.pkg.dev/serverless-runtimes/google-22/runtimes/python313 AS builder

-# Sorta hacky solution to being able to write to /workspace
-USER root
+### create an unprivileged builder user and own workspace
+RUN addgroup --system builder \
+ && adduser  --system --ingroup builder builder \
+ && mkdir -p /workspace \
+ && chown -R builder:builder /workspace
+
+USER builder

 # install UV (requires root)
-USER root
+USER root
 COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv
+USER builder

 WORKDIR /workspace
🧰 Tools
🪛 Checkov (3.2.334)

[LOW] 9-9: Ensure the last USER is not root

(CKV_DOCKER_8)

🪛 Hadolint (2.12.0)

[warning] 9-9: Last USER should not be root

(DL3002)

🤖 Prompt for AI Agents
In apps/a1/Dockerfile around lines 6 to 13, the builder stage is running as
root, which is a security risk. Fix this by creating a dedicated non-root user
and group in the builder stage, changing ownership of /workspace and any other
necessary directories to this user, switching to this user for all build steps,
and only using root temporarily for operations that require elevated privileges
before switching back to the unprivileged user.

Copy link
Copy Markdown

@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: 3

📜 Review details

Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f7bf43e and 8d56273.

📒 Files selected for processing (1)
  • deploy.sh (1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: thromer
PR: thromer/uv-monorepo-example#1
File: Dockerfile:20-20
Timestamp: 2025-06-29T18:28:46.875Z
Learning: uv workspaces provide elegant monorepo management in Docker by using a root pyproject.toml with members configuration and uv sync command, eliminating the need for manual export/build/install steps for each package.
Learnt from: thromer
PR: thromer/uv-monorepo-example#1
File: Dockerfile:28-30
Timestamp: 2025-06-29T22:22:29.398Z
Learning: In the uv-monorepo-example project, the main Dockerfile intentionally creates a minimal scratch-based image containing only Python packages (without the Python interpreter). This is deployed using a separate runtime image (like Dockerfile.merged) that provides the OS and Python interpreter, then copies the packages from the built artifact. This two-stage pattern separates package building from runtime concerns and allows flexible deployment targets.
Learnt from: thromer
PR: thromer/uv-monorepo-example#1
File: Dockerfile:0-0
Timestamp: 2025-06-29T19:36:26.385Z
Learning: In Docker builds using uv sync, editable packages are installed as .pth files that reference source locations, not actual distributions. This breaks in containerized environments where the source paths don't exist. Use uv build + pip install for proper package installation in containers.
Learnt from: thromer
PR: thromer/uv-monorepo-example#1
File: Dockerfile:0-0
Timestamp: 2025-06-29T19:45:02.721Z
Learning: uv export --no-editable still includes local package references (like "." and "../package-name") in the requirements.txt output, which breaks pip install in containers. These need to be filtered out with sed or similar tools.
Learnt from: thromer
PR: thromer/uv-monorepo-example#1
File: lib-new/.python-version:1-1
Timestamp: 2025-06-29T18:50:41.165Z
Learning: In the uv-monorepo-example project, Python version differences between local development (.python-version files specifying 3.11) and Docker runtime (3.12) are intentional. The user uses 3.11 for local development consistency and plans to use CI/CD to ensure 3.12 compatibility.
deploy.sh (2)
Learnt from: thromer
PR: thromer/uv-monorepo-example#1
File: Dockerfile:28-30
Timestamp: 2025-06-29T22:22:29.398Z
Learning: In the uv-monorepo-example project, the main Dockerfile intentionally creates a minimal scratch-based image containing only Python packages (without the Python interpreter). This is deployed using a separate runtime image (like Dockerfile.merged) that provides the OS and Python interpreter, then copies the packages from the built artifact. This two-stage pattern separates package building from runtime concerns and allows flexible deployment targets.
Learnt from: thromer
PR: thromer/uv-monorepo-example#1
File: Dockerfile:20-20
Timestamp: 2025-06-29T18:28:46.875Z
Learning: uv workspaces provide elegant monorepo management in Docker by using a root pyproject.toml with members configuration and uv sync command, eliminating the need for manual export/build/install steps for each package.
🔇 Additional comments (1)
deploy.sh (1)

31-31: Verify --base-image flag support in gcloud run deploy
gcloud run deploy (stable & beta channels) currently lacks a --base-image flag. If you’re relying on an experimental CLI, pin the version or switch to --execution-environment / Dockerfile-based runtime selection.

Comment thread deploy.sh
@@ -0,0 +1,41 @@
#!/usr/bin/bash
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

Use portable she-bang
#!/usr/bin/bash only exists on some distros. Prefer /usr/bin/env bash for portability across build agents & CI runners.

-#!/usr/bin/bash
+#!/usr/bin/env bash
📝 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
#!/usr/bin/bash
#!/usr/bin/env bash
🤖 Prompt for AI Agents
In deploy.sh at line 1, replace the she-bang line from #!/usr/bin/bash to
#!/usr/bin/env bash to ensure portability across different Linux distributions
and CI environments.

Comment thread deploy.sh
@@ -0,0 +1,41 @@
#!/usr/bin/bash

set -o pipefail
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add -euo for safer Bash execution
set -o pipefail alone does not abort on command failures or unset vars. Fail-fast behaviour is critical for deployment scripts.

-set -o pipefail
+set -euo pipefail
📝 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
set -o pipefail
set -euo pipefail
🤖 Prompt for AI Agents
In deploy.sh at line 3, the script currently uses 'set -o pipefail' but lacks
'set -euo' which ensures the script fails fast on errors, unset variables, or
pipeline failures. Update the line to include 'set -euo pipefail' to enable
safer Bash execution by aborting on command failures and unset variables.

Comment thread deploy.sh
Comment on lines +8 to +23
REPO=projects/${PROJECT}/locations/${LOCATION}/repositories/artifacts

ensure_repo() {
repo=projects/${PROJECT}/locations/${LOCATION}/repositories/artifacts
if gcloud artifacts repositories describe $repo >& /dev/null; then
return 0
fi
gcloud artifacts repositories create \
--location=$LOCATION \
--repository-format=DOCKER \
$REPO &&
gcloud artifacts repositories set-cleanup-policies \
--policy=../repository-cleanup-policy.json \
$REPO
return $?
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue

ensure_repo never called & $REPO unused
The repository may not exist, causing the subsequent docker push to fail. Call the helper early and drop the duplicate local variable.

-REPO=projects/${PROJECT}/locations/${LOCATION}/repositories/artifacts
+REPO="projects/${PROJECT}/locations/${LOCATION}/repositories/artifacts"

 ...

-ensure_repo() {
-    repo=projects/${PROJECT}/locations/${LOCATION}/repositories/artifacts
+ensure_repo() {
+    repo="$REPO"
     if gcloud artifacts repositories describe "$repo" >& /dev/null; then
         return 0
     fi
     gcloud artifacts repositories create \
            --location="$LOCATION" \
            --repository-format=DOCKER \
            "$REPO" &&
     gcloud artifacts repositories set-cleanup-policies \
            --policy=../repository-cleanup-policy.json \
            "$REPO"
     return $?
 }

+# ensure repository exists before build/push
+ensure_repo
📝 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
REPO=projects/${PROJECT}/locations/${LOCATION}/repositories/artifacts
ensure_repo() {
repo=projects/${PROJECT}/locations/${LOCATION}/repositories/artifacts
if gcloud artifacts repositories describe $repo >& /dev/null; then
return 0
fi
gcloud artifacts repositories create \
--location=$LOCATION \
--repository-format=DOCKER \
$REPO &&
gcloud artifacts repositories set-cleanup-policies \
--policy=../repository-cleanup-policy.json \
$REPO
return $?
}
REPO="projects/${PROJECT}/locations/${LOCATION}/repositories/artifacts"
ensure_repo() {
repo="$REPO"
if gcloud artifacts repositories describe "$repo" >& /dev/null; then
return 0
fi
gcloud artifacts repositories create \
--location="$LOCATION" \
--repository-format=DOCKER \
"$REPO" &&
gcloud artifacts repositories set-cleanup-policies \
--policy=../repository-cleanup-policy.json \
"$REPO"
return $?
}
# ensure repository exists before build/push
ensure_repo
🤖 Prompt for AI Agents
In deploy.sh around lines 8 to 23, the ensure_repo function is defined but never
called, and the local variable repo inside it duplicates the global REPO
variable. To fix this, remove the local repo variable inside ensure_repo and
replace its usage with the global REPO variable. Then, call ensure_repo early in
the script before any docker push commands to ensure the repository exists and
prevent push failures.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant