eas-cli
Version:
EAS command line tool
151 lines (150 loc) • 7.76 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
const core_1 = require("@oclif/core");
const chalk_1 = tslib_1.__importDefault(require("chalk"));
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 EnvironmentSecretMutation_1 = require("../../graphql/mutations/EnvironmentSecretMutation");
const EnvironmentSecretsQuery_1 = require("../../graphql/queries/EnvironmentSecretsQuery");
const log_1 = tslib_1.__importDefault(require("../../log"));
const ora_1 = require("../../ora");
const projectUtils_1 = require("../../project/projectUtils");
const prompts_1 = require("../../prompts");
const intersection_1 = tslib_1.__importDefault(require("../../utils/expodash/intersection"));
class EnvironmentSecretPush extends EasCommand_1.default {
static description = 'read environment secrets from env file and store on the server';
static hidden = true;
static flags = {
scope: core_1.Flags.enum({
description: 'Scope for the secrets',
options: [EnvironmentSecretsQuery_1.EnvironmentSecretScope.ACCOUNT, EnvironmentSecretsQuery_1.EnvironmentSecretScope.PROJECT],
default: EnvironmentSecretsQuery_1.EnvironmentSecretScope.PROJECT,
}),
'env-file': core_1.Flags.string({
description: 'Env file with secrets',
}),
force: core_1.Flags.boolean({
description: 'Delete and recreate existing secrets',
default: false,
}),
...flags_1.EASNonInteractiveFlag,
};
static contextDefinition = {
...this.ContextOptions.ProjectId,
...this.ContextOptions.LoggedIn,
};
async runAsync() {
log_1.default.warn('This command is deprecated. Use eas env:push instead.');
log_1.default.newLine();
const { flags: { scope, force, 'env-file': maybeEnvFilePath, 'non-interactive': nonInteractive }, } = await this.parse(EnvironmentSecretPush);
const { projectId, loggedIn: { graphqlClient }, } = await this.getContextAsync(EnvironmentSecretPush, {
nonInteractive,
});
const projectDisplayName = await (0, projectUtils_1.getDisplayNameForProjectIdAsync)(graphqlClient, projectId);
const ownerAccount = await (0, projectUtils_1.getOwnerAccountForProjectIdAsync)(graphqlClient, projectId);
const envFilePath = await resolveEnvFilePathAsync(maybeEnvFilePath, nonInteractive);
if (!(await fs_extra_1.default.pathExists(envFilePath))) {
throw new Error(`File "${envFilePath}" does not exist`);
}
const newSecrets = dotenv_1.default.parse(await fs_extra_1.default.readFile(envFilePath));
await ensureSecretsDontExistOnServerAsync(graphqlClient, { projectId, scope, force }, newSecrets);
await createSecretsAsync(graphqlClient, {
scope,
accountId: ownerAccount.id,
accountName: ownerAccount.name,
projectId,
projectDisplayName,
}, newSecrets);
log_1.default.withTick(`Created the following secrets on ${scope} ${chalk_1.default.bold(scope === EnvironmentSecretsQuery_1.EnvironmentSecretScope.ACCOUNT ? ownerAccount.name : projectDisplayName)}:`);
for (const secretName of Object.keys(newSecrets)) {
log_1.default.log(`- ${secretName}`);
}
}
}
exports.default = EnvironmentSecretPush;
async function resolveEnvFilePathAsync(maybeEnvFilePath, nonInteractive) {
if (maybeEnvFilePath) {
return maybeEnvFilePath;
}
const validationMessage = 'Env file must be passed.';
if (nonInteractive) {
throw new Error(validationMessage);
}
const { envFilePath } = await (0, prompts_1.promptAsync)({
type: 'text',
name: 'envFilePath',
message: 'Path to the env file with secrets:',
// eslint-disable-next-line async-protect/async-suffix
validate: async (secretValueRaw) => {
if (!secretValueRaw) {
return validationMessage;
}
const envFilePath = path_1.default.resolve(secretValueRaw);
if (!(await fs_extra_1.default.pathExists(envFilePath))) {
return `File "${envFilePath}" does not exist.`;
}
return true;
},
});
return envFilePath;
}
async function ensureSecretsDontExistOnServerAsync(graphqlClient, { projectId, scope, force }, secrets) {
const serverSecrets = await readAllSecretsFromServerAsync(graphqlClient, projectId, scope);
const commonSecretNames = findCommonSecretNames(serverSecrets, secrets);
if (commonSecretNames.length > 0) {
if (!force) {
log_1.default.log(`This ${scope} already has environment secrets with the following names:`);
for (const name of commonSecretNames) {
log_1.default.log(`- ${name}`);
}
log_1.default.error('Run with --force flag to proceed');
core_1.Errors.exit(1);
}
else {
const spinner = (0, ora_1.ora)('Deleting secrets already present on server...').start();
const commonSecretNameSet = new Set(commonSecretNames);
const commonServerSecrets = serverSecrets.filter(({ name }) => commonSecretNameSet.has(name));
await deleteSecretsAsync(graphqlClient, commonServerSecrets);
spinner.succeed();
}
}
}
async function readAllSecretsFromServerAsync(graphqlClient, projectId, scope) {
const { appSecrets, accountSecrets } = await EnvironmentSecretsQuery_1.EnvironmentSecretsQuery.byAppIdAsync(graphqlClient, projectId);
return scope === EnvironmentSecretsQuery_1.EnvironmentSecretScope.ACCOUNT ? accountSecrets : appSecrets;
}
function findCommonSecretNames(serverSecrets, newSecrets) {
const serverSecretKeys = serverSecrets.map(({ name }) => name);
const newSecretKeys = Object.keys(newSecrets);
const commonKeys = (0, intersection_1.default)(serverSecretKeys, newSecretKeys);
return commonKeys;
}
async function deleteSecretsAsync(graphqlClient, secrets) {
const promises = secrets.map(secret => EnvironmentSecretMutation_1.EnvironmentSecretMutation.deleteAsync(graphqlClient, secret.id));
await Promise.all(promises);
}
async function createSecretsAsync(graphqlClient, { scope, accountId, accountName, projectId, projectDisplayName, }, secrets) {
const promises = [];
const spinner = (0, ora_1.ora)(`Creating secrets on ${scope} ${chalk_1.default.bold(scope === EnvironmentSecretsQuery_1.EnvironmentSecretScope.ACCOUNT ? accountName : projectDisplayName)}...`).start();
for (const [secretName, secretValue] of Object.entries(secrets)) {
if (scope === EnvironmentSecretsQuery_1.EnvironmentSecretScope.ACCOUNT) {
promises.push(EnvironmentSecretMutation_1.EnvironmentSecretMutation.createForAccountAsync(graphqlClient, { name: secretName, value: secretValue, type: generated_1.EnvironmentSecretType.String }, accountId));
}
else {
promises.push(EnvironmentSecretMutation_1.EnvironmentSecretMutation.createForAppAsync(graphqlClient, { name: secretName, value: secretValue, type: generated_1.EnvironmentSecretType.String }, projectId));
}
}
try {
await Promise.all(promises);
spinner.succeed();
}
catch (err) {
spinner.fail();
throw err;
}
}
;