Skip to content
Open
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
10 changes: 10 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
node_modules
build
.git
.github
.pi
.pi-lens
tmp
.env
app/generated/prisma
remix.init/node_modules
5 changes: 4 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
LITEFS_DIR="/litefs/data"
# DATABASE_PATH is a filesystem path used by LiteFS/sqlite3 helpers.
DATABASE_PATH="./prisma/data.db"
DATABASE_URL="file:./data.db?connection_limit=1"
# DATABASE_URL is Prisma's SQLite connection string and must keep the file: prefix.
# Under Prisma 7 config, keep it rooted at the repo so it resolves to prisma/data.db.
DATABASE_URL="file:./prisma/data.db?connection_limit=1"
CACHE_DATABASE_PATH="./other/cache.db"
SESSION_SECRET="super-duper-s3cret"
HONEYPOT_SECRET="super-duper-s3cret"
Expand Down
69 changes: 47 additions & 22 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,22 +22,28 @@ jobs:
- name: ⬇️ Checkout repo
uses: actions/checkout@v4

- name: ⎔ Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 11.1.2

- name: ⎔ Setup node
uses: actions/setup-node@v4
with:
node-version: 22
cache: pnpm

- name: 📥 Download deps
uses: bahmutov/npm-install@v1
- name: 📥 Install deps
run: pnpm install --frozen-lockfile

- name: 🏄 Copy test env vars
run: cp .env.example .env

- name: 🛠 Setup Database
run: npx prisma migrate deploy && npx prisma generate --sql
run: pnpm exec prisma migrate deploy && pnpm exec prisma generate --sql

- name: 🔬 Lint
run: npm run lint
run: pnpm run lint

typecheck:
name: ʦ TypeScript
Expand All @@ -46,25 +52,31 @@ jobs:
- name: ⬇️ Checkout repo
uses: actions/checkout@v4

- name: ⎔ Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 11.1.2

- name: ⎔ Setup node
uses: actions/setup-node@v4
with:
node-version: 22
cache: pnpm

- name: 📥 Download deps
uses: bahmutov/npm-install@v1

- name: 🏗 Build
run: npm run build
- name: 📥 Install deps
run: pnpm install --frozen-lockfile

- name: 🏄 Copy test env vars
run: cp .env.example .env

- name: 🛠 Setup Database
run: npx prisma migrate deploy && npx prisma generate --sql
run: pnpm exec prisma migrate deploy && pnpm exec prisma generate --sql

- name: 🏗 Build
run: pnpm run build

- name: 🔎 Type check
run: npm run typecheck --if-present
run: pnpm run typecheck

vitest:
name: ⚡ Vitest
Expand All @@ -73,22 +85,28 @@ jobs:
- name: ⬇️ Checkout repo
uses: actions/checkout@v4

- name: ⎔ Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 11.1.2

- name: ⎔ Setup node
uses: actions/setup-node@v4
with:
node-version: 22
cache: pnpm

- name: 📥 Download deps
uses: bahmutov/npm-install@v1
- name: 📥 Install deps
run: pnpm install --frozen-lockfile

- name: 🏄 Copy test env vars
run: cp .env.example .env

- name: 🛠 Setup Database
run: npx prisma migrate deploy && npx prisma generate --sql
run: pnpm exec prisma migrate deploy && pnpm exec prisma generate --sql

- name: ⚡ Run vitest
run: npm run test -- --coverage
run: pnpm run test -- --coverage

playwright:
name: 🎭 Playwright
Expand All @@ -101,19 +119,25 @@ jobs:
- name: 🏄 Copy test env vars
run: cp .env.example .env

- name: ⎔ Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 11.1.2

- name: ⎔ Setup node
uses: actions/setup-node@v4
with:
node-version: 22
cache: pnpm

- name: 📥 Download deps
uses: bahmutov/npm-install@v1
- name: 📥 Install deps
run: pnpm install --frozen-lockfile

