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)
Esempio n. 3
0
    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
Esempio n. 4
0
 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": ["**/*"]
             },
         })
Esempio n. 5
0
    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)
Esempio n. 6
0
    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
Esempio n. 7
0
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)
Esempio n. 8
0
    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")))
Esempio n. 9
0
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,
        )