Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 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
117 changes: 117 additions & 0 deletions .github/workflows/api-deployment.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
name: API Deployment

on:
pull_request:
types: [opened, synchronize, reopened]
branches:
- main
paths:
- "apps/api/**"
- "packages/**"
- "package.json"
- "pnpm-lock.yaml"
- "!**/*.md"
- "!**/README*"

concurrency:
group: preview-${{ github.event.pull_request.number }}-${{ github.sha }}
cancel-in-progress: false

env:
STAGE: ${{ github.event_name == 'pull_request' && format('pr-{0}', github.event.number) || (github.ref == 'refs/heads/main' && 'prod' || github.ref_name) }}

jobs:
deploy:
if: ${{ github.event.action != 'closed' }}
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write

steps:
- uses: actions/checkout@v6

- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: 22

- name: Setup pnpm
uses: pnpm/action-setup@v5
with:
version: "10.14.0"

- name: Cache pnpm store
uses: actions/cache@v5
with:
path: ~/.pnpm-store
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-

- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Deploy
working-directory: apps/api
run: pnpm dlx alchemy deploy --stage ${{ env.STAGE }}
env:
ALCHEMY_PASSWORD: ${{ secrets.ALCHEMY_PASSWORD }}
ALCHEMY_STATE_TOKEN: ${{ secrets.ALCHEMY_STATE_TOKEN }}
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_EMAIL: ${{ secrets.CLOUDFLARE_EMAIL }}
PULL_REQUEST: ${{ github.event.number }}
GITHUB_SHA: ${{ github.sha }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NODE_ENV: production

cleanup:
runs-on: ubuntu-latest
if: ${{ github.event_name == 'pull_request' && github.event.action == 'closed' }}
permissions:
id-token: write
contents: read
pull-requests: write

steps:
- uses: actions/checkout@v6

- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: 22

- name: Setup pnpm
uses: pnpm/action-setup@v5
with:
version: "10.14.0"

- name: Cache pnpm store
uses: actions/cache@v5
with:
path: ~/.pnpm-store
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-

- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Safety Check
run: |-
if [ "${{ env.STAGE }}" = "prod" ]; then
echo "ERROR: Cannot destroy prod environment in cleanup job"
exit 1
fi

- name: Destroy Preview Environment
working-directory: apps/api
run: pnpm dlx alchemy destroy --stage ${{ env.STAGE }}
env:
ALCHEMY_PASSWORD: ${{ secrets.ALCHEMY_PASSWORD }}
ALCHEMY_STATE_TOKEN: ${{ secrets.ALCHEMY_STATE_TOKEN }}
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
CLOUDFLARE_EMAIL: ${{ secrets.CLOUDFLARE_EMAIL }}
PULL_REQUEST: ${{ github.event.number }}
NODE_ENV: production
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -263,3 +263,6 @@ $RECYCLE.BIN/

# Wrangler
*.wrangler

# Alchemy
*.alchemy
3 changes: 3 additions & 0 deletions apps/api/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# openssl rand -base64 32
ALCHEMY_STATE_TOKEN=your-generated-token-here
ALCHEMY_PASSWORD=your-generated-password-here
58 changes: 58 additions & 0 deletions apps/api/alchemy.run.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import alchemy from "alchemy";
import { Worker } from "alchemy/cloudflare";
import { GitHubComment } from "alchemy/github";
import { CloudflareStateStore } from "alchemy/state";

const app = await alchemy("realm-api", {
stateStore:
process.env.NODE_ENV === "production"
? (scope) =>
new CloudflareStateStore(scope, {
scriptName: "realm-api-state-store",
})
: undefined, // Uses default FileSystemStateStore
password: process.env.ALCHEMY_PASSWORD,
});

export const worker = await Worker("api", {
name: "realm-api",
entrypoint: "./src/index.ts",
url: true,
adopt: true,
bindings: {},
observability: {
enabled: true,
logs: {
enabled: true,
headSamplingRate: 1,
invocationLogs: true,
persist: true,
},
traces: {
enabled: true,
headSamplingRate: 1,
persist: true,
},
},
domains: ["api.ahargunyllib.dev"],
dev: {
port: 3000,
},
});

if (process.env.PULL_REQUEST) {
// if this is a PR, add a comment to the PR with the preview URL
// it will auto-update with each push
await GitHubComment("preview-comment", {
owner: "ahargunyllib",
repository: "ahargunyllib/realm",
issueNumber: Number(process.env.PULL_REQUEST),
body: `### Preview Deployment

**Commit:** \`${process.env.COMMIT_SHA}\`
**Preview URL:** ${`https://${worker.url}`}
**Deployed at:** ${new Date().toUTCString()}`,
});
}

await app.finalize();
19 changes: 19 additions & 0 deletions apps/api/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"name": "api",
"type": "module",
"version": "0.0.1",
"scripts": {
"typecheck": "tsc --noEmit",
"dev": "alchemy dev --app realm-api",
"destroy": "NODE_ENV=production alchemy destroy --app realm-api",
"deploy": "NODE_ENV=production alchemy deploy --app realm-api"
},
"dependencies": {
"alchemy": "catalog:",
"hono": "^4.7.9"
},
"devDependencies": {
"@realm/tsconfig": "workspace:*",
"typescript": "catalog:"
}
}
3 changes: 3 additions & 0 deletions apps/api/src/env.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import type { worker } from "../alchemy.run";

export type Env = typeof worker.bindings;
File renamed without changes.
File renamed without changes.
21 changes: 0 additions & 21 deletions apps/server/package.json

This file was deleted.

Loading
Loading