(configPath?: string)
| 58 | |
| 59 | |
| 60 | export const loadConfig = async (configPath?: string): Promise<SourcebotConfig> => { |
| 61 | if (!configPath) { |
| 62 | throw new Error('CONFIG_PATH is required but not provided'); |
| 63 | } |
| 64 | |
| 65 | const configContent = await (async () => { |
| 66 | if (isRemotePath(configPath)) { |
| 67 | const response = await fetch(configPath); |
| 68 | if (!response.ok) { |
| 69 | throw new Error(`Failed to fetch config file ${configPath}: ${response.statusText}`); |
| 70 | } |
| 71 | return response.text(); |
| 72 | } else { |
| 73 | // Retry logic for handling race conditions with mounted volumes |
| 74 | const maxAttempts = 5; |
| 75 | const retryDelayMs = 2000; |
| 76 | let lastError: Error | null = null; |
| 77 | |
| 78 | for (let attempt = 1; attempt <= maxAttempts; attempt++) { |
| 79 | try { |
| 80 | return await readFile(configPath, { |
| 81 | encoding: 'utf-8', |
| 82 | }); |
| 83 | } catch (error) { |
| 84 | lastError = error as Error; |
| 85 | |
| 86 | // Only retry on ENOENT errors (file not found) |
| 87 | if ((error as NodeJS.ErrnoException)?.code !== 'ENOENT') { |
| 88 | throw error; // Throw immediately for non-ENOENT errors |
| 89 | } |
| 90 | |
| 91 | // Log warning before retry (except on the last attempt) |
| 92 | if (attempt < maxAttempts) { |
| 93 | console.warn(`Config file not found, retrying in 2s... (Attempt ${attempt}/${maxAttempts})`); |
| 94 | await new Promise(resolve => setTimeout(resolve, retryDelayMs)); |
| 95 | } |
| 96 | } |
| 97 | } |
| 98 | |
| 99 | // If we've exhausted all retries, throw the last ENOENT error |
| 100 | if (lastError) { |
| 101 | throw lastError; |
| 102 | } |
| 103 | |
| 104 | throw new Error('Failed to load config after all retry attempts'); |
| 105 | } |
| 106 | })(); |
| 107 | |
| 108 | const config = JSON.parse(stripJsonComments(configContent)) as SourcebotConfig; |
| 109 | const isValidConfig = ajv.validate(indexSchema, config); |
| 110 | if (!isValidConfig) { |
| 111 | throw new Error(`Config file '${configPath}' is invalid: ${ajv.errorsText(ajv.errors)}`); |
| 112 | } |
| 113 | |
| 114 | return config; |
| 115 | } |
| 116 | |
| 117 |
no test coverage detected