14 Commits

Author SHA1 Message Date
Jiangzhou 90d8ff9f08 ci(docker): switch to registry-backed BuildKit cache (GHCR) (#140)
The previous `type=gha` cache was scoped per Git ref, so each release
tag created a new cache scope and never reused a previous release's
layers. Add to that GHA's 10 GB per-repo limit (which we already hit at
~10 GB before the v0.2.26 release), and the cache effectively never
helped consecutive releases.

Move to `type=registry` cache stored as separate `buildcache-<variant>`
tags on the same GHCR package. Branch/tag-independent, no LRU eviction,
unlimited size for our purposes. Standard pattern for multi-arch
buildx + multi-registry publishes.

Per-variant cache tag (`buildcache-slim`, `buildcache-full`) so the two
variants don't evict each other's layers.

`mode=max` exports all intermediate layers, not just the final ones —
needed for the per-RUN-layer reuse strategy in the Dockerfile.

Also cleaned up 23 orphaned GHA cache entries on the deleted
g/docker-layer-cache-v2 branch (3.4 GB freed).
2026-04-14 18:13:09 -07:00
Jiangzhou 6f84edca2e feat(docker): slim/full image variants + cached deps layer + [full] extra rename (#138)
* perf(docker): split install into stable deps + per-release layers; add GHA cache

Dockerfile previously installed cocoindex-code, cocoindex, torch,
sentence-transformers, and all transitive deps in one RUN. Any change to
the source tree (via COPY . /ccc-src) invalidated that single layer,
forcing a full re-install — ~1 GB of wheels for torch + friends — on
every release. Under QEMU for the arm64 cross-build this was slow
enough to be painful.

Split into two stages:
- `deps`: install cocoindex + cocoindex-code[default] from PyPI. Cache
  key is just the RUN command string, so this layer is reused across
  releases until we bump the pins.
- `builder`: overlay the release version via
  `CCC_INSTALL_SPEC=/ccc-src[default]` with `--no-deps
  --force-reinstall` — only the cocoindex-code package is touched; the
  heavy deps layer stays untouched.

Also add BuildKit layer cache (`type=gha`) to the publish-docker job so
the deps layer persists across workflow runs, not just within a single
build.

* feat(docker,packaging): slim/full image variants; rename [default]→[full] extra

Build two Docker image variants per release:
- slim (:latest, default) — ~450 MB. LiteLLM-only. cocoindex + cocoindex-code
  without sentence-transformers. Targets cloud-backed embeddings.
- full (:full)            — ~5 GB. Bundles sentence-transformers + torch +
  a pre-baked default model. Targets offline-ready local embeddings.

Dockerfile gains a CCC_VARIANT build arg that gates stage 1's
sentence-transformers install and stage 3's model bake. Release workflow
matrices on {slim, full}; each variant has its own GHA cache scope so
layer reuse works across releases without the variants evicting each
other.

Also rename the PyPI `[default]` umbrella extra to `[full]` so pip and
Docker names match. `[embeddings-local]` remains the canonical primary
extra (the one that specifically pulls in sentence-transformers); `[full]`
is its umbrella alias that may bundle additional optional niceties later.
CLI hints that point at missing sentence-transformers continue to name
`[embeddings-local]` directly — the most specific pointer for that case.

README documents both image variants with a comparison table and narrows
the Mac-on-Docker MPS note to only :full users (slim + LiteLLM is
unaffected).
2026-04-14 16:25:42 -07:00
Jiangzhou 745dcd6875 feat(ci): build multi-arch Docker image (linux/amd64 + linux/arm64) (#137)
Adds QEMU setup and the `platforms: linux/amd64,linux/arm64` arg to the
build-push-action. Apple Silicon Macs and arm64 Linux hosts get a native
image (pulled automatically via manifest selection) instead of running
under QEMU emulation. amd64 users are unaffected.

Tradeoff: arm64 is cross-compiled via QEMU on the x86_64 runner, so
release builds are noticeably longer (the torch install + model bake
stage both run emulated). Acceptable at current release cadence; can
upgrade to a native arm64 runner matrix if it becomes painful.
2026-04-14 15:06:31 -07:00
Jiangzhou 4b98490aa2 fix(ci): build Docker image from local source and add test-dispatch (#136)
- Install cocoindex-code from the checked-out source tree (same build-arg
  used by local E2E tests) instead of pulling from PyPI. Avoids a race
  where the just-published wheel hadn't propagated to PyPI's CDN when
  publish-docker started, and ensures the image matches the tagged
  commit byte-for-byte.
- Add a `test_docker` workflow_dispatch input so registry credentials
  (Docker Hub + GHCR) can be verified before the next release without
  having to cut one. Dispatch pushes `:test` only; release pushes
  `:latest` + `:<version>`.
- Drop the needs: publish-to-pypi dependency — with local-source install
  we no longer need PyPI to be up-to-date first.
2026-04-14 13:51:50 -07:00
Jiangzhou 92d611968e feat: unified Docker workspace mount with supervised daemon (#135)
* feat: unified Docker workspace mount with supervised daemon

Reshape the Docker experience around a single bind mount and a single
named volume. Global settings live on the host under
$HOME/.cocoindex_code/ (visible and editable); index data and the model
cache persist in one cocoindex-data volume; daemon runtime state stays
on the container's native filesystem.

CLI and MCP output now show host-side paths via a bidirectional
COCOINDEX_CODE_HOST_PATH_MAPPING translator. A shell wrapper that
forwards $PWD (COCOINDEX_CODE_HOST_CWD) lets ccc work from any project
subdirectory on the host.

The daemon tolerates a missing global_settings.yml (starts in
no-settings mode) so ccc init's interactive picker works in Docker on
first run. A supervisor restart loop in the entrypoint, driven by a new
COCOINDEX_CODE_DAEMON_SUPERVISED contract, makes settings-change
auto-restart safe — editing global_settings.yml triggers an in-place
daemon respawn without taking the container down.

Linux ownership alignment via PUID/PGID, gosu privilege drop, and a
coco user baked into the image. Release workflow now publishes to both
Docker Hub (cocoindex/cocoindex-code) and GHCR
(ghcr.io/cocoindex-io/cocoindex-code).

Also:
- Merge cocoindex-db and cocoindex-model-cache into a single volume
- find_parent_with_marker requires .cocoindex_code/settings.yml, so a
  workspace-root global-only dir doesn't trigger nested-init warnings
- New pytest marker `docker_e2e` gates the Docker-backed E2E suite
  (excluded from default pytest runs)

* fix: mypy on Windows for POSIX-only os.getuid/getgid calls
2026-04-14 13:30:50 -07:00
Jiangzhou baa69ee56f ci: drop macOS 3.14t matrix cell (#133)
`tokenizers` (transitive via `litellm`) ships only `abi3` wheels, with
no free-threaded variant. On macOS arm64 runners since the early-Apr
image update, the source-build fallback fails to link against CPython
3.14t. Excluding the cell cleans up CI signal — end users on standard
Python 3.14 remain unaffected (they pick up the `abi3` wheel).

Ubuntu + 3.14t stays enabled (it still passes, via source build that
Linux's ld accepts). Re-enable macOS once HuggingFace/tokenizers ships
free-threaded wheels or litellm drops the dep.
2026-04-13 22:55:41 -07:00
Jiangzhou 21f858e840 refactor: extract daemon path helpers to avoid CLI importing cocoindex (#130)
* refactor: extract daemon path helpers to avoid CLI importing cocoindex

Move daemon_dir, daemon_socket_path, daemon_pid_path, daemon_log_path,
and connection_family from daemon.py into a new lightweight _daemon_paths.py
module. This prevents the CLI client from transitively importing cocoindex
and its heavy dependencies (numpy, torch, etc.) when it only needs path
utilities.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* ci: mark free-threaded Python CI jobs as continue-on-error

tokenizers lacks pre-built wheels for cp314t (free-threaded ABI),
so uv falls back to compiling from Rust source. This source build
is fragile across macOS runner image updates and broke after the
20260406 image bump. Mark 3.14t jobs as continue-on-error since
free-threaded wheel coverage across the ecosystem is still limited.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 12:38:52 -07:00
Jiangzhou 53d808293f fix: make sure daemons are brought down cleanly (#84)
* fix: make sure daemons are brought down cleanly

* chore: be more verbose in pre-commit workflow

* fix: split CI workflow to avoid prek output capture deadlock on Windows

prek --verbose captures subprocess stdout via pipe. On Windows, the 4KB
pipe buffer fills when pytest produces verbose output, causing a deadlock
(pytest blocks writing, prek blocks waiting for exit). Split into two
steps: prek --skip pytest for lint checks, and pytest running directly
for streaming output.

Also:
- Add OSError catch in _pid_alive for Windows edge cases
- Properly shut down daemon thread in test_daemon.py session fixture

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: catch SystemError from os.kill on Windows

On Windows, os.kill(pid, 0) raises SystemError when the target process
has exited but its handle state is in transition (WinError 87). This
corrupts CPython C exception state, causing subsequent built-in calls
like time.monotonic() to also raise SystemError.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: avoid os.kill on Windows due to CPython C exception state corruption

os.kill(pid, 0) on Windows corrupts CPython C-level exception state
even after the raised OSError is caught. This causes subsequent calls to
C built-ins (time.monotonic, time.sleep) to raise SystemError. Use
ctypes OpenProcess instead, which is the proper Win32 API for checking
process existence.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 15:07:00 -07:00
Jiangzhou aa0207b819 chore: make sure pytest always run in precommit (#64)
* chore: make sure `pytest` always run in precommit

* chore: use uv python for sqlite extension

* fix: path for windows
2026-03-13 09:13:05 -07:00
Jiangzhou He a6e0a01380 fix: precommit, dynamic version 2026-02-10 08:25:54 -08:00
Jiangzhou He e15bd8c95d fix: release workflow 2026-02-07 23:03:21 -08:00
Jiangzhou He 41fad0bccf ops: add release workflow 2026-02-07 22:55:17 -08:00
Jiangzhou He f8b774b04b fix: exclude windows 3.14t from CI 2026-02-07 14:02:13 -08:00
Jiangzhou He 226be1cec2 workflow: pre-commit and CI 2026-02-07 13:57:10 -08:00