Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,26 @@ install.bat
OmniParser_CraftOS
debug_images
workspace

# ── Per-machine runtime state — never belongs in an image ──────────────────
# `COPY . .` takes whatever is in the build context, so without these a local
# `docker build` bakes in the BUILDER's data and publishes it to every user of
# the image. CI escapes it only by checking out clean, which makes this a trap
# that fires exactly once, on someone's laptop.
#
# Same failure as the old PyInstaller spec's blanket app/data entry, which
# shipped 1.1 GB of one machine's memory index and databases.
app/data/.file_index
app/data/.usage
agent_file_system
chroma_db_memory
logs
runtime
*.db
.craftbot-managed
config.json
wheelhouse
downloads-cache
npm-cache
playwright-browsers
hf-cache
49 changes: 0 additions & 49 deletions .github/workflows/ci.yml.disabled

This file was deleted.

73 changes: 73 additions & 0 deletions .github/workflows/launcher.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
name: Launcher

# CI for the native launcher in launcher/ (Rust + Slint). The Release
# workflow builds and ships it; this one catches breakage on the way there,
# on every platform the launcher is shipped for.

on:
push:
paths:
- "launcher/**"
- ".github/workflows/launcher.yml"
pull_request:
paths:
- "launcher/**"
- ".github/workflows/launcher.yml"

env:
CARGO_TERM_COLOR: always

