Skip to content
Merged
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
14 changes: 14 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Keep the build context to the source tree: no VCS history, no host build
# output (the image does its own release build), no Node artifacts.
# tools/jhm + make_dep_file are host-built (gitignored) binaries; if they
# leak into the context, `make bootstrap` skips bootstrap.sh and the .jhm
# tree-root marker it creates, and jhm dies at "Unable to find .jhm".
.git
.claude
tools/jhm
tools/make_dep_file
**/node_modules
**/dist
**/__pycache__
**/package-lock.json
*.log
83 changes: 83 additions & 0 deletions .github/workflows/docker.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# Build (and on demand, publish) the Orly runtime image (#530).
#
# Pull requests touching the docker files build the image and gate it with
# the MCP smoke pointed at the CONTAINERIZED server (the same smoke.mjs that
# gates clients/mcp against a host orlyi) -- no push. A version tag or a
# manual dispatch additionally pushes to ghcr.io/orlyatomics/orly.
name: docker

on:
pull_request:
paths:
- Dockerfile
- .dockerignore
- docker/**
- .github/workflows/docker.yml
push:
tags: ['v*']
workflow_dispatch:

jobs:
image:
name: build image + in-container smoke
runs-on: ubuntu-24.04
timeout-minutes: 90
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v6

- name: Build image
run: docker build -t ghcr.io/orlyatomics/orly:ci .

- name: Start container
run: |
docker run -d --name orly -p 8082:8082 ghcr.io/orlyatomics/orly:ci
for _ in $(seq 1 60); do
if ss -tln | grep -q ':8082'; then exit 0; fi
sleep 1
done
echo "container never opened :8082; logs:"
docker logs orly | tail -40
exit 1

# The same MCP smoke that gates clients/mcp, but ORLY_URL points at the
# container and the `sample` package is the one BAKED into the image --
# exercising install/new_pov/call/call_batch/error against the published
# artifact end to end.
- name: MCP smoke against the container
run: |
(cd clients/ts && npm install --silent && npx tsc)
(cd clients/mcp && npm install --silent && npx tsc)
ORLY_URL=ws://127.0.0.1:8082/ node clients/mcp/smoke/smoke.mjs

# The image also claims in-container package compiles (orlyc + g++ +
# ORLY_SRC_ROOT). Gate that claim: compile a from-scratch user package
# to a loadable .so inside the running container. (This caught a
# missing uuid-dev in the runtime stage during development.)
- name: In-container orlyc compile
run: |
docker exec orly bash -c '
set -e
mkdir -p /tmp/user
printf "package #1;\nhello = (42) where {};\n" > /tmp/user/hello.orly
cd /tmp/user && orlyc -o /tmp/user /tmp/user/hello.orly
test -s /tmp/user/hello.1.so
'

- name: Container logs (for diagnosis)
if: always()
run: docker logs orly 2>&1 | tail -60

- name: Push to ghcr.io
if: github.event_name == 'workflow_dispatch' || startsWith(github.ref, 'refs/tags/v')
run: |
echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u "${{ github.actor }}" --password-stdin
docker tag ghcr.io/orlyatomics/orly:ci ghcr.io/orlyatomics/orly:latest
docker push ghcr.io/orlyatomics/orly:latest
if [[ "${GITHUB_REF}" == refs/tags/v* ]]; then
ver="${GITHUB_REF#refs/tags/}"
docker tag ghcr.io/orlyatomics/orly:ci "ghcr.io/orlyatomics/orly:${ver}"
docker push "ghcr.io/orlyatomics/orly:${ver}"
fi
68 changes: 68 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# Orly runtime image (#530): a mem-sim `orlyi` with the example packages
# pre-compiled, plus `orlyc` and the source headers so user `.orly` packages
# compile INSIDE the container (orlyc shells out to g++ with -I$ORLY_SRC_ROOT).
#
# docker build -t ghcr.io/orlyatomics/orly .
# docker run --rm -p 8082:8082 ghcr.io/orlyatomics/orly
#
# The WebSocket + JSON protocol is then on ws://127.0.0.1:8082/ -- point any
# client driver (clients/{python,go,ts}) or the MCP server (clients/mcp) at it.

# ---- build stage -----------------------------------------------------------

FROM ubuntu:24.04 AS build

RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
build-essential gcc g++ uuid-dev libgmp-dev libaio-dev libsnappy-dev \
libreadline-dev libboost-system-dev zlib1g-dev bison flex python3 \
&& rm -rf /var/lib/apt/lists/*

WORKDIR /src/orly
COPY . .

# Bootstrap jhm + nycr, then build just the two production binaries this
# image ships (jhm emits to /src/out_orly). `make version` degrades to
# "unknown" without git -- fine for an image whose tag carries the version.
ARG JHM_WORKER_COUNT=4
RUN make bootstrap version \
&& PATH="/src/orly/tools:$PATH" \
jhm -c release --worker-count "$JHM_WORKER_COUNT" \
orly/server/orlyi orly/orlyc

# Pre-compile the example packages with the just-built orlyc (each orlyc run
# stands up and tears down its embedded mem-sim server, so this also smokes
# the binaries during the image build). `sample` is the trivial write/read
# package the MCP smoke uses; `graph` is the agent knowledge-graph schema;
# `market` is the prediction market.
RUN mkdir -p /opt/orly-packages /tmp/pkgout && touch /opt/orly-packages/__orly__ \
&& cd /tmp/pkgout \
&& /src/out_orly/release/orly/orlyc -o /tmp/pkgout /src/orly/clients/mcp/smoke/sample.orly \
&& /src/out_orly/release/orly/orlyc -o /tmp/pkgout /src/orly/examples/agent-swarm/graph.orly \
&& /src/out_orly/release/orly/orlyc -o /tmp/pkgout /src/orly/examples/prediction-market/market.orly \
&& cp /tmp/pkgout/*.so /opt/orly-packages/

# ---- runtime stage ---------------------------------------------------------

FROM ubuntu:24.04

# Runtime .so set verified via ldd on orlyi/orlyc, plus what in-container
# package compiles need: g++ (orlyc execs `g++ -I$ORLY_SRC_ROOT ...`) and
# uuid-dev (generated code includes base/uuid.h -> <uuid/uuid.h>; the lib
# alone is not enough -- verified by compiling a package in the container).
RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
g++ uuid-dev libgmp10 libgmpxx4ldbl libaio1t64 libboost-system1.83.0 \
libreadline8t64 libsnappy1v5 zlib1g \
&& rm -rf /var/lib/apt/lists/*

COPY --from=build /src/out_orly/release/orly/server/orlyi /usr/local/bin/orlyi
COPY --from=build /src/out_orly/release/orly/orlyc /usr/local/bin/orlyc
# The source tree = the header root orlyc-generated C++ compiles against.
COPY --from=build /src/orly /opt/orly/src
COPY --from=build /opt/orly-packages /var/lib/orly/packages
COPY docker/entrypoint.sh /usr/local/bin/orly-entrypoint

# Trailing slash matters: the compiler concatenates paths onto this directly.
ENV ORLY_SRC_ROOT=/opt/orly/src/

EXPOSE 8082
ENTRYPOINT ["/usr/local/bin/orly-entrypoint"]
19 changes: 18 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,24 @@ speaking the same [WebSocket + JSON protocol](docs/PROTOCOL.md).

## Quick start

System dependencies (Ubuntu 24.04):
**Docker** (any host — the engine itself is Linux-only, so this is also the
macOS/Windows path):

```sh
docker run --rm -p 8082:8082 ghcr.io/orlyatomics/orly
```

That's a solo mem-sim server with the `sample`, `graph`, and `market` example
packages pre-compiled and installable, speaking the
[WebSocket + JSON protocol](docs/PROTOCOL.md) on `ws://127.0.0.1:8082/` —
point any client driver ([`clients/`](clients/)) or the
[MCP server](clients/mcp/) at it. `orlyc` and the source headers are in the
image, so it compiles your own `.orly` packages too
(`docker exec <ctr> orlyc -o /var/lib/orly/packages yourpkg.orly`). Extra
`docker run ... <flags>` pass straight through to `orlyi`
([#530](https://github.com/orlyatomics/orly/issues/530)).

**From source** — system dependencies (Ubuntu 24.04):

```sh
sudo apt-get install -y \
Expand Down
1 change: 1 addition & 0 deletions changelog.d/530-docker-image.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- **Added**: a publishable Docker runtime image (`Dockerfile` + `.github/workflows/docker.yml` → `ghcr.io/orlyatomics/orly`): a mem-sim `orlyi` with the `sample`/`graph`/`market` example packages pre-compiled, plus `orlyc`, `g++`, and the source headers (`ORLY_SRC_ROOT`) so user `.orly` packages compile inside the container. `docker run -p 8082:8082` is now the two-command onboarding path (pair with `clients/mcp` for agents), and the only way to run Orly on non-Linux hosts. PR builds gate the image with the MCP smoke pointed at the containerized server; version tags and manual dispatch publish (#530).
25 changes: 25 additions & 0 deletions docker/entrypoint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#!/bin/bash
# Default container entrypoint (#530): a solo mem-sim orlyi with the baked
# example packages (sample, graph, market) installable out of the box, WS on
# 8082. Extra `docker run ... <flags>` are appended verbatim, so any orlyi
# flag can be overridden or added. Sizing knobs via env for the common case.
set -e

exec orlyi \
--mem_sim \
--mem_sim_mb="${ORLY_MEM_SIM_MB:-256}" \
--mem_sim_slow_mb="${ORLY_MEM_SIM_SLOW_MB:-64}" \
--create=true \
--instance_name="${ORLY_INSTANCE_NAME:-orly}" \
--starting_state=SOLO \
--port_number=8080 \
--slave_port_number=8081 \
--ws_port_number=8082 \
--reporting_port_number=8083 \
--connection_backlog=32 \
--package_dir=/var/lib/orly/packages \
--max_parallel_frames 4000 \
--page_cache_size 256 \
--block_cache_size 64 \
--le --log_info \
"$@"
Loading