188 lines
5.1 KiB
JavaScript
188 lines
5.1 KiB
JavaScript
#!/usr/bin/env node
|
||
|
||
/**
|
||
* Conditional Cloud Spec Generator
|
||
*
|
||
* This script conditionally runs the cloud spec generation based on environment variables.
|
||
* It's designed to be used in CI/CD pipelines to control when the spec should be updated.
|
||
*
|
||
* Environment variables:
|
||
* - SKIP_CLOUD_SPEC_UPDATE: Skip cloud spec generation entirely
|
||
* - FORCE_UPDATE: Force update even if skip is set
|
||
* - CI: Detect if running in CI environment
|
||
*/
|
||
|
||
import { spawn } from 'child_process'
|
||
import fs from 'fs'
|
||
import path from 'path'
|
||
import { fileURLToPath } from 'url'
|
||
|
||
const __filename = fileURLToPath(import.meta.url)
|
||
const __dirname = path.dirname(__filename)
|
||
|
||
// Configuration
|
||
const CONFIG = {
|
||
CLOUD_SPEC_PATH: path.join(__dirname, '../public/openapi/cloud-openapi.json'),
|
||
GENERATOR_SCRIPT: path.join(__dirname, 'generate-cloud-spec.js'),
|
||
FALLBACK_SPEC_PATH: path.join(__dirname, '../public/openapi/openapi.json'),
|
||
}
|
||
|
||
// Color codes for console output
|
||
const colors = {
|
||
reset: '\x1b[0m',
|
||
green: '\x1b[32m',
|
||
yellow: '\x1b[33m',
|
||
cyan: '\x1b[36m',
|
||
gray: '\x1b[90m',
|
||
}
|
||
|
||
function log(message, type = 'info') {
|
||
const prefix = {
|
||
info: `${colors.cyan}ℹ️ `,
|
||
skip: `${colors.gray}⏭️ `,
|
||
run: `${colors.green}▶️ `,
|
||
warning: `${colors.yellow}⚠️ `,
|
||
}[type] || ''
|
||
console.log(`${prefix}${message}${colors.reset}`)
|
||
}
|
||
|
||
async function shouldRunGenerator() {
|
||
// Check environment variables
|
||
const skipUpdate = process.env.SKIP_CLOUD_SPEC_UPDATE === 'true'
|
||
const forceUpdate = process.env.FORCE_UPDATE === 'true'
|
||
const isCI = process.env.CI === 'true'
|
||
const isPR = process.env.GITHUB_EVENT_NAME === 'pull_request'
|
||
|
||
// Force update overrides all
|
||
if (forceUpdate) {
|
||
log('Force update requested', 'info')
|
||
return true
|
||
}
|
||
|
||
// Skip if explicitly requested
|
||
if (skipUpdate) {
|
||
log('Cloud spec update skipped (SKIP_CLOUD_SPEC_UPDATE=true)', 'skip')
|
||
return false
|
||
}
|
||
|
||
// Skip in PR builds to avoid unnecessary API calls
|
||
if (isPR) {
|
||
log('Cloud spec update skipped (Pull Request build)', 'skip')
|
||
return false
|
||
}
|
||
|
||
// Check if cloud spec already exists
|
||
const specExists = fs.existsSync(CONFIG.CLOUD_SPEC_PATH)
|
||
|
||
// In CI, only update if spec doesn't exist or if scheduled/manual trigger
|
||
if (isCI) {
|
||
const isScheduled = process.env.GITHUB_EVENT_NAME === 'schedule'
|
||
const isManualWithUpdate =
|
||
process.env.GITHUB_EVENT_NAME === 'workflow_dispatch' &&
|
||
process.env.UPDATE_CLOUD_SPEC === 'true'
|
||
|
||
if (isScheduled || isManualWithUpdate) {
|
||
log('Cloud spec update triggered (scheduled/manual)', 'info')
|
||
return true
|
||
}
|
||
|
||
if (!specExists) {
|
||
log('Cloud spec missing, will attempt to generate', 'warning')
|
||
return true
|
||
}
|
||
|
||
log('Cloud spec update skipped (CI build, spec exists)', 'skip')
|
||
return false
|
||
}
|
||
|
||
// For local development, update if spec is missing or older than 24 hours
|
||
if (!specExists) {
|
||
log('Cloud spec missing, generating...', 'info')
|
||
return true
|
||
}
|
||
|
||
// Check if spec is older than 24 hours
|
||
const stats = fs.statSync(CONFIG.CLOUD_SPEC_PATH)
|
||
const ageInHours = (Date.now() - stats.mtime.getTime()) / (1000 * 60 * 60)
|
||
|
||
if (ageInHours > 24) {
|
||
log(`Cloud spec is ${Math.round(ageInHours)} hours old, updating...`, 'info')
|
||
return true
|
||
}
|
||
|
||
log(`Cloud spec is recent (${Math.round(ageInHours)} hours old), skipping update`, 'skip')
|
||
return false
|
||
}
|
||
|
||
async function runGenerator() {
|
||
return new Promise((resolve, reject) => {
|
||
log('Running cloud spec generator...', 'run')
|
||
|
||
const child = spawn('bun', [CONFIG.GENERATOR_SCRIPT], {
|
||
stdio: 'inherit',
|
||
env: { ...process.env }
|
||
})
|
||
|
||
child.on('close', (code) => {
|
||
if (code === 0) {
|
||
resolve()
|
||
} else {
|
||
reject(new Error(`Generator exited with code ${code}`))
|
||
}
|
||
})
|
||
|
||
child.on('error', (err) => {
|
||
reject(err)
|
||
})
|
||
})
|
||
}
|
||
|
||
async function ensureFallback() {
|
||
// If cloud spec doesn't exist but fallback does, copy it
|
||
if (!fs.existsSync(CONFIG.CLOUD_SPEC_PATH) && fs.existsSync(CONFIG.FALLBACK_SPEC_PATH)) {
|
||
log('Using fallback spec as cloud spec', 'warning')
|
||
fs.copyFileSync(CONFIG.FALLBACK_SPEC_PATH, CONFIG.CLOUD_SPEC_PATH)
|
||
return true
|
||
}
|
||
return false
|
||
}
|
||
|
||
async function main() {
|
||
try {
|
||
// Determine if we should run the generator
|
||
const shouldRun = await shouldRunGenerator()
|
||
|
||
if (shouldRun) {
|
||
try {
|
||
await runGenerator()
|
||
log('Cloud spec generation completed', 'info')
|
||
} catch (error) {
|
||
log(`Cloud spec generation failed: ${error.message}`, 'warning')
|
||
|
||
// Try to use fallback
|
||
if (ensureFallback()) {
|
||
log('Fallback spec used successfully', 'info')
|
||
} else {
|
||
log('No fallback available, build may fail', 'warning')
|
||
// Don't exit with error - let the build continue
|
||
}
|
||
}
|
||
} else {
|
||
// Ensure we have at least a fallback spec
|
||
if (!fs.existsSync(CONFIG.CLOUD_SPEC_PATH)) {
|
||
ensureFallback()
|
||
}
|
||
}
|
||
|
||
// Always exit successfully to not break the build
|
||
process.exit(0)
|
||
} catch (error) {
|
||
console.error('Unexpected error:', error)
|
||
// Even on error, try to continue the build
|
||
process.exit(0)
|
||
}
|
||
}
|
||
|
||
// Run the script
|
||
main()
|