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
16 changes: 6 additions & 10 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -1,12 +1,8 @@
#
# https://help.github.com/articles/dealing-with-line-endings/
#
# Linux start script should use lf
/gradlew text eol=lf
# Documentation: https://www.git-scm.com/docs/gitattributes

# These are Windows script files and should use crlf
*.bat text eol=crlf

# Binary files should be left untouched
*.jar binary
# Set default behavior to automatically normalize line endings.
* text=auto eol=lf

# Enforce CRLF line endings in Windows batch scripts to avoid issues.
*.cmd text eol=crlf
*.bat text eol=crlf
86 changes: 24 additions & 62 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,67 +1,29 @@
name: CI

on:
push:
branches: ["**"]
pull_request:
push:
branches: ["**"]
pull_request:

jobs:
checks:
name: Run checks
# Only run on PRs if the source branch is on someone else's repo
if: ${{ github.event_name != 'pull_request' || github.repository != github.event.pull_request.head.repo.full_name }}
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v7
- name: Set up JDK 25
uses: actions/setup-java@v5
with:
distribution: "temurin"
java-version: 25
check-latest: true
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v6
- name: Setup Bun
uses: oven-sh/setup-bun@v2
- name: Install frontend dependencies
run: cd web && bun install
- name: Lint frontend
run: cd web && bun run lint
- name: Check frontend
run: cd web && bun run check
- name: Build frontend
run: cd web && bun run build
- name: Build with Gradle
run: ./gradlew build --stacktrace

publish:
name: Publish container
needs: checks
if: ${{ needs.checks.result == 'success' && github.event_name == 'push' && github.ref == 'refs/heads/master' }}
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v7
- name: Set up JDK 25
uses: actions/setup-java@v5
with:
distribution: "temurin"
java-version: 25
check-latest: true
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v6
- name: Setup Bun
uses: oven-sh/setup-bun@v2
- name: Install frontend dependencies
run: cd web && bun install
- name: Build frontend
run: cd web && bun run build
- name: Login to GitHub Container Registry
uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and publish container
run: ./gradlew jib --stacktrace
checks:
name: Run checks
if: ${{ github.event_name != 'pull_request' || github.repository != github.event.pull_request.head.repo.full_name }}
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v7
- name: Setup Bun
uses: oven-sh/setup-bun@v2
- name: Install dependencies
run: bun install
- name: Check formatting
run: bun run format:check
- name: Lint
run: bun run lint
- name: Check
run: bun run check
- name: Build and test
run: bun run test
- name: Build deployment bundle
run: bunx wrangler deploy --config wrangler.jsonc --dry-run
25 changes: 0 additions & 25 deletions .github/workflows/gradle-dependency-submission.yml

This file was deleted.

30 changes: 17 additions & 13 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
# Ignore Gradle project-specific cache directory
.gradle
.idea/
.DS_Store

# Ignore Gradle build output directory
build
node_modules/
.output/
.vercel/
.netlify/
.wrangler/
/.svelte-kit/
/build/

.idea/
.kotlin/
.env
.env.*
!.env.example
!.env.test

