Skip to content

Conversation

@Devasy
Copy link
Contributor

@Devasy Devasy commented Jan 5, 2026

@coderabbitai review

Summary by CodeRabbit

Release Notes

  • New Features

    • Containerized browser automation service with dedicated deployment.
    • Docker Compose configurations for both production and development environments.
    • Hot-reload development setup for faster local development iteration.
  • Chores

    • Added automated GitHub Actions workflow for building and pushing Docker images to registry.
    • Introduced environment configuration templates for Docker setup.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 5, 2026

Walkthrough

This PR introduces Docker containerization infrastructure for the application, including Dockerfiles for FastAPI and Browser Service with multi-stage builds, Docker Compose configurations for both development and production environments, a GitHub Actions workflow for automated image building and pushing, environment variable configurations for image tags and host paths, and backend code updates to support Docker-in-Docker volume mounting.

Changes

Cohort / File(s) Summary
Docker Infrastructure
Dockerfile.fastapi, Dockerfile.browser-service, Dockerfile.test-runner.archive
Two new multi-stage Dockerfiles introduced: Dockerfile.fastapi with PyTorch/FastAPI runtime on port 5000, and Dockerfile.browser-service with Playwright/Chrome on port 4999. Both use builder stages for dependencies and slim runtime stages. Test runner Dockerfile archived with migration notice.
Docker Compose Orchestration
docker-compose.yml, docker-compose.dev.yml
Base compose configuration defines fastapi and browser-service with networking, volumes, health checks, and logging. Development compose overrides with hot-reload uvicorn command, mounted source directories, and debug environment variables.
CI/CD Pipeline
.github/workflows/build-images.yml
New GitHub Actions workflow builds and pushes FastAPI and Browser Service images to Docker Hub on PR changes, main/develop pushes, or manual dispatch. Includes metadata extraction, Docker Buildx setup, and step summary reporting.
Environment Configuration
.env.example, src/backend/.env.example
Root .env.example defines FASTAPI_IMAGE_TAG, BROWSER_SERVICE_IMAGE_TAG, and HOST_ROBOT_TESTS_DIR with comments. Backend .env.example adds Docker image tag variables under Docker Compose Configuration section.
Backend Docker Support
src/backend/services/docker_service.py, src/backend/crew_ai/agents.py
docker_service.py adds HOST_ROBOT_TESTS_DIR for Docker-in-Docker volume mounting support. agents.py removes Selenium dependency, introduces cleaned LLM wrapper via get_cleaned_llm function, and minor cleanup.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Suggested labels

enhancement

Suggested reviewers

  • monkscode

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Migration to docker' is directly related to the main objective of this PR, which introduces comprehensive Docker support including Dockerfiles, docker-compose configurations, and CI/CD workflows for containerized deployments.
✨ Finishing touches
  • 📝 Generate docstrings

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.

@Devasy
Copy link
Contributor Author

Devasy commented Jan 5, 2026

@coderabbitai review

@sonarqubecloud
Copy link

sonarqubecloud bot commented Jan 5, 2026

Quality Gate Failed Quality Gate failed

Failed conditions
11 Security Hotspots

See analysis details on SonarQube Cloud

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 5, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai bot requested a review from monkscode January 5, 2026 17:51
@coderabbitai coderabbitai bot added the enhancement New feature or request label Jan 5, 2026
Copy link
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: 15

Fix all issues with AI Agents 🤖
In @.env.example:
- Line 23: The default .env entry uses HOST_ROBOT_TESTS_DIR=${PWD}/robot_tests
which will not be expanded by Docker Compose; update the .env.example to remove
the unexpandable ${PWD} and instead instruct users to set an absolute path (or
provide the suggested shell command), e.g., replace or annotate the
HOST_ROBOT_TESTS_DIR line to require an absolute path and add a short note
showing how to populate it via echo "HOST_ROBOT_TESTS_DIR=$(pwd)/robot_tests" >>
.env so users know to run that during setup.

In @docker-compose.dev.yml:
- Around line 21-26: Remove the redundant BROWSER_USE_SERVICE_URL environment
entry from docker-compose.dev.yml (the -
BROWSER_USE_SERVICE_URL=http://browser-service:4999 line) since it duplicates
the same key/value from the base docker-compose.yml; only keep environment
overrides in docker-compose.dev.yml for variables that differ from the base,
leaving DEBUG and LOG_LEVEL if they are intentionally set for dev.

In @docker-compose.yml:
- Around line 23-24: The docker-compose entries using ${PWD} for
HOST_ROBOT_TESTS_DIR and HOST_WORKSPACE_DIR are not portable across shells/OSs;
replace them with explicit Compose variable substitution and require users to
set HOST_ROBOT_TESTS_DIR and HOST_WORKSPACE_DIR in an .env file (or fall back to
COMPOSE_PROJECT_DIR-based relative paths), update docker-compose.yml to
reference those variables only, and add README instructions showing how to
export/set HOST_ROBOT_TESTS_DIR and HOST_WORKSPACE_DIR for Linux/macOS and
Windows (PowerShell) so CI and Windows users have correct absolute paths.
- Around line 84-88: The docker-compose declares named volumes (robot_tests,
logs, chroma_db, data) but services use bind mounts; either remove the unused
named volumes block or convert service mounts to use those named volumes
consistently: update service volume entries to reference
robot_tests:/app/robot_tests, logs:/path, chroma_db:/path, data:/path (replace
/path with the container paths already used), and if you choose named volumes
also change HOST_ROBOT_TESTS_DIR to the in-container path (/app/robot_tests) so
Docker-in-Docker behavior is correct.
- Line 17: The docker-compose service currently mounts the host Docker socket
(/var/run/docker.sock) which grants full daemon access; replace this direct
mount by adding or referencing a docker-socket-proxy sidecar (e.g.,
tecnativa/docker-socket-proxy) and update the service to use the proxy endpoint
instead of /var/run/docker.sock, document the security implications and chosen
mitigation in the README/docs, ensure the service runs with least-privilege
(avoid privileged mode, set read-only root filesystem where possible) and
configure the proxy to filter API calls to only the required Docker operations.
- Line 60: The environment variable BROWSER_SERVICE_URL set to
"http://browser-service:4999" in the browser-service container is redundant;
remove the BROWSER_SERVICE_URL entry from the browser-service service definition
in docker-compose.yml (or conditionally guard its injection) unless the
browser-service application actually reads that variable; search for uses of
BROWSER_SERVICE_URL in the codebase and only keep the variable when referenced
by other services (e.g., fastapi) to avoid configuration noise.
- Around line 26-27: Create the missing template file src/backend/.env.example
containing documented keys required by the app (at minimum GEMINI_API_KEY and
APP_PORT) with placeholder values and brief comments describing each variable;
ensure the file mirrors the .env layout referenced by docker-compose's env_file
and README (include any other environment variables used by backend services if
present), so contributors can copy src/backend/.env.example to src/backend/.env
and populate real secrets.

In @Dockerfile.browser-service:
- Around line 34-38: The ENV block sets PATH="/opt/venv/bin:$PATH" but no venv
is created and packages are installed to /usr/local; replace the invalid entry
with the actual binary location (e.g., PATH="/usr/local/bin:$PATH") or remove
/opt/venv/bin, ensuring PLAYWRIGHT_BROWSERS_PATH and other ENV vars remain
unchanged; mirror the same change applied in the other Dockerfile to keep both
Dockerfiles consistent.
- Around line 41-72: The piped command "wget -q -O -
https://dl-ssl.google.com/linux/linux_signing_key.pub | gpg --dearmor -o
/usr/share/keyrings/google-chrome-keyring.gpg" can hide wget failures because
shell pipefail isn't enabled; enable pipefail by running the RUN block under
bash with pipefail set (e.g., invoke bash -c 'set -o pipefail && ...' or change
the image SHELL to bash -o pipefail -c) so that any failure in wget causes the
whole RUN to fail, or alternatively avoid the pipe by downloading to a temporary
file then running gpg on that file. Ensure you target the RUN containing the
wget|gpg pipeline when applying the change.

In @Dockerfile.fastapi:
- Around line 50-53: The ENV line incorrectly prepends a non-existent virtualenv
path (/opt/venv/bin) to PATH; update the Dockerfile ENV declaration (the block
setting PYTHONUNBUFFERED, PYTHONDONTWRITEBYTECODE, PYTHONIOENCODING, PATH) to
either remove the PATH modification entirely or change it to the actual runtime
bin location used in the image (e.g., PATH="/usr/local/bin:$PATH") so installed
packages under /usr/local are found.
- Around line 44-82: The container currently runs as root; create a non-root
user and switch to it for runtime: after the apt-get installation block, add
steps to create a system user (e.g., appuser) and group, set /app ownership and
permissions (chown -R appuser:appuser /app /app/robot_tests /app/logs
/app/chroma_db /app/data), and drop privileges by adding a USER appuser before
the CMD; ensure HEALTHCHECK and CMD continue to reference the same port/path and
that any runtime files are writable by appuser.
- Around line 16-17: The Dockerfile installs the "uv" package without a pinned
version via the RUN pip install --no-cache-dir uv line; update this to pin a
specific version (or use a build ARG like UV_VERSION or reference a requirements
file) so the RUN pip install step installs uv at a fixed version (e.g.,
uv==<version>) to ensure reproducible builds and make future updates explicit.

In @src/backend/.env.example:
- Around line 78-79: The .env.example variables FASTAPI_IMAGE_TAG and
BROWSER_SERVICE_IMAGE_TAG use monkscode/...:latest but docker-compose.yml falls
back to devasy23/nlrf:fastapi-latest style tags, causing a mismatch; update
FASTAPI_IMAGE_TAG and BROWSER_SERVICE_IMAGE_TAG in src/backend/.env.example to
use the same registry and tag format as the docker-compose.yml fallbacks (e.g.,
devasy23/nlrf:fastapi-latest and devasy23/nlrf:browser-service-latest) or change
the docker-compose.yml defaults to match monkscode/...:latest—ensure both files
reference the identical image names/tags so the env variable names
FASTAPI_IMAGE_TAG and BROWSER_SERVICE_IMAGE_TAG resolve consistently.

In @src/backend/services/docker_service.py:
- Around line 170-181: run_test_in_container currently uses HOST_ROBOT_TESTS_DIR
directly for a bind mount which can cause cryptic Docker mount errors if it's
missing or not an absolute host path; before creating container_config validate
HOST_ROBOT_TESTS_DIR by checking os.path.exists(HOST_ROBOT_TESTS_DIR) and
os.path.isabs(HOST_ROBOT_TESTS_DIR) and if missing or not absolute, log a clear
error/warning (including the value of HOST_ROBOT_TESTS_DIR) and raise or return
early so container creation is skipped; update logging to include the validation
failure and reference the HOST_ROBOT_TESTS_DIR and run_id in messages so
failures are easier to debug.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/backend/crew_ai/agents.py (1)

44-44: Remove or integrate the unused scrape_tool.

The scrape_tool is initialized but never used in any agent. The element_identifier_agent uses batch_browser_use_tool (line 186), while other agents use keyword_search_tool. This creates dead code and maintenance burden.

🔎 Proposed fix to remove unused code
-# Initialize the tools
-# Note: These are tool instances, not classes. CrewAI requires instantiated tools.
-scrape_tool = ScrapeElementFromWebsiteTool()
 # Primary tool: Batch processing for multiple elements with full context
 batch_browser_use_tool = BatchBrowserUseTool()

Alternatively, if this tool is intended for future use, please integrate it into one of the agents or add a comment explaining its purpose.

📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cf010de and c8355ca.

📒 Files selected for processing (10)
  • .env.example
  • .github/workflows/build-images.yml
  • Dockerfile.browser-service
  • Dockerfile.fastapi
  • Dockerfile.test-runner.archive
  • docker-compose.dev.yml
  • docker-compose.yml
  • src/backend/.env.example
  • src/backend/crew_ai/agents.py
  • src/backend/services/docker_service.py
🧰 Additional context used
🪛 Checkov (3.2.334)
Dockerfile.test-runner.archive

[low] 1-82: Ensure that HEALTHCHECK instructions have been added to container images

(CKV_DOCKER_2)


[low] 1-82: Ensure that a user for the container has been created

(CKV_DOCKER_3)

Dockerfile.browser-service

[low] 1-95: Ensure that a user for the container has been created

(CKV_DOCKER_3)

Dockerfile.fastapi

[low] 1-82: Ensure that a user for the container has been created

(CKV_DOCKER_3)

🪛 dotenv-linter (4.0.0)
.env.example

[warning] 11-11: [UnorderedKey] The BROWSER_SERVICE_IMAGE_TAG key should go before the FASTAPI_IMAGE_TAG key

(UnorderedKey)

src/backend/.env.example

[warning] 79-79: [UnorderedKey] The BROWSER_SERVICE_IMAGE_TAG key should go before the FASTAPI_IMAGE_TAG key

(UnorderedKey)

🪛 Hadolint (2.14.0)
Dockerfile.browser-service

[warning] 16-16: Pin versions in pip. Instead of pip install <package> use pip install <package>==<version> or pip install --requirement <requirements file>

(DL3013)


[warning] 41-41: Pin versions in apt get install. Instead of apt-get install <package> use apt-get install <package>=<version>

(DL3008)


[warning] 41-41: Set the SHELL option -o pipefail before RUN with a pipe in it. If you are using /bin/sh in an alpine image or if your shell is symlinked to busybox then consider explicitly setting your SHELL to /bin/ash, or disable this check

(DL4006)

Dockerfile.fastapi

[warning] 17-17: Pin versions in pip. Instead of pip install <package> use pip install <package>==<version> or pip install --requirement <requirements file>

(DL3013)


[warning] 20-20: Pin versions in apt get install. Instead of apt-get install <package> use apt-get install <package>=<version>

(DL3008)


[info] 38-38: Multiple consecutive RUN instructions. Consider consolidation.

(DL3059)


[warning] 56-56: Pin versions in apt get install. Instead of apt-get install <package> use apt-get install <package>=<version>

(DL3008)

🪛 Ruff (0.14.10)
src/backend/services/docker_service.py

180-181: info() call on root logger

(LOG015)


181-181: Logging statement uses f-string

(G004)

🔇 Additional comments (16)
src/backend/crew_ai/agents.py (5)

14-14: LGTM!

The import of get_cleaned_llm is properly integrated and used in the get_llm function to provide robust LLM output parsing.


19-39: LGTM!

The get_llm function is well-documented and provides a clean abstraction for LLM initialization with automatic output cleaning. The implementation correctly delegates to get_cleaned_llm and includes helpful logging.


46-46: LGTM!

The batch_browser_use_tool is properly initialized and correctly integrated into the element_identifier_agent (line 186). The comment clearly explains its purpose.


1-309: Clarify the connection to Docker migration.

The PR title indicates "Migration to docker" and the AI summary mentions "backend code updates to support Docker-in-Docker volume mounting," but the changes in this file primarily involve tool refactoring (SeleniumScrapingTool → ScrapeElementFromWebsiteTool, BatchBrowserUseTool) and LLM wrapper improvements.

Could you clarify how these changes relate to the Docker migration? For example:

  • Are the tool changes required for containerized browser automation?
  • Does the cleaned LLM wrapper address Docker-specific issues?
  • Are there Docker-related changes in the tool implementations themselves?

6-6: Migration to ScrapeElementFromWebsiteTool is complete and properly executed.

No remaining references to SeleniumScrapingTool were found in the codebase. The new tool is correctly imported at line 6 and instantiated at line 44. No further action required for this migration.

Dockerfile.browser-service (1)

68-78: Verify if both Google Chrome and Playwright Chromium are required.

The Dockerfile installs both google-chrome-stable (line 71) and Playwright's Chromium (line 78). If only one browser is needed for your use case, consider removing the redundant installation to reduce image size.

Dockerfile.test-runner.archive (1)

1-4: LGTM - Clear archive header.

The archive header clearly indicates this file is deprecated and has been replaced by Dockerfile.fastapi and Dockerfile.browser-service. Consider deleting this file entirely if it's no longer needed, or moving it to a docs/ or archive/ directory to keep the root clean.

src/backend/.env.example (1)

81-86: Good documentation for Docker-in-Docker support.

The HOST_ROBOT_TESTS_DIR variable is well-documented with clear guidance for both local development and Docker deployment scenarios.

.env.example (1)

25-27: LGTM - Clear reference to backend config.

The comment appropriately directs users to src/backend/.env.example for additional application settings.

src/backend/services/docker_service.py (1)

18-21: LGTM - Docker-in-Docker host path support.

The HOST_ROBOT_TESTS_DIR environment variable with a sensible fallback to the local absolute path correctly addresses the Docker-in-Docker volume mounting challenge. The inline comment clearly explains the purpose.

.github/workflows/build-images.yml (2)

3-24: Good path coverage for workflow triggers.

The workflow correctly triggers on changes to both browser_service/ and browser-service/ directories, covering potential naming variations. The path filters are well-chosen to avoid unnecessary builds.


133-151: This code is correct as written.

The ${{ env.IMAGE_NAME }} syntax properly expands in the run script. GitHub Actions processes ${{ ... }} expressions at workflow parse time before the shell script executes, replacing them with their concrete values (devasy23/nlrf). The environment variable is defined at the workflow level and is available to all jobs, including the summary job. No changes are needed.

docker-compose.dev.yml (3)

1-2: Good documentation of usage pattern.

The comment clearly documents how to use this dev override file with the base compose. This helps prevent common mistakes like running docker compose -f docker-compose.dev.yml up without the base file.

Optional enhancement: Consider adding a dev-specific command to the README or Makefile:

.PHONY: dev
dev:
	docker compose -f docker-compose.yml -f docker-compose.dev.yml up --build

.PHONY: dev-down
dev-down:
	docker compose -f docker-compose.yml -f docker-compose.dev.yml down

13-14: Concern about read-only mounts with hot-reload is already mitigated.

The read-only source mounts are safe because PYTHONDONTWRITEBYTECODE=1 is already set in Dockerfile.fastapi (lines 14 and 51), which prevents Python from writing bytecode files. No additional environment configuration is needed in docker-compose.dev.yml.

Likely an incorrect or invalid review comment.


30-45: Remove the suggested command override—browser-service uses Flask, not FastAPI.

The service runs with app.run(debug=False, ...) and does not support uvicorn. If hot-reload is needed for development, enable Flask's debug mode via an environment variable instead:

environment:
  - DEBUG=true
  - FLASK_ENV=development

Then modify tools/browser_use_service.py to respect the FLASK_ENV variable when calling app.run().

Likely an incorrect or invalid review comment.

docker-compose.yml (1)

39-39: curl is already properly installed in both Docker images.

Verification confirms that curl is explicitly installed in both Dockerfile.fastapi (line 60) and Dockerfile.browser-service (line 48) as part of the runtime dependencies, ensuring the healthchecks in docker-compose.yml will function correctly.

# Example values:
# Linux/Mac: /home/user/Natural-Language-to-Robot-Framework/robot_tests
# Windows: C:/Users/Devasy/Documents/GitHub/Natural-Language-to-Robot-Framework/robot_tests
HOST_ROBOT_TESTS_DIR=${PWD}/robot_tests
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

${PWD} may not expand correctly in .env files.

Docker Compose does not perform shell variable expansion like ${PWD} when reading .env files. This default value will likely be interpreted literally as the string ${PWD}/robot_tests rather than the actual working directory.

🔎 Proposed fix

Update the comment to clarify this limitation and require users to set an absolute path:

 # Example values:
 #   Linux/Mac: /home/user/Natural-Language-to-Robot-Framework/robot_tests
 #   Windows: C:/Users/Devasy/Documents/GitHub/Natural-Language-to-Robot-Framework/robot_tests
-HOST_ROBOT_TESTS_DIR=${PWD}/robot_tests
+# IMPORTANT: Set this to your actual absolute path - ${PWD} does NOT expand in .env files
+HOST_ROBOT_TESTS_DIR=

Alternatively, document that users should run echo "HOST_ROBOT_TESTS_DIR=$(pwd)/robot_tests" >> .env during setup.

📝 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
HOST_ROBOT_TESTS_DIR=${PWD}/robot_tests
# Example values:
# Linux/Mac: /home/user/Natural-Language-to-Robot-Framework/robot_tests
# Windows: C:/Users/Devasy/Documents/GitHub/Natural-Language-to-Robot-Framework/robot_tests
# IMPORTANT: Set this to your actual absolute path - ${PWD} does NOT expand in .env files
HOST_ROBOT_TESTS_DIR=
🤖 Prompt for AI Agents
In @.env.example around line 23, The default .env entry uses
HOST_ROBOT_TESTS_DIR=${PWD}/robot_tests which will not be expanded by Docker
Compose; update the .env.example to remove the unexpandable ${PWD} and instead
instruct users to set an absolute path (or provide the suggested shell command),
e.g., replace or annotate the HOST_ROBOT_TESTS_DIR line to require an absolute
path and add a short note showing how to populate it via echo
"HOST_ROBOT_TESTS_DIR=$(pwd)/robot_tests" >> .env so users know to run that
during setup.

Comment on lines +70 to +80
- name: Build and push FastAPI image
uses: docker/build-push-action@v5
with:
context: .
file: ./Dockerfile.fastapi
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:fastapi-buildcache
cache-to: type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:fastapi-buildcache,mode=max
platforms: linux/amd64
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Security: Avoid pushing images on pull requests.

The workflow pushes images on pull requests (push: true), which could be a security concern if secrets are available to fork PRs. While GitHub Actions typically doesn't expose secrets to fork PRs, it's safer to conditionally push only on push events to main/develop.

🔎 Proposed fix
       - name: Build and push FastAPI image
         uses: docker/build-push-action@v5
         with:
           context: .
           file: ./Dockerfile.fastapi
-          push: true
+          push: ${{ github.event_name == 'push' }}
           tags: ${{ steps.meta.outputs.tags }}
           labels: ${{ steps.meta.outputs.labels }}

Apply the same change to the browser-service job (lines 114-124).

📝 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
- name: Build and push FastAPI image
uses: docker/build-push-action@v5
with:
context: .
file: ./Dockerfile.fastapi
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:fastapi-buildcache
cache-to: type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:fastapi-buildcache,mode=max
platforms: linux/amd64
- name: Build and push FastAPI image
uses: docker/build-push-action@v5
with:
context: .
file: ./Dockerfile.fastapi
push: ${{ github.event_name == 'push' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:fastapi-buildcache
cache-to: type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:fastapi-buildcache,mode=max
platforms: linux/amd64

Comment on lines +21 to +26
environment:
# Development environment
- DEBUG=true
- LOG_LEVEL=DEBUG
# Browser service URL for Docker network
- BROWSER_USE_SERVICE_URL=http://browser-service:4999
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Redundant environment variables already in base compose.

The BROWSER_USE_SERVICE_URL environment variable (line 26) duplicates what's already defined in the base docker-compose.yml (line 21 with the same value). When using compose file merging, these duplicate definitions add noise without changing behavior.

🔎 Suggested cleanup
     volumes:
       # Mount source code for hot-reload
       - ./src:/app/src:ro
       - ./tools:/app/tools:ro
       # Keep persistent data volumes from base compose
       - ./robot_tests:/app/robot_tests
       - ./logs:/app/logs
       - ./chroma_db:/app/chroma_db
       - ./data:/app/data
       - /var/run/docker.sock:/var/run/docker.sock
     environment:
       # Development environment
       - DEBUG=true
       - LOG_LEVEL=DEBUG
-      # Browser service URL for Docker network
-      - BROWSER_USE_SERVICE_URL=http://browser-service:4999
     # Override image tag to use locally built version
     image: nlrf-fastapi:dev

Only override environment variables in the dev compose if they differ from the base configuration.

📝 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
environment:
# Development environment
- DEBUG=true
- LOG_LEVEL=DEBUG
# Browser service URL for Docker network
- BROWSER_USE_SERVICE_URL=http://browser-service:4999
environment:
# Development environment
- DEBUG=true
- LOG_LEVEL=DEBUG
🤖 Prompt for AI Agents
In @docker-compose.dev.yml around lines 21-26, Remove the redundant
BROWSER_USE_SERVICE_URL environment entry from docker-compose.dev.yml (the -
BROWSER_USE_SERVICE_URL=http://browser-service:4999 line) since it duplicates
the same key/value from the base docker-compose.yml; only keep environment
overrides in docker-compose.dev.yml for variables that differ from the base,
leaving DEBUG and LOG_LEVEL if they are intentionally set for dev.

- ./chroma_db:/app/chroma_db
- ./data:/app/data
# Docker socket for robot test execution (Docker-in-Docker)
- /var/run/docker.sock:/var/run/docker.sock
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Security risk: Docker socket mount grants full Docker daemon access.

Mounting /var/run/docker.sock gives the container unrestricted access to the Docker daemon, effectively granting root-level control over the host. This is necessary for Docker-in-Docker but creates a significant security boundary crossing.

Consider these mitigations:

  • Document the security implications in README/docs
  • Use Docker socket proxy (e.g., tecnativa/docker-socket-proxy) with restricted permissions instead of direct socket mount
  • Implement least-privilege API filtering to allow only required Docker operations
  • Run container with read-only root filesystem where possible
  • Add security scanning for the spawned containers
🔎 Example using docker-socket-proxy
+  docker-proxy:
+    image: tecnativa/docker-socket-proxy
+    container_name: nlrf-docker-proxy
+    privileged: true
+    environment:
+      - CONTAINERS=1
+      - IMAGES=1
+      - POST=1
+    volumes:
+      - /var/run/docker.sock:/var/run/docker.sock:ro
+    networks:
+      - nlrf-network
+
   fastapi:
     image: ${FASTAPI_IMAGE_TAG:-devasy23/nlrf:fastapi-latest}
     # ... existing config ...
     volumes:
       - ./robot_tests:/app/robot_tests
       - ./logs:/app/logs
       - ./chroma_db:/app/chroma_db
       - ./data:/app/data
-      - /var/run/docker.sock:/var/run/docker.sock
+    environment:
+      - DOCKER_HOST=tcp://docker-proxy:2375
     # ... rest of config ...
+    depends_on:
+      docker-proxy:
+        condition: service_started

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In @docker-compose.yml around line 17, The docker-compose service currently
mounts the host Docker socket (/var/run/docker.sock) which grants full daemon
access; replace this direct mount by adding or referencing a docker-socket-proxy
sidecar (e.g., tecnativa/docker-socket-proxy) and update the service to use the
proxy endpoint instead of /var/run/docker.sock, document the security
implications and chosen mitigation in the README/docs, ensure the service runs
with least-privilege (avoid privileged mode, set read-only root filesystem where
possible) and configure the proxy to filter API calls to only the required
Docker operations.

Comment on lines +23 to +24
- HOST_ROBOT_TESTS_DIR=${PWD}/robot_tests
- HOST_WORKSPACE_DIR=${PWD}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

${PWD} may not resolve correctly across different environments.

The ${PWD} variable behaves differently across shells, operating systems (especially Windows), and CI/CD environments. In Windows, ${PWD} might use forward slashes while Docker expects Windows-style paths, or might not be set at all in some contexts.

🔎 Portable alternatives

Option 1: Use Docker Compose variable substitution with .env file

In .env or src/backend/.env:

HOST_ROBOT_TESTS_DIR=/absolute/path/to/robot_tests
HOST_WORKSPACE_DIR=/absolute/path/to/workspace

Then reference as-is in docker-compose.yml (already done on lines 23-24).

Option 2: Use relative paths with COMPOSE_PROJECT_DIR

-      - HOST_ROBOT_TESTS_DIR=${PWD}/robot_tests
-      - HOST_WORKSPACE_DIR=${PWD}
+      - HOST_ROBOT_TESTS_DIR=${COMPOSE_PROJECT_DIR:-$(pwd)}/robot_tests
+      - HOST_WORKSPACE_DIR=${COMPOSE_PROJECT_DIR:-$(pwd)}

Option 3: Document in README

Add clear instructions for users to set these in their .env file:

# For Linux/macOS
export HOST_ROBOT_TESTS_DIR=$(pwd)/robot_tests
export HOST_WORKSPACE_DIR=$(pwd)

# For Windows (PowerShell)
$env:HOST_ROBOT_TESTS_DIR="$PWD/robot_tests"
$env:HOST_WORKSPACE_DIR="$PWD"

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In @docker-compose.yml around lines 23-24, The docker-compose entries using
${PWD} for HOST_ROBOT_TESTS_DIR and HOST_WORKSPACE_DIR are not portable across
shells/OSs; replace them with explicit Compose variable substitution and require
users to set HOST_ROBOT_TESTS_DIR and HOST_WORKSPACE_DIR in an .env file (or
fall back to COMPOSE_PROJECT_DIR-based relative paths), update
docker-compose.yml to reference those variables only, and add README
instructions showing how to export/set HOST_ROBOT_TESTS_DIR and
HOST_WORKSPACE_DIR for Linux/macOS and Windows (PowerShell) so CI and Windows
users have correct absolute paths.

Comment on lines +16 to +17
# Install UV package manager
RUN pip install --no-cache-dir uv
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

🧩 Analysis chain

🌐 Web query:

latest stable version uv python package manager

💡 Result:

Latest stable uv release: 0.9.21 — released December 30, 2025. [1][2]

Sources:
[1] PyPI project page for uv.
[2] GitHub releases for astral-sh/uv.


Pin the uv package version for reproducible builds.

The uv package manager is installed without version pinning. For reproducible builds, consider pinning to a specific version.

🔎 Proposed fix
 # Install UV package manager
-RUN pip install --no-cache-dir uv
+RUN pip install --no-cache-dir uv==0.9.21
📝 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
# Install UV package manager
RUN pip install --no-cache-dir uv
# Install UV package manager
RUN pip install --no-cache-dir uv==0.9.21
🧰 Tools
🪛 Hadolint (2.14.0)

[warning] 17-17: Pin versions in pip. Instead of pip install <package> use pip install <package>==<version> or pip install --requirement <requirements file>

(DL3013)

🤖 Prompt for AI Agents
In @Dockerfile.fastapi around lines 16-17, The Dockerfile installs the "uv"
package without a pinned version via the RUN pip install --no-cache-dir uv line;
update this to pin a specific version (or use a build ARG like UV_VERSION or
reference a requirements file) so the RUN pip install step installs uv at a
fixed version (e.g., uv==<version>) to ensure reproducible builds and make
future updates explicit.

Comment on lines +44 to +82
FROM python:3.12-slim

# Set working directory
WORKDIR /app

# Prevent Python from writing pyc files and enable unbuffered output
ENV PYTHONUNBUFFERED=1 \
PYTHONDONTWRITEBYTECODE=1 \
PYTHONIOENCODING=utf-8 \
PATH="/opt/venv/bin:$PATH"

# Install only runtime dependencies (curl for healthcheck, ca-certificates for HTTPS)
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
--mount=type=cache,target=/var/lib/apt,sharing=locked \
apt-get update && \
apt-get install -y --no-install-recommends \
curl \
ca-certificates && \
rm -rf /var/lib/apt/lists/* /var/cache/apt/* /usr/share/doc/* /usr/share/man/* /tmp/*

# Copy installed Python packages from builder stage
COPY --from=builder /build/install /usr/local

# Copy application source code
COPY src/ /app/src/
COPY tools/ /app/tools/

# Create necessary directories for persistent data
RUN mkdir -p /app/robot_tests /app/logs /app/chroma_db /app/data

# Expose FastAPI port
EXPOSE 5000

# Health check endpoint
HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \
CMD curl -f http://localhost:5000/health || exit 1

# Run FastAPI server with uvicorn
CMD ["uvicorn", "src.backend.main:app", "--host", "0.0.0.0", "--port", "5000"]
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Security: Container runs as root.

The container runs as root by default, which is a security risk for production deployments. Consider adding a non-root user for the runtime stage.

🔎 Proposed fix - add non-root user

Add after the apt-get installation block (around line 62):

     rm -rf /var/lib/apt/lists/* /var/cache/apt/* /usr/share/doc/* /usr/share/man/* /tmp/*

+# Create non-root user for security
+RUN useradd --create-home --shell /bin/bash appuser
+
 # Copy installed Python packages from builder stage
 COPY --from=builder /build/install /usr/local

Then add before the CMD instruction:

+# Switch to non-root user
+USER appuser
+
 # Run FastAPI server with uvicorn
 CMD ["uvicorn", "src.backend.main:app", "--host", "0.0.0.0", "--port", "5000"]

Note: Ensure the /app directories have appropriate permissions for appuser.

📝 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 python:3.12-slim
# Set working directory
WORKDIR /app
# Prevent Python from writing pyc files and enable unbuffered output
ENV PYTHONUNBUFFERED=1 \
PYTHONDONTWRITEBYTECODE=1 \
PYTHONIOENCODING=utf-8 \
PATH="/opt/venv/bin:$PATH"
# Install only runtime dependencies (curl for healthcheck, ca-certificates for HTTPS)
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
--mount=type=cache,target=/var/lib/apt,sharing=locked \
apt-get update && \
apt-get install -y --no-install-recommends \
curl \
ca-certificates && \
rm -rf /var/lib/apt/lists/* /var/cache/apt/* /usr/share/doc/* /usr/share/man/* /tmp/*
# Copy installed Python packages from builder stage
COPY --from=builder /build/install /usr/local
# Copy application source code
COPY src/ /app/src/
COPY tools/ /app/tools/
# Create necessary directories for persistent data
RUN mkdir -p /app/robot_tests /app/logs /app/chroma_db /app/data
# Expose FastAPI port
EXPOSE 5000
# Health check endpoint
HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \
CMD curl -f http://localhost:5000/health || exit 1
# Run FastAPI server with uvicorn
CMD ["uvicorn", "src.backend.main:app", "--host", "0.0.0.0", "--port", "5000"]
FROM python:3.12-slim
# Set working directory
WORKDIR /app
# Prevent Python from writing pyc files and enable unbuffered output
ENV PYTHONUNBUFFERED=1 \
PYTHONDONTWRITEBYTECODE=1 \
PYTHONIOENCODING=utf-8 \
PATH="/opt/venv/bin:$PATH"
# Install only runtime dependencies (curl for healthcheck, ca-certificates for HTTPS)
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
--mount=type=cache,target=/var/lib/apt,sharing=locked \
apt-get update && \
apt-get install -y --no-install-recommends \
curl \
ca-certificates && \
rm -rf /var/lib/apt/lists/* /var/cache/apt/* /usr/share/doc/* /usr/share/man/* /tmp/*
# Create non-root user for security
RUN useradd --create-home --shell /bin/bash appuser
# Copy installed Python packages from builder stage
COPY --from=builder /build/install /usr/local
# Copy application source code
COPY src/ /app/src/
COPY tools/ /app/tools/
# Create necessary directories for persistent data
RUN mkdir -p /app/robot_tests /app/logs /app/chroma_db /app/data
# Expose FastAPI port
EXPOSE 5000
# Health check endpoint
HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \
CMD curl -f http://localhost:5000/health || exit 1
# Switch to non-root user
USER appuser
# Run FastAPI server with uvicorn
CMD ["uvicorn", "src.backend.main:app", "--host", "0.0.0.0", "--port", "5000"]
🧰 Tools
🪛 Checkov (3.2.334)

[low] 1-82: Ensure that a user for the container has been created

(CKV_DOCKER_3)

🪛 Hadolint (2.14.0)

[warning] 56-56: Pin versions in apt get install. Instead of apt-get install <package> use apt-get install <package>=<version>

(DL3008)

🤖 Prompt for AI Agents
In @Dockerfile.fastapi around lines 44-82, The container currently runs as root;
create a non-root user and switch to it for runtime: after the apt-get
installation block, add steps to create a system user (e.g., appuser) and group,
set /app ownership and permissions (chown -R appuser:appuser /app
/app/robot_tests /app/logs /app/chroma_db /app/data), and drop privileges by
adding a USER appuser before the CMD; ensure HEALTHCHECK and CMD continue to
reference the same port/path and that any runtime files are writable by appuser.

Comment on lines +50 to +53
ENV PYTHONUNBUFFERED=1 \
PYTHONDONTWRITEBYTECODE=1 \
PYTHONIOENCODING=utf-8 \
PATH="/opt/venv/bin:$PATH"
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Incorrect PATH configuration: /opt/venv/bin does not exist.

The PATH includes /opt/venv/bin, but no virtual environment is created in this Dockerfile. Python packages are installed to /build/install in the builder stage and copied to /usr/local in the runtime stage. This PATH entry is unnecessary and misleading.

🔎 Proposed fix
 ENV PYTHONUNBUFFERED=1 \
     PYTHONDONTWRITEBYTECODE=1 \
-    PYTHONIOENCODING=utf-8 \
-    PATH="/opt/venv/bin:$PATH"
+    PYTHONIOENCODING=utf-8
📝 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
ENV PYTHONUNBUFFERED=1 \
PYTHONDONTWRITEBYTECODE=1 \
PYTHONIOENCODING=utf-8 \
PATH="/opt/venv/bin:$PATH"
ENV PYTHONUNBUFFERED=1 \
PYTHONDONTWRITEBYTECODE=1 \
PYTHONIOENCODING=utf-8
🤖 Prompt for AI Agents
In @Dockerfile.fastapi around lines 50-53, The ENV line incorrectly prepends a
non-existent virtualenv path (/opt/venv/bin) to PATH; update the Dockerfile ENV
declaration (the block setting PYTHONUNBUFFERED, PYTHONDONTWRITEBYTECODE,
PYTHONIOENCODING, PATH) to either remove the PATH modification entirely or
change it to the actual runtime bin location used in the image (e.g.,
PATH="/usr/local/bin:$PATH") so installed packages under /usr/local are found.

Comment on lines +78 to +79
FASTAPI_IMAGE_TAG=monkscode/nlrf-fastapi:latest
BROWSER_SERVICE_IMAGE_TAG=monkscode/nlrf-browser-service:latest
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify image tag formats across env files
echo "=== src/backend/.env.example ===" 
grep -E "(FASTAPI_IMAGE_TAG|BROWSER_SERVICE_IMAGE_TAG)" src/backend/.env.example

echo ""
echo "=== .env.example ===" 
grep -E "(FASTAPI_IMAGE_TAG|BROWSER_SERVICE_IMAGE_TAG)" .env.example

echo ""
echo "=== docker-compose files ===" 
find . -name "docker-compose*.yml" -o -name "docker-compose*.yaml" | head -20

Repository: monkscode/Natural-Language-to-Robot-Framework

Length of output: 441


🏁 Script executed:

#!/bin/bash
echo "=== docker-compose.yml ===" 
grep -A 2 -B 2 "FASTAPI_IMAGE_TAG\|BROWSER_SERVICE_IMAGE_TAG\|image:" docker-compose.yml | head -40

echo ""
echo "=== docker-compose.dev.yml ===" 
grep -A 2 -B 2 "FASTAPI_IMAGE_TAG\|BROWSER_SERVICE_IMAGE_TAG\|image:" docker-compose.dev.yml | head -40

echo ""
echo "=== Check env file references in docker-compose files ===" 
grep "env_file" docker-compose.yml docker-compose.dev.yml

Repository: monkscode/Natural-Language-to-Robot-Framework

Length of output: 791


🏁 Script executed:

#!/bin/bash
echo "=== Full env_file references in docker-compose.yml ===" 
grep -A 1 "env_file:" docker-compose.yml

Repository: monkscode/Natural-Language-to-Robot-Framework

Length of output: 228


Inconsistent image tag formats between environment files.

The image tags in src/backend/.env.example use monkscode/nlrf-fastapi:latest format, but docker-compose.yml references these variables with fallbacks to devasy23/nlrf:fastapi-latest format. Since docker-compose.yml loads env from ./src/backend/.env, this inconsistency creates a mismatch between the configuration template and the production defaults. Align both files to use the same registry and tag format.

🧰 Tools
🪛 dotenv-linter (4.0.0)

[warning] 79-79: [UnorderedKey] The BROWSER_SERVICE_IMAGE_TAG key should go before the FASTAPI_IMAGE_TAG key

(UnorderedKey)

🤖 Prompt for AI Agents
In @src/backend/.env.example around lines 78-79, The .env.example variables
FASTAPI_IMAGE_TAG and BROWSER_SERVICE_IMAGE_TAG use monkscode/...:latest but
docker-compose.yml falls back to devasy23/nlrf:fastapi-latest style tags,
causing a mismatch; update FASTAPI_IMAGE_TAG and BROWSER_SERVICE_IMAGE_TAG in
src/backend/.env.example to use the same registry and tag format as the
docker-compose.yml fallbacks (e.g., devasy23/nlrf:fastapi-latest and
devasy23/nlrf:browser-service-latest) or change the docker-compose.yml defaults
to match monkscode/...:latest—ensure both files reference the identical image
names/tags so the env variable names FASTAPI_IMAGE_TAG and
BROWSER_SERVICE_IMAGE_TAG resolve consistently.

Comment on lines +170 to +181
# Docker-in-Docker: Use HOST_ROBOT_TESTS_DIR for volume mount to reference host path
container_config = {
"image": IMAGE_TAG,
"command": robot_command,
"volumes": {os.path.abspath(ROBOT_TESTS_DIR): {'bind': '/app/robot_tests', 'mode': 'rw'}},
"volumes": {HOST_ROBOT_TESTS_DIR: {'bind': '/app/robot_tests', 'mode': 'rw'}},
"working_dir": "/app",
"detach": True, # Run detached to manage container lifecycle
"auto_remove": False, # Don't auto-remove so we can get logs properly
"name": f"robot-test-{run_id}" # Give container a unique name
}
logging.info(
f"🐳 DOCKER SERVICE: Container config created for robot-test-{run_id}")
f"🐳 DOCKER SERVICE: Container config created for robot-test-{run_id} with volume {HOST_ROBOT_TESTS_DIR}:/app/robot_tests")
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Consider validating HOST_ROBOT_TESTS_DIR before container creation.

If HOST_ROBOT_TESTS_DIR is misconfigured (doesn't exist on the host or is set to a container-internal path), the spawned test container will fail with a cryptic mount error. Adding early validation could provide a clearer error message.

🔎 Proposed validation

Add at the start of run_test_in_container:

# Validate HOST_ROBOT_TESTS_DIR for Docker-in-Docker scenarios
if os.getenv('HOST_ROBOT_TESTS_DIR') and not os.path.isabs(HOST_ROBOT_TESTS_DIR):
    logging.warning(
        f"⚠️ HOST_ROBOT_TESTS_DIR ({HOST_ROBOT_TESTS_DIR}) is not an absolute path. "
        "This may cause volume mount failures in Docker-in-Docker scenarios."
    )
🧰 Tools
🪛 Ruff (0.14.10)

180-181: info() call on root logger

(LOG015)


181-181: Logging statement uses f-string

(G004)

🤖 Prompt for AI Agents
In @src/backend/services/docker_service.py around lines 170-181,
run_test_in_container currently uses HOST_ROBOT_TESTS_DIR directly for a bind
mount which can cause cryptic Docker mount errors if it's missing or not an
absolute host path; before creating container_config validate
HOST_ROBOT_TESTS_DIR by checking os.path.exists(HOST_ROBOT_TESTS_DIR) and
os.path.isabs(HOST_ROBOT_TESTS_DIR) and if missing or not absolute, log a clear
error/warning (including the value of HOST_ROBOT_TESTS_DIR) and raise or return
early so container creation is skipped; update logging to include the validation
failure and reference the HOST_ROBOT_TESTS_DIR and run_id in messages so
failures are easier to debug.

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

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant