diff --git a/.dockerignore b/.dockerignore index be74e5c..01753dc 100644 --- a/.dockerignore +++ b/.dockerignore @@ -3,17 +3,20 @@ __pycache__ *.pyc *.pyo staging.db +# gitignored secrets — belt-and-suspenders with the RUN rm -f in Dockerfile config/user.yaml +config/plain_text_resume.yaml config/notion.yaml config/email.yaml config/tokens.yaml config/craigslist.yaml +config/adzuna.yaml +.env .streamlit.pid .streamlit.log aihawk/ docs/ tests/ -.env data/ log/ unsloth_compiled_cache/ diff --git a/.forgejo/workflows/release.yml b/.forgejo/workflows/release.yml index fda07af..a3e7f2c 100644 --- a/.forgejo/workflows/release.yml +++ b/.forgejo/workflows/release.yml @@ -1,12 +1,20 @@ # Tag-triggered release workflow. -# Generates changelog and creates Forgejo release on v* tags. -# Copied from Circuit-Forge/cf-agents workflows/release.yml +# Generates changelog, publishes Docker images to GHCR, and creates Forgejo release. # -# Docker push is intentionally disabled — BSL 1.1 registry policy not yet resolved. -# Tracked in Circuit-Forge/cf-agents#3. Re-enable the Docker steps when that lands. +# Images published on v* tags: +# ghcr.io/circuitforgellc/peregrine:latest — FastAPI API (includes cf-orch) +# ghcr.io/circuitforgellc/peregrine: +# ghcr.io/circuitforgellc/peregrine-web:latest — Vue SPA (base path /) +# ghcr.io/circuitforgellc/peregrine-web: # -# Required secrets: FORGEJO_RELEASE_TOKEN -# (GHCR_TOKEN not needed until Docker push is enabled) +# The cloud image (compose.cloud.yml) is never published — it is built and +# deployed directly on Heimdall from Dockerfile.cfcore with sibling repos. +# +# Required secrets: +# FORGEJO_RELEASE_TOKEN — Forgejo API token for creating releases +# GH_GHCR_TOKEN — GitHub PAT with packages:write for GHCR push +# FORGEJO_CF_ORCH_TOKEN — Forgejo token to install private circuitforge-orch +# during the API image build (BSL client for paid tier) name: Release @@ -32,28 +40,56 @@ jobs: env: OUTPUT: CHANGES.md - # ── Docker (disabled — BSL registry policy pending cf-agents#3) ────────── - # - name: Set up QEMU - # uses: docker/setup-qemu-action@v3 - # - name: Set up Buildx - # uses: docker/setup-buildx-action@v3 - # - name: Log in to GHCR - # uses: docker/login-action@v3 - # with: - # registry: ghcr.io - # username: ${{ github.actor }} - # password: ${{ secrets.GHCR_TOKEN }} - # - name: Build and push Docker image - # uses: docker/build-push-action@v6 - # with: - # context: . - # push: true - # platforms: linux/amd64,linux/arm64 - # tags: | - # ghcr.io/circuitforgellc/peregrine:${{ github.ref_name }} - # ghcr.io/circuitforgellc/peregrine:latest - # cache-from: type=gha - # cache-to: type=gha,mode=max + # ── Docker setup ───────────────────────────────────────────────────────── + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to GHCR + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GH_GHCR_TOKEN }} + + # ── API image ───────────────────────────────────────────────────────────── + # cf-orch (BSL, private) is installed via BuildKit secret — token never + # appears in any image layer. Community builds without the secret fall back + # to local backends automatically. + - name: Build and push API image + uses: docker/build-push-action@v6 + with: + context: . + dockerfile: Dockerfile + push: true + platforms: linux/amd64,linux/arm64 + secrets: | + forgejo_token=${{ secrets.FORGEJO_CF_ORCH_TOKEN }} + tags: | + ghcr.io/circuitforgellc/peregrine:${{ github.ref_name }} + ghcr.io/circuitforgellc/peregrine:latest + cache-from: type=gha,scope=api + cache-to: type=gha,mode=max,scope=api + + # ── Web image ───────────────────────────────────────────────────────────── + # Published with VITE_BASE_PATH=/ (self-hosted default). + # Cloud and demo deployments build locally with VITE_BASE_PATH=/peregrine/. + - name: Build and push web image + uses: docker/build-push-action@v6 + with: + context: . + dockerfile: docker/web/Dockerfile + push: true + platforms: linux/amd64,linux/arm64 + build-args: | + VITE_BASE_PATH=/ + tags: | + ghcr.io/circuitforgellc/peregrine-web:${{ github.ref_name }} + ghcr.io/circuitforgellc/peregrine-web:latest + cache-from: type=gha,scope=web + cache-to: type=gha,mode=max,scope=web # ── Forgejo Release ─────────────────────────────────────────────────────── - name: Create Forgejo release diff --git a/Dockerfile b/Dockerfile index b2cca30..75aff50 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,30 +1,59 @@ -# Dockerfile +# Dockerfile — Peregrine release build +# Self-contained single-repo context. Used for published images and community builds. +# +# cf-core: installed from public Forgejo via requirements.txt +# cf-orch: BSL-licensed cloud inference client; installed only when the +# forgejo_token BuildKit secret is present (release CI). +# Community builds skip it gracefully — local Ollama/vllm still work. +# +# Release CI (Forgejo): +# docker buildx build --secret id=forgejo_token,env=FORGEJO_TOKEN -t peregrine:latest . +# +# Community / source build: +# docker buildx build -t peregrine:latest . +# +# Previously this file ran Streamlit (app/app.py). Streamlit was removed in +# peregrine#104. The runtime is now uvicorn (FastAPI). Dockerfile.cfcore remains +# for the cloud deployment on Heimdall, where sibling repos are available. + FROM python:3.11-slim WORKDIR /app -# System deps for companyScraper (beautifulsoup4, fake-useragent, lxml) and PDF gen -# libsqlcipher-dev: required to build pysqlcipher3 (SQLCipher AES-256 encryption for cloud mode) RUN apt-get update && apt-get install -y --no-install-recommends \ gcc libffi-dev curl libsqlcipher-dev git \ && rm -rf /var/lib/apt/lists/* COPY requirements.txt . -# Install Python dependencies RUN pip install --no-cache-dir -r requirements.txt -# Install Playwright browser (cached separately from Python deps so requirements -# changes don't bust the ~600–900 MB Chromium layer and vice versa) +# cf-orch BSL client — cloud inference routing for paid/premium tier. +# The --mount=type=secret keeps the token out of all image layers. +# If no secret is provided the pip install is skipped; the app falls back to +# local backends (Ollama, vllm) and tier gating blocks cloud-orch features. +RUN --mount=type=secret,id=forgejo_token \ + TOKEN=$(cat /run/secrets/forgejo_token 2>/dev/null || true) && \ + if [ -n "$TOKEN" ]; then \ + pip install --no-cache-dir \ + "git+https://x-access-token:${TOKEN}@git.opensourcesolarpunk.com/Circuit-Forge/circuitforge-orch.git@main" \ + && echo "cf-orch installed"; \ + else \ + echo "cf-orch skipped (community build — local backends available)"; \ + fi + +# Chromium for Playwright-based scrapers (companyScraper, job board scraping) RUN playwright install chromium && playwright install-deps chromium -# Bundle companyScraper (company research web scraper) COPY scrapers/ /app/scrapers/ - COPY . . -EXPOSE 8501 +# Strip gitignored secrets that may exist in a local checkout. +# Defense-in-depth: .dockerignore already excludes these, but an explicit rm +# guarantees they never appear in the image even if .dockerignore is misconfigured. +RUN rm -f config/user.yaml config/plain_text_resume.yaml config/notion.yaml \ + config/email.yaml config/tokens.yaml config/craigslist.yaml \ + config/adzuna.yaml .env -CMD ["streamlit", "run", "app/app.py", \ - "--server.port=8501", \ - "--server.headless=true", \ - "--server.fileWatcherType=none"] +EXPOSE 8601 + +CMD ["uvicorn", "dev_api:app", "--host", "0.0.0.0", "--port", "8601"] diff --git a/compose.demo.yml b/compose.demo.yml index 7d07d37..bd61cb8 100644 --- a/compose.demo.yml +++ b/compose.demo.yml @@ -16,6 +16,7 @@ services: api: + image: ghcr.io/circuitforgellc/peregrine:latest build: . command: > bash -c "uvicorn dev_api:app --host 0.0.0.0 --port 8601" @@ -42,6 +43,8 @@ services: # No host port — nginx proxies /api/ → api:8601 internally web: + # Built with VITE_BASE_PATH=/peregrine/ — not the same as the published + # peregrine-web:latest image (which uses base path /). Always build locally. build: context: . dockerfile: docker/web/Dockerfile diff --git a/compose.yml b/compose.yml index 0cc6c39..d7df620 100644 --- a/compose.yml +++ b/compose.yml @@ -3,9 +3,10 @@ services: api: + image: ghcr.io/circuitforgellc/peregrine:latest build: - context: .. - dockerfile: peregrine/Dockerfile.cfcore + context: . + dockerfile: Dockerfile command: > bash -c "uvicorn dev_api:app --host 0.0.0.0 --port 8601" volumes: @@ -31,6 +32,7 @@ services: restart: unless-stopped web: + image: ghcr.io/circuitforgellc/peregrine-web:latest build: context: . dockerfile: docker/web/Dockerfile