database.mv.db
database.trace.db
db/
!src/main/resources/db/
!src/main/resources/db/migration/
!src/main/resources/db/migration/**
.DS_Store
vite.config.js.timestamp-*
vite.config.ts.timestamp-*

local-test-data/
File renamed without changes.
18 changes: 18 additions & 0 deletions .oxfmtrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"$schema": "./node_modules/oxfmt/configuration_schema.json",
"endOfLine": "lf",
"useTabs": false,
"tabWidth": 4,
"printWidth": 160,
"svelte": true,
"sortTailwindcss": {
"stylesheet": "./src/app.css"
},
"ignorePatterns": [
"bun.lock",
".agents/skills/kumo-design/**",
".agents/skills/svelte-code-writer/**",
".agents/skills/svelte-core-bestpractices/**",
"skills-lock.json"
]
}
19 changes: 19 additions & 0 deletions .oxlintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"$schema": "./node_modules/oxlint/configuration_schema.json",
"env": {
"browser": true,
"node": true
},
"categories": {
"correctness": "error"
},
"options": {
"typeAware": true
},
"ignorePatterns": [
".agents/skills/kumo-design/**",
".agents/skills/svelte-code-writer/**",
".agents/skills/svelte-core-bestpractices/**",
"skills-lock.json"
]
}
72 changes: 44 additions & 28 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,51 +1,67 @@
# Patch Roulette

REST API and web interface for managing Paper updates.
Patch Roulette manages Paper update work through a Cloudflare Worker, a Durable Object SQLite database, and a SvelteKit web interface.

> [!NOTE]
> This project is intended for internal use and does not guarantee stability, compatibility, support, or follow semantic versioning.
> [!NOTE]
> This project is intended for internal use and does not guarantee stability, compatibility, support, or semantic versioning.

## Overview
## Architecture

### REST API
- The Cloudflare Worker serves both the SvelteKit UI and the API.
- API routes are under `/api` and are used directly by browser and CLI clients; see [docs/api.md](docs/api.md).
- A single Durable Object owns the SQLite database and serializes patch claims.
- Drizzle ORM manages the Durable Object schema and migrations.
- Cloudflare Access protects every deployed hostname and every route, including `/api`.
- Managed OAuth supplies CLI authentication. The application has no custom API-token system.

Powered by Spring Boot, backend for the web interface and `paperweight`. Routes are under `/api`.
The Worker trusts Cloudflare Access as the authentication boundary. It extracts the `iss` and `sub` claims from `Cf-Access-Jwt-Assertion` and maps that external identity to an internal user. The deployed `workers.dev` or custom hostname must therefore be protected by the Access application; do not leave an alternate hostname unprotected.

### paperweight
## Development

`paperweight` has tasks to interface with the REST API during the update process.
Install the dependencies:

### Web Interface
```sh
bun install
```

SvelteKit frontend using Kumo Svelte and Tailwind CSS for styling. Hosted as static files by the Spring Boot server.
Run the Vite frontend and local Worker together:

#### Pages
```sh
bun run dev
```

- [`/`](https://patch-roulette.papermc.io/): Management dashboard
- [`/login`](https://patch-roulette.papermc.io/login) : Login page
The local Worker uses a fixed development identity and local Durable Object SQLite state. Wrangler's normal local persistence is left enabled. Reset it with:

## Development
```sh
bun run db:reset
```

### Setup
Generate Drizzle migrations after changing `src/lib/db/schema.ts`:

- Install [Bun](https://bun.sh/) and execute `bun install` in `/web` to install the required dependencies for the frontend.
- Install a JVM 25 or newer for the Gradle runtime (prefer a JDK to avoid extra downloads for a compiler).
```sh
bun run db:generate
```

### Running Locally
## Checks

- Run the frontend with `bun run devLocalServer` or `bun run devProdServer` in `/web`. `devLocalServer` will use localhost as the API, while `devProdServer` will use the production API at https://patch-roulette.papermc.io/api.
- Run the backend with `./gradlew bootRun` in the project root.
```sh
bun run check
bun run format:check
bun run lint
bun run test
bunx wrangler deploy --config wrangler.jsonc --dry-run
```

### Checks
## Migration

- Run the frontend checks with `bun run lint`, `bun run check`, and `bun run build` in `/web`.
- Run the backend checks and tests with `./gradlew build` in the project root.
The temporary Spring-to-Worker migration procedure is documented in [docs/legacy.md](docs/legacy.md).

### Code Style
## Deployment

- The frontend uses ESLint and Prettier for code style. Run `bun run format` to reformat and `bun run lint` to check style.
- The backend uses Immaculate with Palantir Java Format. Run `./gradlew immaculateApply` to reformat and `./gradlew immaculateCheck` to check style.
Build and deploy the Worker:

### Deployment
```sh
bun run deploy
```

- Published to the GitHub Container Registry after successful pushes to `master` through the `publish` job in the CI workflow.
Configure Cloudflare Access separately for the actual hostname used by the deployment. Protect the entire hostname, including `/api/*` and the Managed OAuth discovery endpoints. If both a `workers.dev` hostname and a custom hostname are reachable, protect both or disable the unused hostname.
Loading