Skip to content

Commit de17155

Browse files
committed
feat(cli): pre-create .env files in scaffold + run-locally docs (0.1.2)
Copies each app's .env.example -> .env (gitignored) with a generated dev BETTER_AUTH_SECRET, so scaffolded projects run locally with no env hand-editing. Generated README gains a Run-locally section (Docker + Encore CLI prereqs). Renamed env.d.ts -> tiged.d.ts to free env.ts. Bump 0.1.1 -> 0.1.2.
1 parent a97fb8d commit de17155

5 files changed

Lines changed: 65 additions & 2 deletions

File tree

tools/create-rhitta/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "create-rhitta",
3-
"version": "0.1.1",
3+
"version": "0.1.2",
44
"description": "Scaffold a new Rhitta monorepo project.",
55
"license": "MIT",
66
"type": "module",

tools/create-rhitta/src/env.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
/**
2+
* Produce a ready-to-use `.env` body from a committed `.env.example`, filling in
3+
* the dev-time `BETTER_AUTH_SECRET` with a generated value. Examples without that
4+
* line (e.g. the web app's) pass through unchanged.
5+
*/
6+
export function fillEnvFromExample(example: string, betterAuthSecret: string): string {
7+
return example.replace(/^BETTER_AUTH_SECRET=.*$/m, `BETTER_AUTH_SECRET=${betterAuthSecret}`)
8+
}

tools/create-rhitta/src/post.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
11
import { execFileSync } from 'node:child_process'
2+
import { randomBytes } from 'node:crypto'
23
import { cpSync, existsSync, readFileSync, rmSync, symlinkSync, writeFileSync } from 'node:fs'
34
import { resolve } from 'node:path'
5+
import { fillEnvFromExample } from './env.js'
46
import { renderReadme } from './readme.js'
57
import { deriveToolVersions } from './toolversions.js'
68
import type { ScaffoldParams } from './types.js'
79

10+
/** Apps whose committed `.env.example` is copied to a runnable `.env`. */
11+
const ENV_APPS = ['api', 'web', 'mobile']
12+
813
/** Move the transformed temp tree into the target dir, write README, git init, install, build. */
914
export function finalize(
1015
tempTree: string,
@@ -39,6 +44,17 @@ export function finalize(
3944
const ruby = existsSync(rubyVersionPath) ? readFileSync(rubyVersionPath, 'utf8') : undefined
4045
writeFileSync(resolve(target, '.tool-versions'), deriveToolVersions(nvmrc, packageManager, ruby))
4146

47+
// Copy each app's .env.example -> .env (gitignored) with a generated dev auth secret,
48+
// so the project runs locally without hand-editing env files.
49+
const betterAuthSecret = randomBytes(32).toString('base64')
50+
for (const app of ENV_APPS) {
51+
const example = resolve(target, 'apps', app, '.env.example')
52+
const dotenv = resolve(target, 'apps', app, '.env')
53+
if (existsSync(example) && !existsSync(dotenv)) {
54+
writeFileSync(dotenv, fillEnvFromExample(readFileSync(example, 'utf8'), betterAuthSecret))
55+
}
56+
}
57+
4258
if (opts.git) {
4359
execFileSync('git', ['init', '-q'], { cwd: target })
4460
}

tools/create-rhitta/src/readme.ts

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,26 @@ pnpm build # build shared packages (they export from dist/)
2121
pnpm validate # lint + typecheck + structure validation
2222
\`\`\`
2323
24-
Run each app: \`pnpm dev:api\`, \`pnpm dev:web\`, \`pnpm dev:mobile\`.
24+
## Run locally
25+
26+
Prerequisites:
27+
28+
- **Docker** running — the API's database is provisioned automatically by Encore.
29+
- **Encore CLI** — \`curl -fsSL https://encore.dev/install.sh | bash\`.
30+
31+
\`.env\` files are pre-created for you from each app's \`.env.example\` (with a generated
32+
dev auth secret). Optional integrations (Anthropic, Resend, S3) fall back to in-memory
33+
adapters when their keys are blank, so everything runs with no extra setup — drop in real
34+
keys in \`apps/api/.env\` when you want them.
35+
36+
\`\`\`bash
37+
pnpm dev:api # Encore — auto-provisions a local Postgres (Docker) + runs migrations
38+
pnpm dev:web # TanStack Start (Vite), points at the local API
39+
pnpm dev:mobile # Expo — run \`pnpm --filter @rhitta/mobile prebuild:clean\` first for native iOS/Android
40+
\`\`\`
41+
42+
No manual database setup or connection strings: Encore owns the local Postgres and applies
43+
the Drizzle migrations on \`dev:api\` startup.
2544
2645
## Workspaces
2746
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { describe, expect, it } from 'vitest'
2+
import { fillEnvFromExample } from '../src/env.js'
3+
4+
describe('fillEnvFromExample', () => {
5+
it('replaces the BETTER_AUTH_SECRET placeholder with the generated secret', () => {
6+
const example =
7+
'DATABASE_URL=postgres://x\nBETTER_AUTH_SECRET=replace-me\nBETTER_AUTH_URL=http://localhost:4000\n'
8+
const out = fillEnvFromExample(example, 'GENERATED==')
9+
expect(out).toContain('BETTER_AUTH_SECRET=GENERATED==')
10+
expect(out).not.toContain('replace-me')
11+
// untouched lines survive
12+
expect(out).toContain('DATABASE_URL=postgres://x')
13+
expect(out).toContain('BETTER_AUTH_URL=http://localhost:4000')
14+
})
15+
16+
it('passes through an example with no BETTER_AUTH_SECRET line unchanged', () => {
17+
const example = 'VITE_API_URL=http://localhost:4000\n'
18+
expect(fillEnvFromExample(example, 'GENERATED==')).toBe(example)
19+
})
20+
})

0 commit comments

Comments
 (0)