- name: 📥 Install Playwright Browsers
run: npm run test:e2e:install
run: pnpm run test:e2e:install

- name: 🛠 Setup Database
run: npx prisma migrate deploy && npx prisma generate --sql
run: pnpm exec prisma migrate deploy && pnpm exec prisma generate --sql

- name: 🏦 Cache Database
id: db-cache
Expand All @@ -123,17 +147,18 @@ jobs:
key:
db-cache-schema_${{ hashFiles('./prisma/schema.prisma')
}}-migrations_${{ hashFiles('./prisma/migrations/*/migration.sql')
}}-seed_${{ hashFiles('./prisma/seed.ts', './prisma.config.ts')
}}

- name: 🌱 Seed Database
if: steps.db-cache.outputs.cache-hit != 'true'
run: npx prisma migrate reset --force
run: pnpm exec prisma migrate reset --force

- name: 🏗 Build
run: npm run build
run: pnpm run build

- name: 🎭 Playwright tests
run: npx playwright test
run: pnpm exec playwright test

- name: 📊 Upload report
uses: actions/upload-artifact@v4
Expand Down
12 changes: 12 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,16 @@ node_modules

# generated files
/app/components/ui/icons
/app/generated/prisma
.react-router/
# Local Pi runtime state
.atl/
.pi/
.pi-lens/

# Local agent/workflow scratch artifacts
/tmp/
/false

