Skip to content

Fix dependencies and add CICD check#4

Open
0xBigfish wants to merge 3 commits intoRUB-NDS:mainfrom
0xBigfish:fix-dependencies
Open

Fix dependencies and add CICD check#4
0xBigfish wants to merge 3 commits intoRUB-NDS:mainfrom
0xBigfish:fix-dependencies

Conversation

@0xBigfish
Copy link
Copy Markdown

Fix dependencies and add CICD check

Fix missing and conflicting dependencies in the project to make it runnable again. Also added a workflow to ensure the project is runnable via docker compose build and docker compose up.

Dependencies

Pipfile.lock

There was a missing Pipfile.lock in brain in the docker file, resulting in conflicting dependencies. The wrong marshmallow version was used, making the API incompatible with the code:

brain-1          |   File "/app/.venv/lib/python3.10/site-packages/marshmallow/fields.py", line 1223, in __init__
brain-1          |     super().__init__(**kwargs)
brain-1          | TypeError: Field.__init__() got an unexpected keyword argument 'description'

searxng

The docker image image: searxng/searxng:2023.12.11-8a4104b99 is not available anymore. Since searxng uses a rolling release model, it has been changed to image: searxng/searxng:latest.

CI/CD

To ensure the project stays runnable, a workflow was added that builds and runs using docker compose and waits 2 minutes for all containers to become healthy. If they do not become healthy in that time frame, the workflow fails.
The workflow runs on every pull-request to main and also on every monday at 3:00 AM UTC.

A status badge can be included in the readme, see https://docs.github.com/en/actions/how-tos/monitor-workflows/add-a-status-badge

Error Log

Wrong marshmallow Version

How to reproduce: Clone the repository, build the project using docker compose build and the start the containers using docker compose up.

Log

Mind the => => # Pipfile.lock not found, creating...

$ docker compose build 

<SNIP>

[+] Building 41.3s (13/15)                                                                                                                                     
 => [internal] load .dockerignore                                                                                                                         0.0s
 => => transferring context: 2B                                                                                                                           0.0s
 => CACHED [ 1/10] FROM docker.io/library/ubuntu:22.04@sha256:c7eb020043d8fc2ae0793fb35a37bff1cf33f156d4d4b12ccc7f3ef8706c38b1                            0.0s 
 => [internal] load build context                                                                                                                         0.0s 
 => => transferring context: 5.28kB                                                                                                                       0.0s 
 => [ 2/10] RUN apt update                                                                                                                                3.5s
 => [ 3/10] RUN apt upgrade -y                                                                                                                            4.1s 
 => [ 4/10] RUN apt install -y python3 python3-pip curl                                                                                                  16.6s 
 => [ 5/10] RUN pip install pipenv                                                                                                                        3.8s 
 => [ 6/10] RUN mkdir -p /app                                                                                                                             0.2s 
 => [ 7/10] WORKDIR /app                                                                                                                                  0.0s 
 => [ 8/10] ADD Pipfile .                                                                                                                                 0.0s 
 => [ 9/10] RUN pipenv install                                                                                                                           12.7s 
 => => # ✔ Successfully created virtual environment!                                                                                                           
 => => # Virtualenv location: /app/.venv                                                                                                                       
 => => # Pipfile.lock not found, creating...                                                                                                                   
 => => # Locking  dependencies...                                                                                                                              
 => => # Building requirements...                                                                                                                              
 => => # Resolving dependencies...             

This resulted in the gunicorn server failing endlessly

$ docker compose up

<SNIP>

