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
56 changes: 56 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
name: CI

on:
pull_request:
branches: [main]
push:
branches: [main]

jobs:
test:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.12'
cache: 'pip'

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

- name: Install Python dependencies
run: |
pip install -r requirements.txt
pip install -e .

- name: Install frontend dependencies
run: cd frontend && npm ci

- name: Run Python tests
run: pytest --tb=short
continue-on-error: true

- name: Run frontend tests
run: cd frontend && npm test -- --run
continue-on-error: true

- name: Build frontend
run: cd frontend && npm run build

- name: Check build artifacts
run: |
if [ -d "kernelboard/static/app" ]; then
echo "✅ Frontend build successful"
ls -la kernelboard/static/app
else
echo "❌ Frontend build failed - no output directory"
exit 1
fi
52 changes: 52 additions & 0 deletions .github/workflows/review-app-comment.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
name: Comment Review App URL

on:
pull_request:
types: [opened, synchronize]

permissions:
pull-requests: write
issues: write

jobs:
comment:
runs-on: ubuntu-latest
steps:
- name: Wait for Heroku deployment
run: sleep 30

- name: Comment PR with Review App URL
uses: actions/github-script@v7
with:
script: |
const prNumber = context.payload.pull_request.number;

const comments = await github.rest.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: prNumber,
});

const botComment = comments.data.find(c =>
c.body.includes('Review App Preview')
);

const body = `## 🔍 Review App Preview

A Heroku Review App is being deployed for this PR.

**View your preview:** Check the [Heroku Pipeline](https://dashboard.heroku.com/pipelines/kernelboard) for the review app URL.

> The review app may take a few minutes to build after pushing.`;

if (botComment) {
// Don't update if already commented
return;
}

await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: prNumber,
body: body
});
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -223,3 +223,9 @@ and pass the url to your .env file:
DISCORD_CLUSTER_MANAGER_API_BASE_URL=http://localhost:8080
```
Please notice, you need to make sure both of them connects to same db instance.

## PR Preview Deployments

This project supports automatic PR preview deployments via Heroku Review Apps. When you open a PR, a preview environment is automatically created.

For setup instructions, see [docs/review-apps-setup.md](docs/review-apps-setup.md).
71 changes: 71 additions & 0 deletions app.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
{
"name": "kernelboard",
"description": "Kernelboard - GPU kernel development platform",
"repository": "https://github.com/your-org/kernelboard",
"formation": {
"web": {
"quantity": 1,
"size": "basic"
}
},
"addons": [
{
"plan": "heroku-postgresql:essential-0"
},
{
"plan": "heroku-redis:mini"
}
],
"buildpacks": [
{
"url": "heroku/nodejs"
},
{
"url": "heroku/python"
}
],
"env": {
"SECRET_KEY": {
"generator": "secret"
},
"FLASK_DEBUG": {
"value": "false"
},
"DISCORD_BOT_TOKEN": {
"required": false,
"description": "Discord bot token for fetching scheduled events (optional for previews)"
},
"DISCORD_GUILD_ID": {
"required": false,
"description": "Discord server/guild ID (optional for previews)"
},
"DISCORD_CLIENT_ID": {
"required": false,
"description": "Discord OAuth2 client ID (optional for previews)"
},
"DISCORD_CLIENT_SECRET": {
"required": false,
"description": "Discord OAuth2 client secret (optional for previews)"
},
"DISCORD_CLUSTER_MANAGER_API_BASE_URL": {
"required": false,
"description": "GPU cluster manager API URL"
}
},
"environments": {
"review": {
"addons": [
"heroku-postgresql:essential-0",
"heroku-redis:mini"
],
"env": {
"FLASK_DEBUG": {
"value": "true"
}
}
}
},
"scripts": {
"postdeploy": "echo 'Review app deployed successfully'"
}
}
76 changes: 76 additions & 0 deletions docs/review-apps-setup.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# Heroku Review Apps Setup

This document describes how to set up Heroku Review Apps for PR preview deployments.

## Prerequisites

- Heroku CLI installed (`brew tap heroku/brew && brew install heroku`)
- Access to the Heroku team (`kernelbot`)
- Admin access to the GitHub repository

## Setup Steps

### 1. Create a Heroku Pipeline

```bash
heroku pipelines:create kernelboard --app kernelboard --stage production --team kernelbot
```

### 2. Connect GitHub

1. Go to the [Heroku Dashboard](https://dashboard.heroku.com)
2. Navigate to your pipeline
3. Click "Connect to GitHub" and authorize the repository

### 3. Enable Review Apps

1. In the pipeline view, click "Enable Review Apps"
2. Check "Create new review apps for new pull requests automatically"
3. Check "Destroy stale review apps automatically" (recommended: after 5 days)

```bash
heroku reviewapps:enable --pipeline kernelboard --autodeploy --autodestroy
```

### 4. Configure Environment Variables

Review apps auto-provision:
- `DATABASE_URL` (from heroku-postgresql add-on)
- `REDIS_URL` (from heroku-redis add-on)
- `SECRET_KEY` (auto-generated in code)

Discord environment variables are optional for previews. The app will run without them, but Discord login and scheduled events won't work.

If you want full functionality, inherit these from production:
- `DISCORD_BOT_TOKEN`
- `DISCORD_GUILD_ID`
- `DISCORD_CLIENT_ID`
- `DISCORD_CLIENT_SECRET`
- `DISCORD_CLUSTER_MANAGER_API_BASE_URL`

## How It Works

1. Create a PR against `main`
2. Heroku automatically builds and deploys a preview
3. A comment with a link to the pipeline appears on the PR
4. The preview updates on every push to the PR
5. The preview is destroyed when the PR is closed/merged

## Troubleshooting

### Review app crashes on startup

Check the logs:
```bash
heroku logs --tail --app <review-app-name>
```

Common issues:
- Missing `DATABASE_URL` or `REDIS_URL` - check that add-ons were provisioned
- The `app.json` file must be present in the repository root

### Review apps not being created

1. Verify GitHub is connected: Check pipeline settings in Heroku Dashboard
2. Verify review apps are enabled: `heroku reviewapps --pipeline kernelboard`
3. Check that `app.json` exists in the repository root
22 changes: 18 additions & 4 deletions kernelboard/lib/env.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,34 @@
import os
import secrets


def check_env_vars():
"""
Check that required environment variables are set. If they are not set,
print a message and exit.

Core infrastructure vars (DATABASE_URL, REDIS_URL) are always required.
Other vars are optional for preview/review app deployments.
"""

# Core infrastructure - always required
required_env_vars = [
"DATABASE_URL",
"DISCORD_CLIENT_ID",
"DISCORD_CLIENT_SECRET",
"REDIS_URL",
"SECRET_KEY",
"DISCORD_CLUSTER_MANAGER_API_BASE_URL",
]

# Optional for preview deployments - set defaults if not provided
optional_with_defaults = {
"SECRET_KEY": secrets.token_hex(32),
"DISCORD_CLIENT_ID": "preview-disabled",
"DISCORD_CLIENT_SECRET": "preview-disabled",
"DISCORD_CLUSTER_MANAGER_API_BASE_URL": "http://localhost:8080",
}

for var, default in optional_with_defaults.items():
if os.getenv(var) is None:
os.environ[var] = default

missing_env_vars = [var for var in required_env_vars if os.getenv(var) is None]

if missing_env_vars:
Expand Down
Loading