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
32 changes: 32 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Build artifacts — keep these out of the build context (they bloat it and can
# leak local toolchain state). The broad **/ patterns cover the binding worktrees
# (node/target, python/target, wasm/target) in addition to the root target/.
target/
**/target/

# Node
node_modules/
**/node_modules/

# Git
.git/
.gitignore

# Non-redistributable / large corpora and binding build outputs
bench/
fuzz/target/
python/dist/
wasm/pkg/

# Local agent / scratch state
.claude/
.zcode/
thoughts/

# Release archives
*.tar.gz

# CI / editor noise
.editorconfig

# Keep tests/fixtures/ — the CI smoke test converts one of these.
26 changes: 26 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -96,3 +96,29 @@ jobs:
# Must run from the repo root: with python/ as cwd, the source anydoc/
# package dir shadows the installed compiled module.
- run: python -m unittest discover -s python/tests

docker:
name: Docker image (CLI)
runs-on: blacksmith-8vcpu-ubuntu-2404
# CI builds amd64 only; arm64 emulation under QEMU is slow and the
# Dockerfile is arch-neutral (verified locally on the darwin/arm64 host).
steps:
- uses: actions/checkout@v7
- uses: docker/setup-buildx-action@v3
- name: Build image
run: docker build -t anydoc .
- name: Smoke convert (non-empty Markdown)
run: |
set -o pipefail
docker run --rm -v "$PWD":/work -w /work anydoc convert tests/fixtures/docx/text.docx > /tmp/out.md
test -s /tmp/out.md
- name: No-args prints usage and exits non-zero
run: |
set +e
docker run --rm anydoc > /tmp/usage.txt 2>&1
code=$?
if [ "$code" -eq 0 ]; then
echo "::error::no-args invocation exited 0; expected non-zero"
exit 1
fi
grep -q 'usage:' /tmp/usage.txt
74 changes: 74 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# syntax=docker/dockerfile:1
#
# Minimal CLI image for firecrawl/anydoc (issue #11).
#
# Builds the existing examples/convert.rs as the container entrypoint so a user
# can run:
#
# docker run --rm -v "$PWD":/work anydoc convert <file>
#
# Multi-stage: the builder carries the Rust toolchain and is discarded; the
# runtime image holds only the compiled `convert` binary + a minimal glibc userland.
# No Node/Python/WASM bindings, no network service, nothing baked in.
#
# NOTE on base images: the runtime is debian:bookworm-slim (glibc) rather than
# alpine/musl. pdf-inspector (the only thing one might suspect of needing native
# deps) is pure Rust, so musl is feasible, but glibc matches the CI runner
# (ubuntu-2404) and a musl/static validation is deferred to a follow-up.

########################################
# Builder
########################################
FROM rust:1.88-bookworm AS build

WORKDIR /srv

# The root Cargo.toml is a workspace root (members = node/python/wasm), so
# cargo refuses to load it unless every member manifest is present and parses.
# Copy the manifests (root + the three tiny binding manifests) so the workspace
# resolves, then stub every crate's src/lib.rs. This lets us pre-build the root
# package's dependencies in a layer cached independently of the real sources.
# We build only the root package (-p anydoc): the binding crates depend ON
# anydoc, not the other way around, so their heavy deps (pyo3, napi,
# wasm-bindgen) are never compiled — the stubs just have to parse.
COPY Cargo.toml Cargo.lock ./
COPY node/Cargo.toml ./node/Cargo.toml
COPY python/Cargo.toml ./python/Cargo.toml
COPY wasm/Cargo.toml ./wasm/Cargo.toml

RUN mkdir -p src node/src python/src wasm/src \
&& echo 'fn main() {}' > src/lib.rs \
&& echo '' > node/src/lib.rs \
&& echo '' > python/src/lib.rs \
&& echo '' > wasm/src/lib.rs \
&& cargo build --release --locked -p anydoc --lib \
&& rm -rf src

# Now copy the real sources and build the CLI example. `--locked` keeps the
# build hermetic against Cargo.lock drift.
COPY src/ ./src/
COPY examples/ ./examples/
COPY README.md LICENSE ./

RUN cargo build --release --locked --example convert

# The example binary lands here.
# target/release/examples/convert

########################################
# Runtime
########################################
FROM debian:bookworm-slim

# ca-certificates keeps future https fetches working; nothing else is needed at
# runtime (documents are read from the mounted /work volume).
RUN apt-get update \
&& apt-get install -y --no-install-recommends ca-certificates \
&& rm -rf /var/lib/apt/lists/*

COPY --from=build /srv/target/release/examples/convert /usr/local/bin/convert

# Documents live on the host; the container converts from a mounted volume.
WORKDIR /work

ENTRYPOINT ["convert"]