packages/deploy-aws/src/lib/get_stage_configs.ts
import { Artifact } from '@aws-cdk/aws-codepipeline-api'
import { StageConfigParams } from '../types'
const curlData = `-d '{ "state": "'$PIPELINE_STATE'", "target_url": "'$TARGET_URL'", "description": "'"$PIPELINE_DESC"' - Seagull Test CI", "context": "'$TEST_PIPELINE_CONTEXT'"}'`
export function getSourceConfig(params: StageConfigParams, index: number) {
return {
atIndex: index,
branch: params.branch,
oauthToken: params.ssmSecret.secret,
owner: params.owner,
pipeline: params.pipeline,
poll: params.poll,
repo: params.repo,
}
}
export function getBuildConfig(
params: StageConfigParams,
index: number,
inputArtifact?: Artifact
) {
return {
...getCommonConfig(params, index, inputArtifact),
build: getBuild(params),
}
}
export function getTestConfig(
params: StageConfigParams,
index: number,
inputArtifact?: Artifact
) {
return {
...getCommonConfig(params, index, inputArtifact),
postBuild: getTest(params),
}
}
export function getTestEnd2EndConfig(
params: StageConfigParams,
index: number,
inputArtifact?: Artifact
) {
return {
...getCommonConfig(params, index, inputArtifact),
postBuild: getTestEnd2End(params),
}
}
export function getDeployConfig(
params: StageConfigParams,
index: number,
inputArtifact?: Artifact
) {
return {
...getCommonConfig(params, index, inputArtifact),
// build: getBuild(params),
postBuild: getDeploy(params),
}
}
function getCommonConfig(
params: StageConfigParams,
index: number,
inputArtifact?: Artifact
) {
return {
atIndex: index,
build: { commands: [], finally: [] },
computeTypeSize: params.computeTypeSize,
env: getEnv(params),
inputArtifact,
install: getInstall(params),
pipeline: params.pipeline,
postBuild: { commands: [] },
role: params.role,
}
}
function getInstall(params: StageConfigParams) {
const curlCmd = getCurlToSendTestResult(params.owner, params.repo, curlData)
const upgradeNpm = addStateChangeToCmd('npm i -g npm@7.7.6')
const runInstall = addStateChangeToCmd('npm ci')
const commands = [curlCmd, upgradeNpm, checkState(), runInstall, checkState()]
return getCommandConfig(params, commands)
}
function getBuild(params: StageConfigParams) {
// Fixes an issue where the max file watch count is exceeded, triggering ENOSPC
// https://stackoverflow.com/questions/22475849/node-js-error-enospc#32600959
const incFileWatcherLimit = addStateChangeToCmd(
`echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf && sudo sysctl -p`
)
const runBuild = addStateChangeToCmd('npm run build')
return getCommandConfig(params, [incFileWatcherLimit, runBuild, checkState()])
}
function getTest(params: StageConfigParams) {
const runTest = addStateChangeToCmd('npm run test')
return getCommandConfig(params, [runTest, checkState()])
}
function getTestEnd2End(params: StageConfigParams) {
const commands = [
'export CFURL="https://$(cat $CODEBUILD_SRC_DIR_cfurl/tmp/cfurl.txt;)"; export TARGET_URL=$CFURL',
'echo "Cloudfront URL: $CFURL"',
addStateChangeToCmd(`npm run test:e2e`),
checkState(),
`export PIPELINE_STATE="success";export PIPELINE_DESC="successful";`,
]
return getCommandConfig(params, commands)
}
function getDeploy(params: StageConfigParams) {
const mergeCommands = [...Array(params.buildWorkers).keys()].map(i =>
addStateChangeToCmd(`cp -Rf $CODEBUILD_SRC_DIR_dist${i}/dist/* dist`)
)
const commands = [
addStateChangeToCmd('mkdir dist'),
...mergeCommands,
checkState(),
addStateChangeToCmd('npm run deploy'),
checkState(),
sendDeploymentInfo(params.owner, params.repo),
]
return getCommandConfig(params, commands)
}
function getCommandConfig(params: StageConfigParams, commands: string[]) {
const curlCmd = getCurlToSendTestResult(params.owner, params.repo, curlData)
return { commands, finally: [curlCmd] }
}
function sendDeploymentInfo(owner: string, name: string) {
const data = `-d '{ "state": "success", "target_url": "'"https://$(cat /tmp/cfurl.txt)"'", "description": "repository was successfully deployed", "context": "continuous-integration/seagull-deployment"}'`
return getCurlToSendTestResult(owner, name, data)
}
function checkState() {
return `if [ "$PIPELINE_STATE" = "failure" ]; then exit 1; fi`
}
function addStateChangeToCmd(cmd: string) {
return `export PIPELINE_DESC='${cmd}'; if ${cmd}; then echo "success at ${cmd}!"; else export PIPELINE_STATE="failure"; fi`
}
function getCurlToSendTestResult(owner: string, name: string, data: string) {
const curlDomain = 'https://api.github.com'
const curlPath = `/repos/${owner}/${name}/statuses/$CODEBUILD_RESOLVED_SOURCE_VERSION`
const curlParams = `?access_token=$ACCESS_TOKEN`
const curlUrl = `"${curlDomain}${curlPath}${curlParams}"`
const curlHeaders = `-H 'Content-Type: application/json'`
return `curl -s -g -X POST ${curlUrl} ${curlHeaders} ${data} > /dev/null`
}
function getEnv(params: StageConfigParams) {
const parameterStore = { ACCESS_TOKEN: params.ssmSecret.name }
const variables = {
BRANCH_NAME: params.branch,
COMPUTE_TYPE_SIZE: params.computeTypeSize,
DEPLOY_MODE: params.stage,
PIPELINE_DESC: 'Bootstraping pipeline',
PIPELINE_STATE: 'pending',
STAGE: params.stage,
TARGET_URL: params.pipelineLink,
TEST_PIPELINE_CONTEXT: 'continuous-integration/seagull',
VPC_ID: params.vpcId,
SUBNET_IDS: params.subnetIds,
...(params.excludedPages ? { PAGES_TO_EXCLUDE: params.excludedPages } : {}),
}
return { 'parameter-store': parameterStore, variables }
}