6bf8bebf51
CI / Test and Build (push) Failing after 1s
CI / Migrate Dev DB (push) Has been skipped
CI / Migrate DB (push) Has been skipped
CodeQL / Analyze actions (push) Has been cancelled
CodeQL / Analyze javascript-typescript (push) Has been cancelled
CI / Detect Version (push) Has been cancelled
CI / Detect Desktop Changes (push) Has been cancelled
CI / Build AMD64 (blacksmith-2vcpu-ubuntu-2404, ./docker/cron.Dockerfile, ubuntu-latest, ghcr.io/simstudioai/cron) (push) Has been cancelled
CI / Build AMD64 (blacksmith-2vcpu-ubuntu-2404, ./docker/db.Dockerfile, ECR_MIGRATIONS, ubuntu-latest, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build AMD64 (blacksmith-4vcpu-ubuntu-2404, ./docker/pii.Dockerfile, ECR_PII, ubuntu-latest, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build AMD64 (blacksmith-4vcpu-ubuntu-2404, ./docker/realtime.Dockerfile, ECR_REALTIME, ubuntu-latest, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build AMD64 (blacksmith-8vcpu-ubuntu-2404, ./docker/app.Dockerfile, ECR_APP, linux-x64-8-core, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (blacksmith-4vcpu-ubuntu-2404-arm, ./docker/cron.Dockerfile, ubuntu-24.04-arm, ghcr.io/simstudioai/cron) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (blacksmith-4vcpu-ubuntu-2404-arm, ./docker/db.Dockerfile, ubuntu-24.04-arm, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (blacksmith-4vcpu-ubuntu-2404-arm, ./docker/pii.Dockerfile, ubuntu-24.04-arm, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (blacksmith-4vcpu-ubuntu-2404-arm, ./docker/realtime.Dockerfile, ubuntu-24.04-arm, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (blacksmith-8vcpu-ubuntu-2404-arm, ./docker/app.Dockerfile, linux-arm64-8-core, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Check Docs Changes (push) Has been cancelled
Publish CLI Package / publish-npm (push) Has been cancelled
Publish Python SDK / publish-pypi (push) Has been cancelled
CI / Deploy Trigger.dev (Dev) (push) Has been cancelled
Helm Chart / Lint, test, and validate chart (push) Has been cancelled
Helm Chart / Chart version bumped (push) Has been cancelled
Publish TypeScript SDK / publish-npm (push) Has been cancelled
CI / Build Dev ECR (blacksmith-8vcpu-ubuntu-2404, ./docker/app.Dockerfile, ECR_APP, linux-x64-8-core) (push) Has been cancelled
CI / Promote Images (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/cron) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build Dev ECR (blacksmith-2vcpu-ubuntu-2404, ./docker/db.Dockerfile, ECR_MIGRATIONS, ubuntu-latest) (push) Has been cancelled
CI / Build Dev ECR (blacksmith-4vcpu-ubuntu-2404, ./docker/pii.Dockerfile, ECR_PII, ubuntu-latest) (push) Has been cancelled
CI / Build Dev ECR (blacksmith-4vcpu-ubuntu-2404, ./docker/realtime.Dockerfile, ECR_REALTIME, ubuntu-latest) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Process Docs (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
CI / Check Desktop Signing Secrets (push) Has been cancelled
CI / Desktop Release (push) Has been cancelled
CI / Create Desktop Prerelease (push) Has been cancelled
CI / Desktop Prerelease Build (push) Has been cancelled
CI / Publish Desktop Prerelease (push) Has been cancelled
CI / Prune Desktop Prereleases (push) Has been cancelled
Helm Chart / Install on kind and run helm test (push) Has been cancelled
113 lines
3.5 KiB
TypeScript
113 lines
3.5 KiB
TypeScript
import { readFileSync } from 'node:fs'
|
|
import net from 'node:net'
|
|
import path from 'node:path'
|
|
import tls from 'node:tls'
|
|
import { getErrorMessage } from '@sim/utils/errors'
|
|
import { sleep } from '@sim/utils/helpers'
|
|
import postgres from 'postgres'
|
|
import { ROOT } from './env-files.ts'
|
|
|
|
export interface PgProbeResult {
|
|
ok: boolean
|
|
error?: string
|
|
pgvectorAvailable?: boolean
|
|
migrations?: { applied: number | null; journal: number }
|
|
}
|
|
|
|
function journalMigrationCount(): number {
|
|
const journalPath = path.join(ROOT, 'packages/db/migrations/meta/_journal.json')
|
|
const journal = JSON.parse(readFileSync(journalPath, 'utf8')) as { entries: unknown[] }
|
|
return journal.entries.length
|
|
}
|
|
|
|
export async function pgProbe(dsn: string): Promise<PgProbeResult> {
|
|
const sql = postgres(dsn, { max: 1, connect_timeout: 5, onnotice: () => {} })
|
|
try {
|
|
await sql`select 1`
|
|
const vector = await sql`select 1 from pg_available_extensions where name = 'vector'`
|
|
let applied: number | null = null
|
|
try {
|
|
const rows = await sql`select count(*)::int as n from drizzle.__drizzle_migrations`
|
|
applied = rows[0].n as number
|
|
} catch {
|
|
applied = null
|
|
}
|
|
return {
|
|
ok: true,
|
|
pgvectorAvailable: vector.length > 0,
|
|
migrations: { applied, journal: journalMigrationCount() },
|
|
}
|
|
} catch (error) {
|
|
return { ok: false, error: getErrorMessage(error, 'connection failed') }
|
|
} finally {
|
|
await sql.end({ timeout: 1 })
|
|
}
|
|
}
|
|
|
|
export function redisPing(url: string, timeoutMs = 2000): Promise<{ ok: boolean; error?: string }> {
|
|
return new Promise((resolve) => {
|
|
let parsed: URL
|
|
try {
|
|
parsed = new URL(url)
|
|
} catch {
|
|
resolve({ ok: false, error: 'invalid REDIS_URL' })
|
|
return
|
|
}
|
|
const port = Number(parsed.port || 6379)
|
|
const secure = parsed.protocol === 'rediss:'
|
|
const socket = secure
|
|
? tls.connect({
|
|
host: parsed.hostname,
|
|
port,
|
|
servername: process.env.REDIS_TLS_SERVERNAME || parsed.hostname,
|
|
})
|
|
: net.connect({ host: parsed.hostname, port })
|
|
let buffer = ''
|
|
const done = (result: { ok: boolean; error?: string }) => {
|
|
socket.destroy()
|
|
resolve(result)
|
|
}
|
|
socket.setTimeout(timeoutMs, () => done({ ok: false, error: 'timeout' }))
|
|
socket.once('error', (error) => done({ ok: false, error: getErrorMessage(error) }))
|
|
socket.once(secure ? 'secureConnect' : 'connect', () => {
|
|
const auth = parsed.password
|
|
? `AUTH ${parsed.username || ''} ${parsed.password}\r\n`.replace('AUTH ', 'AUTH ')
|
|
: ''
|
|
socket.write(`${auth}PING\r\n`)
|
|
})
|
|
socket.on('data', (chunk) => {
|
|
buffer += chunk.toString()
|
|
if (buffer.includes('+PONG')) done({ ok: true })
|
|
else if (
|
|
buffer.includes('-ERR') ||
|
|
buffer.includes('-NOAUTH') ||
|
|
buffer.includes('-WRONGPASS')
|
|
)
|
|
done({ ok: false, error: buffer.split('\r\n')[0] })
|
|
})
|
|
})
|
|
}
|
|
|
|
export async function httpHealth(url: string, timeoutMs = 3000): Promise<boolean> {
|
|
try {
|
|
const res = await fetch(url, { signal: AbortSignal.timeout(timeoutMs) })
|
|
return res.ok
|
|
} catch {
|
|
return false
|
|
}
|
|
}
|
|
|
|
/** Polls a probe until it succeeds or the window elapses. */
|
|
export async function waitFor(
|
|
probe: () => Promise<boolean>,
|
|
totalMs: number,
|
|
intervalMs = 2000
|
|
): Promise<boolean> {
|
|
const deadline = Date.now() + totalMs
|
|
while (Date.now() < deadline) {
|
|
if (await probe()) return true
|
|
await sleep(intervalMs)
|
|
}
|
|
return probe()
|
|
}
|