| @@ -22,8 +22,7 @@ const decodeLocalExecutorConfig = Schema.decodeUnknownSync(LocalExecutorConfigSc · 21 unmodified lines |
| 22 | 22 | const PROJECT_CONFIG_BASENAME = "executor.jsonc"; |
| 23 | 23 | const PROJECT_CONFIG_FALLBACK_BASENAME = "executor.json"; |
| 24 | 24 | const PROJECT_CONFIG_DIRECTORY = ".executor"; |
| 25 | | −const HOME_CONFIG_DIRECTORY = join(homedir(), ".config", "executor"); |
| 26 | | −const HOME_CONFIG_PATH = join(HOME_CONFIG_DIRECTORY, PROJECT_CONFIG_BASENAME); |
| 25 | +const EXECUTOR_CONFIG_DIR_ENV = "EXECUTOR_CONFIG_DIR"; |
| 27 | 26 | |
| 28 | 27 | const normalizeSlashPath = (value: string): string => |
| 29 | 28 | value.replaceAll("\\", "/"); |
| @@ -54,6 +53,80 @@ const trimOrUndefined = (value: string | undefined | null): string | undefined = · 24 unmodified lines |
| 54 | 53 | return trimmed && trimmed.length > 0 ? trimmed : undefined; |
| 55 | 54 | }; |
| 56 | 55 | |
| 56 | +const defaultExecutorConfigDirectory = (input: { |
| 57 | + env?: NodeJS.ProcessEnv; |
| 58 | + platform?: NodeJS.Platform; |
| 59 | + homeDirectory?: string; |
| 60 | +} = {}): string => { |
| 61 | + const env = input.env ?? process.env; |
| 62 | + const platform = input.platform ?? process.platform; |
| 63 | + const homeDirectory = input.homeDirectory ?? homedir(); |
| 64 | + const explicitConfigDirectory = trimOrUndefined(env[EXECUTOR_CONFIG_DIR_ENV]); |
| 65 | + |
| 66 | + if (explicitConfigDirectory) { |
| 67 | + return explicitConfigDirectory; |
| 68 | + } |
| 69 | + |
| 70 | + if (platform === "win32") { |
| 71 | + return join( |
| 72 | + trimOrUndefined(env.LOCALAPPDATA) ?? join(homeDirectory, "AppData", "Local"), |
| 73 | + "Executor", |
| 74 | + ); |
| 75 | + } |
| 76 | + |
| 77 | + if (platform === "darwin") { |
| 78 | + return join(homeDirectory, "Library", "Application Support", "Executor"); |
| 79 | + } |
| 80 | + |
| 81 | + return join( |
| 82 | + trimOrUndefined(env.XDG_CONFIG_HOME) ?? join(homeDirectory, ".config"), |
| 83 | + "executor", |
| 84 | + ); |
| 85 | +}; |
| 86 | + |
| 87 | +const legacyExecutorConfigDirectory = (input: { |
| 88 | + homeDirectory?: string; |
| 89 | +} = {}): string => |
| 90 | + join(input.homeDirectory ?? homedir(), ".config", "executor"); |
| 91 | + |
| 92 | +export const resolveDefaultHomeConfigCandidates = (input: { |
| 93 | + env?: NodeJS.ProcessEnv; |
| 94 | + platform?: NodeJS.Platform; |
| 95 | + homeDirectory?: string; |
| 96 | +} = {}): string[] => { |
| 97 | + const homeDirectory = input.homeDirectory ?? homedir(); |
| 98 | + const canonicalDirectory = defaultExecutorConfigDirectory({ |
| 99 | + env: input.env, |
| 100 | + platform: input.platform, |
| 101 | + homeDirectory, |
| 102 | + }); |
| 103 | + const legacyDirectory = legacyExecutorConfigDirectory({ homeDirectory }); |
| 104 | + const directories = canonicalDirectory === legacyDirectory |
| 105 | + ? [canonicalDirectory] |
| 106 | + : [canonicalDirectory, legacyDirectory]; |
| 107 | + |
| 108 | + return directories.flatMap((directory) => [ |
| 109 | + join(directory, PROJECT_CONFIG_BASENAME), |
| 110 | + join(directory, PROJECT_CONFIG_FALLBACK_BASENAME), |
| 111 | + ]); |
| 112 | +}; |
| 113 | + |
| 114 | +export const resolveHomeConfigPath = async (input: { |
| 115 | + env?: NodeJS.ProcessEnv; |
| 116 | + platform?: NodeJS.Platform; |
| 117 | + homeDirectory?: string; |
| 118 | +} = {}): Promise<string> => { |
| 119 | + const candidates = resolveDefaultHomeConfigCandidates(input); |
| 120 | + |
| 121 | + for (const candidate of candidates) { |
| 122 | + if (await fileExists(candidate)) { |
| 123 | + return candidate; |
| 124 | + } |
| 125 | + } |
| 126 | + |
| 127 | + return candidates[0]!; |
| 128 | +}; |
| 129 | + |
| 57 | 130 | const formatJsoncParseErrors = (content: string, errors: readonly JsoncParseError[]): string => { |
| 58 | 131 | const lines = content.split("\n"); |
| 59 | 132 | |
| @@ -232,6 +305,7 @@ export const resolveLocalWorkspaceContext = async (input: { · 172 unmodified lines |
| 232 | 305 | ); |
| 233 | 306 | const workspaceName = basename(workspaceRoot) || "workspace"; |
| 234 | 307 | const projectConfigPath = await resolveProjectConfigPath(workspaceRoot); |
| 308 | + const homeConfigPath = await resolveHomeConfigPath(); |
| 235 | 309 | |
| 236 | 310 | return { |
| 237 | 311 | cwd, |
| @@ -239,7 +313,7 @@ export const resolveLocalWorkspaceContext = async (input: { · 1 unmodified line |
| 239 | 313 | workspaceName, |
| 240 | 314 | configDirectory: join(workspaceRoot, PROJECT_CONFIG_DIRECTORY), |
| 241 | 315 | projectConfigPath, |
| 242 | | − homeConfigPath: HOME_CONFIG_PATH, |
| 316 | + homeConfigPath, |
| 243 | 317 | artifactsDirectory: join(workspaceRoot, PROJECT_CONFIG_DIRECTORY, "artifacts"), |
| 244 | 318 | stateDirectory: join(workspaceRoot, PROJECT_CONFIG_DIRECTORY, "state"), |
| 245 | 319 | installationId: `local_${stableHash(normalizeSlashPath(workspaceRoot))}`, |