diff --git a/.dockerignore b/.dockerignore index 490419b..e06b2a9 100644 --- a/.dockerignore +++ b/.dockerignore @@ -56,6 +56,13 @@ docker-compose.yml # Documentation *.md +# Not needed at runtime (make test-docker bind-mounts tests/ + pytest.ini) +tests/ +docs/ +deploy/ +scripts/ +.github/ + # Testing .pytest_cache/ .coverage diff --git a/.github/workflows/ci-docker.yml b/.github/workflows/ci-docker.yml index 74cd5ca..d7f9abe 100644 --- a/.github/workflows/ci-docker.yml +++ b/.github/workflows/ci-docker.yml @@ -3,39 +3,25 @@ name: CI + Docker Build & Push on: push: branches: [ main ] - pull_request: - branches: [ main ] + paths-ignore: + - 'docs/**' + - 'mkdocs.yml' + - '**/*.md' workflow_dispatch: permissions: contents: read packages: write +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: test: - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: '3.11' - - - name: Install system dependencies - run: | - sudo apt-get update - sudo apt-get install -y libzbar0 - - - name: Install Python dependencies - run: | - python -m pip install --upgrade pip - pip install -r requirements.txt - pip install pytest - - - name: Run tests - run: pytest -v + uses: ./.github/workflows/test.yml + permissions: + contents: read docker-build: needs: test @@ -47,6 +33,9 @@ jobs: - name: Checkout code uses: actions/checkout@v4 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + - name: Log in to GitHub Container Registry uses: docker/login-action@v3 with: @@ -54,14 +43,21 @@ jobs: username: ${{ github.repository_owner }} password: ${{ secrets.GITHUB_TOKEN }} - - name: Build Docker image - run: | - docker build \ - -t ghcr.io/owasp/pwnzzai:latest \ - -t ghcr.io/owasp/pwnzzai:${{ github.sha }} \ - . + - name: Docker metadata + id: meta + uses: docker/metadata-action@v5 + with: + images: ghcr.io/owasp/pwnzzai + tags: | + type=raw,value=latest + type=raw,value=${{ github.sha }} - - name: Push Docker image - run: | - docker push ghcr.io/owasp/pwnzzai:latest - docker push ghcr.io/owasp/pwnzzai:${{ github.sha }} + - name: Build and push + uses: docker/build-push-action@v6 + with: + context: . + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index c2a3c57..7d0c7c7 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -2,20 +2,32 @@ name: Lint on: pull_request: + paths-ignore: + - 'docs/**' + - 'mkdocs.yml' + - '**/*.md' push: branches: [ main ] + paths-ignore: + - 'docs/**' + - 'mkdocs.yml' + - '**/*.md' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true jobs: lint: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - - uses: actions/setup-python@v4 + - uses: actions/setup-python@v5 with: python-version: '3.11' - run: pip install ruff - - run: ruff check . \ No newline at end of file + - run: ruff check . diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index fc0cb6d..5f74d4b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,11 +1,21 @@ name: Run Tests -on: - push: - branches: [main] +on: pull_request: branches: [main] + paths-ignore: + - 'docs/**' + - 'mkdocs.yml' + - '**/*.md' + workflow_dispatch: + workflow_call: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true +permissions: + contents: read jobs: test: @@ -17,8 +27,12 @@ jobs: - name: Setup Python uses: actions/setup-python@v5 - with: + with: python-version: '3.12' + cache: pip + cache-dependency-path: | + requirements.txt + requirements-test.txt - name: Install System Dependencies run: | @@ -30,9 +44,6 @@ jobs: python -m pip install --upgrade pip pip install -r requirements.txt pip install -r requirements-test.txt - - name: Run Tests - run: pytest -v - - + run: pytest -v diff --git a/Dockerfile b/Dockerfile index d035713..2ce8d67 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,17 +2,20 @@ FROM python:3.11-slim WORKDIR /app -RUN apt-get update && apt-get install -y build-essential \ +# libzbar0: runtime for pyzbar (QR labs). Wheels cover numpy/sklearn/Pillow/faiss; no compiler. +RUN apt-get update && apt-get install -y --no-install-recommends \ libzbar0 \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* COPY requirements.txt . -RUN python -m pip install --upgrade pip && \ - pip install \ - --retries 10 \ - --timeout 600 \ - -r requirements.txt +# CPU torch first so sentence-transformers does not pull the CUDA wheel (~GB of nvidia-*). +# Do not pin torch in requirements.txt: host/macOS venvs stay unchanged. +RUN python -m pip install --upgrade --no-cache-dir pip && \ + pip install --no-cache-dir --retries 10 --timeout 600 \ + torch --index-url https://download.pytorch.org/whl/cpu && \ + pip install --no-cache-dir --retries 10 --timeout 600 \ + -r requirements.txt COPY . . @@ -23,7 +26,8 @@ EXPOSE 8080 ENV FLASK_APP=main.py ENV PYTHONUNBUFFERED=1 +# Compose DNS to the separate ollama service (not installed in this image). ENV OLLAMA_HOST=http://ollama:11434 -# Start Ollama in background, wait for it, then start Flask -CMD ollama serve > /tmp/ollama.log 2>&1 & sleep 5 && flask run --host=0.0.0.0 --port=8080 --no-reload +# Flask only. Ollama runs as compose service ollama/ollama:latest. +CMD ["flask", "run", "--host=0.0.0.0", "--port=8080", "--no-reload"] diff --git a/Dockerfile.ollama b/Dockerfile.ollama deleted file mode 100644 index ca0e39e..0000000 --- a/Dockerfile.ollama +++ /dev/null @@ -1,7 +0,0 @@ -FROM ollama/ollama:latest - -EXPOSE 11434 - -ENV OLLAMA_KEEP_ALIVE=-1 - -CMD ["ollama", "serve"] \ No newline at end of file diff --git a/Makefile b/Makefile index 83bf75c..13a0167 100644 --- a/Makefile +++ b/Makefile @@ -136,13 +136,21 @@ test-docker-build: ## Build $(TEST_IMAGE) from Dockerfile for test runs docker build -t "$(TEST_IMAGE)" . .PHONY: test-docker -test-docker: test-docker-build ## Run pytest -v inside $(TEST_IMAGE) (installs requirements-test in container) - docker run --rm -t "$(TEST_IMAGE)" \ +test-docker: test-docker-build ## Run pytest -v inside $(TEST_IMAGE) (bind-mounts tests; image excludes tests/) + docker run --rm -t \ + -v "$(CURDIR)/tests:/app/tests:ro" \ + -v "$(CURDIR)/pytest.ini:/app/pytest.ini:ro" \ + -v "$(CURDIR)/requirements-test.txt:/app/requirements-test.txt:ro" \ + "$(TEST_IMAGE)" \ sh -lc 'python -m pip install -q -r requirements-test.txt && pytest -v' .PHONY: test-docker-cov -test-docker-cov: test-docker-build ## pytest with coverage inside container - docker run --rm -t "$(TEST_IMAGE)" \ +test-docker-cov: test-docker-build ## pytest with coverage inside container (bind-mounts tests) + docker run --rm -t \ + -v "$(CURDIR)/tests:/app/tests:ro" \ + -v "$(CURDIR)/pytest.ini:/app/pytest.ini:ro" \ + -v "$(CURDIR)/requirements-test.txt:/app/requirements-test.txt:ro" \ + "$(TEST_IMAGE)" \ sh -lc 'python -m pip install -q -r requirements-test.txt && pytest --cov=application --cov-report=term-missing -v' # ============================================================================= diff --git a/deploy/Dockerfile.pwnzzai-workshop b/deploy/Dockerfile.pwnzzai-workshop index 4e7f0ff..22a59b0 100644 --- a/deploy/Dockerfile.pwnzzai-workshop +++ b/deploy/Dockerfile.pwnzzai-workshop @@ -7,17 +7,19 @@ FROM python:3.11-slim WORKDIR /app -RUN apt-get update && apt-get install -y --no-install-recommends build-essential \ +# libzbar0: runtime for pyzbar (QR labs). Wheels cover numpy/sklearn/Pillow/faiss; no compiler. +RUN apt-get update && apt-get install -y --no-install-recommends \ libzbar0 \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* COPY requirements.txt . -RUN python -m pip install --upgrade pip && \ - pip install \ - --retries 10 \ - --timeout 600 \ - -r requirements.txt +# CPU torch first so sentence-transformers does not pull the CUDA wheel (~GB of nvidia-*). +RUN python -m pip install --upgrade --no-cache-dir pip && \ + pip install --no-cache-dir --retries 10 --timeout 600 \ + torch --index-url https://download.pytorch.org/whl/cpu && \ + pip install --no-cache-dir --retries 10 --timeout 600 \ + -r requirements.txt COPY . .