Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

examples: add public deployment of LinearLite #1929

Merged
merged 38 commits into from
Nov 15, 2024
Merged
Show file tree
Hide file tree
Changes from 30 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
7f0b44e
examples: add public deployment of LinearLite
KyleAMathews Nov 1, 2024
b5eb8b7
Ignore .sst dir
robacourt Nov 4, 2024
366684a
Updated dependencies; lint
balegas Nov 5, 2024
2820dee
CI deploy
balegas Nov 6, 2024
aa0fca7
Changed job names; updated pnpm-lock
balegas Nov 6, 2024
6ea7568
Set working dir
balegas Nov 6, 2024
1c7fed1
Removed tini
balegas Nov 6, 2024
9c3c9a4
build deps
balegas Nov 6, 2024
cf2e819
Attempt refresh - (now I understand all your commit messages Kyle) :D
balegas Nov 6, 2024
7eb1a1b
with remove
balegas Nov 6, 2024
3d3793c
env missing
balegas Nov 6, 2024
1bdb044
Try again
balegas Nov 6, 2024
bd907e6
logs
balegas Nov 6, 2024
c0727fb
cleared persisted state
balegas Nov 7, 2024
4107784
printlogs again
balegas Nov 7, 2024
db69979
refresh
balegas Nov 7, 2024
561bd6d
upgrade sst
KyleAMathews Nov 7, 2024
79efa3a
less
KyleAMathews Nov 7, 2024
2415a16
Random fix
KyleAMathews Nov 7, 2024
f720944
no -example, it's cleaner
KyleAMathews Nov 7, 2024
88a8207
Catch errors so it doesn't break deployment
balegas Nov 7, 2024
06601e8
Making action generic for all examples
balegas Nov 7, 2024
256f5ad
Trying to be smart
balegas Nov 7, 2024
b21179c
Added nextjs example
balegas Nov 9, 2024
397af11
fix indentation
balegas Nov 13, 2024
82471a3
a few more corrections
balegas Nov 13, 2024
d7c7356
redeploy
balegas Nov 13, 2024
6d12488
Whats in there?
balegas Nov 13, 2024
b06f8b1
Missing piece
balegas Nov 13, 2024
1f68c74
Added teardown examples stack
balegas Nov 13, 2024
8df6331
Merge remote-tracking branch 'origin/main' into balegas/sst-linearlite
KyleAMathews Nov 15, 2024
43fe5e3
Use token parameter instead of authorization header
KyleAMathews Nov 15, 2024
67a5832
Add comment with links to deployed examples
KyleAMathews Nov 15, 2024
688ff1d
prettier
KyleAMathews Nov 15, 2024
0fc164b
Fix
KyleAMathews Nov 15, 2024
23a54d3
always use stylecheck not lint
KyleAMathews Nov 15, 2024
4031da7
commit .sst/platform/config.d.ts so typecheck works
KyleAMathews Nov 15, 2024
1841bac
Fix examples
KyleAMathews Nov 15, 2024
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
57 changes: 57 additions & 0 deletions .github/workflows/deploy_examples.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
name: Deploy Examples

on:
push:
branches: ['main']
pull_request:
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Create a pr deployment when one of the examples is modified. Because we need to teardown the PR stack and it breaks with Neon, maybe we comment this out for now?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah good call — too much overhead right now — we can run a site locally for a PR if we want to test it.

paths: ['examples/*/**']

concurrency:
group: ${{ github.event_name == 'push' && 'prod-deploy-group' || format('examples-pr-{0}', github.event.number) }}

jobs:
deploy-examples:
name: Deploy Examples
environment: ${{ github.event_name == 'push' && 'Production' || 'Pull request' }}
runs-on: ubuntu-latest

