( type: InstallationType, )
| 315 | } |
| 316 | |
| 317 | async function detectConfigurationIssues( |
| 318 | type: InstallationType, |
| 319 | ): Promise<Array<{ issue: string; fix: string }>> { |
| 320 | const warnings: Array<{ issue: string; fix: string }> = [] |
| 321 | |
| 322 | // Managed-settings forwards-compat: the schema preprocess silently drops |
| 323 | // unknown strictPluginOnlyCustomization surface names so one future enum |
| 324 | // value doesn't null out the entire policy file (settings.ts:101). But |
| 325 | // admins should KNOW — read the raw file and diff. Runs before the |
| 326 | // development-mode early return: this is config correctness, not an |
| 327 | // install-path check, and it's useful to see during dev testing. |
| 328 | try { |
| 329 | const raw = await readFile( |
| 330 | join(getManagedFilePath(), 'managed-settings.json'), |
| 331 | 'utf-8', |
| 332 | ) |
| 333 | const parsed: unknown = jsonParse(raw) |
| 334 | const field = |
| 335 | parsed && typeof parsed === 'object' |
| 336 | ? (parsed as Record<string, unknown>).strictPluginOnlyCustomization |
| 337 | : undefined |
| 338 | if (field !== undefined && typeof field !== 'boolean') { |
| 339 | if (!Array.isArray(field)) { |
| 340 | // .catch(undefined) in the schema silently drops this, so the rest |
| 341 | // of managed settings survive — but the admin typed something |
| 342 | // wrong (an object, a string, etc.). |
| 343 | warnings.push({ |
| 344 | issue: `managed-settings.json: strictPluginOnlyCustomization has an invalid value (expected true or an array, got ${typeof field})`, |
| 345 | fix: `The field is silently ignored (schema .catch rescues it). Set it to true, or an array of: ${CUSTOMIZATION_SURFACES.join(', ')}.`, |
| 346 | }) |
| 347 | } else { |
| 348 | const unknown = field.filter( |
| 349 | x => |
| 350 | typeof x === 'string' && |
| 351 | !(CUSTOMIZATION_SURFACES as readonly string[]).includes(x), |
| 352 | ) |
| 353 | if (unknown.length > 0) { |
| 354 | warnings.push({ |
| 355 | issue: `managed-settings.json: strictPluginOnlyCustomization has ${unknown.length} value(s) this client doesn't recognize: ${unknown.map(String).join(', ')}`, |
| 356 | fix: `These are silently ignored (forwards-compat). Known surfaces for this version: ${CUSTOMIZATION_SURFACES.join(', ')}. Either remove them, or this client is older than the managed-settings intended.`, |
| 357 | }) |
| 358 | } |
| 359 | } |
| 360 | } |
| 361 | } catch { |
| 362 | // ENOENT (no managed settings) / parse error — not this check's concern. |
| 363 | // Parse errors are surfaced by the settings loader itself. |
| 364 | } |
| 365 | |
| 366 | const config = getGlobalConfig() |
| 367 | |
| 368 | // Skip most warnings for development mode |
| 369 | if (type === 'development') { |
| 370 | return warnings |
| 371 | } |
| 372 | |
| 373 | // Check if ~/.local/bin is in PATH for native installations |
| 374 | if (type === 'native') { |
no test coverage detected