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
65 changes: 1 addition & 64 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: CI / Deploy
name: CI

on:
push:
Expand Down Expand Up @@ -79,66 +79,3 @@ jobs:

sys.exit(1 if failed else 0)
"

deploy:
needs: check
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
permissions:
id-token: write
contents: read
steps:
- uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'

- name: Install dependencies
run: npm install --legacy-peer-deps

- name: Build
run: npm run build

- name: Run D1 Migrations
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
run: npx wrangler d1 migrations apply openboot --remote

- name: Deploy
uses: cloudflare/wrangler-action@v3
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}

- name: Health Check
run: |
echo "Waiting 10 seconds for deployment to propagate..."
sleep 10

echo "Running health check..."
HEALTH_RESPONSE=$(curl -s https://openboot.dev/api/health)
echo "Health check response: $HEALTH_RESPONSE"

STATUS=$(echo $HEALTH_RESPONSE | jq -r '.status')
if [ "$STATUS" != "healthy" ]; then
echo "Health check failed! Status: $STATUS"
echo "Full response: $HEALTH_RESPONSE"
exit 1
fi

echo "Health check passed!"
echo "API: $(echo $HEALTH_RESPONSE | jq -r '.checks.api')"
echo "Database: $(echo $HEALTH_RESPONSE | jq -r '.checks.database')"
echo "Version: $(echo $HEALTH_RESPONSE | jq -r '.version')"

- name: Post-deploy smoke test
run: ./scripts/smoke-test-api.sh https://openboot.dev

- name: Post-deploy contract validation
run: |
pip install jsonschema
git clone --depth 1 https://github.com/openbootdotdev/openboot-contract.git /tmp/contract
SERVER_URL=https://openboot.dev /tmp/contract/golden-path/contract-smoke.sh
72 changes: 72 additions & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
name: CD

on:
workflow_run:
workflows: [CI]
types: [completed]
branches: [main]

jobs:
deploy:
if: github.event.workflow_run.conclusion == 'success'
runs-on: ubuntu-latest
permissions:
id-token: write
contents: read
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.event.workflow_run.head_sha }}

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'

- name: Install dependencies
run: npm install --legacy-peer-deps

- name: Build
run: npm run build

- name: Run D1 Migrations
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
run: npx wrangler d1 migrations apply openboot --remote

- name: Deploy
uses: cloudflare/wrangler-action@v3
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}

- name: Health Check
run: |
echo "Waiting 10 seconds for deployment to propagate..."
sleep 10

