[cron] setup app env properly when running tasks

This commit is contained in:
Chris Wanstrath 2026-02-09 21:17:02 -08:00
parent 47030d7d36
commit 7c04aceef9
4 changed files with 35 additions and 30 deletions

View File

@ -1,4 +1,5 @@
import { join } from 'path' import { join } from 'path'
import { loadAppEnv } from '@because/toes/tools'
import type { CronJob } from './schedules' import type { CronJob } from './schedules'
import { getNextRun } from './scheduler' import { getNextRun } from './scheduler'
@ -32,7 +33,7 @@ export async function executeJob(job: CronJob, onUpdate: () => void): Promise<vo
try { try {
const proc = Bun.spawn(['bun', 'run', RUNNER, job.file], { const proc = Bun.spawn(['bun', 'run', RUNNER, job.file], {
cwd, cwd,
env: { ...process.env, DATA_DIR: join(TOES_DIR, job.app) }, env: { ...process.env, ...loadAppEnv(job.app), DATA_DIR: join(TOES_DIR, job.app) },
stdout: 'pipe', stdout: 'pipe',
stderr: 'pipe', stderr: 'pipe',
}) })

View File

@ -3,6 +3,7 @@ import type { Subprocess } from 'bun'
import { DEFAULT_EMOJI } from '@types' import { DEFAULT_EMOJI } from '@types'
import { appendFileSync, existsSync, mkdirSync, readdirSync, readFileSync, realpathSync, renameSync, symlinkSync, unlinkSync, writeFileSync } from 'fs' import { appendFileSync, existsSync, mkdirSync, readdirSync, readFileSync, realpathSync, renameSync, symlinkSync, unlinkSync, writeFileSync } from 'fs'
import { join, resolve } from 'path' import { join, resolve } from 'path'
import { loadAppEnv } from '../tools/env'
import { appLog, hostLog, setApps } from './tui' import { appLog, hostLog, setApps } from './tui'
export type { AppState } from '@types' export type { AppState } from '@types'
@ -495,34 +496,6 @@ function loadApp(dir: string): LoadResult {
} }
} }
function loadAppEnv(appName: string): Record<string, string> {
const envDir = join(TOES_DIR, 'env')
const env: Record<string, string> = {}
const parseEnvFile = (path: string) => {
if (!existsSync(path)) return
const content = readFileSync(path, 'utf-8')
for (const line of content.split('\n')) {
const trimmed = line.trim()
if (!trimmed || trimmed.startsWith('#')) continue
const eqIndex = trimmed.indexOf('=')
if (eqIndex === -1) continue
const key = trimmed.slice(0, eqIndex).trim()
let value = trimmed.slice(eqIndex + 1).trim()
// Remove surrounding quotes
if ((value.startsWith('"') && value.endsWith('"')) ||
(value.startsWith("'") && value.endsWith("'"))) {
value = value.slice(1, -1)
}
if (key) env[key] = value
}
}
parseEnvFile(join(envDir, '_global.env'))
parseEnvFile(join(envDir, `${appName}.env`))
return env
}
function maybeResetBackoff(app: App) { function maybeResetBackoff(app: App) {
if (app.started && Date.now() - app.started >= STABLE_RUN_TIME) { if (app.started && Date.now() - app.started >= STABLE_RUN_TIME) {
@ -599,7 +572,7 @@ async function runApp(dir: string, port: number) {
info(app, `Starting on port ${port}...`) info(app, `Starting on port ${port}...`)
// Load env vars from TOES_DIR/env/ // Load env vars from TOES_DIR/env/
const appEnv = loadAppEnv(dir) const appEnv = loadAppEnv(dir, TOES_DIR)
const proc = Bun.spawn(['bun', 'run', 'toes'], { const proc = Bun.spawn(['bun', 'run', 'toes'], {
cwd, cwd,

30
src/tools/env.ts Normal file
View File

@ -0,0 +1,30 @@
import { existsSync, readFileSync } from 'fs'
import { join } from 'path'
export function loadAppEnv(appName: string, toesDir?: string): Record<string, string> {
const envDir = join(toesDir || process.env.TOES_DIR!, 'env')
const env: Record<string, string> = {}
const parseEnvFile = (path: string) => {
if (!existsSync(path)) return
const content = readFileSync(path, 'utf-8')
for (const line of content.split('\n')) {
const trimmed = line.trim()
if (!trimmed || trimmed.startsWith('#')) continue
const eqIndex = trimmed.indexOf('=')
if (eqIndex === -1) continue
const key = trimmed.slice(0, eqIndex).trim()
let value = trimmed.slice(eqIndex + 1).trim()
if ((value.startsWith('"') && value.endsWith('"')) ||
(value.startsWith("'") && value.endsWith("'"))) {
value = value.slice(1, -1)
}
if (key) env[key] = value
}
}
parseEnvFile(join(envDir, '_global.env'))
parseEnvFile(join(envDir, `${appName}.env`))
return env
}

View File

@ -1,2 +1,3 @@
export { theme } from '../client/themes' export { theme } from '../client/themes'
export { loadAppEnv } from './env'
export { baseStyles, ToolScript } from './scripts.tsx' export { baseStyles, ToolScript } from './scripts.tsx'