# Local generated declarations from JS tooling
/scripts/*.d.ts
1 change: 0 additions & 1 deletion .npmrc
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
legacy-peer-deps=true
registry=https://registry.npmjs.org/
30 changes: 19 additions & 11 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,26 @@ instructions:

### System Requirements

- [Node.js](https://nodejs.org/) >= 20.0.0
- [npm](https://npmjs.com/) >= 8.18.0
- [Node.js](https://nodejs.org/) ^22.18.0
- [pnpm](https://pnpm.io/) 11.1.2 (via Corepack recommended)
- [git](https://git-scm.com/) >= 2.38.0

### Setup steps

1. Fork repo
2. clone the repo
3. Copy `.env.example` into `.env`
4. Run `npm install && npm run setup -s` to install dependencies and run
validation
4. Run `corepack enable && pnpm install && pnpm run setup -s` to install
dependencies and set up the local database, Prisma client, and Playwright
browsers
5. Create a branch for your PR with `git checkout -b pr/your-branch-name`

> Important: `DATABASE_PATH` and `DATABASE_URL` are not interchangeable.
> `DATABASE_PATH` is the sqlite/LiteFS filesystem path, while Prisma requires
> `DATABASE_URL` to stay a SQLite URL that starts with `file:`. If you export a
> global `DATABASE_URL` in your shell, the setup script now prefers the value in
> the project's `.env` file.

> Tip: Keep your `main` branch pointing at the original repository and make pull
> requests from branches on your fork. To do this, run:
>
Expand Down Expand Up @@ -50,29 +57,30 @@ cd ./epic-stack
cp .env.example .env

# Install deps
npm install
corepack enable
pnpm install

# Run the build
npm run build
pnpm run build

# setup the database
npx prisma migrate deploy
pnpm exec prisma migrate deploy

# generate the prisma client
npx prisma generate --sql
pnpm exec prisma generate --sql

# Install playwright browsers
npm run test:e2e:install
pnpm run test:e2e:install

# run build, typecheck, linting
npm run validate
pnpm run validate
```

If that all worked without trouble, you should be able to start development
with:

```sh
npm run dev
pnpm run dev
```

And open up `http://localhost:3000` and rock!
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
</div>

```sh
npx epicli
pnpm dlx epicli
```

[![The Epic Stack](https://github-production-user-asset-6210df.s3.amazonaws.com/1500684/246885449-1b00286c-aa3d-44b2-9ef2-04f694eb3592.png)](https://www.epicweb.dev/epic-stack)
Expand Down
52 changes: 16 additions & 36 deletions app/routes/_auth/auth.$provider/callback.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,14 @@ import { loader } from './callback.ts'

const ROUTE_PATH = '/auth/github/callback'
const PARAMS = { provider: 'github' }
const LOADER_ARGS_BASE = {
params: PARAMS,
context: {} as AppLoadContext,
unstable_pattern: ROUTE_PATH,
function getLoaderArgs(request: Request) {
return {
request,
params: PARAMS,
context: {} as AppLoadContext,
pattern: ROUTE_PATH,
url: new URL(request.url),
}
}

afterEach(async () => {
Expand All @@ -31,10 +35,7 @@ afterEach(async () => {

test('a new user goes to onboarding', async () => {
const request = await setupRequest()
const response = await loader({
request,
...LOADER_ARGS_BASE,
}).catch((e) => e)
const response = await loader(getLoaderArgs(request)).catch((e) => e)
expect(response).toHaveRedirect('/onboarding/github')
})

Expand All @@ -46,10 +47,7 @@ test('when auth fails, send the user to login with a toast', async () => {
}),
)
const request = await setupRequest()
const response = await loader({
request,
...LOADER_ARGS_BASE,
}).catch((e) => e)
const response = await loader(getLoaderArgs(request)).catch((e) => e)
invariant(response instanceof Response, 'response should be a Response')
expect(response).toHaveRedirect('/login')
await expect(response).toSendToast(
Expand All @@ -68,10 +66,7 @@ test('when a user is logged in, it creates the connection', async () => {
sessionId: session.id,
code: githubUser.code,
})
const response = await loader({
request,
...LOADER_ARGS_BASE,
})
const response = await loader(getLoaderArgs(request))
expect(response).toHaveRedirect('/settings/profile/connections')
await expect(response).toSendToast(
expect.objectContaining({
Expand Down Expand Up @@ -107,10 +102,7 @@ test(`when a user is logged in and has already connected, it doesn't do anything
sessionId: session.id,
code: githubUser.code,
})
const response = await loader({
request,
...LOADER_ARGS_BASE,
})
const response = await loader(getLoaderArgs(request))
expect(response).toHaveRedirect('/settings/profile/connections')
await expect(response).toSendToast(
expect.objectContaining({
Expand All @@ -125,10 +117,7 @@ test('when a user exists with the same email, create connection and make session
const email = githubUser.primaryEmail.toLowerCase()
const { userId } = await setupUser({ ...createUser(), email })
const request = await setupRequest({ code: githubUser.code })
const response = await loader({
request,
...LOADER_ARGS_BASE,
})
const response = await loader(getLoaderArgs(request))

expect(response).toHaveRedirect('/')

Expand Down Expand Up @@ -172,10 +161,7 @@ test('gives an error if the account is already connected to another user', async
sessionId: session.id,
code: githubUser.code,
})
const response = await loader({
request,
...LOADER_ARGS_BASE,
})
const response = await loader(getLoaderArgs(request))
expect(response).toHaveRedirect('/settings/profile/connections')
await expect(response).toSendToast(
expect.objectContaining({
Expand All @@ -198,10 +184,7 @@ test('if a user is not logged in, but the connection exists, make a session', as
},
})
const request = await setupRequest({ code: githubUser.code })
const response = await loader({
request,
...LOADER_ARGS_BASE,
})
const response = await loader(getLoaderArgs(request))
expect(response).toHaveRedirect('/')
await expect(response).toHaveSessionForUser(userId)
})
Expand All @@ -225,10 +208,7 @@ test('if a user is not logged in, but the connection exists and they have enable
},
})
const request = await setupRequest({ code: githubUser.code })
const response = await loader({
request,
...LOADER_ARGS_BASE,
})
const response = await loader(getLoaderArgs(request))
const searchParams = new URLSearchParams({
type: twoFAVerificationType,
target: userId,
Expand Down
Loading
Loading