feat: auto-setup SDKs and data store before dev/build/test (#614)

Co-authored-by: Mackenzie Zastrow <zastrowm@users.noreply.github.com>
This commit is contained in:
Mackenzie Zastrow
2026-03-08 13:14:22 -04:00
committed by GitHub
co-authored by Mackenzie Zastrow
parent 495050c41b
commit 9e7ff59f3b
6 changed files with 108 additions and 8 deletions
-6
View File
@@ -35,11 +35,5 @@ jobs:
- name: Typecheck snippets
run: npm run typecheck:snippets
# Astro 5's content layer only generates data-store.json when the dev server runs.
# Without it, vitest tests using getCollection() fail with "collection does not exist".
# See: https://github.com/withastro/astro/issues/12836
- name: Prepare tests
run: npm run test:prep
- name: Test
run: npm test
+2
View File
@@ -3,6 +3,7 @@ import { defineConfig } from 'astro/config'
import starlight from '@astrojs/starlight'
import path from 'node:path'
import remarkMkdocsSnippets from './src/plugins/remark-mkdocs-snippets.ts'
import sdkSetupPlugin from './src/plugins/vite-plugin-sdk-setup.ts'
import { loadSidebarFromMkdocs } from "./src/sidebar.ts"
import AutoImport from './src/plugins/astro-auto-import.ts'
@@ -22,6 +23,7 @@ export default defineConfig({
site: 'https://strandsagents.com',
base: process.env.ASTRO_BASE_PATH || '/',
vite: {
plugins: [sdkSetupPlugin()],
// TODO once we separate out CMS build from TS verification, fix this
// https://github.com/withastro/astro/issues/14117
ssr: {
+1 -2
View File
@@ -7,12 +7,11 @@
"build:all": "npm run sdk:clone && npm run sdk:generate && npm run build",
"dev": "astro dev",
"test": "vitest run",
"test:prep": "timeout 10s npm run dev || true",
"typecheck": "tsc --noEmit",
"typecheck:snippets": "npm run typecheck --prefix test-snippets",
"format": "prettier --write docs",
"format:check": "prettier --check docs",
"clean": "rm -rf .build",
"clean": "rm -rf .build && rm -rf .astro",
"preview": "astro preview",
"cms:build": "npm run build:all",
"docs:update": "tsx scripts/update-docs.ts",
+45
View File
@@ -0,0 +1,45 @@
/**
* Vite plugin that automatically runs `npm run sdk:clone` and `npm run sdk:generate`
* if the SDK build artifacts are not present, simplifying the getting-started experience.
*/
import { execSync } from 'node:child_process'
import { existsSync } from 'node:fs'
import type { Plugin } from 'vite'
const SDK_PATHS = ['.build/sdk-typescript', '.build/sdk-python']
function areSdksPresent(): boolean {
return SDK_PATHS.every((p) => existsSync(p))
}
function run(command: string): void {
console.log(`[sdk-setup] Running: ${command}`)
execSync(command, { stdio: 'inherit' })
}
function setupSdks(): void {
if (areSdksPresent()) return
console.log('[sdk-setup] SDK build artifacts not found — running sdk:clone and sdk:generate...')
run('npm run sdk:clone')
run('npm run sdk:generate')
console.log('[sdk-setup] SDK setup complete.')
}
export default function sdkSetupPlugin(): Plugin {
return {
name: 'vite-plugin-sdk-setup',
enforce: 'pre',
// Used during `astro build`
buildStart() {
setupSdks()
},
// Used during `astro dev` — buildStart fires before the file watcher is running,
// so newly generated files would be missed by Astro's content layer. configureServer
// runs after the watcher is set up, and we emit a change event to trigger a re-sync.
configureServer(server) {
setupSdks()
server.watcher.emit('change', '.build/api-docs')
},
}
}
+59
View File
@@ -0,0 +1,59 @@
import { existsSync, readFileSync } from 'node:fs'
import { spawn } from 'node:child_process'
const DATA_STORE_PATH = '.astro/data-store.json'
const TIMEOUT_MS = 60_000
const SYNCED_MARKER = 'Synced content'
function isDataStorePopulated(): boolean {
if (!existsSync(DATA_STORE_PATH)) return false
try {
return readFileSync(DATA_STORE_PATH, 'utf-8').trim().length > 100
} catch {
return false
}
}
export async function setup() {
if (isDataStorePopulated()) return
console.log('[global-setup] Data store not populated — starting astro dev temporarily...')
const server = spawn('npx', ['astro', 'dev'], {
stdio: ['ignore', 'pipe', 'pipe'],
detached: true,
})
try {
await new Promise<void>((resolve, reject) => {
const timer = setTimeout(() => reject(new Error('Timed out waiting for "Synced content"')), TIMEOUT_MS)
const onData = (chunk: Buffer) => {
process.stdout.write(chunk)
if (chunk.toString().includes(SYNCED_MARKER)) {
clearTimeout(timer)
resolve()
}
}
server.stdout.on('data', onData)
server.stderr.on('data', onData)
server.on('error', (err) => {
clearTimeout(timer)
reject(err)
})
server.on('close', (code) => {
if (code !== null) {
clearTimeout(timer)
reject(new Error(`Astro dev server exited with code ${code}`))
}
})
})
} finally {
if (server.pid != null) {
process.kill(-server.pid, 'SIGTERM')
}
}
console.log('[global-setup] Data store ready.')
}
+1
View File
@@ -3,5 +3,6 @@ import { getViteConfig } from 'astro/config'
export default getViteConfig({
test: {
include: ['test/**/*.test.ts'],
globalSetup: ['test/global-setup.ts'],
},
})