From 8c15b5f19a0c289fd0dad8c35b6734232d957836 Mon Sep 17 00:00:00 2001 From: dviryamin Date: Mon, 28 Oct 2024 19:45:10 +0200 Subject: [PATCH] feat(codebuild): add support of organization webhook in github source (#31740) ### Issue # (if applicable) closes #31736 ### Reason for this change Currently, the AWS CDK `aws-codebuild` module lacks native support for creating GitHub organization-level webhooks through the `Source` construct. Users need to manually use the `CfnProject` to enable organization webhooks, which adds complexity and inconsistency to the CodeBuild setup. This PR introduces support for creating GitHub organization webhooks directly within the `Source` construct, streamlining the experience and making it more consistent with the rest of the AWS CDK's high-level constructs. ### Description of changes - Enhanced the `GitHubSource` construct in the `aws-codebuild` module to support GitHub organization webhooks. - Updated the `GitHubSource` construct's configuration to so when repo is not specified an organization webhook is created. - Added filter of `REPOSITORY_NAME` to FilterGroups. ### Description of how you validated changes - [x] Added unit tests to validate that organization-level webhooks are created correctly when repo is not specified. - [x] Ran integration tests to ensure that the changes do not break any existing functionality related to project webhooks. - [x] Tested deployment of a CodeBuild project with organization webhook setup in a sample CDK application to verify end-to-end functionality. ### Checklist - [x] My code adheres to the [CONTRIBUTING GUIDE](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) and [DESIGN GUIDELINES](https://github.com/aws/aws-cdk/blob/main/docs/DESIGN_GUIDELINES.md) ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../cdk.out | 1 + .../codebuild-github-org-webhook.assets.json | 19 ++ ...codebuild-github-org-webhook.template.json | 181 +++++++++++ ...efaultTestDeployAssertB287C481.assets.json | 19 ++ ...aultTestDeployAssertB287C481.template.json | 36 +++ .../integ.json | 13 + .../manifest.json | 127 ++++++++ .../tree.json | 306 ++++++++++++++++++ .../test/integ.github-org-webhook.ts | 30 ++ packages/aws-cdk-lib/aws-codebuild/README.md | 17 +- .../aws-cdk-lib/aws-codebuild/lib/source.ts | 53 ++- .../aws-codebuild/test/project.test.ts | 72 +++++ 12 files changed, 866 insertions(+), 8 deletions(-) create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-codebuild/test/integ.github-org-webhook.js.snapshot/cdk.out create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-codebuild/test/integ.github-org-webhook.js.snapshot/codebuild-github-org-webhook.assets.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-codebuild/test/integ.github-org-webhook.js.snapshot/codebuild-github-org-webhook.template.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-codebuild/test/integ.github-org-webhook.js.snapshot/codebuildgithuborgwebhookintegDefaultTestDeployAssertB287C481.assets.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-codebuild/test/integ.github-org-webhook.js.snapshot/codebuildgithuborgwebhookintegDefaultTestDeployAssertB287C481.template.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-codebuild/test/integ.github-org-webhook.js.snapshot/integ.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-codebuild/test/integ.github-org-webhook.js.snapshot/manifest.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-codebuild/test/integ.github-org-webhook.js.snapshot/tree.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-codebuild/test/integ.github-org-webhook.ts diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-codebuild/test/integ.github-org-webhook.js.snapshot/cdk.out b/packages/@aws-cdk-testing/framework-integ/test/aws-codebuild/test/integ.github-org-webhook.js.snapshot/cdk.out new file mode 100644 index 0000000000000..c6e612584e352 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-codebuild/test/integ.github-org-webhook.js.snapshot/cdk.out @@ -0,0 +1 @@ +{"version":"38.0.1"} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-codebuild/test/integ.github-org-webhook.js.snapshot/codebuild-github-org-webhook.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-codebuild/test/integ.github-org-webhook.js.snapshot/codebuild-github-org-webhook.assets.json new file mode 100644 index 0000000000000..c2cf28e236d72 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-codebuild/test/integ.github-org-webhook.js.snapshot/codebuild-github-org-webhook.assets.json @@ -0,0 +1,19 @@ +{ + "version": "38.0.1", + "files": { + "48cf06ed9dfbc62f5e0225887741d30dca747e7ae3fcda0cb25f83c07f8c64bc": { + "source": { + "path": "codebuild-github-org-webhook.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "48cf06ed9dfbc62f5e0225887741d30dca747e7ae3fcda0cb25f83c07f8c64bc.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-codebuild/test/integ.github-org-webhook.js.snapshot/codebuild-github-org-webhook.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-codebuild/test/integ.github-org-webhook.js.snapshot/codebuild-github-org-webhook.template.json new file mode 100644 index 0000000000000..55b9cfe7a69c2 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-codebuild/test/integ.github-org-webhook.js.snapshot/codebuild-github-org-webhook.template.json @@ -0,0 +1,181 @@ +{ + "Resources": { + "MyProjectRole9BBE5233": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "codebuild.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "MyProjectRoleDefaultPolicyB19B7C29": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "logs:CreateLogGroup", + "logs:CreateLogStream", + "logs:PutLogEvents" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":logs:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":log-group:/aws/codebuild/", + { + "Ref": "MyProject39F7B0AE" + }, + ":*" + ] + ] + }, + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":logs:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":log-group:/aws/codebuild/", + { + "Ref": "MyProject39F7B0AE" + } + ] + ] + } + ] + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "MyProjectRoleDefaultPolicyB19B7C29", + "Roles": [ + { + "Ref": "MyProjectRole9BBE5233" + } + ] + } + }, + "MyProject39F7B0AE": { + "Type": "AWS::CodeBuild::Project", + "Properties": { + "Artifacts": { + "Type": "NO_ARTIFACTS" + }, + "Cache": { + "Type": "NO_CACHE" + }, + "EncryptionKey": "alias/aws/s3", + "Environment": { + "ComputeType": "BUILD_GENERAL1_SMALL", + "Image": "aws/codebuild/standard:7.0", + "ImagePullCredentialsType": "CODEBUILD", + "PrivilegedMode": false, + "Type": "LINUX_CONTAINER" + }, + "ServiceRole": { + "Fn::GetAtt": [ + "MyProjectRole9BBE5233", + "Arn" + ] + }, + "Source": { + "Location": "CODEBUILD_DEFAULT_WEBHOOK_SOURCE_LOCATION", + "ReportBuildStatus": false, + "Type": "GITHUB" + }, + "Triggers": { + "FilterGroups": [ + [ + { + "Pattern": "WORKFLOW_JOB_QUEUED", + "Type": "EVENT" + }, + { + "Pattern": "aws-cdk.*", + "Type": "REPOSITORY_NAME" + }, + { + "ExcludeMatchedPattern": true, + "Pattern": "aws-cdk-lib", + "Type": "REPOSITORY_NAME" + } + ] + ], + "ScopeConfiguration": { + "Name": "aws" + }, + "Webhook": true + } + } + } + }, + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-codebuild/test/integ.github-org-webhook.js.snapshot/codebuildgithuborgwebhookintegDefaultTestDeployAssertB287C481.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-codebuild/test/integ.github-org-webhook.js.snapshot/codebuildgithuborgwebhookintegDefaultTestDeployAssertB287C481.assets.json new file mode 100644 index 0000000000000..cd6049c5c7e20 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-codebuild/test/integ.github-org-webhook.js.snapshot/codebuildgithuborgwebhookintegDefaultTestDeployAssertB287C481.assets.json @@ -0,0 +1,19 @@ +{ + "version": "38.0.1", + "files": { + "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { + "source": { + "path": "codebuildgithuborgwebhookintegDefaultTestDeployAssertB287C481.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-codebuild/test/integ.github-org-webhook.js.snapshot/codebuildgithuborgwebhookintegDefaultTestDeployAssertB287C481.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-codebuild/test/integ.github-org-webhook.js.snapshot/codebuildgithuborgwebhookintegDefaultTestDeployAssertB287C481.template.json new file mode 100644 index 0000000000000..ad9d0fb73d1dd --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-codebuild/test/integ.github-org-webhook.js.snapshot/codebuildgithuborgwebhookintegDefaultTestDeployAssertB287C481.template.json @@ -0,0 +1,36 @@ +{ + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-codebuild/test/integ.github-org-webhook.js.snapshot/integ.json b/packages/@aws-cdk-testing/framework-integ/test/aws-codebuild/test/integ.github-org-webhook.js.snapshot/integ.json new file mode 100644 index 0000000000000..b4db89f11ab70 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-codebuild/test/integ.github-org-webhook.js.snapshot/integ.json @@ -0,0 +1,13 @@ +{ + "version": "38.0.1", + "testCases": { + "codebuild-github-org-webhook-integ/DefaultTest": { + "stacks": [ + "codebuild-github-org-webhook" + ], + "stackUpdateWorkflow": true, + "assertionStack": "codebuild-github-org-webhook-integ/DefaultTest/DeployAssert", + "assertionStackName": "codebuildgithuborgwebhookintegDefaultTestDeployAssertB287C481" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-codebuild/test/integ.github-org-webhook.js.snapshot/manifest.json b/packages/@aws-cdk-testing/framework-integ/test/aws-codebuild/test/integ.github-org-webhook.js.snapshot/manifest.json new file mode 100644 index 0000000000000..8cd0636c22693 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-codebuild/test/integ.github-org-webhook.js.snapshot/manifest.json @@ -0,0 +1,127 @@ +{ + "version": "38.0.1", + "artifacts": { + "codebuild-github-org-webhook.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "codebuild-github-org-webhook.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "codebuild-github-org-webhook": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "codebuild-github-org-webhook.template.json", + "terminationProtection": false, + "validateOnSynth": false, + "notificationArns": [], + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/48cf06ed9dfbc62f5e0225887741d30dca747e7ae3fcda0cb25f83c07f8c64bc.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "codebuild-github-org-webhook.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "codebuild-github-org-webhook.assets" + ], + "metadata": { + "/codebuild-github-org-webhook/MyProject/Role/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "MyProjectRole9BBE5233" + } + ], + "/codebuild-github-org-webhook/MyProject/Role/DefaultPolicy/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "MyProjectRoleDefaultPolicyB19B7C29" + } + ], + "/codebuild-github-org-webhook/MyProject/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "MyProject39F7B0AE" + } + ], + "/codebuild-github-org-webhook/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/codebuild-github-org-webhook/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "codebuild-github-org-webhook" + }, + "codebuildgithuborgwebhookintegDefaultTestDeployAssertB287C481.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "codebuildgithuborgwebhookintegDefaultTestDeployAssertB287C481.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "codebuildgithuborgwebhookintegDefaultTestDeployAssertB287C481": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "codebuildgithuborgwebhookintegDefaultTestDeployAssertB287C481.template.json", + "terminationProtection": false, + "validateOnSynth": false, + "notificationArns": [], + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "codebuildgithuborgwebhookintegDefaultTestDeployAssertB287C481.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "codebuildgithuborgwebhookintegDefaultTestDeployAssertB287C481.assets" + ], + "metadata": { + "/codebuild-github-org-webhook-integ/DefaultTest/DeployAssert/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/codebuild-github-org-webhook-integ/DefaultTest/DeployAssert/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "codebuild-github-org-webhook-integ/DefaultTest/DeployAssert" + }, + "Tree": { + "type": "cdk:tree", + "properties": { + "file": "tree.json" + } + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-codebuild/test/integ.github-org-webhook.js.snapshot/tree.json b/packages/@aws-cdk-testing/framework-integ/test/aws-codebuild/test/integ.github-org-webhook.js.snapshot/tree.json new file mode 100644 index 0000000000000..d2db9fbc0ef5d --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-codebuild/test/integ.github-org-webhook.js.snapshot/tree.json @@ -0,0 +1,306 @@ +{ + "version": "tree-0.1", + "tree": { + "id": "App", + "path": "", + "children": { + "codebuild-github-org-webhook": { + "id": "codebuild-github-org-webhook", + "path": "codebuild-github-org-webhook", + "children": { + "MyProject": { + "id": "MyProject", + "path": "codebuild-github-org-webhook/MyProject", + "children": { + "Role": { + "id": "Role", + "path": "codebuild-github-org-webhook/MyProject/Role", + "children": { + "ImportRole": { + "id": "ImportRole", + "path": "codebuild-github-org-webhook/MyProject/Role/ImportRole", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "codebuild-github-org-webhook/MyProject/Role/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Role", + "aws:cdk:cloudformation:props": { + "assumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "codebuild.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.CfnRole", + "version": "0.0.0" + } + }, + "DefaultPolicy": { + "id": "DefaultPolicy", + "path": "codebuild-github-org-webhook/MyProject/Role/DefaultPolicy", + "children": { + "Resource": { + "id": "Resource", + "path": "codebuild-github-org-webhook/MyProject/Role/DefaultPolicy/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Policy", + "aws:cdk:cloudformation:props": { + "policyDocument": { + "Statement": [ + { + "Action": [ + "logs:CreateLogGroup", + "logs:CreateLogStream", + "logs:PutLogEvents" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":logs:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":log-group:/aws/codebuild/", + { + "Ref": "MyProject39F7B0AE" + }, + ":*" + ] + ] + }, + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":logs:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":log-group:/aws/codebuild/", + { + "Ref": "MyProject39F7B0AE" + } + ] + ] + } + ] + } + ], + "Version": "2012-10-17" + }, + "policyName": "MyProjectRoleDefaultPolicyB19B7C29", + "roles": [ + { + "Ref": "MyProjectRole9BBE5233" + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.CfnPolicy", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.Policy", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.Role", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "codebuild-github-org-webhook/MyProject/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::CodeBuild::Project", + "aws:cdk:cloudformation:props": { + "artifacts": { + "type": "NO_ARTIFACTS" + }, + "cache": { + "type": "NO_CACHE" + }, + "encryptionKey": "alias/aws/s3", + "environment": { + "type": "LINUX_CONTAINER", + "image": "aws/codebuild/standard:1.0", + "imagePullCredentialsType": "CODEBUILD", + "privilegedMode": false, + "computeType": "BUILD_GENERAL1_SMALL" + }, + "serviceRole": { + "Fn::GetAtt": [ + "MyProjectRole9BBE5233", + "Arn" + ] + }, + "source": { + "type": "GITHUB", + "reportBuildStatus": false, + "location": "CODEBUILD_DEFAULT_WEBHOOK_SOURCE_LOCATION" + }, + "triggers": { + "webhook": true, + "filterGroups": [ + [ + { + "type": "EVENT", + "pattern": "WORKFLOW_JOB_QUEUED" + }, + { + "type": "REPOSITORY_NAME", + "pattern": "aws-cdk.*" + }, + { + "type": "REPOSITORY_NAME", + "pattern": "aws-cdk-lib", + "excludeMatchedPattern": true + } + ] + ], + "scopeConfiguration": { + "name": "aws" + } + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_codebuild.CfnProject", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_codebuild.Project", + "version": "0.0.0" + } + }, + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "codebuild-github-org-webhook/BootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "codebuild-github-org-webhook/CheckBootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnRule", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Stack", + "version": "0.0.0" + } + }, + "codebuild-github-org-webhook-integ": { + "id": "codebuild-github-org-webhook-integ", + "path": "codebuild-github-org-webhook-integ", + "children": { + "DefaultTest": { + "id": "DefaultTest", + "path": "codebuild-github-org-webhook-integ/DefaultTest", + "children": { + "Default": { + "id": "Default", + "path": "codebuild-github-org-webhook-integ/DefaultTest/Default", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "DeployAssert": { + "id": "DeployAssert", + "path": "codebuild-github-org-webhook-integ/DefaultTest/DeployAssert", + "children": { + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "codebuild-github-org-webhook-integ/DefaultTest/DeployAssert/BootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "codebuild-github-org-webhook-integ/DefaultTest/DeployAssert/CheckBootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnRule", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Stack", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests-alpha.IntegTestCase", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests-alpha.IntegTest", + "version": "0.0.0" + } + }, + "Tree": { + "id": "Tree", + "path": "Tree", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.App", + "version": "0.0.0" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-codebuild/test/integ.github-org-webhook.ts b/packages/@aws-cdk-testing/framework-integ/test/aws-codebuild/test/integ.github-org-webhook.ts new file mode 100644 index 0000000000000..a589403934b9e --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-codebuild/test/integ.github-org-webhook.ts @@ -0,0 +1,30 @@ +import * as cdk from 'aws-cdk-lib'; +import * as codebuild from 'aws-cdk-lib/aws-codebuild'; + +class GitHubOrgWebhookTestStack extends cdk.Stack { + constructor(scope: cdk.App, id: string) { + super(scope, id); + + const source = codebuild.Source.gitHub({ + owner: 'aws', + reportBuildStatus: false, + webhookFilters: [ + codebuild.FilterGroup.inEventOf( + codebuild.EventAction.WORKFLOW_JOB_QUEUED, + ) + .andRepositoryNameIs('aws-cdk.*') + .andRepositoryNameIsNot('aws-cdk-lib'), + ], + }); + new codebuild.Project(this, 'MyProject', { + source, + grantReportGroupPermissions: false, + }); + } +} + +const app = new cdk.App(); + +new GitHubOrgWebhookTestStack(app, 'codebuild-github-org-webhook'); + +app.synth(); diff --git a/packages/aws-cdk-lib/aws-codebuild/README.md b/packages/aws-cdk-lib/aws-codebuild/README.md index 303aaf40ff35e..28f22e0e8b461 100644 --- a/packages/aws-cdk-lib/aws-codebuild/README.md +++ b/packages/aws-cdk-lib/aws-codebuild/README.md @@ -61,7 +61,7 @@ Example: ```ts const gitHubSource = codebuild.Source.gitHub({ owner: 'awslabs', - repo: 'aws-cdk', + repo: 'aws-cdk', // optional, default: undefined if unspecified will create organization webhook webhook: true, // optional, default: true if `webhookFilters` were provided, false otherwise webhookTriggersBatchBuild: true, // optional, default is false webhookFilters: [ @@ -76,6 +76,21 @@ const gitHubSource = codebuild.Source.gitHub({ }); ``` +The `GitHubSource` is also able to trigger all repos in GitHub Organizations +Example: +```ts +const gitHubSource = codebuild.Source.gitHub({ + owner: 'aws', + webhookTriggersBatchBuild: true, // optional, default is false + webhookFilters: [ + codebuild.FilterGroup + .inEventOf(codebuild.EventAction.WORKFLOW_JOB_QUEUED) + .andRepositoryNameIs("aws-.*") + .andRepositoryNameIsNot("aws-cdk-lib"), + ], // optional, by default all pushes and Pull Requests will trigger a build +}); +``` + To provide GitHub credentials, please either go to AWS CodeBuild Console to connect or call `ImportSourceCredentials` to persist your personal access token. Example: diff --git a/packages/aws-cdk-lib/aws-codebuild/lib/source.ts b/packages/aws-cdk-lib/aws-codebuild/lib/source.ts index 5e2250fdafe3e..2bd0251234b34 100644 --- a/packages/aws-cdk-lib/aws-codebuild/lib/source.ts +++ b/packages/aws-cdk-lib/aws-codebuild/lib/source.ts @@ -219,6 +219,7 @@ enum WebhookFilterTypes { HEAD_REF = 'HEAD_REF', ACTOR_ACCOUNT_ID = 'ACTOR_ACCOUNT_ID', BASE_REF = 'BASE_REF', + REPOSITORY_NAME = 'REPOSITORY_NAME', } /** @@ -421,6 +422,30 @@ export class FilterGroup { return this.addFilePathFilter(pattern, false); } + /** + * Create a new FilterGroup with an added condition: + * the push that is the source of the event affect only a repository name that matches the given pattern. + * Note that you can only use this method if this Group contains only the `WORKFLOW_JOB_QUEUED` event action, + * only for GitHub sources and only for organization webhook. + * + * @param pattern a regular expression + */ + public andRepositoryNameIs(pattern: string): FilterGroup { + return this.addRepositoryNameFilter(pattern, true); + } + + /** + * Create a new FilterGroup with an added condition: + * the push that is the source of the event must not affect a repository name that matches the given pattern. + * Note that you can only use this method if this Group contains only the `WORKFLOW_JOB_QUEUED` event action, + * only for GitHub sources and only for organization webhook. + * + * @param pattern a regular expression + */ + public andRepositoryNameIsNot(pattern: string): FilterGroup { + return this.addRepositoryNameFilter(pattern, false); + } + /** @internal */ public get _actions(): EventAction[] { return set2Array(this.actions); @@ -475,6 +500,10 @@ export class FilterGroup { return this.addFilter(WebhookFilterTypes.FILE_PATH, pattern, include); } + private addRepositoryNameFilter(pattern: string, include: boolean): FilterGroup { + return this.addFilter(WebhookFilterTypes.REPOSITORY_NAME, pattern, include); + } + private addFilter(type: WebhookFilterTypes, pattern: string, include: boolean) { return new FilterGroup(this.actions, this.filters.concat([{ type, @@ -718,7 +747,7 @@ abstract class CommonGithubSource extends ThirdPartyGitSource { */ export interface GitHubSourceProps extends CommonGithubSourceProps { /** - * The GitHub account/user that owns the repo. + * The GitHub Organization/user that owns the repo. * * @example 'awslabs' */ @@ -728,8 +757,9 @@ export interface GitHubSourceProps extends CommonGithubSourceProps { * The name of the repo (without the username). * * @example 'aws-cdk' + * @default undefined will create an organization webhook */ - readonly repo: string; + readonly repo?: string; } /** @@ -737,11 +767,14 @@ export interface GitHubSourceProps extends CommonGithubSourceProps { */ class GitHubSource extends CommonGithubSource { public readonly type = GITHUB_SOURCE_TYPE; - private readonly httpsCloneUrl: string; - + private readonly sourceLocation: string; + private readonly organization?: string; + protected readonly webhookFilters: FilterGroup[]; constructor(props: GitHubSourceProps) { super(props); - this.httpsCloneUrl = `https://github.com/${props.owner}/${props.repo}.git`; + this.organization = props.repo === undefined ? props.owner : undefined; + this.webhookFilters = props.webhookFilters ?? (this.organization ? [FilterGroup.inEventOf(EventAction.WORKFLOW_JOB_QUEUED)] : []); + this.sourceLocation = this.organization ? 'CODEBUILD_DEFAULT_WEBHOOK_SOURCE_LOCATION' : `https://github.com/${props.owner}/${props.repo}.git`; } public bind(_scope: Construct, project: IProject): SourceConfig { @@ -749,10 +782,16 @@ class GitHubSource extends CommonGithubSource { return { sourceProperty: { ...superConfig.sourceProperty, - location: this.httpsCloneUrl, + location: this.sourceLocation, }, sourceVersion: superConfig.sourceVersion, - buildTriggers: superConfig.buildTriggers, + buildTriggers: this.organization + ? { + ...superConfig.buildTriggers, + scopeConfiguration: { + name: this.organization, + }, + } : superConfig.buildTriggers, }; } } diff --git a/packages/aws-cdk-lib/aws-codebuild/test/project.test.ts b/packages/aws-cdk-lib/aws-codebuild/test/project.test.ts index 789c1bdb967d0..0379dfae7dbcd 100644 --- a/packages/aws-cdk-lib/aws-codebuild/test/project.test.ts +++ b/packages/aws-cdk-lib/aws-codebuild/test/project.test.ts @@ -180,6 +180,78 @@ describe('GitHub source', () => { }); }); + test('can create organizational webhook', () => { + // GIVEN + const stack = new cdk.Stack(); + + // WHEN + new codebuild.Project(stack, 'Project', { + source: codebuild.Source.gitHub({ + owner: 'testowner', + }), + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::CodeBuild::Project', { + Source: { + Type: 'GITHUB', + Location: 'CODEBUILD_DEFAULT_WEBHOOK_SOURCE_LOCATION', + }, + Triggers: { + ScopeConfiguration: { + Name: 'testowner', + }, + FilterGroups: [ + [ + { + Type: 'EVENT', + Pattern: 'WORKFLOW_JOB_QUEUED', + }, + ], + ], + }, + }); + }); + + test('can create organizational webhook with filters', () => { + // GIVEN + const stack = new cdk.Stack(); + + // WHEN + const filter = codebuild.FilterGroup.inEventOf(codebuild.EventAction.WORKFLOW_JOB_QUEUED).andRepositoryNameIs('testrepo'); + new codebuild.Project(stack, 'Project', { + source: codebuild.Source.gitHub({ + owner: 'testowner', + webhookFilters: [filter], + }), + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::CodeBuild::Project', { + Source: { + Type: 'GITHUB', + Location: 'CODEBUILD_DEFAULT_WEBHOOK_SOURCE_LOCATION', + }, + Triggers: { + ScopeConfiguration: { + Name: 'testowner', + }, + FilterGroups: [ + [ + { + Type: 'EVENT', + Pattern: 'WORKFLOW_JOB_QUEUED', + }, + { + Type: 'REPOSITORY_NAME', + Pattern: 'testrepo', + }, + ], + ], + }, + }); + }); + test('can be added to a CodePipeline', () => { const stack = new cdk.Stack(); const project = new codebuild.Project(stack, 'Project', {