echo "Running health check..."
HEALTH_RESPONSE=$(curl -s https://openboot.dev/api/health)
echo "Health check response: $HEALTH_RESPONSE"

STATUS=$(echo $HEALTH_RESPONSE | jq -r '.status')
if [ "$STATUS" != "healthy" ]; then
echo "Health check failed! Status: $STATUS"
echo "Full response: $HEALTH_RESPONSE"
exit 1
fi

echo "Health check passed!"
echo "API: $(echo $HEALTH_RESPONSE | jq -r '.checks.api')"
echo "Database: $(echo $HEALTH_RESPONSE | jq -r '.checks.database')"
echo "Version: $(echo $HEALTH_RESPONSE | jq -r '.version')"

- name: Post-deploy smoke test
run: ./scripts/smoke-test-api.sh https://openboot.dev

- name: Post-deploy contract validation
run: |
pip install jsonschema
git clone --depth 1 https://github.com/openbootdotdev/openboot-contract.git /tmp/contract
SERVER_URL=https://openboot.dev /tmp/contract/golden-path/contract-smoke.sh
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
Web dashboard and install API for [OpenBoot](https://github.com/openbootdotdev/openboot).

[![CI](https://github.com/openbootdotdev/openboot.dev/actions/workflows/ci.yml/badge.svg)](https://github.com/openbootdotdev/openboot.dev/actions/workflows/ci.yml)
[![CD](https://github.com/openbootdotdev/openboot.dev/actions/workflows/deploy.yml/badge.svg)](https://github.com/openbootdotdev/openboot.dev/actions/workflows/deploy.yml)

**Live at [openboot.dev](https://openboot.dev)**

Expand Down Expand Up @@ -41,7 +42,7 @@ GOOGLE_CLIENT_SECRET=...

## Deployment

Push to `main` runs CI (type check + tests + build) and, on success, auto-deploys to [openboot.dev](https://openboot.dev). PRs run CI only. The deploy job lives in `.github/workflows/ci.yml`; see [docs/HARNESS.md](./docs/HARNESS.md) for the full pipeline.
Push to `main` runs CI (`ci.yml`: type check + tests + build + contract validation). On success, CD (`deploy.yml`) fires via `workflow_run` and ships to [openboot.dev](https://openboot.dev) (D1 migrations + wrangler deploy + health check + smoke test). PRs run CI only. See [docs/HARNESS.md](./docs/HARNESS.md) for the full pipeline.

Secrets needed: `CLOUDFLARE_API_TOKEN`, `CLOUDFLARE_ACCOUNT_ID`

Expand Down
11 changes: 6 additions & 5 deletions docs/HARNESS.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,9 @@ Three regulation categories:
| Behav. | `svelte-check` (TypeScript across `.ts` and `.svelte`) | `npm run check` / `.claude/hooks/stop.sh` / CI | `tsconfig.json` |
| Behav. | `vitest run` (unit + smoke) | `npm test` / pre-push / CI | `vitest.config.ts` |
| Behav. | `vitest --coverage` → Codecov (informational) | CI | `.github/workflows/ci.yml` |
| Behav. | Contract schema validation against `openboot-contract` | CI `check` job + post-deploy | `.github/workflows/ci.yml` |
| Behav. | Post-deploy health check (`/api/health`) | CI `deploy` job | `.github/workflows/ci.yml` |
| Behav. | Post-deploy smoke test + contract round-trip | CI `deploy` job | `scripts/smoke-test-api.sh` |
| Behav. | Contract schema validation against `openboot-contract` | CI `check` job + post-deploy | `.github/workflows/ci.yml`, `.github/workflows/deploy.yml` |
| Behav. | Post-deploy health check (`/api/health`) | CD `deploy` job | `.github/workflows/deploy.yml` |
| Behav. | Post-deploy smoke test + contract round-trip | CD `deploy` job | `scripts/smoke-test-api.sh` |
| Feedfwd. | Agent conventions | every AI turn | `CLAUDE.md`, `AGENTS.md` |
| Feedfwd. | Session-start hook (warm `svelte-kit sync`) | every Claude session | `.claude/hooks/session-start.sh` |
| Feedfwd. | `ship-pr` skill — push → CI → review → triage → squash → cleanup; **no `--auto`** | model-loaded | `.claude/skills/ship-pr/SKILL.md` |
Expand Down Expand Up @@ -106,8 +106,9 @@ ideally one rule per PR so the diff is reviewable:
up violations directly when a new rule is added.
- **No agent-driven changes to `main` without human review.** All AI
changes go through PR review and the existing CI matrix.
- **No auto-release / tag automation.** Push to `main` auto-deploys; there
is no separate release cadence to automate.
- **No auto-release / tag automation.** Push to `main` triggers `ci.yml`;
on success `deploy.yml` fires via `workflow_run` and ships to production.
There is no separate release cadence to automate.
- **No "stale baseline" sensor.** N/A while there are no baselines.

## How agents should think about this file
Expand Down
12 changes: 4 additions & 8 deletions src/docs/api-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,28 +50,23 @@ GET /api/configs
"configs": [
{
"id": "cfg_abc123",
"user_id": "usr_xyz",
"slug": "my-setup",
"name": "My Dev Setup",
"description": "Personal development environment",
"base_preset": "developer",
"packages": [{ "name": "node", "type": "formula" }],
"custom_script": "",
"dotfiles_repo": "",
"snapshot": null,
"snapshot_at": null,
"visibility": "unlisted",
"alias": null,
"install_count": 12,
"forked_from": null,
"created_at": "2024-01-15T10:30:00Z",
"updated_at": "2024-02-10T14:20:00Z"
}
]
}
```

`packages` and `snapshot` are returned as parsed JSON (not strings).
`packages` and `snapshot` are returned as parsed JSON (not strings). For the full row (including `custom_script`, `dotfiles_repo`, etc.), use `GET /api/configs/:slug`.

### Get Config (Dashboard)

Expand Down Expand Up @@ -289,7 +284,7 @@ Get the shell install script for a config. The CLI's `install.sh` curls this URL
GET /:username/:slug/install
```

**Auth required:** Only for `private` configs. Send a Bearer token belonging to the owner; otherwise the endpoint returns `403 Config is private` as plain text. (The browser-friendly auth flow for private configs is served via the curl-detection path at `/:username/:slug`, not here.)
**Auth required:** Only for `private` configs. Send a Bearer token belonging to the owner; otherwise the endpoint returns `403 Config is private` as plain text. (The page route `/:username/:slug` returns 404 for non-owners of a private config — there is no interactive auth prompt.)

**Response:** Shell script, `Content-Type: text/plain; charset=utf-8`.

Expand Down Expand Up @@ -461,7 +456,8 @@ GET /api/user
"user": {
"id": "usr_abc123",
"username": "johndoe",
"email": "john@example.com"
"email": "john@example.com",
"avatar_url": "https://avatars.githubusercontent.com/u/123?v=4"
}
}
```
Expand Down
Loading