From d6950fa722dae9a6de6a9eb67a07d811e64bb87a Mon Sep 17 00:00:00 2001 From: Piotr Szeremeta Date: Mon, 19 Aug 2024 18:37:02 +0200 Subject: [PATCH] Implement eas env:load and env:unload --- packages/eas-cli/src/commands/env/init.ts | 35 +++++------ packages/eas-cli/src/commands/env/load.ts | 67 +++++++++++++++++++++ packages/eas-cli/src/commands/env/unload.ts | 23 +++++++ 3 files changed, 108 insertions(+), 17 deletions(-) create mode 100644 packages/eas-cli/src/commands/env/load.ts create mode 100644 packages/eas-cli/src/commands/env/unload.ts diff --git a/packages/eas-cli/src/commands/env/init.ts b/packages/eas-cli/src/commands/env/init.ts index 737d5ac567..6f6efaee72 100644 --- a/packages/eas-cli/src/commands/env/init.ts +++ b/packages/eas-cli/src/commands/env/init.ts @@ -33,13 +33,15 @@ export default class EnvironmentVariableInit extends EasCommand { await this.setupEnvrcFileAsync(); await this.addDirenvHookToShellConfigAsync(); await this.addToGitIgnoreAsync(); + Log.log('Running direnv allow...'); + await spawnAsync('direnv', ['allow']); } private async addDirenvHookToShellConfigAsync(): Promise { const direnvConfig = this.getShellDirenvConfig(); if (direnvConfig && (await pathExists(direnvConfig.shellConfigPath))) { - const { shellConfigPath, direnvHookCmd } = direnvConfig; + const { shellConfigPath, direnvHookCmd, direnvInitCmd } = direnvConfig; const confirm = await confirmAsync({ message: `Do you want to add the direnv hook to ${shellConfigPath}?`, @@ -61,6 +63,7 @@ export default class EnvironmentVariableInit extends EasCommand { await appendFile(shellConfigPath, `\n${direnvHookCmd}\n`, 'utf8'); Log.log(`Added direnv hook to ${shellConfigPath}`); + await spawnAsync(direnvInitCmd[0], direnvInitCmd[1], { stdio: 'inherit' }); } else { Log.log("Unable to determine the user's shell"); Log.log('You may need to add the direnv hook to your shell config manually.'); @@ -68,7 +71,11 @@ export default class EnvironmentVariableInit extends EasCommand { } } - private getShellDirenvConfig(): { shellConfigPath: string; direnvHookCmd: string } | null { + private getShellDirenvConfig(): { + shellConfigPath: string; + direnvHookCmd: string; + direnvInitCmd: [string, string[]]; + } | null { const shellEnv = process.env.SHELL; if (!shellEnv) { return null; @@ -78,16 +85,19 @@ export default class EnvironmentVariableInit extends EasCommand { return { shellConfigPath: path.join(os.homedir(), '.bashrc'), direnvHookCmd: 'eval "$(direnv hook bash)"', + direnvInitCmd: ['eval', ['"$(direnv hook bash)"']], }; } else if (shellEnv.endsWith('zsh')) { return { shellConfigPath: path.join(os.homedir(), '.zshrc'), direnvHookCmd: 'eval "$(direnv hook zsh)"', + direnvInitCmd: ['eval', ['"$(direnv hook zsh)"']], }; } else if (shellEnv.endsWith('fish')) { return { shellConfigPath: path.join(os.homedir(), '.config/fish/config.fish'), direnvHookCmd: 'direnv hook fish | source', + direnvInitCmd: ['direnv', ['hook', 'fish']], }; } else { return null; @@ -97,24 +107,17 @@ export default class EnvironmentVariableInit extends EasCommand { private async addToGitIgnoreAsync(): Promise { if (await pathExists('.gitignore')) { const gitignoreContent = await readFile('.gitignore', 'utf8'); - const envrcPresent = gitignoreContent.includes('.envrc'); - const envLocalPresent = - gitignoreContent.includes('.env.local') || gitignoreContent.includes('.env.*'); - if (!envrcPresent || !envLocalPresent) { + const filesToIgnore = ['.envrc', '.env.eas.local', '.env.eas.local.original']; + const linesToAdd = filesToIgnore.filter(file => !gitignoreContent.includes(file)); + + if (linesToAdd.length > 0) { const confirm = await confirmAsync({ - message: 'Do you want to add .envrc and .env.local to .gitignore?', + message: `Do you want to add ${linesToAdd.join(',')} to .gitignore?`, }); if (confirm) { - const linesToAdd = []; - if (!envrcPresent) { - linesToAdd.push('.envrc'); - } - if (!envLocalPresent) { - linesToAdd.push('.env.local'); - } await appendFile('.gitignore', linesToAdd.join('\n') + '\n', 'utf8'); - Log.log('.envrc and .env.local added to .gitignore'); + Log.log(`${linesToAdd.join(',')} added to .gitignore`); } else { Log.log('Skipping adding .envrc and .env.local to .gitignore'); } @@ -148,8 +151,6 @@ export default class EnvironmentVariableInit extends EasCommand { await writeFile('.envrc', ENVRC_TEMPLATE, 'utf8'); Log.log('.envrc file created'); } - Log.log('Running direnv allow...'); - await spawnAsync('direnv', ['allow']); } private async ensureDirenvInstalledAsync(): Promise { diff --git a/packages/eas-cli/src/commands/env/load.ts b/packages/eas-cli/src/commands/env/load.ts new file mode 100644 index 0000000000..79a0005c4c --- /dev/null +++ b/packages/eas-cli/src/commands/env/load.ts @@ -0,0 +1,67 @@ +import assert from 'assert'; +import * as fs from 'fs-extra'; +import { exists } from 'fs-extra'; + +import { withSudoModeAsync } from '../../authUtils'; +import EasCommand from '../../commandUtils/EasCommand'; +import { EASEnvironmentFlag, EASNonInteractiveFlag } from '../../commandUtils/flags'; +import { EnvironmentVariableFragment } from '../../graphql/generated'; +import { EnvironmentVariablesQuery } from '../../graphql/queries/EnvironmentVariablesQuery'; +import Log from '../../log'; + +const EnvLocalFile = '.env.local'; +const EnvOriginalLocalFile = `${EnvLocalFile}.original`; + +export default class EnvironmentVariableLoad extends EasCommand { + static override description = 'change environment variables'; + + static override hidden = true; + + static override flags = { + ...EASEnvironmentFlag, + ...EASNonInteractiveFlag, + }; + static override contextDefinition = { + ...this.ContextOptions.ProjectConfig, + ...this.ContextOptions.LoggedIn, + ...this.ContextOptions.SessionManagment, + }; + + async runAsync(): Promise { + const { + flags: { environment, 'non-interactive': nonInteractive }, + } = await this.parse(EnvironmentVariableLoad); + + const { + privateProjectConfig: { projectId }, + loggedIn: { graphqlClient }, + sessionManager, + } = await this.getContextAsync(EnvironmentVariableLoad, { + nonInteractive, + }); + + assert(environment, 'Environment is required'); + + if ((await exists(EnvLocalFile)) && !(await exists(EnvOriginalLocalFile))) { + await fs.rename(EnvLocalFile, EnvOriginalLocalFile); + } + Log.log('Pulling environment variables...'); + + const environmentVariables = await withSudoModeAsync(sessionManager, async () => { + assert(environment); + return await EnvironmentVariablesQuery.byAppIdWithSensitiveAsync(graphqlClient, { + appId: projectId, + environment, + }); + }); + + const envFileContent = environmentVariables + .filter((variable: EnvironmentVariableFragment) => variable.value !== null) + .map((variable: EnvironmentVariableFragment) => { + return `${variable.name}=${variable.value}`; + }) + .join('\n'); + await fs.writeFile(EnvLocalFile, envFileContent); + Log.log(`Environment variables for ${environment} have been loaded.`); + } +} diff --git a/packages/eas-cli/src/commands/env/unload.ts b/packages/eas-cli/src/commands/env/unload.ts new file mode 100644 index 0000000000..c99eab23b8 --- /dev/null +++ b/packages/eas-cli/src/commands/env/unload.ts @@ -0,0 +1,23 @@ +import * as fs from 'fs-extra'; +import { exists } from 'fs-extra'; + +import EasCommand from '../../commandUtils/EasCommand'; +import Log from '../../log'; + +const EnvLocalFile = '.env.local'; +const EnvOriginalLocalFile = `${EnvLocalFile}.original`; + +export default class EnvironmentVariableUnload extends EasCommand { + static override description = 'unload environment variables'; + + static override hidden = true; + + async runAsync(): Promise { + if (await exists(EnvOriginalLocalFile)) { + await fs.rename(EnvOriginalLocalFile, EnvLocalFile); + } else { + await fs.remove(EnvLocalFile); + } + Log.log(`Unloaded environment variables.`); + } +}