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
9 changes: 9 additions & 0 deletions devops/docker/compose/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,15 @@
# GITHUB_NATS_ACK_TIMEOUT_SECS=10
# GITHUB_MAX_BODY_SIZE=26214400

# --- Linear Source ---
# LINEAR_WEBHOOK_SECRET=
# LINEAR_WEBHOOK_PORT=8080
# LINEAR_SUBJECT_PREFIX=linear
# LINEAR_STREAM_NAME=LINEAR
# LINEAR_STREAM_MAX_AGE_SECS=604800
# LINEAR_WEBHOOK_TIMESTAMP_TOLERANCE_SECS=60
# LINEAR_NATS_ACK_TIMEOUT_MS=10000

# --- Slack Source ---
# SLACK_SIGNING_SECRET=
# SLACK_WEBHOOK_PORT=3000
Expand Down
31 changes: 31 additions & 0 deletions devops/docker/compose/compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,31 @@ services:
start_period: 10s
retries: 3

trogon-source-linear:
build:
context: ../../../rsworkspace
dockerfile: ../devops/docker/compose/services/trogon-source-linear/Dockerfile
environment:
NATS_URL: "nats:4222"
LINEAR_WEBHOOK_SECRET: "${LINEAR_WEBHOOK_SECRET}"
LINEAR_WEBHOOK_PORT: "${LINEAR_WEBHOOK_PORT:-8080}"
LINEAR_SUBJECT_PREFIX: "${LINEAR_SUBJECT_PREFIX:-linear}"
LINEAR_STREAM_NAME: "${LINEAR_STREAM_NAME:-LINEAR}"
LINEAR_STREAM_MAX_AGE_SECS: "${LINEAR_STREAM_MAX_AGE_SECS:-604800}"
LINEAR_WEBHOOK_TIMESTAMP_TOLERANCE_SECS: "${LINEAR_WEBHOOK_TIMESTAMP_TOLERANCE_SECS:-60}"
LINEAR_NATS_ACK_TIMEOUT_MS: "${LINEAR_NATS_ACK_TIMEOUT_MS:-10000}"
RUST_LOG: "${RUST_LOG:-info}"
Comment thread
cursor[bot] marked this conversation as resolved.
depends_on:
nats:
condition: service_healthy
restart: unless-stopped
healthcheck:
test: ["CMD", "curl", "-sf", "http://localhost:${LINEAR_WEBHOOK_PORT:-8080}/health"]
interval: 10s
timeout: 3s
start_period: 10s
retries: 3

trogon-source-slack:
build:
context: ../../../rsworkspace
Expand Down Expand Up @@ -73,6 +98,7 @@ services:
environment:
NGROK_AUTHTOKEN: "${NGROK_AUTHTOKEN:-}"
GITHUB_ADDR: "trogon-source-github:${GITHUB_WEBHOOK_PORT:-8080}"
LINEAR_ADDR: "trogon-source-linear:${LINEAR_WEBHOOK_PORT:-8080}"
SLACK_ADDR: "trogon-source-slack:${SLACK_WEBHOOK_PORT:-3000}"
entrypoint:
- /bin/sh
Expand All @@ -84,6 +110,9 @@ services:
github:
addr: $${GITHUB_ADDR}
proto: http
linear:
addr: $${LINEAR_ADDR}
proto: http
slack:
addr: $${SLACK_ADDR}
proto: http
Expand All @@ -92,6 +121,8 @@ services:
depends_on:
trogon-source-github:
condition: service_healthy
trogon-source-linear:
condition: service_healthy
trogon-source-slack:
condition: service_healthy
restart: unless-stopped
Expand Down
45 changes: 45 additions & 0 deletions devops/docker/compose/services/trogon-source-linear/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# ── Stage 1: chef — generate dependency recipe ──────────────────────────────
FROM rust:1.93-slim AS chef

RUN cargo install cargo-chef --locked

WORKDIR /build

# ── Stage 2: planner — capture dependency graph ─────────────────────────────
FROM chef AS planner

COPY Cargo.toml Cargo.lock ./
COPY crates/ crates/

RUN cargo chef prepare --recipe-path recipe.json

# ── Stage 3: builder — cached dependency build + final compile ──────────────
FROM chef AS builder

COPY --from=planner /build/recipe.json recipe.json
RUN cargo chef cook --release --recipe-path recipe.json -p trogon-source-linear

COPY Cargo.toml Cargo.lock ./
COPY crates/ crates/

RUN cargo build --release -p trogon-source-linear && \
strip target/release/trogon-source-linear

# ── Stage 4: runtime ────────────────────────────────────────────────────────
FROM debian:bookworm-20250317-slim AS runtime

RUN apt-get update && apt-get install -y --no-install-recommends \
ca-certificates curl \
&& rm -rf /var/lib/apt/lists/*

RUN useradd --no-create-home --shell /usr/sbin/nologin trogon

COPY --from=builder /build/target/release/trogon-source-linear /usr/local/bin/trogon-source-linear

USER trogon

EXPOSE 8080

STOPSIGNAL SIGTERM

ENTRYPOINT ["/usr/local/bin/trogon-source-linear"]
72 changes: 72 additions & 0 deletions devops/docker/compose/services/trogon-source-linear/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# Receiving Linear Webhooks Locally

## Prerequisites

- Docker Compose
- A [Linear](https://linear.app) workspace with admin access
- An [ngrok](https://ngrok.com) account (free tier works)

## 1. Get your ngrok tunnel URL

Start only ngrok and NATS to obtain a public URL before configuring Linear:

```bash
docker compose --profile dev up ngrok nats
```

Find the tunnel URL in the ngrok container logs:

```bash
docker compose logs ngrok
```

Look for the `linear` tunnel URL (e.g. `https://abc123.ngrok-free.app`).

## 2. Configure your Linear webhook

1. Go to **Settings → API → Webhooks**
2. Click **New webhook**
3. Set:
- **URL**: `https://<ngrok-url>/webhook`
4. Select the resource types you want to receive events for
5. Save
6. Copy the **signing secret** that Linear generates

## 3. Start the webhook receiver

Set `LINEAR_WEBHOOK_SECRET` in your `.env` to the signing secret from step 2,
then bring up the full stack:

```bash
docker compose --profile dev up
```

## 4. Verify

Trigger an event in Linear (e.g. create an issue). You should see:

- The webhook receiver log the incoming event
- The event published to NATS on `linear.{type}.{action}`

You can inspect NATS with:

```bash
nats sub -s nats://nats.trogonai.orb.local:4222 "linear.>"
```

Without `--profile dev`, ngrok is excluded and only the core services start.

## Environment variables

| Variable | Required | Default | Description |
|---|---|---|---|
| `LINEAR_WEBHOOK_SECRET` | yes | — | Signing secret from Linear's webhook settings |
| `NGROK_AUTHTOKEN` | yes (dev profile) | — | ngrok auth token |
| `LINEAR_WEBHOOK_PORT` | no | `8080` | HTTP port for the webhook receiver |
| `LINEAR_SUBJECT_PREFIX` | no | `linear` | NATS subject prefix |
| `LINEAR_STREAM_NAME` | no | `LINEAR` | JetStream stream name |
| `LINEAR_STREAM_MAX_AGE_SECS` | no | `604800` | Max message age in seconds (7 days) |
| `LINEAR_WEBHOOK_TIMESTAMP_TOLERANCE_SECS` | no | `60` | Replay-attack window in seconds (0 to disable) |
| `LINEAR_NATS_ACK_TIMEOUT_MS` | no | `10000` | JetStream ACK timeout in milliseconds |
| `NATS_URL` | no | `localhost:4222` | NATS server URL(s) |
| `RUST_LOG` | no | `info` | Log level |
Loading
Loading