def __init__(self, scope: core.Construct, id: str, *, project_name: str, github_owner, github_repo, buildspec_path = None, environment_variables = {}, base_branch: str = "main", release_branch: str = "bump_version", create_webhooks = False): build_environment = BuildEnvironment(build_image=self.BUILD_IMAGE, privileged = True, compute_type = ComputeType.LARGE) trigger_on_pr_merged = FilterGroup.in_event_of(EventAction.PULL_REQUEST_MERGED).and_base_branch_is(base_branch).and_branch_is(release_branch).and_commit_message_is("release:.*") if create_webhooks: github_source = Source.git_hub(owner = github_owner, report_build_status = True, repo = github_repo, webhook = True, webhook_filters = [trigger_on_pr_merged]) else: github_source = Source.git_hub(owner = github_owner, report_build_status = True, repo = github_repo) super().__init__(scope, id, project_name = project_name, environment_variables = environment_variables, build_spec=BuildSpec.from_object_to_yaml(BUILD_SPEC) if buildspec_path is None else BuildSpec.from_source_filename(buildspec_path), badge = True, source = github_source, environment = build_environment)
def __init__(self, scope: core.Construct, id: str, *, project_name: str, github_owner, github_repo, buildspec_path, environment_variables={}, base_branch: str = "main"): build_environment = BuildEnvironment(build_image=self.BUILD_IMAGE, privileged=True, compute_type=ComputeType.LARGE) trigger_on_pr = FilterGroup.in_event_of( EventAction.PULL_REQUEST_CREATED, EventAction.PULL_REQUEST_UPDATED).and_base_branch_is(base_branch) super().__init__( scope, id, project_name=project_name, environment_variables=environment_variables, build_spec=BuildSpec.from_source_filename(buildspec_path), badge=True, source=Source.git_hub(owner=github_owner, report_build_status=True, repo=github_repo, webhook=True, webhook_filters=[trigger_on_pr]), environment=build_environment)
def create_build_project(self, envs: EnvSettings): project = PipelineProject( self, "ImageResizeLambdaBuild", project_name=f"{envs.project_name}-build-image-resize-lambda", environment=BuildEnvironment( build_image=LinuxBuildImage.STANDARD_3_0), build_spec=BuildSpec.from_source_filename( "./infra/stacks/ci/buildspecs/image_resize.yaml"), ) return project
def create_build_spec(): return BuildSpec.from_object( value={ "version": "0.2", "phases": { "build": { "commands": [ "echo $CODEBUILD_RESOLVED_SOURCE_VERSION | cut -c 1-7 > VERSION" ] } }, "artifacts": { "files": ["**/*"] }, })
def __init__( self, scope: Construct, id: str, envs: EnvSettings, build_stage: IStage, input_artifact: Artifacts, ): super().__init__(scope, id) project = PipelineProject( self, "CDKStackBuild", project_name=f"{envs.project_name}-build-stack", environment=BuildEnvironment(build_image=LinuxBuildImage.STANDARD_4_0, privileged=True,), build_spec=BuildSpec.from_source_filename("./infra/stacks/ci/buildspecs/cdk.yaml"), cache=Cache.local(LocalCacheMode.CUSTOM), ) self.build_action = self.create_build_action("stack", project, input_artifact) build_stage.add_action(self.build_action)
def create_build_projects(self, envs: EnvSettings, functions: List[Function]): projects = [] for (function, code) in functions: function_name = self.get_function_base_name(function) project = PipelineProject( self, f"WorkerBuild-{function_name}", project_name=f"{envs.project_name}-build-{function_name}", environment=BuildEnvironment(build_image=LinuxBuildImage.STANDARD_3_0), build_spec=BuildSpec.from_source_filename("./infra/stacks/ci/buildspecs/workers.yaml"), ) projects.append((project, function_name, code, function)) return projects
def generate_buildspec(): buildspec = { "version": 0.2, "phases": { "install": { "runtime-versions": { "nodejs": "latest" }, }, "build": { "commands": [ "echo Build started on `date`", "pwd", "ls", "cd express-minapp/", "echo HOST=$HOST >> .env", "echo USERNAME=$USERNAME >> .env", "echo PASSWORD=$PASSWORD >> .env", "echo DATABASE=$DATABASE >> .env", "npm install" ], }, "post_build": { "commands": ["echo Build completed on `date`"] } }, "artifacts": { "files": [ "package.json", "src/index.js", "src/middlewares.js", "package-lock.json", ".env", ], "name": "express-minapp", "base-directory": "express-minapp" }, "cache": { "paths": ["node_modules/**/*"] } } return BuildSpec.from_object(buildspec)
def __init__(self, scope: core.Construct, construct_id: str, cert_arn: str, hosted_zone_id: str, domain_name: str, **kwargs) -> None: """ :param cert_arn: ARN of certificate to use :param hosted_zone_id: ID of hosted zone to use :param domain_name: Domain name to use """ super().__init__(scope, construct_id, **kwargs) ################################## # WEBSITE HOSTING INFRASTRUCTURE # ################################## # Grab hosted zone for the website to contain our records and an SSL certificate for HTTPS. These two have to # be grabbed from existing resources instead of created here because CloudFormation will time out waiting for a # newly-created cert to validate. self.hosted_zone = PublicHostedZone.from_public_hosted_zone_id( self, "personal-site-hosted-zone", hosted_zone_id) self.cert = Certificate.from_certificate_arn(self, "personal-site-cert", cert_arn) # Add an S3 bucket to host the website content self.website_bucket = Bucket(self, "personal-site-bucket", bucket_name=domain_name, removal_policy=RemovalPolicy.DESTROY, public_read_access=True, website_index_document="index.html", website_error_document="index.html") # Create a cloudfront distribution for the site self.distribution = Distribution( self, "personal-site-cf-distribution", default_behavior={ "origin": S3Origin(self.website_bucket), "allowed_methods": AllowedMethods.ALLOW_GET_HEAD_OPTIONS, "viewer_protocol_policy": ViewerProtocolPolicy.REDIRECT_TO_HTTPS }, certificate=self.cert, minimum_protocol_version=SecurityPolicyProtocol.TLS_V1_2_2019, enable_ipv6=True, domain_names=[domain_name, f"www.{domain_name}"]) # Point traffic to base and www.base to the cloudfront distribution, for both IPv4 and IPv6 ARecord(self, "personal-site-a-record", zone=self.hosted_zone, record_name=f"{domain_name}.", target=RecordTarget.from_alias( CloudFrontTarget(self.distribution))) ARecord(self, "personal-site-a-record-www", zone=self.hosted_zone, target=RecordTarget.from_alias( CloudFrontTarget(self.distribution)), record_name=f"www.{domain_name}.") AaaaRecord(self, "personal-site-aaaa-record", zone=self.hosted_zone, record_name=f"{domain_name}.", target=RecordTarget.from_alias( CloudFrontTarget(self.distribution))) AaaaRecord(self, "personal-site-aaaa-record-www", zone=self.hosted_zone, target=RecordTarget.from_alias( CloudFrontTarget(self.distribution)), record_name=f"www.{domain_name}.") ############################# # WEBSITE CD INFRASTRUCTURE # ############################# # CodeBuild project to build the website self.code_build_project = \ Project(self, "personal-site-builder", project_name="PersonalWebsite", description="Builds & deploys a personal static website on changes from GitHub", source=Source.git_hub( owner="c7c8", repo="crmyers.dev", clone_depth=1, branch_or_ref="master", webhook_filters=[ FilterGroup.in_event_of(EventAction.PUSH, EventAction.PULL_REQUEST_MERGED).and_branch_is( "master")]), artifacts=Artifacts.s3(bucket=self.website_bucket, include_build_id=False, package_zip=False, path="/"), build_spec=BuildSpec.from_object_to_yaml({ "version": "0.2", "phases": { "install": { "runtime-versions": { "nodejs": 10, } }, "pre_build": { "commands": ["npm install"] }, "build": { "commands": [ "npm run-script build &&", f"aws cloudfront create-invalidation --distribution-id={self.distribution.distribution_id} --paths '/*'" ] } }, "artifacts": { "files": ["./*"], "name": ".", "discard-paths": "no", "base-directory": "dist/crmyers-dev" } })) self.code_build_project.role.add_to_policy( PolicyStatement( effect=Effect.ALLOW, resources=[ f"arn:aws:cloudfront::{self.account}:distribution/{self.distribution.distribution_id}" ], actions=['cloudfront:CreateInvalidation'])) # Set up an SNS topic for text message notifications self.deployment_topic = Topic(self, 'personal-site-deployment-topic', topic_name='WebsiteDeployments', display_name='Website Deployments') self.deployment_topic.add_subscription(SmsSubscription("+19255968684")) self.code_build_project.on_build_failed( "BuildFailed", target=targets.SnsTopic(self.deployment_topic, message=RuleTargetInput.from_text( "Build for crmyers.dev FAILED"))) self.code_build_project.on_build_succeeded( "BuildSucceeded", target=targets.SnsTopic(self.deployment_topic, message=RuleTargetInput.from_text( "Build for crmyers.dev SUCCEEDED")))
class ApiCiConfig(Construct): backend_spec: BuildSpec = BuildSpec.from_source_filename( "./infra/stacks/ci/buildspecs/app.yaml") frontend_spec: BuildSpec = BuildSpec.from_source_filename( "./infra/stacks/ci/buildspecs/frontend.yaml") input_artifact: Artifact = None def __init__( self, scope: Construct, id: str, envs: EnvSettings, build_stage: IStage, repos: List[Repository], input_artifact: Artifact, ): super().__init__(scope, id) self.input_artifact = input_artifact backend_build_project = self.create_backend_build_project( envs, repos["app"]) front_build_project = self.create_front_build_project(envs, repos) build_stage.add_action( self.create_build_action("backend", backend_build_project, order=1)) build_stage.add_action( self.create_build_action("frontend", front_build_project, order=2)) def create_backend_build_project(self, envs: EnvSettings, repo: Repository): project = PipelineProject( self, "BackendBuild", project_name=f"{envs.project_name}-build-backend", build_spec=self.backend_spec, environment=BuildEnvironment( environment_variables={ "REPOSITORY_URI": BuildEnvironmentVariable(value=repo.repository_uri), "PUSH_IMAGES": BuildEnvironmentVariable(value="1"), }, build_image=LinuxBuildImage.STANDARD_2_0, privileged=True, ), cache=Cache.local(LocalCacheMode.DOCKER_LAYER), ) repo.grant_pull_push(project) return project def create_front_build_project(self, envs: EnvSettings, repos: dict): project = PipelineProject( self, "FrontendBuildProject", project_name=f"{envs.project_name}-build-frontend", environment=BuildEnvironment( environment_variables={ "NGINX_REPOSITORY_URI": BuildEnvironmentVariable( value=repos["nginx"].repository_uri), "APP_REPOSITORY_URI": BuildEnvironmentVariable( value=repos["app"].repository_uri), "WEBAPP_REPOSITORY_URI": BuildEnvironmentVariable( value=repos["webapp"].repository_uri), "PUSH_IMAGES": BuildEnvironmentVariable(value="1"), }, build_image=LinuxBuildImage.STANDARD_2_0, privileged=True, ), build_spec=self.frontend_spec, cache=Cache.local(LocalCacheMode.DOCKER_LAYER), ) for repo in repos.values(): repo.grant_pull_push(project) return project def create_build_action(self, name: str, project: PipelineProject, order: int): return CodeBuildAction(action_name=f"build-{name}", project=project, input=self.input_artifact, run_order=order) @staticmethod def prepare_api_changes(envs: EnvSettings, cdk_artifact: Artifact): return CloudFormationCreateReplaceChangeSetAction( action_name="prepare-app-changes", stack_name=f"{envs.project_name}-api", change_set_name="APIStagedChangeSet", admin_permissions=True, template_path=cdk_artifact.at_path( "infra/cdk.out/schema-cms-api.template.json"), run_order=2, ) @staticmethod def execute_api_changes(envs: EnvSettings): return CloudFormationExecuteChangeSetAction( action_name="execute-app-changes", stack_name=f"{envs.project_name}-api", change_set_name="APIStagedChangeSet", run_order=4, )