brain-1          | [2026-02-06 13:16:25 +0000] [1] [INFO] Starting gunicorn 25.0.1
brain-1          | [2026-02-06 13:16:25 +0000] [1] [INFO] Listening at: http://0.0.0.0:8080 (1)
brain-1          | [2026-02-06 13:16:25 +0000] [1] [INFO] Using worker: eventlet
brain-1          | [2026-02-06 13:16:25 +0000] [9] [INFO] Booting worker with pid: 9
brain-1          | [2026-02-06 13:16:25 +0000] [9] [WARNING] The eventlet worker is DEPRECATED and will be removed in Gunicorn 26.0. Please migrate to gevent, gthread, or another supported worker type.
brain-1          | [2026-02-06 13:16:26 +0000] [10] [INFO] Booting worker with pid: 10
brain-1          | [2026-02-06 13:16:26 +0000] [10] [WARNING] The eventlet worker is DEPRECATED and will be removed in Gunicorn 26.0. Please migrate to gevent, gthread, or another supported worker type.
brain-1          | [2026-02-06 13:16:26 +0000] [9] [ERROR] Exception in worker process
brain-1          | Traceback (most recent call last):
brain-1          |   File "/app/.venv/lib/python3.10/site-packages/gunicorn/arbiter.py", line 683, in spawn_worker
brain-1          |     worker.init_process()
brain-1          |   File "/app/.venv/lib/python3.10/site-packages/gunicorn/workers/geventlet.py", line 173, in init_process
brain-1          |     super().init_process()
brain-1          |   File "/app/.venv/lib/python3.10/site-packages/gunicorn/workers/base.py", line 136, in init_process
brain-1          |     self.load_wsgi()
brain-1          |   File "/app/.venv/lib/python3.10/site-packages/gunicorn/workers/base.py", line 148, in load_wsgi
brain-1          |     self.wsgi = self.app.wsgi()
brain-1          |   File "/app/.venv/lib/python3.10/site-packages/gunicorn/app/base.py", line 66, in wsgi
brain-1          |     self.callable = self.load()
brain-1          |   File "/app/.venv/lib/python3.10/site-packages/gunicorn/app/wsgiapp.py", line 57, in load
brain-1          |     return self.load_wsgiapp()
brain-1          |   File "/app/.venv/lib/python3.10/site-packages/gunicorn/app/wsgiapp.py", line 47, in load_wsgiapp
brain-1          |     return util.import_app(self.app_uri)
brain-1          |   File "/app/.venv/lib/python3.10/site-packages/gunicorn/util.py", line 377, in import_app
brain-1          |     mod = importlib.import_module(module)
brain-1          |   File "/usr/lib/python3.10/importlib/__init__.py", line 126, in import_module
brain-1          |     return _bootstrap._gcd_import(name[level:], package, level)
brain-1          |   File "<frozen importlib._bootstrap>", line 1050, in _gcd_import
brain-1          |   File "<frozen importlib._bootstrap>", line 1027, in _find_and_load
brain-1          |   File "<frozen importlib._bootstrap>", line 1006, in _find_and_load_unlocked
brain-1          |   File "<frozen importlib._bootstrap>", line 688, in _load_unlocked
brain-1          |   File "<frozen importlib._bootstrap_external>", line 883, in exec_module
brain-1          |   File "<frozen importlib._bootstrap>", line 241, in _call_with_frames_removed
brain-1          |   File "/app/app.py", line 11, in <module>
brain-1          |     from blueprints.bp_api import bp_api
brain-1          |   File "/app/blueprints/bp_api.py", line 4, in <module>
brain-1          |     from blueprints.api.bp_list import bp_list
brain-1          |   File "/app/blueprints/api/bp_list.py", line 16, in <module>
brain-1          |     "date": Date(required=True, description="date of the list (YYYY-MM-DD)"),
brain-1          |   File "/app/.venv/lib/python3.10/site-packages/marshmallow/fields.py", line 1223, in __init__
brain-1          |     super().__init__(**kwargs)
brain-1          | TypeError: Field.__init__() got an unexpected keyword argument 'description'
brain-1          | Field.__init__() got an unexpected keyword argument 'description'
brain-1          | [2026-02-06 13:16:26 +0000] [9] [INFO] Worker exiting (pid: 9)
brain-1          | [2026-02-06 13:16:26 +0000] [10] [ERROR] Exception in worker process
brain-1          | Traceback (most recent call last):
brain-1          |   File "/app/.venv/lib/python3.10/site-packages/gunicorn/arbiter.py", line 683, in spawn_worker
brain-1          |     worker.init_process()
brain-1          |   File "/app/.venv/lib/python3.10/site-packages/gunicorn/workers/geventlet.py", line 173, in init_process
brain-1          |     super().init_process()
brain-1          |   File "/app/.venv/lib/python3.10/site-packages/gunicorn/workers/base.py", line 136, in init_process
brain-1          |     self.load_wsgi()
brain-1          |   File "/app/.venv/lib/python3.10/site-packages/gunicorn/workers/base.py", line 148, in load_wsgi
brain-1          |     self.wsgi = self.app.wsgi()
brain-1          |   File "/app/.venv/lib/python3.10/site-packages/gunicorn/app/base.py", line 66, in wsgi
brain-1          |     self.callable = self.load()
brain-1          |   File "/app/.venv/lib/python3.10/site-packages/gunicorn/app/wsgiapp.py", line 57, in load
brain-1          |     return self.load_wsgiapp()
brain-1          |   File "/app/.venv/lib/python3.10/site-packages/gunicorn/app/wsgiapp.py", line 47, in load_wsgiapp
brain-1          |     return util.import_app(self.app_uri)
brain-1          |   File "/app/.venv/lib/python3.10/site-packages/gunicorn/util.py", line 377, in import_app
brain-1          |     mod = importlib.import_module(module)
brain-1          |   File "/usr/lib/python3.10/importlib/__init__.py", line 126, in import_module
brain-1          |     return _bootstrap._gcd_import(name[level:], package, level)
brain-1          |   File "<frozen importlib._bootstrap>", line 1050, in _gcd_import
brain-1          |   File "<frozen importlib._bootstrap>", line 1027, in _find_and_load
brain-1          |   File "<frozen importlib._bootstrap>", line 1006, in _find_and_load_unlocked
brain-1          |   File "<frozen importlib._bootstrap>", line 688, in _load_unlocked
brain-1          |   File "<frozen importlib._bootstrap_external>", line 883, in exec_module
brain-1          |   File "<frozen importlib._bootstrap>", line 241, in _call_with_frames_removed
brain-1          |   File "/app/app.py", line 11, in <module>
brain-1          |     from blueprints.bp_api import bp_api
brain-1          |   File "/app/blueprints/bp_api.py", line 4, in <module>
brain-1          |     from blueprints.api.bp_list import bp_list
brain-1          |   File "/app/blueprints/api/bp_list.py", line 16, in <module>
brain-1          |     "date": Date(required=True, description="date of the list (YYYY-MM-DD)"),
brain-1          |   File "/app/.venv/lib/python3.10/site-packages/marshmallow/fields.py", line 1223, in __init__
brain-1          |     super().__init__(**kwargs)
brain-1          | TypeError: Field.__init__() got an unexpected keyword argument 'description'
brain-1          | Field.__init__() got an unexpected keyword argument 'description'
brain-1          | [2026-02-06 13:16:26 +0000] [10] [INFO] Worker exiting (pid: 10)
brain-1          | [2026-02-06 13:16:26 +0000] [1] [ERROR] Worker (pid:9) exited with code 3.
brain-1          | [2026-02-06 13:16:26 +0000] [1] [INFO] Worker (pid:10) was sent SIGTERM!
brain-1          | [2026-02-06 13:16:26 +0000] [1] [ERROR] Shutting down: Master
brain-1          | [2026-02-06 13:16:26 +0000] [1] [ERROR] Reason: Worker failed to boot.
brain-1 exited with code 3                                                                                                                                     

Without it installation failed due to wrong dependency resolution.
Previous version was not available anymore. Fix by always using latest.

Should probably use a pipeline to ensure project stays runnable in case API changes.
Since searxng uses rolling releases, and older versions are not
available anymore, this workflow should ensure that the project is
always runnable when using searxng:latest.
The workflow runs on every pull request and every monday at 3:00 AM UTC.
@0xBigfish 0xBigfish marked this pull request as ready for review February 6, 2026 16:16
@lonetis lonetis requested a review from Copilot February 16, 2026 12:42
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR addresses critical runtime issues by fixing missing and conflicting Python dependencies, updating an unavailable Docker image, and adding CI/CD health checks to prevent future regressions. The changes fix the project which was broken due to marshmallow API incompatibility caused by missing Pipfile.lock.

Changes:

  • Added missing Pipfile.lock to brain service Dockerfile to ensure consistent dependency resolution
  • Updated searxng Docker image from unavailable pinned version to latest tag
  • Added GitHub Actions workflow to validate Docker Compose build and health checks on PRs and weekly

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 6 comments.

File Description
brain/Dockerfile Adds Pipfile.lock copy step to resolve marshmallow version conflicts
docker-compose.yml Updates searxng image from unavailable 2023.12.11 version to latest tag
.github/workflows/healthcheck.yml New CI/CD workflow for validating Docker Compose health on PRs and weekly schedule

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

retry-interval: 10
compose-file: "docker-compose.yml"
skip-exited: "false" # unhealthy containers might exit and restart
skip-no-healthcheck: "true"
Copy link

Copilot AI Feb 16, 2026

Choose a reason for hiding this comment

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

The worker service in docker-compose.yml does not have a healthcheck defined, but has 'skip-no-healthcheck' set to 'true' in the workflow. This means the workflow will skip health checking for the worker service. Consider adding a healthcheck to the worker service or documenting why it doesn't need one, to ensure comprehensive health validation.

Suggested change
skip-no-healthcheck: "true"
skip-no-healthcheck: "false"

Copilot uses AI. Check for mistakes.
healthcheck:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
Copy link

Copilot AI Feb 16, 2026

Choose a reason for hiding this comment

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

Consider updating 'actions/checkout@v3' to 'actions/checkout@v4', which is the latest stable version. This ensures you're using the most up-to-date and secure version of the action.

Suggested change
- uses: actions/checkout@v3
- uses: actions/checkout@v4

Copilot uses AI. Check for mistakes.
Comment on lines +29 to +35
uses: jaracogmbh/docker-compose-health-check-action@v1.0.0
with:
max-retries: 12
retry-interval: 10
compose-file: "docker-compose.yml"
skip-exited: "false" # unhealthy containers might exit and restart
skip-no-healthcheck: "true"
Copy link

Copilot AI Feb 16, 2026

Choose a reason for hiding this comment

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

Verify that the third-party action 'jaracogmbh/docker-compose-health-check-action@v1.0.0' is actively maintained and trustworthy. Consider using a commit SHA instead of a tag for better security, or evaluate if a simple bash script checking 'docker compose ps' output would be more maintainable and transparent.

Suggested change
uses: jaracogmbh/docker-compose-health-check-action@v1.0.0
with:
max-retries: 12
retry-interval: 10
compose-file: "docker-compose.yml"
skip-exited: "false" # unhealthy containers might exit and restart
skip-no-healthcheck: "true"
run: |
set -euo pipefail
max_retries=12
retry_interval=10
compose_file="docker-compose.yml"
skip_exited=false # unhealthy containers might exit and restart
skip_no_healthcheck=true
echo "Starting Docker Compose health check..."
attempt=1
while [ "$attempt" -le "$max_retries" ]; do
echo "Health check attempt ${attempt}/${max_retries}..."
# Get all containers belonging to the compose project
mapfile -t containers < <(docker compose -f "${compose_file}" ps -aq)
if [ "${#containers[@]}" -eq 0 ]; then
echo "No containers found for compose file '${compose_file}'."
exit 1
fi
all_healthy=true
any_starting=false
for container in "${containers[@]}"; do
# Optionally skip exited containers
if [ "${skip_exited}" = true ]; then
container_state=$(docker inspect -f '{{.State.Status}}' "${container}")
if [ "${container_state}" = "exited" ] || [ "${container_state}" = "dead" ]; then
echo "Skipping exited container ${container} (state: ${container_state})."
continue
fi
fi
# Get health status; "none" if no healthcheck is defined
health_status=$(docker inspect -f '{{if .State.Health}}{{.State.Health.Status}}{{else}}none{{end}}' "${container}")
if [ "${health_status}" = "none" ] && [ "${skip_no_healthcheck}" = true ]; then
echo "Skipping container ${container} (no healthcheck defined)."
continue
fi
echo "Container ${container} health status: ${health_status}"
case "${health_status}" in
healthy)
;;
starting)
all_healthy=false
any_starting=true
;;
unhealthy)
echo "Container ${container} is unhealthy."
exit 1
;;
*)
echo "Container ${container} is in unexpected health status: ${health_status}"
all_healthy=false
;;
esac
done
if [ "${all_healthy}" = true ]; then
echo "All relevant containers are healthy."
exit 0
fi
if [ "$attempt" -eq "$max_retries" ]; then
echo "Health check did not succeed within ${max_retries} attempts."
exit 1
fi
if [ "${any_starting}" = true ]; then
echo "Some containers are still starting. Retrying in ${retry_interval} seconds..."
else
echo "Some containers are not healthy yet. Retrying in ${retry_interval} seconds..."
fi
attempt=$((attempt + 1))
sleep "${retry_interval}"
done

Copilot uses AI. Check for mistakes.
Comment on lines +14 to +16
- name: Set up Docker
uses: docker/setup-docker-action@v4

Copy link

Copilot AI Feb 16, 2026

Choose a reason for hiding this comment

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

The GitHub Action 'docker/setup-docker-action' does not exist. Docker is pre-installed on GitHub-hosted ubuntu-latest runners, so this step is unnecessary and should be removed. If you need a specific Docker version or setup, consider using 'docker/setup-buildx-action' instead.

Suggested change
- name: Set up Docker
uses: docker/setup-docker-action@v4

Copilot uses AI. Check for mistakes.
Comment on lines +17 to +21
- name: Set up Docker Compose
uses: docker/setup-compose-action@v1
with:
version: latest # use latest version of docker compose

Copy link

Copilot AI Feb 16, 2026

Choose a reason for hiding this comment

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

The GitHub Action 'docker/setup-compose-action' does not exist. Docker Compose is pre-installed on GitHub-hosted ubuntu-latest runners. Remove this step and the workflow will work correctly. The 'docker compose' command is already available.

Suggested change
- name: Set up Docker Compose
uses: docker/setup-compose-action@v1
with:
version: latest # use latest version of docker compose

Copilot uses AI. Check for mistakes.
Comment thread docker-compose.yml

searxng:
image: searxng/searxng:2023.12.11-8a4104b99
image: searxng/searxng:latest
Copy link

Copilot AI Feb 16, 2026

Choose a reason for hiding this comment

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

Using 'latest' tag for the searxng image violates the established codebase convention of pinning Docker images to specific versions (see traefik:v2.10, rabbitmq:3.12-management, mongo:4.4.6, etc. in docker-compose.yml:6,174,211,228,255,285). While the old pinned version is unavailable, consider pinning to a specific recent version (e.g., 'searxng/searxng:2024.x.x-xxxxx') rather than 'latest' to ensure reproducible builds and controlled updates.

Suggested change
image: searxng/searxng:latest
image: searxng/searxng:2024.01.01-12345678

Copilot uses AI. Check for mistakes.
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.

2 participants