eas-cli
Version:
EAS command line tool
183 lines (182 loc) • 9.81 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
const core_1 = require("@oclif/core");
const dotenv_1 = tslib_1.__importDefault(require("dotenv"));
const fs_extra_1 = tslib_1.__importDefault(require("fs-extra"));
const path_1 = tslib_1.__importDefault(require("path"));
const EasCommand_1 = tslib_1.__importDefault(require("../../commandUtils/EasCommand"));
const flags_1 = require("../../commandUtils/flags");
const generated_1 = require("../../graphql/generated");
const EnvironmentVariableMutation_1 = require("../../graphql/mutations/EnvironmentVariableMutation");
const EnvironmentVariablesQuery_1 = require("../../graphql/queries/EnvironmentVariablesQuery");
const log_1 = tslib_1.__importDefault(require("../../log"));
const prompts_1 = require("../../prompts");
const prompts_2 = require("../../utils/prompts");
const variableUtils_1 = require("../../utils/variableUtils");
class EnvPush extends EasCommand_1.default {
static description = 'push environment variables from .env file to the selected environment';
static contextDefinition = {
...this.ContextOptions.ProjectId,
...this.ContextOptions.LoggedIn,
};
static flags = {
...flags_1.EASMultiEnvironmentFlag,
path: core_1.Flags.string({
description: 'Path to the input `.env` file',
default: '.env.local',
}),
};
static args = [
{
name: 'environment',
description: "Environment to push variables to. One of 'production', 'preview', or 'development'.",
required: false,
},
];
async runAsync() {
const { args, flags } = await this.parse(EnvPush);
let { environment: environments, path: envPath } = this.parseFlagsAndArgs(flags, args);
const { projectId, loggedIn: { graphqlClient }, } = await this.getContextAsync(EnvPush, {
nonInteractive: false,
});
if (!environments) {
environments = await (0, prompts_2.promptVariableEnvironmentAsync)({
nonInteractive: false,
multiple: true,
});
}
const updateVariables = await this.parseEnvFileAsync(envPath, environments);
const variableNames = Object.keys(updateVariables);
for (const environment of environments) {
const displayedEnvironment = environment.toLocaleLowerCase();
const existingVariables = await EnvironmentVariablesQuery_1.EnvironmentVariablesQuery.byAppIdAsync(graphqlClient, {
appId: projectId,
environment,
filterNames: variableNames,
});
const existingDifferentVariables = [];
// Remove variables that are the same as the ones in the environment
existingVariables.forEach(existingVariable => {
const existingVariableUpdate = updateVariables[existingVariable.name];
if (existingVariableUpdate) {
const hasMoreEnvironments = existingVariableUpdate.environments.some(newEnv => !existingVariable.environments?.includes(newEnv));
if (existingVariableUpdate.value !== existingVariable.value || hasMoreEnvironments) {
existingDifferentVariables.push(existingVariable);
}
else {
delete updateVariables[existingVariable.name];
}
}
});
const existingDifferentSharedVariables = existingDifferentVariables.filter(variable => variable.scope === generated_1.EnvironmentVariableScope.Shared);
if (existingDifferentSharedVariables.length > 0) {
const existingDifferentSharedVariablesNames = existingDifferentSharedVariables.map(variable => variable.name);
log_1.default.error('Account-wide variables cannot be overwritten by eas env:push command.');
log_1.default.error('Remove them from the env file or unlink them from the project to continue:');
existingDifferentSharedVariablesNames.forEach(name => {
log_1.default.error(`- ${name}`);
});
throw new Error('Account-wide variables cannot be overwritten by eas env:push command');
}
if (existingDifferentVariables.length > 0) {
log_1.default.warn(`Some variables already exist in the ${displayedEnvironment} environment.`);
const variableNames = existingDifferentVariables.map(variable => variable.name);
const confirmationMessage = variableNames.length > 1
? `The ${variableNames.join(', ')} environment variables already exist in ${displayedEnvironment} environment. Do you want to override them all?`
: `The ${variableNames[0]} environment variable already exists in ${displayedEnvironment} environment. Do you want to override it?`;
const confirm = await (0, prompts_1.confirmAsync)({
message: confirmationMessage,
});
let variablesToOverwrite = [];
if (!confirm && existingDifferentVariables.length === 0) {
throw new Error('No new variables to push.');
}
if (confirm) {
variablesToOverwrite = existingDifferentVariables.map(variable => variable.name);
}
else {
const promptResult = await (0, prompts_1.promptAsync)({
type: 'multiselect',
name: 'variablesToOverwrite',
message: 'Select variables to overwrite:',
// @ts-expect-error property missing from `@types/prompts`
optionsPerPage: 20,
choices: existingDifferentVariables.map(variable => ({
title: `${variable.name}: ${updateVariables[variable.name].value} (was ${variable.value ?? '(secret)'})`,
value: variable.name,
})),
});
variablesToOverwrite = promptResult.variablesToOverwrite;
}
for (const existingVariable of existingVariables) {
const name = existingVariable.name;
if (variablesToOverwrite.includes(name)) {
updateVariables[name]['overwrite'] = true;
}
else {
delete updateVariables[name];
}
}
}
// Check if any of the sensitive variables already exist in the environment. Prompt the user to overwrite them.
const existingSensitiveVariables = existingVariables.filter(variable => variable.visibility !== generated_1.EnvironmentVariableVisibility.Public);
if (existingSensitiveVariables.length > 0) {
const existingSensitiveVariablesNames = existingSensitiveVariables.map(variable => `- ${variable.name}`);
const confirm = await (0, prompts_1.confirmAsync)({
message: `You are about to overwrite sensitive variables.\n${existingSensitiveVariablesNames.join('\n')}\n Do you want to continue?`,
});
if (!confirm) {
throw new Error('Aborting...');
}
}
}
const variablesToPush = Object.values(updateVariables);
if (variablesToPush.length === 0) {
log_1.default.log('No new variables to push.');
return;
}
await EnvironmentVariableMutation_1.EnvironmentVariableMutation.createBulkEnvironmentVariablesForAppAsync(graphqlClient, variablesToPush, projectId);
log_1.default.log(`Uploaded env file to ${environments.join(', ').toLocaleLowerCase()}.`);
}
parseFlagsAndArgs(flags, { environment }) {
if (environment && !(0, variableUtils_1.isEnvironment)(environment.toUpperCase())) {
throw new Error("Invalid environment. Use one of 'production', 'preview', or 'development'.");
}
const environments = flags.environment ??
(environment ? [environment.toUpperCase()] : undefined);
return {
...flags,
environment: environments,
};
}
async parseEnvFileAsync(envPath, environments) {
if (!(await fs_extra_1.default.exists(envPath))) {
throw new Error(`File ${envPath} does not exist.`);
}
const pushInput = {};
const variables = dotenv_1.default.parse(await fs_extra_1.default.readFile(envPath, 'utf8'));
const hasFileVariables = Object.values(variables).some(value => value.includes(path_1.default.join('.eas', '.env')));
if (hasFileVariables) {
log_1.default.warn('File variables are not supported in this command.');
}
for (const [name, value] of Object.entries(variables)) {
// Skip file variables
const fileVariablePath = path_1.default.join('.eas', '.env', name);
if (value.endsWith(fileVariablePath)) {
log_1.default.warn(`Skipping file variable ${name}`);
continue;
}
pushInput[name] = {
name,
value,
environments,
visibility: name.startsWith('EXPO_SENSITIVE')
? generated_1.EnvironmentVariableVisibility.Sensitive
: generated_1.EnvironmentVariableVisibility.Public,
};
}
return pushInput;
}
}
exports.default = EnvPush;
;