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
144 changes: 144 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,27 @@ jobs:
name: python-coverage-html
path: python/htmlcov

js-tests:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4

- name: Set up Node
uses: actions/setup-node@v4
with:
node-version: "20"
cache: npm
cache-dependency-path: js/package-lock.json

- name: Install JS dependencies
working-directory: js
run: npm ci

- name: Run JS tests
working-directory: js
run: npm test

rust-lint:
runs-on: ubuntu-latest
timeout-minutes: 10
Expand Down Expand Up @@ -579,6 +600,74 @@ jobs:
name: ${{ matrix.artifact-name }}
path: target/wheels/*.whl

build-npm-bundle:
runs-on: ${{ matrix.os }}
needs: [python-lint, rust-lint]
timeout-minutes: 30
if: startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main' || (github.event_name == 'pull_request' && contains(toJson(github.event.pull_request.labels), 'full-build'))
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-latest
label: linux-x86_64
artifact-name: rappel-npm-linux-x86_64
- os: ubuntu-24.04-arm
label: linux-aarch64
artifact-name: rappel-npm-linux-aarch64
- os: macos-latest
label: macos
artifact-name: rappel-npm-macos
- os: windows-latest
label: windows
artifact-name: rappel-npm-windows
steps:
- uses: actions/checkout@v4

- name: Set up uv
uses: astral-sh/setup-uv@v7

- name: Install protoc (Linux)
if: runner.os == 'Linux'
run: sudo apt-get update && sudo apt-get install -y protobuf-compiler

- name: Install protoc (macOS)
if: runner.os == 'macOS'
run: brew install protobuf

- name: Install protoc (Windows)
if: runner.os == 'Windows'
shell: powershell
run: choco install protoc --version=25.1 -y

- name: Set up Rust
uses: dtolnay/rust-toolchain@stable

- name: Rust cache
uses: Swatinem/rust-cache@v2.8.2
with:
shared-key: rappel

- name: Set up Node
uses: actions/setup-node@v4
with:
node-version: "20"
cache: npm
cache-dependency-path: js/package-lock.json

- name: Install JS dependencies
working-directory: js
run: npm ci

- name: Build npm bundle
run: uv run scripts/build_npm_bundle.py --out-dir target/npm --label ${{ matrix.label }}

- name: Upload npm bundle artifact
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.artifact-name }}
path: target/npm/*.tgz

package-release:
runs-on: ubuntu-latest
needs: [python-tests, rust-tests, build-wheel]
Expand Down Expand Up @@ -645,3 +734,58 @@ jobs:
uses: pypa/gh-action-pypi-publish@release/v1
with:
packages-dir: target/release-wheels

package-npm:
runs-on: ubuntu-latest
needs: [build-npm-bundle]
timeout-minutes: 10
if: startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main' || (github.event_name == 'pull_request' && contains(toJson(github.event.pull_request.labels), 'full-build'))
steps:
- uses: actions/checkout@v4

- name: Set up uv
uses: astral-sh/setup-uv@v7

- name: Set up Node
uses: actions/setup-node@v4
with:
node-version: "20"

- name: Download Linux x86_64 bundle
uses: actions/download-artifact@v4
with:
name: rappel-npm-linux-x86_64
path: artifacts/linux-x86_64

- name: Download Linux aarch64 bundle
uses: actions/download-artifact@v4
with:
name: rappel-npm-linux-aarch64
path: artifacts/linux-aarch64

- name: Download macOS bundle
uses: actions/download-artifact@v4
with:
name: rappel-npm-macos
path: artifacts/macos

- name: Download Windows bundle
uses: actions/download-artifact@v4
with:
name: rappel-npm-windows
path: artifacts/windows

- name: Assemble npm bundles
run: |
uv run scripts/assemble_npm_bundles.py \
--output target/npm-release \
linux-x86_64=artifacts/linux-x86_64 \
linux-aarch64=artifacts/linux-aarch64 \
macos=artifacts/macos \
windows=artifacts/windows

- name: Upload npm bundles
uses: actions/upload-artifact@v4
with:
name: rappel-npm-bundles
path: target/npm-release
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -221,3 +221,6 @@ target/wheels/
*.whl

autospec

node_modules

20 changes: 17 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
PY_PROTO_OUT := python/proto
JS_PROTO_OUT := js/proto

.PHONY: all build-proto clean lint lint-verify python-lint python-lint-verify rust-lint rust-lint-verify coverage python-coverage rust-coverage
.PHONY: all build-proto clean lint lint-verify python-lint python-lint-verify rust-lint rust-lint-verify js-lint js-lint-verify coverage python-coverage rust-coverage

all: build-proto

build-proto:
@mkdir -p $(PY_PROTO_OUT)
@mkdir -p $(JS_PROTO_OUT)
cd python && uv run python -m grpc_tools.protoc \
--proto_path=../proto \
--plugin=protoc-gen-mypy="$$(pwd)/.venv/bin/protoc-gen-mypy" \
Expand All @@ -15,16 +17,22 @@ build-proto:
--mypy_out=../$(PY_PROTO_OUT) \
--mypy_grpc_out=../$(PY_PROTO_OUT) \
../proto/messages.proto ../proto/ast.proto
cd js && ./node_modules/.bin/grpc_tools_node_protoc \
--proto_path=../proto \
--proto_path=./proto \
--js_out=import_style=commonjs,binary:./proto \
--grpc_out=grpc_js:./proto \
../proto/messages.proto ../proto/ast.proto
@python scripts/fix_proto_imports.py
$(MAKE) lint

clean:
rm -rf target
rm -rf $(PY_PROTO_OUT)

lint: python-lint rust-lint
lint: python-lint rust-lint js-lint

lint-verify: python-lint-verify rust-lint-verify
lint-verify: python-lint-verify rust-lint-verify js-lint-verify

python-lint:
cd python && uv run ruff format .
Expand All @@ -50,6 +58,12 @@ rust-lint-verify:
cargo fmt -- --check
cargo clippy --all-targets --all-features -- -D warnings

js-lint:
cd js && npm exec biome lint --write src tests

js-lint-verify:
cd js && npm exec biome lint src tests

# Coverage targets
coverage: python-coverage rust-coverage

Expand Down
46 changes: 46 additions & 0 deletions example_apps/js/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# syntax=docker/dockerfile:1.7

FROM rust:1.88-bookworm AS rust-builder

ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install -y --no-install-recommends \
pkg-config \
libssl-dev \
libprotobuf-dev \
protobuf-compiler \
&& rm -rf /var/lib/apt/lists/*

WORKDIR /repo
COPY . /repo
RUN cargo build --release --bin rappel-bridge --bin start-workers

FROM node:20-bookworm-slim AS node-builder

WORKDIR /repo
COPY js /repo/js
COPY proto /repo/proto
COPY example_apps/js /repo/example_apps/js

RUN cd /repo/js && npm install && npm run build && npm pack --silent
RUN cd /repo/example_apps/js && npm install
RUN rm -rf /repo/example_apps/js/node_modules/@rappel/js
RUN cd /repo/example_apps/js && npm install --no-save ../../js/*.tgz
RUN cd /repo/example_apps/js && npm run build

FROM node:20-bookworm-slim AS runtime

ENV NODE_ENV=production

WORKDIR /app

COPY --from=rust-builder /repo/target/release/rappel-bridge /usr/local/bin/rappel-bridge
COPY --from=rust-builder /repo/target/release/start-workers /usr/local/bin/start-workers

COPY proto /app/proto

COPY --from=node-builder /repo/example_apps/js /app/example_apps/js

EXPOSE 8001 24117 24118 24119

WORKDIR /app/example_apps/js
CMD ["node", "dist/server.js"]
17 changes: 17 additions & 0 deletions example_apps/js/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
IMAGE_NAME ?= rappel-example-app-js
COMPOSE_CMD ?= docker compose -f docker-compose.yml

.PHONY: build up down logs

build:
@echo "Building $(IMAGE_NAME) image"
docker build -f Dockerfile -t $(IMAGE_NAME) ../..

up:
$(COMPOSE_CMD) up --build -d

logs:
$(COMPOSE_CMD) logs -f

down:
$(COMPOSE_CMD) down -v
67 changes: 67 additions & 0 deletions example_apps/js/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
## Rappel JS example app

`example_apps/js` mirrors the Python example app, but defines workflows + actions in TypeScript
and serves the UI with an Express server. Worker execution is managed by the Rust pool using
the Node runtime.

### Prerequisites

- A running Rappel bridge + Postgres (use the root docker-compose or your local stack).
- `RAPPEL_BRIDGE_GRPC_ADDR` or `RAPPEL_BRIDGE_GRPC_HOST`/`RAPPEL_BRIDGE_GRPC_PORT` set.
- `RAPPEL_DATABASE_URL` set if you want the reset endpoint to work.

### Install + build

```bash
cd js
npm install
npm run build

cd ../example_apps/js
npm install
npm run build
```

### Run

Recommended (Rust-managed worker pool with Node runtime):

```bash
cd js
npm run build

cd ../example_apps/js
npm run build

RAPPEL_DATABASE_URL=postgresql://rappel:rappel@localhost:5432/rappel_example_js \
RAPPEL_WORKER_RUNTIME=node \
RAPPEL_NODE_WORKER_SCRIPT=$(pwd)/node_modules/@rappel/js/dist/worker-cli.js \
RAPPEL_USER_MODULE=$(pwd)/dist/workflows.js \
cargo run --bin start-workers
```

Start the web server in another terminal:

```bash
cd example_apps/js
RAPPEL_BRIDGE_GRPC_ADDR=127.0.0.1:24117 npm run start
```

Then visit http://localhost:8001/.

Notes:
- Actions are defined in `src/workflows.ts` and loaded via `RAPPEL_USER_MODULE`.
- Schedule endpoints use the default schedule name `default`.

### Docker

```bash
cd example_apps/js
make up
```

Visit http://localhost:8001/ and tear down with:

```bash
make down
```
Loading