| @@ -22,8 +22,7 @@ const decodeLocalExecutorConfig = Schema.decodeUnknownSync(LocalExecutorConfigSc · 21 unmodified lines |
| 22 | const PROJECT_CONFIG_BASENAME = "executor.jsonc"; | 22 | const PROJECT_CONFIG_BASENAME = "executor.jsonc"; |
| 23 | const PROJECT_CONFIG_FALLBACK_BASENAME = "executor.json"; | 23 | const PROJECT_CONFIG_FALLBACK_BASENAME = "executor.json"; |
| 24 | const PROJECT_CONFIG_DIRECTORY = ".executor"; | 24 | const PROJECT_CONFIG_DIRECTORY = ".executor"; |
| 25 | const HOME_CONFIG_DIRECTORY = join(homedir(), ".config", "executor"); | 25 | const EXECUTOR_CONFIG_DIR_ENV = "EXECUTOR_CONFIG_DIR"; |
| 26 | const HOME_CONFIG_PATH = join(HOME_CONFIG_DIRECTORY, PROJECT_CONFIG_BASENAME); | | |
| 27 | | 26 | |
| 28 | const normalizeSlashPath = (value: string): string => | 27 | const normalizeSlashPath = (value: string): string => |
| 29 | value.replaceAll("\\", "/"); | 28 | value.replaceAll("\\", "/"); |
| @@ -54,6 +53,80 @@ const trimOrUndefined = (value: string | undefined | null): string | undefined = · 24 unmodified lines |
| 54 | return trimmed && trimmed.length > 0 ? trimmed : undefined; | 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 | const formatJsoncParseErrors = (content: string, errors: readonly JsoncParseError[]): string => { | 130 | const formatJsoncParseErrors = (content: string, errors: readonly JsoncParseError[]): string => { |
| 58 | const lines = content.split("\n"); | 131 | const lines = content.split("\n"); |
| 59 | | 132 | |
| @@ -232,6 +305,7 @@ export const resolveLocalWorkspaceContext = async (input: { · 172 unmodified lines |
| 232 | ); | 305 | ); |
| 233 | const workspaceName = basename(workspaceRoot) || "workspace"; | 306 | const workspaceName = basename(workspaceRoot) || "workspace"; |
| 234 | const projectConfigPath = await resolveProjectConfigPath(workspaceRoot); | 307 | const projectConfigPath = await resolveProjectConfigPath(workspaceRoot); |
| | 308 | const homeConfigPath = await resolveHomeConfigPath(); |
| 235 | | 309 | |
| 236 | return { | 310 | return { |
| 237 | cwd, | 311 | cwd, |
| @@ -239,7 +313,7 @@ export const resolveLocalWorkspaceContext = async (input: { · 1 unmodified line |
| 239 | workspaceName, | 313 | workspaceName, |
| 240 | configDirectory: join(workspaceRoot, PROJECT_CONFIG_DIRECTORY), | 314 | configDirectory: join(workspaceRoot, PROJECT_CONFIG_DIRECTORY), |
| 241 | projectConfigPath, | 315 | projectConfigPath, |
| 242 | homeConfigPath: HOME_CONFIG_PATH, | 316 | homeConfigPath, |
| 243 | artifactsDirectory: join(workspaceRoot, PROJECT_CONFIG_DIRECTORY, "artifacts"), | 317 | artifactsDirectory: join(workspaceRoot, PROJECT_CONFIG_DIRECTORY, "artifacts"), |
| 244 | stateDirectory: join(workspaceRoot, PROJECT_CONFIG_DIRECTORY, "state"), | 318 | stateDirectory: join(workspaceRoot, PROJECT_CONFIG_DIRECTORY, "state"), |
| 245 | installationId: `local_${stableHash(normalizeSlashPath(workspaceRoot))}`, | 319 | installationId: `local_${stableHash(normalizeSlashPath(workspaceRoot))}`, |