jobs:
test:
name: Test (${{ matrix.os_label }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-latest
os_label: linux
- os: windows-latest
os_label: windows
- os: macos-latest
os_label: macos

defaults:
run:
working-directory: launcher

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Set up Rust
uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt, clippy

- name: Cache cargo
uses: Swatinem/rust-cache@v2
with:
workspaces: launcher

# Same insurance as the Release workflow: Slint has no C build deps,
# but a few crates probe for these headers.
- name: Linux build prerequisites
if: matrix.os_label == 'linux'
run: |
sudo apt-get update
sudo apt-get install -y libxkbcommon-dev libgl1-mesa-dev libfontconfig1-dev

- name: Format
if: matrix.os_label == 'linux'
run: cargo fmt --all -- --check

- name: Clippy
if: matrix.os_label == 'linux'
run: cargo clippy --release -- -D warnings

- name: Test
run: cargo test --release

- name: Build
run: cargo build --release
202 changes: 202 additions & 0 deletions .github/workflows/parity.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
# Three guarantees. This workflow turns "the install paths have drifted" from
# something discovered by reading code into a number that moves.
#
# 1. locks — the hash-pinned lock matches requirements.txt everywhere
# 2. parity — pip and conda installs produce the same environment
# 3. installer-e2e — an install produces the same thing as a checkout
#
# A lock can only be generated on the platform it describes, so they are
# generated by hand and committed. CI only verifies they are current.
#
# There is no frozen-agent job: the agent is no longer a PyInstaller bundle,
# which is what removed the whole class of divergence these jobs guard.

name: Install parity

on:
# No push trigger: nothing here writes to the repo, so there is nothing a
# push would accomplish that a PR does not.
pull_request:
paths:
- "requirements.txt"
- "environment.yml"
- "requirements/**"
- "scripts/generate_lock.py"
- "scripts/parity_check.py"
- "scripts/package_source.py"
- "scripts/test_install_e2e.py"
- "app/provision/**"
- "app/paths.py"
- ".github/workflows/parity.yml"
workflow_dispatch:

jobs:
# ──────────────────────────────────────────────
# Every committed lock came from the current
# requirements.txt.
# ──────────────────────────────────────────────
locks:
name: Locks match requirements.txt
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: actions/setup-python@v5
with:
python-version: "3.10"

# Checks all platforms' locks from one runner, and needs no pip resolve:
# it compares the source digest recorded in each lock's header. See
# scripts/generate_lock.py for why re-resolving here would be wrong.
#
# This only CHECKS. Locks are generated by hand — `python
# scripts/generate_lock.py` — on the platform each one describes, and
# committed. release.yml refuses to build a payload if one is missing.
- name: Check locks
run: python scripts/generate_lock.py --check

# ──────────────────────────────────────────────
# pip vs conda must produce the same environment.
# environment.yml used to carry its own package
# list, which drifted by 15 packages. It now
# provides only the runtime, and BOTH paths
# install the same lock — so this job checks
# that the two runtimes agree, not two lists.
# ──────────────────────────────────────────────
parity:
name: pip vs conda (${{ matrix.os_label }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-latest
os_label: linux
- os: windows-latest
os_label: windows
steps:
- uses: actions/checkout@v4

- uses: actions/setup-python@v5
with:
python-version: "3.10"

- name: Install via pip (from the lock)
shell: bash
run: |
python -m venv .venv-pip
if [ -f .venv-pip/bin/python ]; then PY=.venv-pip/bin/python;
else PY=.venv-pip/Scripts/python.exe; fi
echo "PIP_PY=$PY" >> "$GITHUB_ENV"
"$PY" -m pip install --upgrade pip
# `ls | head -1` would pick another platform's lock once Linux and
# macOS locks exist. app.provision.deps.find_lock resolves the exact
# (platform, python) tag and refuses to fall back — a Linux lock
# pins CUDA-flavoured torch wheels that do not exist on Windows.
LOCK=$("$PY" -c "from app.provision.deps import find_lock; print(find_lock('.') or '')")
if [ -z "$LOCK" ]; then
echo "::error::no lock for this platform — run scripts/generate_lock.py"
exit 1
fi
echo "using $LOCK"
"$PY" -m pip install --require-hashes -r "$LOCK"

- name: Fingerprint pip install
shell: bash
run: |
"$PIP_PY" scripts/parity_check.py --label pip > fp-pip.json

- uses: conda-incubator/setup-miniconda@v3
with:
activate-environment: craftbot
environment-file: environment.yml
auto-activate-base: false

# environment.yml deliberately lists no Python packages — they come from
# the shared lock. Without this step the conda env holds only the
# interpreter and system binaries, and the comparison below would report
# a 200-package "divergence" that is really just a missing install.
- name: Install the lock into the conda env
shell: bash -el {0}
run: |
LOCK=$(python -c "from app.provision.deps import find_lock; print(find_lock('.') or '')")
if [ -z "$LOCK" ]; then
echo "::error::no lock for this platform — run scripts/generate_lock.py"
exit 1
fi
echo "using $LOCK"
python -m pip install --require-hashes -r "$LOCK"

- name: Fingerprint conda install
shell: bash -el {0}
run: python scripts/parity_check.py --label conda > fp-conda.json

- name: Compare
shell: bash
run: python scripts/parity_check.py --compare fp-pip.json fp-conda.json

- name: Upload fingerprints
if: always()
uses: actions/upload-artifact@v4
with:
name: fingerprints-${{ matrix.os_label }}
path: fp-*.json

# ──────────────────────────────────────────────
# The installer path must produce the same thing
# as a source checkout. This is the acceptance
# test for the whole architecture, so it runs on
# every platform we ship.
# ──────────────────────────────────────────────
installer-e2e:
name: Installer E2E (${{ matrix.os_label }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-latest
os_label: linux
- os: windows-latest
os_label: windows
- os: macos-latest
os_label: macos
steps:
- uses: actions/checkout@v4

- uses: actions/setup-python@v5
with:
python-version: "3.10"

- uses: actions/setup-node@v4
with:
node-version: "20"

- name: Build frontend
shell: bash
env:
VITE_BACKEND_PORT: "7926"
run: |
cd app/ui_layer/browser/frontend
npm install
npx vite build

- name: Build the source payload
shell: bash
run: |
python scripts/package_source.py

# Structural only: verifies path resolution, the managed-install marker
# and payload completeness. The full run installs 239 packages and is
# too slow for every PR — the `parity` job covers dependency equality.
- name: Install E2E (structural)
shell: bash
run: |
python scripts/test_install_e2e.py --skip-deps

- name: Upload payload
if: always()
uses: actions/upload-artifact@v4
with:
name: payload-${{ matrix.os_label }}
path: dist/CraftBot-src.zip
Loading
Loading