Skip to content

Instantly share code, notes, and snippets.

@matheus1lva
Created December 11, 2024 16:13
Show Gist options
  • Save matheus1lva/e3830702351c85ac5c5db8acb4a98c0b to your computer and use it in GitHub Desktop.
Save matheus1lva/e3830702351c85ac5c5db8acb4a98c0b to your computer and use it in GitHub Desktop.
import { CfnOutput } from 'aws-cdk-lib'
import * as codebuild from 'aws-cdk-lib/aws-codebuild'
import * as codepipeline_actions from 'aws-cdk-lib/aws-codepipeline-actions'
import { StackContext } from 'sst/constructs'
import { BasePipeline } from './base-pipeline'
export interface PipelineStackConfig {
readonly bitbucketOwner: string
readonly bitbucketRepo: string
readonly bitbucketConnectionArn?: string
}
export function BranchPipeline({ stack, app }: StackContext) {
const { pipeline, buildRole, sourceOutput, pipelineRole } = new BasePipeline(stack, 'MainPipeline', {
app,
stack,
baseName: 'branch-pipeline',
pullRequests: true,
})
const testProject = new codebuild.PipelineProject(stack, `BranchPipelineTestProject`, {
buildSpec: codebuild.BuildSpec.fromObject({
version: '0.2',
phases: {
install: {
'runtime-versions': {
nodejs: '20',
},
commands: ['npm install -g pnpm', 'pnpm install --frozen-lockfile --store-dir=/root/.pnpm-store'],
},
build: {
commands: ['pnpm test'],
},
},
cache: {
paths: ['node_modules/**/*', '**/node_modules/**/*', 'web/.next/cache/**/*', '/root/.pnpm-store/**/*'],
},
}),
environment: {
buildImage: codebuild.LinuxBuildImage.STANDARD_7_0,
privileged: true,
environmentVariables: {
DOCKER: { value: 'true' },
NODE_OPTIONS: { value: '--max_old_space_size=8192' },
},
},
role: buildRole,
})
pipeline.addStage({
stageName: 'Test',
actions: [
new codepipeline_actions.CodeBuildAction({
actionName: 'Test',
project: testProject,
input: sourceOutput,
role: pipelineRole,
}),
],
})
// Add outputs
new CfnOutput(stack, 'PipelineURL', {
value: `https://${stack.region}.console.aws.amazon.com/codepipeline/home?region=${stack.region}#/view/${pipeline.pipelineName}`,
description: 'Pipeline URL',
})
return {
pipeline,
}
}
import { CfnOutput, RemovalPolicy } from 'aws-cdk-lib'
import * as codebuild from 'aws-cdk-lib/aws-codebuild'
import * as codepipeline from 'aws-cdk-lib/aws-codepipeline'
import * as codepipeline_actions from 'aws-cdk-lib/aws-codepipeline-actions'
import * as iam from 'aws-cdk-lib/aws-iam'
import * as s3 from 'aws-cdk-lib/aws-s3'
import { Construct } from 'constructs'
import { StackContext, use } from 'sst/constructs'
import { BITBUCKET_BRANCH, BITBUCKET_OWNER, BITBUCKET_REPO } from './config'
import { Connections } from './connections'
export interface BasePipelineProps {
app: StackContext['app']
stack: StackContext['stack']
baseName: string
pullRequests?: boolean
branch?: string
triggerOnPush?: boolean
}
export class BasePipeline extends Construct {
pipeline: codepipeline.Pipeline
buildRole: iam.Role
sourceOutput: codepipeline.Artifact
sourceAction: codepipeline_actions.CodeStarConnectionsSourceAction
pipelineRole: iam.Role
constructor(scope: Construct, id: string, props: BasePipelineProps) {
super(scope, id)
const { app, stack, triggerOnPush = true } = props
const baseName = app.logicalPrefixedName(props.baseName)
this.sourceOutput = new codepipeline.Artifact(`${baseName}SourceOutput`)
const branch = props.branch ?? BITBUCKET_BRANCH
this.buildRole = new iam.Role(stack, `${baseName}CodeBuildRole`, {
assumedBy: new iam.ServicePrincipal('codebuild.amazonaws.com'),
})
const { connectionArn, connectionPolicy } = use(Connections)
this.buildRole.addToPolicy(
new iam.PolicyStatement({
effect: iam.Effect.ALLOW,
actions: [
'logs:CreateLogGroup',
'logs:CreateLogStream',
'logs:PutLogEvents',
's3:GetBucketAcl',
's3:GetBucketLocation',
's3:GetObject',
's3:GetObjectVersion',
's3:PutObject',
'sts:AssumeRole',
],
resources: ['*'],
}),
)
// Grant permissions to the build role
this.buildRole.addManagedPolicy(iam.ManagedPolicy.fromAwsManagedPolicyName('AdministratorAccess'))
// Create Pipeline role
this.pipelineRole = new iam.Role(stack, `${baseName}CodePipelineRole`, {
assumedBy: new iam.ServicePrincipal('codepipeline.amazonaws.com'),
})
this.pipelineRole.addToPolicy(
new iam.PolicyStatement({
effect: iam.Effect.ALLOW,
actions: [
'logs:CreateLogGroup',
'logs:CreateLogStream',
'logs:PutLogEvents',
's3:GetObject',
's3:GetObjectVersion',
's3:GetBucketVersioning',
's3:PutObject',
'codebuild:BatchGetBuilds',
'codebuild:StartBuild',
'codebuild:StopBuild',
'codestar-connections:UseConnection',
'codestar-connections:GetConnection',
'codestar-connections:PassConnection',
],
resources: ['*'],
}),
)
this.pipelineRole.addManagedPolicy(connectionPolicy)
this.sourceAction = new codepipeline_actions.CodeStarConnectionsSourceAction({
actionName: 'Source',
owner: BITBUCKET_OWNER,
repo: BITBUCKET_REPO,
branch: branch,
output: this.sourceOutput,
connectionArn: connectionArn,
triggerOnPush,
variablesNamespace: 'pipeVariables',
codeBuildCloneOutput: true,
role: this.pipelineRole,
})
// create the artifact bucket
const artifactBucket = new s3.Bucket(stack, `${baseName}ArtifactBucket`, {
versioned: true,
removalPolicy: RemovalPolicy.DESTROY,
autoDeleteObjects: true,
})
// Create Pipeline
this.pipeline = new codepipeline.Pipeline(stack, `${baseName}Pipeline`, {
pipelineType: codepipeline.PipelineType.V2,
pipelineName: `${baseName}-pipeline`,
crossAccountKeys: true,
restartExecutionOnUpdate: true,
executionMode: codepipeline.ExecutionMode.PARALLEL,
role: this.pipelineRole,
artifactBucket,
...(props.pullRequests && {
triggers: [
{
providerType: codepipeline.ProviderType.CODE_STAR_SOURCE_CONNECTION,
gitConfiguration: {
sourceAction: this.sourceAction,
pullRequestFilter: [
{
events: [codepipeline.GitPullRequestEvent.OPEN, codepipeline.GitPullRequestEvent.UPDATED],
branchesIncludes: [branch],
},
],
},
},
],
}),
})
// Create Lint Project
const lintProject = new codebuild.PipelineProject(stack, `${baseName}LintProject`, {
buildSpec: codebuild.BuildSpec.fromObject({
version: '0.2',
phases: {
install: {
'runtime-versions': {
nodejs: '20',
},
commands: ['npm install -g pnpm', 'pnpm install --store-dir=/root/.pnpm-store'],
},
build: {
commands: ['pnpm lint'],
},
},
cache: {
paths: ['node_modules/**/*', '**/node_modules/**/*', 'web/.next/cache/**/*', '/root/.pnpm-store/**/*'],
},
}),
environment: {
buildImage: codebuild.LinuxBuildImage.STANDARD_7_0,
privileged: true,
environmentVariables: {
DOCKER: { value: 'true' },
NODE_OPTIONS: { value: '--max_old_space_size=8192' },
},
},
role: this.buildRole,
})
this.pipeline.addStage({
stageName: 'Source',
actions: [this.sourceAction],
})
this.pipeline.addStage({
stageName: 'Lint',
actions: [
new codepipeline_actions.CodeBuildAction({
actionName: 'Lint',
project: lintProject,
input: this.sourceOutput,
role: this.pipelineRole,
}),
],
})
new CfnOutput(stack, `${baseName}PipelineURL`, {
value: `https://${stack.region}.console.aws.amazon.com/codepipeline/home?region=${stack.region}#/view/${this.pipeline.pipelineName}`,
description: 'Pipeline URL',
})
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment