Skip to content
This repository has been archived by the owner on Aug 8, 2023. It is now read-only.

Commit

Permalink
BEC-269 | Implement deploy-all script (#22)
Browse files Browse the repository at this point in the history
* Implement deploy-all script
  • Loading branch information
Ashar2shahid authored Apr 15, 2022
1 parent 5d7b011 commit 84b5c60
Show file tree
Hide file tree
Showing 6 changed files with 150 additions and 4 deletions.
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@
"scripts": {
"create-boilerplate": "ts-node src/create-boilerplate.ts",
"create-config": "ts-node src/create-config.ts",
"deploy-all": "yarn deploy-airnode; yarn deploy-airkeeper",
"deploy-airkeeper": "yarn ts-node src/deploy-airkeeper",
"deploy-airnode": "yarn ts-node src/deploy-airnode",
"generate-gateway-key": "ts-node src/generate-gateway-key.ts",
"build": "yarn run clean && yarn run compile",
"clean": "rimraf -rf ./dist *.tgz",
Expand Down
16 changes: 13 additions & 3 deletions src/create-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ const main = async () => {
const response = await promptQuestions(questions(apiChoices));
const apiData = operationsRepository.apis[response.apiName];

//// Build Config.json ////
//// Build config.json ////

// Get all the chains the API will be deployed on
const apiChains = [
Expand Down Expand Up @@ -148,7 +148,7 @@ const main = async () => {
apiCredentials,
};

//// Build Secrets.env ////
//// Build secrets.env ////

const oisSecrets = Object.values(apiData.ois).flatMap((ois) =>
Object.keys(ois.apiSpecifications.components.securitySchemes).map((security) =>
Expand All @@ -170,7 +170,16 @@ const main = async () => {
content: secretsArray.join('\n'),
};

//// Build Airkeeper.json ////
//// Build aws.env ////

const awsSecretsArray = [`AWS_ACCESS_KEY_ID=`, `AWS_SECRET_ACCESS_KEY=`, `# Optional`, `AWS_SESSION_TOKEN=`];

const aws = {
filename: '.env',
content: awsSecretsArray.join('\n'),
};

//// Build airkeeper.json ////

const airkeeperChains = apiChains.map((chainName) => {
const chainId = chainNameToChainId[chainName];
Expand Down Expand Up @@ -303,6 +312,7 @@ const main = async () => {
config,
airkeeper,
secrets,
aws,
},
},
},
Expand Down
68 changes: 68 additions & 0 deletions src/deploy-airkeeper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { join } from 'path';
import { PromptObject } from 'prompts';
import { OperationsRepository } from './types';
import { cliPrint, runAndHandleErrors, runShellCommand } from './utils/cli';
import { promptQuestions } from './utils/prompts';
import { readOperationsRepository } from './utils/read-operations';

const questions = (operationsRepository: OperationsRepository): PromptObject[] => {
return [
{
type: 'autocomplete',
name: 'name',
message: 'Which API Integration do you want to deploy?',
choices: Object.keys(operationsRepository.apis).map((api) => ({ title: api, value: api })),
},
{
type: 'autocomplete',
name: 'deployment',
message: (prev) => `Which deployment of ${prev} do you want to deploy?`,
choices: (prev) =>
Object.keys(operationsRepository.apis[prev].deployments).map((deployment) => ({
title: deployment,
value: deployment,
})),
},
];
};

const main = async () => {
const airkeeperVersion = require('@api3/airkeeper/package.json').version;
const operationsRepository = readOperationsRepository();
const response = await promptQuestions(questions(operationsRepository));

const deploymentDirectory = join(__dirname, '..', 'data', 'apis', response.name, 'deployments', response.deployment);
const awsSecretsFilePath = join(deploymentDirectory, 'aws.env');

const config = operationsRepository.apis[response.name].deployments[response.deployment].config;
const stage = config.nodeSettings.stage;
const cloudProvider = config.nodeSettings.cloudProvider.type;
if (cloudProvider === 'local') return cliPrint.error('🛑 Cloud provider is local. Please deploy to AWS/GCP.');

const region = config.nodeSettings.cloudProvider.region;
if (!region) return cliPrint.error('🛑 Cloud provider region is not set.');

const airkeeperDeployCommand = [
`docker run -it --rm`,
`--env-file ${awsSecretsFilePath}`,
`-v ${deploymentDirectory}:/app/config`,
`api3/airkeeper:${airkeeperVersion} deploy --stage ${stage} --region ${region}`,
].join(' ');

console.log(`⏳ - Deploying Airkeeper`);

const deployment = runShellCommand(airkeeperDeployCommand);

if (deployment.status !== 0) return cliPrint.error('🛑 Airkeeper deployment failed.');

console.log(
[
`🗽 - Airkeeper has been deployed`,
`⏩ - if you also deployed Airnode, please forward the "receipt.json" to the API3 team.`,
`The "receipt.json" contains sensitive information and should not be shared or made public.`,
],
join('\n')
);
};

runAndHandleErrors(main);
64 changes: 64 additions & 0 deletions src/deploy-airnode.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { existsSync } from 'fs';
import { join } from 'path';
import { PromptObject } from 'prompts';
import { OperationsRepository } from './types';
import { cliPrint, runAndHandleErrors, runShellCommand } from './utils/cli';
import { promptQuestions } from './utils/prompts';
import { readOperationsRepository } from './utils/read-operations';

const questions = (operationsRepository: OperationsRepository): PromptObject[] => {
return [
{
type: 'autocomplete',
name: 'name',
message: 'Which API Integration do you want to deploy?',
choices: Object.keys(operationsRepository.apis).map((api) => ({ title: api, value: api })),
},
{
type: 'autocomplete',
name: 'deployment',
message: (prev) => `Which deployment of ${prev} do you want to deploy?`,
choices: (prev) =>
Object.keys(operationsRepository.apis[prev].deployments).map((deployment) => ({
title: deployment,
value: deployment,
})),
},
];
};

const main = async () => {
const nodeVersion = require('@api3/airnode-node/package.json').version;
const operationsRepository = readOperationsRepository();
const response = await promptQuestions(questions(operationsRepository));

const deploymentDirectory = join(__dirname, '..', 'data', 'apis', response.name, 'deployments', response.deployment);
const awsSecretsFilePath = join(deploymentDirectory, 'aws.env');
const receiptPath = join(deploymentDirectory, 'receipt.json');

const airnodeDeployCommand = [
`docker run -it --rm`,
`-e USER_ID=$(id -u) -e GROUP_ID=$(id -g)`,
`--env-file ${awsSecretsFilePath}`,
`-v ${deploymentDirectory}:/app/config`,
`-v ${deploymentDirectory}:/app/output`,
`api3/airnode-deployer:${nodeVersion} deploy`,
].join(' ');

console.log(`⏳ - Deploying Airnode...`);

const deployment = runShellCommand(airnodeDeployCommand);

if (deployment.status !== 0 || !existsSync(receiptPath)) return cliPrint.error('🛑 Airnode deployment failed.');

console.log(
[
`☁ - Airnode has been deployed`,
`⏩ - Please forward the "receipt.json" in the deployments folder to the API3 team.`,
`The "receipt.json" contains sensitive information and should not be shared or made public.`,
],
join('\n')
);
};

runAndHandleErrors(main);
2 changes: 1 addition & 1 deletion src/utils/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export const runAndHandleErrors = (fn: () => Promise<unknown>) => {
*/
export const runShellCommand = (command: string) => {
cliPrint.info(command);
spawnSync(command, {
return spawnSync(command, {
shell: true,
stdio: 'inherit',
});
Expand Down
1 change: 1 addition & 0 deletions src/utils/validation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ export const deploymentSetSchema = z.object({
config: airnodeConfigSchema,
airkeeper: airkeeperConfigSchema,
secrets: secretsSchema,
aws: secretsSchema,
});

export const deploymentsSchema = z.record(deploymentSetSchema);
Expand Down

0 comments on commit 84b5c60

Please sign in to comment.