env:
DEPLOY_ENV: ${{ github.event_name == 'push' && 'production' || format('pr-{0}', github.event.number) }}
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_DEFAULT_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_DEFAULT_ACCOUNT_ID }}
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
NEON_API_KEY: ${{ secrets.NEON_API_KEY }}
NEON_PROJECT_ID: ${{ secrets.NEON_PROJECT_ID }}
ELECTRIC_API: ${{ secrets.ELECTRIC_API }}
ELECTRIC_ADMIN_API: ${{ secrets.ELECTRIC_ADMIN_API }}
# HONEYCOMB_API_KEY: ${{ secrets.HONEYCOMB_API_KEY }} TODO

steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
with:
version: 9
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'pnpm'

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

- name: Cache SST state
uses: actions/cache@v4
with:
path: .sst
key: sst-cache-main-${{ runner.os }}
restore-keys: |
sst-cache-main-${{ runner.os }}

- name: Deploy Linearlite
working-directory: examples/linearlite
run: pnpm sst deploy --stage ${{ env.DEPLOY_ENV }}

- name: Deploy NextJs example
working-directory: examples/nextjs-example
run: pnpm sst deploy --stage ${{ env.DEPLOY_ENV }}
62 changes: 62 additions & 0 deletions .github/workflows/teardown_examples_pr_stack.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
name: Teardown Examples PR stack

on:
pull_request:
paths: ['examples/*/**']
types: [closed]

concurrency:
group: examples-pr-${{ github.event.number }}

jobs:
teardown-pr-stack:
name: Teardown Examples PR stack
environment: Pull request
runs-on: ubuntu-latest

env:
DEPLOY_ENV: ${{ github.event_name == 'push' && 'production' || format('pr-{0}', github.event.number) }}
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_DEFAULT_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_DEFAULT_ACCOUNT_ID }}
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
NEON_API_KEY: ${{ secrets.NEON_API_KEY }}
NEON_PROJECT_ID: ${{ secrets.NEON_PROJECT_ID }}
ELECTRIC_API: ${{ secrets.ELECTRIC_API }}
ELECTRIC_ADMIN_API: ${{ secrets.ELECTRIC_ADMIN_API }}
# HONEYCOMB_API_KEY: ${{ secrets.HONEYCOMB_API_KEY }} TODO

steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
with:
version: 9
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'pnpm'

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

- name: Cache SST state
uses: actions/cache@v4
with:
path: .sst
key: sst-cache-${{ github.event.number }}-${{ runner.os }}
restore-keys: |
sst-cache-${{ runner.os }}

- name: Remove Linearlite
working-directory: examples/linearlite
run: |
export PR_NUMBER=${{ github.event.number }}
echo "Removing stage pr-$PR_NUMBER"
pnpm sst remove --stage "pr-$PR_NUMBER"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use $DEPLOY_ENV here to be consistent with other workflow?


- name: Remove NextJs example
working-directory: examples/nextjs-example
run: |
export PR_NUMBER=${{ github.event.number }}
echo "Removing stage pr-$PR_NUMBER"
pnpm sst remove --stage "pr-$PR_NUMBER"
3 changes: 2 additions & 1 deletion examples/linearlite/.gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
dist
.env.local
db/data/
db/data/
.sst/
15 changes: 8 additions & 7 deletions examples/linearlite/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,19 @@
"private": true,
"type": "module",
"scripts": {
"backend:up": "PROJECT_NAME=linearlite pnpm -C ../../ run example-backend:up && pnpm db:migrate && pnpm db:load-data",
"backend:down": "PROJECT_NAME=linearlite pnpm -C ../../ run example-backend:down",
"db:migrate": "dotenv -e ../../.env.dev -- pnpm exec pg-migrations apply --directory ./db/migrations",
"backend:up": "PROJECT_NAME=linearlite pnpm -C ../../ run example-backend:up && pnpm db:migrate && pnpm db:load-data",
"build": "vite build",
"db:load-data": "dotenv -e ../../.env.dev -- node ./db/load_data.js",
"db:migrate": "dotenv -e ../../.env.dev -- pnpm exec pg-migrations apply --directory ./db/migrations",
"dev": "vite",
"build": "vite build",
"stylecheck": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
"preview": "vite preview",
"typecheck": "tsc --noEmit",
"process-data": "node ./db/process_data.js"
"process-data": "node ./db/process_data.js",
"stylecheck": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
"typecheck": "tsc --noEmit"
},
"dependencies": {
"@electric-sql/client": "workspace:*",
"@electric-sql/react": "workspace:*",
"@firefox-devtools/react-contextmenu": "^5.1.1",
"@headlessui/react": "^1.7.17",
Expand All @@ -35,7 +36,6 @@
"classnames": "^2.5.1",
"dayjs": "^1.11.11",
"dotenv": "^16.4.5",
"@electric-sql/client": "workspace:*",
"fractional-indexing": "^3.2.0",
"jsonwebtoken": "^9.0.2",
"lodash.debounce": "^4.0.8",
Expand Down Expand Up @@ -79,6 +79,7 @@
"eslint-plugin-react-refresh": "^0.4.3",
"fs-extra": "^10.0.0",
"postcss": "^8.4.39",
"sst": "3.3.7",
"tailwindcss": "^3.4.4",
"typescript": "^5.5.3",
"vite": "^4.4.5"
Expand Down
6 changes: 5 additions & 1 deletion examples/linearlite/src/electric.tsx
Original file line number Diff line number Diff line change
@@ -1 +1,5 @@
export const baseUrl = import.meta.env.ELECTRIC_URL ?? `http://localhost:3000`
export const baseUrl = import.meta.env.VITE_ELECTRIC_URL
? new URL(import.meta.env.VITE_ELECTRIC_URL).origin
: `http://localhost:3000`
export const token = import.meta.env.VITE_ELECTRIC_TOKEN ?? ``
export const databaseId = import.meta.env.VITE_DATABASE_ID ?? ``
6 changes: 5 additions & 1 deletion examples/linearlite/src/pages/Issue/Comments.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { formatDate } from '../../utils/date'
import { showWarning } from '../../utils/notification'
import { Comment, Issue } from '../../types/types'
import { useShape } from '@electric-sql/react'
import { baseUrl } from '../../electric'
import { baseUrl, databaseId, token } from '../../electric'

export interface CommentsProps {
issue: Issue
Expand All @@ -19,6 +19,10 @@ function Comments(commentProps: CommentsProps) {
const allComments = useShape({
url: `${baseUrl}/v1/shape`,
table: `comment`,
databaseId,
headers: {
Authorization: `Bearer ${token}`,
},
})! as Comment[]

const comments = allComments.data.filter(
Expand Down
11 changes: 8 additions & 3 deletions examples/linearlite/src/shapes.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import { baseUrl } from './electric'
import { ShapeStreamOptions } from '@electric-sql/client'
import { baseUrl, databaseId, token } from './electric'

export const issueShape = {
url: `${baseUrl}/v1/shape`,
export const issueShape: ShapeStreamOptions = {
url: `${baseUrl}/v1/shape/`,
table: `issue`,
databaseId,
headers: {
Authorization: `Bearer ${token}`,
},
}
12 changes: 12 additions & 0 deletions examples/linearlite/src/sst-env.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/* This file is auto-generated by SST. Do not edit. */
/* tslint:disable */
/* eslint-disable */
/// <reference types="vite/client" />
interface ImportMetaEnv {
readonly VITE_ELECTRIC_URL: string
readonly VITE_ELECTRIC_TOKEN: string
readonly VITE_DATABASE_ID: string
}
interface ImportMeta {
readonly env: ImportMetaEnv
}
10 changes: 10 additions & 0 deletions examples/linearlite/sst-env.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/* This file is auto-generated by SST. Do not edit. */
/* tslint:disable */
/* eslint-disable */
/* deno-fmt-ignore-file */
import "sst"
export {}
declare module "sst" {
export interface Resource {
}
}
117 changes: 117 additions & 0 deletions examples/linearlite/sst.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
// eslint-disable-next-line @typescript-eslint/triple-slash-reference
/// <reference path="./.sst/platform/config.d.ts" />

import { execSync } from 'child_process'

export default $config({
app(input) {
return {
name: `linearlite`,
removal: input?.stage === `production` ? `retain` : `remove`,
home: `aws`,
providers: {
cloudflare: `5.42.0`,
aws: {
version: `6.57.0`,
},
neon: `0.6.3`,
},
}
},
async run() {
const project = neon.getProjectOutput({ id: process.env.NEON_PROJECT_ID! })
const base = {
projectId: project.id,
branchId: project.defaultBranchId,
}

const db = new neon.Database(`linearlite`, {
...base,
ownerName: `neondb_owner`,
})

const databaseUri = getNeonDbUri(project, db)
try {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to be defensive? I actually haven't tested the exception path because everything is working now.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah I'd say so until we have our own Provider that handles errors correctly

databaseUri.apply(applyMigrations)

const electricInfo = databaseUri.apply((uri) =>
addDatabaseToElectric(uri)
)

const website = deployLinearLite(electricInfo)
return {
databaseUri,
database_id: electricInfo.id,
electric_token: electricInfo.token,
website: website.url,
}
} catch (e) {
console.error('Failed to deploy linearlite stack', e)

Check failure on line 49 in examples/linearlite/sst.config.ts

View workflow job for this annotation

GitHub Actions / Check and build examples/linearlite

Strings must use backtick
}
}

Check failure on line 51 in examples/linearlite/sst.config.ts

View workflow job for this annotation

GitHub Actions / Check and build examples/linearlite

Insert `,`
})

function applyMigrations(uri: string) {
execSync(`pnpm exec pg-migrations apply --directory ./db/migrations`, {
env: {
...process.env,
DATABASE_URL: uri,
},
})
}

function deployLinearLite(
electricInfo: $util.Output<{ id: string; token: string }>
) {
return new sst.aws.StaticSite(`linearlite`, {
environment: {
VITE_ELECTRIC_URL: process.env.ELECTRIC_API!,
VITE_ELECTRIC_TOKEN: electricInfo.token,
VITE_DATABASE_ID: electricInfo.id,
},
build: {
command: `pnpm run --filter @electric-sql/client --filter @electric-sql/react --filter @electric-examples/linearlite build`,
output: `dist`,
},
domain: {
name: `linearlite${$app.stage === `production` ? `` : `-stage-${$app.stage}`}.electric-sql.com`,
dns: sst.cloudflare.dns(),
},
})
}

function getNeonDbUri(
project: $util.Output<neon.GetProjectResult>,
db: neon.Database
) {
const passwordOutput = neon.getBranchRolePasswordOutput({
projectId: project.id,
branchId: project.defaultBranchId,
roleName: db.ownerName,
})

return $interpolate`postgresql://${passwordOutput.roleName}:${passwordOutput.password}@${project.databaseHost}/${db.name}?sslmode=require`
}

async function addDatabaseToElectric(
uri: string
): Promise<{ id: string; token: string }> {
const adminApi = process.env.ELECTRIC_ADMIN_API

const result = await fetch(`${adminApi}/v1/databases`, {
method: `PUT`,
headers: { 'Content-Type': `application/json` },
body: JSON.stringify({
database_url: uri,
region: `us-east-1`,
}),
})

if (!result.ok) {
throw new Error(
`Could not add database to Electric (${result.status}): ${await result.text()}`
)
}

return await result.json()
}
6 changes: 1 addition & 5 deletions examples/nextjs-example/app/db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,7 @@ import pgPkg from "pg"
const { Client } = pgPkg

const db = new Client({
host: `localhost`,
port: 54321,
password: `password`,
user: `postgres`,
database: `electric`,
connectionString: process.env.DATABASE_URL,
})

db.connect()
Expand Down
Loading
Loading