diff --git a/.github/workflows/README.md b/.github/workflows/README.md index a963b61de4eb3..c4a166772274e 100644 --- a/.github/workflows/README.md +++ b/.github/workflows/README.md @@ -55,11 +55,6 @@ When approved this pushes the PR to the testing pipeline, thus starting the cli integ test build. Owner: Core CDK team -### Initial Priority Assignment - -[project-prioritization-assignment.yml](project-prioritization-assignment.yml): GitHub action for automatically adding PR's with priorities to the project priority board based on their labels. -Owner: CDK Support team - ## Issue Triggered ### Closed Issue Message @@ -108,13 +103,3 @@ Owner: Core CDK team [update-contributors.yml](update-contributors.yml): GitHub action that runs monthly to create a pull request for updating a CONTRIBUTORS file with the top contributors. Owner: Core CDK team - -### R2 Priority Assignment - -[project-prioritization-r2-assignment.yml](project-prioritization-r2-assignment.yml): GitHub action that runs every 6 hours to add PR's to the priority project board that satisfies R2 Priority. -Owner: CDK Support team - -### R5 Priority Assignment - -[project-prioritization-r5-assignment.yml](project-prioritization-r5-assignment.yml): GitHub action that runs every day to add PR's to the priority project board that satisfies R5 Priority. -Owner: CDK Support team diff --git a/.github/workflows/project-prioritization-assignment.yml b/.github/workflows/project-prioritization-assignment.yml deleted file mode 100644 index 5bd0ec706ba53..0000000000000 --- a/.github/workflows/project-prioritization-assignment.yml +++ /dev/null @@ -1,23 +0,0 @@ -name: PR Prioritization -on: - pull_request: - types: - - labeled - - opened - - reopened - - synchronize - - ready_for_review - - -jobs: - prioritize: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - name: Add PR to Project & Set Priority - uses: actions/github-script@v7 - with: - github-token: ${{ secrets.PROJEN_GITHUB_TOKEN }} - script: | - const script = require('./scripts/prioritization/assign-priority.js') - await script({github, context}) diff --git a/.github/workflows/project-prioritization-r2-assignment.yml b/.github/workflows/project-prioritization-r2-assignment.yml deleted file mode 100644 index d61c28b7f9175..0000000000000 --- a/.github/workflows/project-prioritization-r2-assignment.yml +++ /dev/null @@ -1,19 +0,0 @@ -name: PR Prioritization R2 Check -on: - schedule: - - cron: '0 */6 * * 1-5' # Runs every 6 hours during weekdays - workflow_dispatch: # Manual trigger - -jobs: - update_project_status: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - - name: Check and assign R2 Priority to PRs - uses: actions/github-script@v7 - with: - github-token: ${{ secrets.PROJEN_GITHUB_TOKEN }} - script: | - const script = require('./scripts/prioritization/assign-r2-priority.js') - await script({github}) diff --git a/.github/workflows/project-prioritization-r5-assignment.yml b/.github/workflows/project-prioritization-r5-assignment.yml deleted file mode 100644 index 300e12fa80b34..0000000000000 --- a/.github/workflows/project-prioritization-r5-assignment.yml +++ /dev/null @@ -1,18 +0,0 @@ -name: PR Prioritization R5 Check -on: - schedule: - - cron: '0 6 * * 1-5' # Runs at 6AM every day during weekdays - workflow_dispatch: # Manual trigger - -jobs: - update_project_status: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - name: Check and Assign R5 Priority to PRs - uses: actions/github-script@v7 - with: - github-token: ${{ secrets.PROJEN_GITHUB_TOKEN }} - script: | - const script = require('./scripts/prioritization/assign-r5-priority.js') - await script({github}) \ No newline at end of file diff --git a/packages/@aws-cdk/aws-amplify-alpha/README.md b/packages/@aws-cdk/aws-amplify-alpha/README.md index eaeed3c8b9c33..fb062ae9a56f6 100644 --- a/packages/@aws-cdk/aws-amplify-alpha/README.md +++ b/packages/@aws-cdk/aws-amplify-alpha/README.md @@ -250,7 +250,7 @@ const amplifyApp = new amplify.App(this, 'MyApp', { Amplify uses Amazon CloudFront to manage the caching configuration for your hosted applications. A cache configuration is applied to each app to optimize for the best performance. -Setting the `cacheConfigType` field on the Amplify `App` construct can be used to control cache configguration. By default, the value is set to `AMPLIFY_MANAGED`. If you want to exclude all cookies from the cache key, set `AMPLIFY_MANAGED_NO_COOKIES`. +Setting the `cacheConfigType` field on the Amplify `App` construct can be used to control cache configuration. By default, the value is set to `AMPLIFY_MANAGED`. If you want to exclude all cookies from the cache key, set `AMPLIFY_MANAGED_NO_COOKIES`. For more information, see [Managing the cache configuration for an app](https://docs.aws.amazon.com/amplify/latest/userguide/caching.html). diff --git a/packages/@aws-cdk/aws-ec2-alpha/README.md b/packages/@aws-cdk/aws-ec2-alpha/README.md index a01295ac1da67..b37359349caa6 100644 --- a/packages/@aws-cdk/aws-ec2-alpha/README.md +++ b/packages/@aws-cdk/aws-ec2-alpha/README.md @@ -105,6 +105,38 @@ new VpcV2(this, 'Vpc', { Since `VpcV2` does not create subnets automatically, users have full control over IP addresses allocation across subnets. +### Bring your own IPv6 addresses (BYOIP) + +If you have your own IP address that you would like to use with EC2, you can set up an IPv6 pool via the AWS CLI, and use that pool ID in your application. + +Once you have certified your IP address block with an ROA and have obtained an X-509 certificate, you can run the following command to provision your CIDR block in your AWS account: + +```shell +aws ec2 provision-byoip-cidr --region --cidr --cidr-authorization-context Message="1|aws||||SHA256".Signature="" +``` + +When your BYOIP CIDR is provisioned, you can run the following command to retrieve your IPv6 pool ID, which will be used in your VPC declaration: + +```shell +aws ec2 describe-byoip-cidr --region +``` + +For more help on setting up your IPv6 address, please review the [EC2 Documentation](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-byoip.html). + +Once you have provisioned your address block, you can use the IPv6 in your VPC as follows: + +```ts +const myVpc = new VpcV2(this, 'Vpc', { + primaryAddressBlock: IpAddresses.ipv4('10.1.0.0/16'), + secondaryAddressBlocks: [IpAddresses.ipv6ByoipPool({ + cidrBlockName: 'MyByoipCidrBlock', + ipv6PoolId: 'ipv6pool-ec2-someHashValue', + ipv6CidrBlock: '2001:db8::/32' + })], + enableDnsHostnames: true, + enableDnsSupport: true, +}); +``` ## Routing diff --git a/packages/@aws-cdk/aws-ec2-alpha/lib/subnet-v2.ts b/packages/@aws-cdk/aws-ec2-alpha/lib/subnet-v2.ts index 0cd0210aff93a..ca1ea6bf77c96 100644 --- a/packages/@aws-cdk/aws-ec2-alpha/lib/subnet-v2.ts +++ b/packages/@aws-cdk/aws-ec2-alpha/lib/subnet-v2.ts @@ -456,7 +456,7 @@ function storeSubnetToVpcByType(vpc: IVpcV2, subnet: SubnetV2, type: SubnetType) function validateSupportIpv6(vpc: IVpcV2) { if (vpc.secondaryCidrBlock) { if (vpc.secondaryCidrBlock.some((secondaryAddress) => secondaryAddress.amazonProvidedIpv6CidrBlock === true || - secondaryAddress.ipv6IpamPoolId != undefined)) { + secondaryAddress.ipv6IpamPoolId !== undefined || secondaryAddress.ipv6Pool !== undefined)) { return true; } else { throw new Error('To use IPv6, the VPC must enable IPv6 support.'); diff --git a/packages/@aws-cdk/aws-ec2-alpha/lib/util.ts b/packages/@aws-cdk/aws-ec2-alpha/lib/util.ts index 7566ce711e0c4..bb9377b6dc86f 100644 --- a/packages/@aws-cdk/aws-ec2-alpha/lib/util.ts +++ b/packages/@aws-cdk/aws-ec2-alpha/lib/util.ts @@ -323,13 +323,54 @@ export class CidrBlockIpv6 { } /** - * @returns Maximum IPv6 address for a provided CIDR + * Calculates the maximum IPv6 address in the CIDR block + * @returns The maximum IPv6 address as a string */ public maxIp(): string { + /** + * Calculate how many 16-bit blocks are needed for the network portion + * e.g. for /56, networkPartLength = ceil(56/16) = 4 blocks + */ + const networkPartLength = Math.ceil(this.cidrPrefix / 16); + /** + * Calculate remaining bits in last network block + * e.g. for /56, remainingBits = 56 % 16 = 8 bits + */ + const remainingBits = this.cidrPrefix % 16; + /** + * Create copy of network portion of address + * e.g. [2001, db8, 0, 0] for 2001:db8::/56 + */ const endIP = [...this.networkPart]; - const hostPart = Array(8 - this.networkPart.length).fill(BigInt(0xffff)); - endIP.push(...hostPart); + /** + * If there are remaining bits in last network block, + * create appropriate bitmask and apply to last network block + * e.g. for /56: mask = (1 << (16-8)) - 1 = 0x00FF + */ + if (remainingBits > 0) { + const lastNetworkIndex = networkPartLength - 1; + const mask = (BigInt(1) << BigInt(16 - remainingBits)) - BigInt(1); + /** + * Apply bitmask to last network block using bitwise OR + * e.g. if lastNetworkIndex=3 and mask=0x00FF: + * networkPart[3]=0x0000 | 0x00FF = 0x00FF + */ + endIP[lastNetworkIndex] = this.networkPart[lastNetworkIndex] | mask; + } + + /** + * Fill remaining blocks with maximum value 0xFFFF + * e.g. [2001, db8, 0, ff, ffff, ffff, ffff, ffff] + */ + for (let i = networkPartLength; i < 8; i++) { + endIP.push(BigInt('0xffff')); + } + + /** + * Convert blocks to hex strings and join with colons + * e.g. 2001:db8:0:ff:ffff:ffff:ffff:ffff + */ return endIP.map(this.formatIPv6Part).join(':'); } @@ -342,26 +383,18 @@ export class CidrBlockIpv6 { * @returns true if two ranges overlap, false otherwise */ public rangesOverlap(range1: string, range2: string): boolean { - const [start1, end1] = this.getIPv6Range(range1); - const [start2, end2] = this.getIPv6Range(range2); + // Create new CidrBlockIpv6 instances for both ranges + const cidr1 = new CidrBlockIpv6(range1); + const cidr2 = new CidrBlockIpv6(range2); - return (start1 <= end2) && (start2 <= end1); - } + // Convert min and max IPs to numeric values for comparison + const start1 = this.ipv6ToNumber(cidr1.minIp()); + const end1 = this.ipv6ToNumber(cidr1.maxIp()); + const start2 = this.ipv6ToNumber(cidr2.minIp()); + const end2 = this.ipv6ToNumber(cidr2.maxIp()); - /** - * - * @param cidr - * @returns Range in the from of big int number [start, end] - */ - private getIPv6Range(cidr: string): [bigint, bigint] { - const [ipv6Address, prefixLength] = cidr.split('/'); - const ipv6Number = this.ipv6ToNumber(ipv6Address); - const mask = (BigInt(1) << BigInt(128 - Number(prefixLength))) - BigInt(1); - const networkPrefix = ipv6Number & ~mask; - const start = networkPrefix; - const end = networkPrefix | mask; - - return [start, end]; + // Check for overlap + return (start1 <= end2) && (start2 <= end1); } /** diff --git a/packages/@aws-cdk/aws-ec2-alpha/lib/vpc-v2-base.ts b/packages/@aws-cdk/aws-ec2-alpha/lib/vpc-v2-base.ts index eee807bdbe968..c714ce351de68 100644 --- a/packages/@aws-cdk/aws-ec2-alpha/lib/vpc-v2-base.ts +++ b/packages/@aws-cdk/aws-ec2-alpha/lib/vpc-v2-base.ts @@ -430,7 +430,7 @@ export abstract class VpcV2Base extends Resource implements IVpcV2 { let useIpv6; if (this.secondaryCidrBlock) { useIpv6 = (this.secondaryCidrBlock.some((secondaryAddress) => secondaryAddress.amazonProvidedIpv6CidrBlock === true || - secondaryAddress.ipv6IpamPoolId != undefined)); + secondaryAddress.ipv6IpamPoolId !== undefined || secondaryAddress.ipv6CidrBlock !== undefined)); } if (!useIpv6) { diff --git a/packages/@aws-cdk/aws-ec2-alpha/lib/vpc-v2.ts b/packages/@aws-cdk/aws-ec2-alpha/lib/vpc-v2.ts index 4b0571891a367..9af4af9b03ee9 100644 --- a/packages/@aws-cdk/aws-ec2-alpha/lib/vpc-v2.ts +++ b/packages/@aws-cdk/aws-ec2-alpha/lib/vpc-v2.ts @@ -17,6 +17,27 @@ export interface SecondaryAddressProps { readonly cidrBlockName: string; } +/** + * Additional props needed for BYOIP IPv6 address props + */ +export interface Ipv6PoolSecondaryAddressProps extends SecondaryAddressProps { + /** + * ID of the IPv6 address pool from which to allocate the IPv6 CIDR block. + * Note: BYOIP Pool ID is different from the IPAM Pool ID. + * To onboard your IPv6 address range to your AWS account please refer to the below documentation + * @see https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/byoip-onboard.html + */ + readonly ipv6PoolId: string; + + /** + * A valid IPv6 CIDR block from the IPv6 address pool onboarded to AWS using BYOIP. + * The most specific IPv6 address range that you can bring is /48 for CIDRs that are publicly advertisable + * and /56 for CIDRs that are not publicly advertisable. + * @see https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-byoip.html#byoip-definitions + */ + readonly ipv6CidrBlock: string; +} + /** * IpAddress options to define VPC V2 */ @@ -49,6 +70,13 @@ export class IpAddresses { public static amazonProvidedIpv6(props: SecondaryAddressProps) : IIpAddresses { return new AmazonProvided(props); } + + /** + * A BYOIP IPv6 address pool + */ + public static ipv6ByoipPool(props: Ipv6PoolSecondaryAddressProps): IIpAddresses { + return new Ipv6Pool(props); + } } /** @@ -121,6 +149,20 @@ export interface VpcCidrOptions { * @default - no IPAM IPv4 CIDR range is provisioned using IPAM */ readonly ipv4IpamProvisionedCidrs?: string[]; + + /** + * IPv6 CIDR block from the BOYIP IPv6 address pool. + * + * @default - None + */ + readonly ipv6CidrBlock?: string; + + /** + * ID of the BYOIP IPv6 address pool from which to allocate the IPv6 CIDR block. + * + * @default - None + */ + readonly ipv6PoolId?: string; } /** @@ -499,7 +541,7 @@ export class VpcV2 extends VpcV2Base { throw new Error('Cidr Block Name is required to create secondary IP address'); } - if (secondaryVpcOptions.amazonProvided || secondaryVpcOptions.ipv6IpamPool) { + if (secondaryVpcOptions.amazonProvided || secondaryVpcOptions.ipv6IpamPool || secondaryVpcOptions.ipv6PoolId) { this.useIpv6 = true; } //validate CIDR ranges per RFC 1918 @@ -520,6 +562,10 @@ export class VpcV2 extends VpcV2Base { ipv6NetmaskLength: secondaryVpcOptions.ipv6NetmaskLength, ipv6IpamPoolId: secondaryVpcOptions.ipv6IpamPool?.ipamPoolId, amazonProvidedIpv6CidrBlock: secondaryVpcOptions.amazonProvided, + //BYOIP IPv6 Address + ipv6CidrBlock: secondaryVpcOptions?.ipv6CidrBlock, + //BYOIP Pool for IPv6 address + ipv6Pool: secondaryVpcOptions?.ipv6PoolId, }); if (secondaryVpcOptions.dependencies) { for (const dep of secondaryVpcOptions.dependencies) { @@ -633,6 +679,22 @@ class IpamIpv4 implements IIpAddresses { } } +/** + * Supports assigning IPv6 address to VPC in an address pool + */ +class Ipv6Pool implements IIpAddresses { + + constructor(private readonly props: Ipv6PoolSecondaryAddressProps) { + } + allocateVpcCidr(): VpcCidrOptions { + return { + ipv6CidrBlock: this.props.ipv6CidrBlock, + ipv6PoolId: this.props.ipv6PoolId, + cidrBlockName: this.props?.cidrBlockName, + }; + } +} + /** * Interface to create L2 for VPC Cidr Block */ @@ -658,6 +720,16 @@ export interface IVPCCidrBlock { * IPAM pool for IPv4 address type */ readonly ipv4IpamPoolId ?: string; + + /** + * The IPv6 CIDR block from the specified IPv6 address pool. + */ + readonly ipv6CidrBlock?: string; + + /** + * The ID of the IPv6 address pool from which to allocate the IPv6 CIDR block. + */ + readonly ipv6Pool?: string; } /** @@ -721,6 +793,21 @@ export interface VPCCidrBlockattributes { * @default - no IPAM IPv4 CIDR range is provisioned using IPAM */ readonly ipv4IpamProvisionedCidrs?: string[]; + + /** + * The IPv6 CIDR block from the specified IPv6 address pool. + * + * @default - No IPv6 CIDR block associated with VPC. + */ + readonly ipv6CidrBlock?: string; + + /** + * The ID of the IPv6 address pool from which to allocate the IPv6 CIDR block. + * Note: BYOIP Pool ID is different than IPAM Pool ID. + * + * @default - No BYOIP pool associated with VPC. + */ + readonly ipv6Pool?: string; } /** @@ -748,6 +835,9 @@ class VPCCidrBlock extends Resource implements IVPCCidrBlock { public readonly amazonProvidedIpv6CidrBlock ?: boolean = props.amazonProvidedIpv6CidrBlock; public readonly ipv6IpamPoolId ?: string = props.ipv6IpamPoolId; public readonly ipv4IpamPoolId ?: string = props.ipv4IpamPoolId; + //BYOIP Pool Attributes + public readonly ipv6Pool?: string = props.ipv6Pool; + public readonly ipv6CidrBlock?: string = props.ipv6CidrBlock; } return new Import(scope, id); } @@ -762,6 +852,10 @@ class VPCCidrBlock extends Resource implements IVPCCidrBlock { public readonly ipv4IpamPoolId?: string; + public readonly ipv6CidrBlock?: string; + + public readonly ipv6Pool?: string; + constructor(scope: Construct, id: string, props: VPCCidrBlockProps) { super(scope, id); this.resource = new CfnVPCCidrBlock(this, id, props); @@ -770,6 +864,9 @@ class VPCCidrBlock extends Resource implements IVPCCidrBlock { this.ipv6IpamPoolId = props.ipv6IpamPoolId; this.ipv4IpamPoolId = props.ipv4IpamPoolId; this.amazonProvidedIpv6CidrBlock = props.amazonProvidedIpv6CidrBlock; + //BYOIP Pool and CIDR Block + this.ipv6CidrBlock = props.ipv6CidrBlock; + this.ipv6Pool = props.ipv6Pool; } } diff --git a/packages/@aws-cdk/aws-ec2-alpha/test/integ.byoip-ipv6.js.snapshot/cdk.out b/packages/@aws-cdk/aws-ec2-alpha/test/integ.byoip-ipv6.js.snapshot/cdk.out new file mode 100644 index 0000000000000..91e1a8b9901d5 --- /dev/null +++ b/packages/@aws-cdk/aws-ec2-alpha/test/integ.byoip-ipv6.js.snapshot/cdk.out @@ -0,0 +1 @@ +{"version":"39.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-ec2-alpha/test/integ.byoip-ipv6.js.snapshot/integ.json b/packages/@aws-cdk/aws-ec2-alpha/test/integ.byoip-ipv6.js.snapshot/integ.json new file mode 100644 index 0000000000000..1953228ca8e7a --- /dev/null +++ b/packages/@aws-cdk/aws-ec2-alpha/test/integ.byoip-ipv6.js.snapshot/integ.json @@ -0,0 +1,12 @@ +{ + "version": "39.0.0", + "testCases": { + "integtest-model/DefaultTest": { + "stacks": [ + "vpc-byoip-ipv6" + ], + "assertionStack": "integtest-model/DefaultTest/DeployAssert", + "assertionStackName": "integtestmodelDefaultTestDeployAssertCF40BD53" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-ec2-alpha/test/integ.byoip-ipv6.js.snapshot/integtestmodelDefaultTestDeployAssertCF40BD53.assets.json b/packages/@aws-cdk/aws-ec2-alpha/test/integ.byoip-ipv6.js.snapshot/integtestmodelDefaultTestDeployAssertCF40BD53.assets.json new file mode 100644 index 0000000000000..928ed61bb88d0 --- /dev/null +++ b/packages/@aws-cdk/aws-ec2-alpha/test/integ.byoip-ipv6.js.snapshot/integtestmodelDefaultTestDeployAssertCF40BD53.assets.json @@ -0,0 +1,19 @@ +{ + "version": "39.0.0", + "files": { + "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { + "source": { + "path": "integtestmodelDefaultTestDeployAssertCF40BD53.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/aws-ec2-alpha/test/integ.byoip-ipv6.js.snapshot/integtestmodelDefaultTestDeployAssertCF40BD53.template.json b/packages/@aws-cdk/aws-ec2-alpha/test/integ.byoip-ipv6.js.snapshot/integtestmodelDefaultTestDeployAssertCF40BD53.template.json new file mode 100644 index 0000000000000..ad9d0fb73d1dd --- /dev/null +++ b/packages/@aws-cdk/aws-ec2-alpha/test/integ.byoip-ipv6.js.snapshot/integtestmodelDefaultTestDeployAssertCF40BD53.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/aws-ec2-alpha/test/integ.byoip-ipv6.js.snapshot/manifest.json b/packages/@aws-cdk/aws-ec2-alpha/test/integ.byoip-ipv6.js.snapshot/manifest.json new file mode 100644 index 0000000000000..cbc7813bcae89 --- /dev/null +++ b/packages/@aws-cdk/aws-ec2-alpha/test/integ.byoip-ipv6.js.snapshot/manifest.json @@ -0,0 +1,155 @@ +{ + "version": "39.0.0", + "artifacts": { + "vpc-byoip-ipv6.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "vpc-byoip-ipv6.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "vpc-byoip-ipv6": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "vpc-byoip-ipv6.template.json", + "terminationProtection": false, + "validateOnSynth": false, + "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}/2ac973cd3fa2ed1a3934fbe78ee77203b26df0366d34997e96d1570af96f2614.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "vpc-byoip-ipv6.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": [ + "vpc-byoip-ipv6.assets" + ], + "metadata": { + "/vpc-byoip-ipv6/VPC-integ-test-1/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCintegtest1EBA1CB75" + } + ], + "/vpc-byoip-ipv6/VPC-integ-test-1/MyByoipIpv6Block/MyByoipIpv6Block": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCintegtest1MyByoipIpv6BlockC474849A" + } + ], + "/vpc-byoip-ipv6/Subnet-integ-test-1/Subnet": [ + { + "type": "aws:cdk:logicalId", + "data": "Subnetintegtest1Subnet33DEE36B" + } + ], + "/vpc-byoip-ipv6/Subnet-integ-test-1/RouteTable/RouteTable": [ + { + "type": "aws:cdk:logicalId", + "data": "Subnetintegtest1RouteTableD7246BB7" + } + ], + "/vpc-byoip-ipv6/Subnet-integ-test-1/RouteTableAssociation": [ + { + "type": "aws:cdk:logicalId", + "data": "Subnetintegtest1RouteTableAssociation25E3C72D" + } + ], + "/vpc-byoip-ipv6/Subnet-integ-test-2/Subnet": [ + { + "type": "aws:cdk:logicalId", + "data": "Subnetintegtest2Subnet5A56D422" + } + ], + "/vpc-byoip-ipv6/Subnet-integ-test-2/RouteTable/RouteTable": [ + { + "type": "aws:cdk:logicalId", + "data": "Subnetintegtest2RouteTable2528D856" + } + ], + "/vpc-byoip-ipv6/Subnet-integ-test-2/RouteTableAssociation": [ + { + "type": "aws:cdk:logicalId", + "data": "Subnetintegtest2RouteTableAssociation9569A2C6" + } + ], + "/vpc-byoip-ipv6/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/vpc-byoip-ipv6/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "vpc-byoip-ipv6" + }, + "integtestmodelDefaultTestDeployAssertCF40BD53.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "integtestmodelDefaultTestDeployAssertCF40BD53.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "integtestmodelDefaultTestDeployAssertCF40BD53": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "integtestmodelDefaultTestDeployAssertCF40BD53.template.json", + "terminationProtection": false, + "validateOnSynth": false, + "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": [ + "integtestmodelDefaultTestDeployAssertCF40BD53.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": [ + "integtestmodelDefaultTestDeployAssertCF40BD53.assets" + ], + "metadata": { + "/integtest-model/DefaultTest/DeployAssert/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/integtest-model/DefaultTest/DeployAssert/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "integtest-model/DefaultTest/DeployAssert" + }, + "Tree": { + "type": "cdk:tree", + "properties": { + "file": "tree.json" + } + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-ec2-alpha/test/integ.byoip-ipv6.js.snapshot/tree.json b/packages/@aws-cdk/aws-ec2-alpha/test/integ.byoip-ipv6.js.snapshot/tree.json new file mode 100644 index 0000000000000..47b5abdeda2b3 --- /dev/null +++ b/packages/@aws-cdk/aws-ec2-alpha/test/integ.byoip-ipv6.js.snapshot/tree.json @@ -0,0 +1,361 @@ +{ + "version": "tree-0.1", + "tree": { + "id": "App", + "path": "", + "children": { + "vpc-byoip-ipv6": { + "id": "vpc-byoip-ipv6", + "path": "vpc-byoip-ipv6", + "children": { + "VPC-integ-test-1": { + "id": "VPC-integ-test-1", + "path": "vpc-byoip-ipv6/VPC-integ-test-1", + "children": { + "Resource": { + "id": "Resource", + "path": "vpc-byoip-ipv6/VPC-integ-test-1/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::VPC", + "aws:cdk:cloudformation:props": { + "cidrBlock": "10.1.0.0/16", + "enableDnsHostnames": true, + "enableDnsSupport": true, + "instanceTenancy": "default", + "tags": [ + { + "key": "Name", + "value": "vpc-byoip-ipv6/VPC-integ-test-1" + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnVPC", + "version": "0.0.0" + } + }, + "MyByoipIpv6Block": { + "id": "MyByoipIpv6Block", + "path": "vpc-byoip-ipv6/VPC-integ-test-1/MyByoipIpv6Block", + "children": { + "MyByoipIpv6Block": { + "id": "MyByoipIpv6Block", + "path": "vpc-byoip-ipv6/VPC-integ-test-1/MyByoipIpv6Block/MyByoipIpv6Block", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::VPCCidrBlock", + "aws:cdk:cloudformation:props": { + "ipv6CidrBlock": "2600:f0f0:8::/56", + "ipv6Pool": "ipv6pool-ec2-0a95217e154b65493", + "vpcId": { + "Fn::GetAtt": [ + "VPCintegtest1EBA1CB75", + "VpcId" + ] + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnVPCCidrBlock", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2-alpha.VpcV2", + "version": "0.0.0" + } + }, + "Subnet-integ-test-1": { + "id": "Subnet-integ-test-1", + "path": "vpc-byoip-ipv6/Subnet-integ-test-1", + "children": { + "Subnet": { + "id": "Subnet", + "path": "vpc-byoip-ipv6/Subnet-integ-test-1/Subnet", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::Subnet", + "aws:cdk:cloudformation:props": { + "assignIpv6AddressOnCreation": false, + "availabilityZone": "us-west-2a", + "cidrBlock": "10.1.1.0/24", + "ipv6CidrBlock": "2600:f0f0:8:1::/64", + "vpcId": { + "Fn::GetAtt": [ + "VPCintegtest1EBA1CB75", + "VpcId" + ] + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnSubnet", + "version": "0.0.0" + } + }, + "Acl": { + "id": "Acl", + "path": "vpc-byoip-ipv6/Subnet-integ-test-1/Acl", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "RouteTable": { + "id": "RouteTable", + "path": "vpc-byoip-ipv6/Subnet-integ-test-1/RouteTable", + "children": { + "RouteTable": { + "id": "RouteTable", + "path": "vpc-byoip-ipv6/Subnet-integ-test-1/RouteTable/RouteTable", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::RouteTable", + "aws:cdk:cloudformation:props": { + "tags": [ + { + "key": "Name", + "value": "DefaultCDKRouteTable" + } + ], + "vpcId": { + "Fn::GetAtt": [ + "VPCintegtest1EBA1CB75", + "VpcId" + ] + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnRouteTable", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2-alpha.RouteTable", + "version": "0.0.0" + } + }, + "RouteTableAssociation": { + "id": "RouteTableAssociation", + "path": "vpc-byoip-ipv6/Subnet-integ-test-1/RouteTableAssociation", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::SubnetRouteTableAssociation", + "aws:cdk:cloudformation:props": { + "routeTableId": { + "Fn::GetAtt": [ + "Subnetintegtest1RouteTableD7246BB7", + "RouteTableId" + ] + }, + "subnetId": { + "Ref": "Subnetintegtest1Subnet33DEE36B" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnSubnetRouteTableAssociation", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2-alpha.SubnetV2", + "version": "0.0.0" + } + }, + "Subnet-integ-test-2": { + "id": "Subnet-integ-test-2", + "path": "vpc-byoip-ipv6/Subnet-integ-test-2", + "children": { + "Subnet": { + "id": "Subnet", + "path": "vpc-byoip-ipv6/Subnet-integ-test-2/Subnet", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::Subnet", + "aws:cdk:cloudformation:props": { + "assignIpv6AddressOnCreation": false, + "availabilityZone": "us-west-2a", + "cidrBlock": "10.1.0.0/24", + "ipv6CidrBlock": "2600:f0f0:8:0::/64", + "vpcId": { + "Fn::GetAtt": [ + "VPCintegtest1EBA1CB75", + "VpcId" + ] + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnSubnet", + "version": "0.0.0" + } + }, + "Acl": { + "id": "Acl", + "path": "vpc-byoip-ipv6/Subnet-integ-test-2/Acl", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "RouteTable": { + "id": "RouteTable", + "path": "vpc-byoip-ipv6/Subnet-integ-test-2/RouteTable", + "children": { + "RouteTable": { + "id": "RouteTable", + "path": "vpc-byoip-ipv6/Subnet-integ-test-2/RouteTable/RouteTable", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::RouteTable", + "aws:cdk:cloudformation:props": { + "tags": [ + { + "key": "Name", + "value": "DefaultCDKRouteTable" + } + ], + "vpcId": { + "Fn::GetAtt": [ + "VPCintegtest1EBA1CB75", + "VpcId" + ] + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnRouteTable", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2-alpha.RouteTable", + "version": "0.0.0" + } + }, + "RouteTableAssociation": { + "id": "RouteTableAssociation", + "path": "vpc-byoip-ipv6/Subnet-integ-test-2/RouteTableAssociation", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::SubnetRouteTableAssociation", + "aws:cdk:cloudformation:props": { + "routeTableId": { + "Fn::GetAtt": [ + "Subnetintegtest2RouteTable2528D856", + "RouteTableId" + ] + }, + "subnetId": { + "Ref": "Subnetintegtest2Subnet5A56D422" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnSubnetRouteTableAssociation", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2-alpha.SubnetV2", + "version": "0.0.0" + } + }, + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "vpc-byoip-ipv6/BootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "vpc-byoip-ipv6/CheckBootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnRule", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Stack", + "version": "0.0.0" + } + }, + "integtest-model": { + "id": "integtest-model", + "path": "integtest-model", + "children": { + "DefaultTest": { + "id": "DefaultTest", + "path": "integtest-model/DefaultTest", + "children": { + "Default": { + "id": "Default", + "path": "integtest-model/DefaultTest/Default", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.4.2" + } + }, + "DeployAssert": { + "id": "DeployAssert", + "path": "integtest-model/DefaultTest/DeployAssert", + "children": { + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "integtest-model/DefaultTest/DeployAssert/BootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "integtest-model/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.4.2" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.App", + "version": "0.0.0" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-ec2-alpha/test/integ.byoip-ipv6.js.snapshot/vpc-byoip-ipv6.assets.json b/packages/@aws-cdk/aws-ec2-alpha/test/integ.byoip-ipv6.js.snapshot/vpc-byoip-ipv6.assets.json new file mode 100644 index 0000000000000..b1aceb6a1dfef --- /dev/null +++ b/packages/@aws-cdk/aws-ec2-alpha/test/integ.byoip-ipv6.js.snapshot/vpc-byoip-ipv6.assets.json @@ -0,0 +1,19 @@ +{ + "version": "39.0.0", + "files": { + "2ac973cd3fa2ed1a3934fbe78ee77203b26df0366d34997e96d1570af96f2614": { + "source": { + "path": "vpc-byoip-ipv6.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "2ac973cd3fa2ed1a3934fbe78ee77203b26df0366d34997e96d1570af96f2614.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/aws-ec2-alpha/test/integ.byoip-ipv6.js.snapshot/vpc-byoip-ipv6.template.json b/packages/@aws-cdk/aws-ec2-alpha/test/integ.byoip-ipv6.js.snapshot/vpc-byoip-ipv6.template.json new file mode 100644 index 0000000000000..83856929ba1d8 --- /dev/null +++ b/packages/@aws-cdk/aws-ec2-alpha/test/integ.byoip-ipv6.js.snapshot/vpc-byoip-ipv6.template.json @@ -0,0 +1,176 @@ +{ + "Resources": { + "VPCintegtest1EBA1CB75": { + "Type": "AWS::EC2::VPC", + "Properties": { + "CidrBlock": "10.1.0.0/16", + "EnableDnsHostnames": true, + "EnableDnsSupport": true, + "InstanceTenancy": "default", + "Tags": [ + { + "Key": "Name", + "Value": "vpc-byoip-ipv6/VPC-integ-test-1" + } + ] + } + }, + "VPCintegtest1MyByoipIpv6BlockC474849A": { + "Type": "AWS::EC2::VPCCidrBlock", + "Properties": { + "Ipv6CidrBlock": "2600:f0f0:8::/56", + "Ipv6Pool": "ipv6pool-ec2-0a95217e154b65493", + "VpcId": { + "Fn::GetAtt": [ + "VPCintegtest1EBA1CB75", + "VpcId" + ] + } + } + }, + "Subnetintegtest1Subnet33DEE36B": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "AssignIpv6AddressOnCreation": false, + "AvailabilityZone": "us-west-2a", + "CidrBlock": "10.1.1.0/24", + "Ipv6CidrBlock": "2600:f0f0:8:1::/64", + "VpcId": { + "Fn::GetAtt": [ + "VPCintegtest1EBA1CB75", + "VpcId" + ] + } + }, + "DependsOn": [ + "VPCintegtest1MyByoipIpv6BlockC474849A" + ] + }, + "Subnetintegtest1RouteTableD7246BB7": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "Tags": [ + { + "Key": "Name", + "Value": "DefaultCDKRouteTable" + } + ], + "VpcId": { + "Fn::GetAtt": [ + "VPCintegtest1EBA1CB75", + "VpcId" + ] + } + }, + "DependsOn": [ + "VPCintegtest1MyByoipIpv6BlockC474849A" + ] + }, + "Subnetintegtest1RouteTableAssociation25E3C72D": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Fn::GetAtt": [ + "Subnetintegtest1RouteTableD7246BB7", + "RouteTableId" + ] + }, + "SubnetId": { + "Ref": "Subnetintegtest1Subnet33DEE36B" + } + }, + "DependsOn": [ + "VPCintegtest1MyByoipIpv6BlockC474849A" + ] + }, + "Subnetintegtest2Subnet5A56D422": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "AssignIpv6AddressOnCreation": false, + "AvailabilityZone": "us-west-2a", + "CidrBlock": "10.1.0.0/24", + "Ipv6CidrBlock": "2600:f0f0:8:0::/64", + "VpcId": { + "Fn::GetAtt": [ + "VPCintegtest1EBA1CB75", + "VpcId" + ] + } + }, + "DependsOn": [ + "VPCintegtest1MyByoipIpv6BlockC474849A" + ] + }, + "Subnetintegtest2RouteTable2528D856": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "Tags": [ + { + "Key": "Name", + "Value": "DefaultCDKRouteTable" + } + ], + "VpcId": { + "Fn::GetAtt": [ + "VPCintegtest1EBA1CB75", + "VpcId" + ] + } + }, + "DependsOn": [ + "VPCintegtest1MyByoipIpv6BlockC474849A" + ] + }, + "Subnetintegtest2RouteTableAssociation9569A2C6": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Fn::GetAtt": [ + "Subnetintegtest2RouteTable2528D856", + "RouteTableId" + ] + }, + "SubnetId": { + "Ref": "Subnetintegtest2Subnet5A56D422" + } + }, + "DependsOn": [ + "VPCintegtest1MyByoipIpv6BlockC474849A" + ] + } + }, + "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/aws-ec2-alpha/test/integ.byoip-ipv6.ts b/packages/@aws-cdk/aws-ec2-alpha/test/integ.byoip-ipv6.ts new file mode 100644 index 0000000000000..03338ef5ee674 --- /dev/null +++ b/packages/@aws-cdk/aws-ec2-alpha/test/integ.byoip-ipv6.ts @@ -0,0 +1,55 @@ +/* + * This integration test deploys a VPC that contains a BYOIP IPv6 address. + * The address is owned by the CDK maintainers, who are able to run and + * update the test if need be for future changes. + * + * Notes on how to run this integ test + * Replace the ipv6PoolId and ipv6CidrBlock for VPC with the one that is owned by your account. + */ + +import * as vpc_v2 from '../lib/vpc-v2'; +import { IntegTest } from '@aws-cdk/integ-tests-alpha'; +import * as cdk from 'aws-cdk-lib'; +import { IpCidr, SubnetV2 } from '../lib'; +import { SubnetType } from 'aws-cdk-lib/aws-ec2'; + +const app = new cdk.App(); + +const stack = new cdk.Stack(app, 'vpc-byoip-ipv6'); + +const myVpc = new vpc_v2.VpcV2(stack, 'VPC-integ-test-1', { + primaryAddressBlock: vpc_v2.IpAddresses.ipv4('10.1.0.0/16'), + secondaryAddressBlocks: [ + vpc_v2.IpAddresses.ipv6ByoipPool({ + ipv6PoolId: 'ipv6pool-ec2-0a95217e154b65493', //To Be Replaced + cidrBlockName: 'MyByoipIpv6Block', + ipv6CidrBlock: '2600:f0f0:8::/56', //To Be Replaced + }), + ], + enableDnsHostnames: true, + enableDnsSupport: true, +}); + +new SubnetV2(stack, 'Subnet-integ-test-1', { + vpc: myVpc, + ipv4CidrBlock: new IpCidr('10.1.1.0/24'), + ipv6CidrBlock: new IpCidr('2600:f0f0:8:1::/64'), //To Be Replaced + availabilityZone: 'us-west-2a', + subnetType: SubnetType.PRIVATE_ISOLATED, +}); + +/** + * Check for non-ovelapping subnet range + */ +new SubnetV2(stack, 'Subnet-integ-test-2', { + vpc: myVpc, + ipv4CidrBlock: new IpCidr('10.1.0.0/24'), + ipv6CidrBlock: new IpCidr('2600:f0f0:8:0::/64'), //To Be Replaced + availabilityZone: 'us-west-2a', + subnetType: SubnetType.PRIVATE_ISOLATED, +}); + +new IntegTest(app, 'integtest-model', { + testCases: [stack], +}); + diff --git a/packages/@aws-cdk/aws-ec2-alpha/test/subnet-v2.test.ts b/packages/@aws-cdk/aws-ec2-alpha/test/subnet-v2.test.ts index b43f9be207f74..a78c1c3806a1a 100644 --- a/packages/@aws-cdk/aws-ec2-alpha/test/subnet-v2.test.ts +++ b/packages/@aws-cdk/aws-ec2-alpha/test/subnet-v2.test.ts @@ -221,6 +221,27 @@ describe('Subnet V2 with custom IP and routing', () => { }); }); + test('Create Subnet with IPv6 if VPC has IPv6 CIDR block enabled', () => { + const TestVPC = new vpc.VpcV2(stack, 'TestVPC', { + primaryAddressBlock: vpc.IpAddresses.ipv4('10.1.0.0/16'), + secondaryAddressBlocks: [vpc.IpAddresses.ipv6ByoipPool( + { + ipv6PoolId: 'test-byoip-pool', + ipv6CidrBlock: '2001:db8:1::/56', + cidrBlockName: 'secondaryIpv6Pool', + }, + )], + }); + + new subnet.SubnetV2(stack, 'IpamSubnet', { + vpc: TestVPC, + ipv4CidrBlock: new subnet.IpCidr('10.1.0.0/24'), + ipv6CidrBlock: new subnet.IpCidr('2001:db8:1::/64'), + availabilityZone: 'us-east-1a', + subnetType: SubnetType.PUBLIC, + }); + }); + test('Should throw error if overlapping CIDR block(IPv6) for the subnet', () => { const ipam = new Ipam(stack, 'TestIpam', { operatingRegion: ['us-west-1'], diff --git a/packages/@aws-cdk/aws-ec2-alpha/test/util.test.ts b/packages/@aws-cdk/aws-ec2-alpha/test/util.test.ts index 2d5f1890674f4..56764458f3044 100644 --- a/packages/@aws-cdk/aws-ec2-alpha/test/util.test.ts +++ b/packages/@aws-cdk/aws-ec2-alpha/test/util.test.ts @@ -1,4 +1,4 @@ -import { CidrBlock } from '../lib/util'; +import { CidrBlock, CidrBlockIpv6 } from '../lib/util'; describe('Tests for the CidrBlock.rangesOverlap method to check if IPv4 ranges overlap', () =>{ test('Should return false for non-overlapping IP ranges', () => { @@ -35,4 +35,14 @@ describe('Tests for the CidrBlock.rangesOverlap method to check if IPv4 ranges o const range2 = ['10.0.16.0', '10.0.19.255'] as [string, string]; expect(testCidr.rangesOverlap(range1, range2)).toBe(false); }); + + test('Should return false for non-overlapping IPv6 ranges with prefix', () => { + const testCidr = new CidrBlockIpv6('2001:db8::/32'); + expect(testCidr.rangesOverlap('2001:db8:8::/56', '2001:db8:9::/56')).toBe(false); + }); + + test('Should return true for overlapping IPv6 ranges with prefix', () => { + const testCidr = new CidrBlockIpv6('2001:db8::/32'); + expect(testCidr.rangesOverlap('2001:db8::1/64', '2001:db8::1/60')).toBe(true); + }); }); diff --git a/packages/@aws-cdk/aws-ec2-alpha/test/vpc-v2.test.ts b/packages/@aws-cdk/aws-ec2-alpha/test/vpc-v2.test.ts index 6c3087ce42a77..beb8fcda41c79 100644 --- a/packages/@aws-cdk/aws-ec2-alpha/test/vpc-v2.test.ts +++ b/packages/@aws-cdk/aws-ec2-alpha/test/vpc-v2.test.ts @@ -256,5 +256,53 @@ describe('Vpc V2 with full control', () => { }, }); }); -}); + test('VPC with secondary IPv6 Pool address', () => { + new vpc.VpcV2(stack, 'TestVpc', { + primaryAddressBlock: vpc.IpAddresses.ipv4('10.1.0.0/16'), + secondaryAddressBlocks: [vpc.IpAddresses.ipv6ByoipPool({ + cidrBlockName: 'SecondaryIPv6', + ipv6PoolId: 'SecondaryIPv6Pool', + ipv6CidrBlock: '2001:db8::/32', + })], + enableDnsHostnames: true, + enableDnsSupport: true, + }); + + Template.fromStack(stack).hasResourceProperties('AWS::EC2::VPCCidrBlock', { + VpcId: { + 'Fn::GetAtt': [ + 'TestVpcE77CE678', + 'VpcId', + ], + }, + Ipv6CidrBlock: '2001:db8::/32', + Ipv6Pool: 'SecondaryIPv6Pool', + }); + }); + + test('VPC with multiple IPv6 Pool addresses', () => { + // WHEN + new vpc.VpcV2(stack, 'TestVpc', { + primaryAddressBlock: vpc.IpAddresses.ipv4('10.1.0.0/16'), + secondaryAddressBlocks: [ + vpc.IpAddresses.ipv6ByoipPool({ + cidrBlockName: 'SecondaryIPv6One', + ipv6PoolId: 'Pool1', + ipv6CidrBlock: '2001:db8:1::/48', + }), + vpc.IpAddresses.ipv6ByoipPool({ + cidrBlockName: 'SecondaryIPv6Two', + ipv6PoolId: 'Pool2', + ipv6CidrBlock: '2001:db8:2::/48', + }), + ], + enableDnsHostnames: true, + enableDnsSupport: true, + }); + + // THEN + const template = Template.fromStack(stack); + template.resourceCountIs('AWS::EC2::VPCCidrBlock', 2); + }); +}); diff --git a/packages/@aws-cdk/aws-lambda-python-alpha/README.md b/packages/@aws-cdk/aws-lambda-python-alpha/README.md index 896e80210352a..01e519b0e1f1f 100644 --- a/packages/@aws-cdk/aws-lambda-python-alpha/README.md +++ b/packages/@aws-cdk/aws-lambda-python-alpha/README.md @@ -233,7 +233,7 @@ new python.PythonFunction(this, 'function', { }); ``` -The index URL or the token are only used during bundling and thus not included in the final asset. Setting only environment variable for `PIP_INDEX_URL` or `PIP_EXTRA_INDEX_URL` should work for accesing private Python repositories with `pip`, `pipenv` and `poetry` based dependencies. +The index URL or the token are only used during bundling and thus not included in the final asset. Setting only environment variable for `PIP_INDEX_URL` or `PIP_EXTRA_INDEX_URL` should work for accessing private Python repositories with `pip`, `pipenv` and `poetry` based dependencies. If you also want to use the Code Artifact repo for building the base Docker image for bundling, use `buildArgs`. However, note that setting custom build args for bundling will force the base bundling image to be rebuilt every time (i.e. skip the Docker cache). Build args can be customized as: @@ -299,7 +299,7 @@ container for Docker bundling or on the host OS for local bundling. ## Docker based bundling in complex Docker configurations By default the input and output of Docker based bundling is handled via bind mounts. -In situtations where this does not work, like Docker-in-Docker setups or when using a remote Docker socket, you can configure an alternative, but slower, variant that also works in these situations. +In situations where this does not work, like Docker-in-Docker setups or when using a remote Docker socket, you can configure an alternative, but slower, variant that also works in these situations. ```ts const entry = '/path/to/function'; diff --git a/packages/@aws-cdk/aws-lambda-python-alpha/lib/function.ts b/packages/@aws-cdk/aws-lambda-python-alpha/lib/function.ts index 510725c028af2..3bf00c9655148 100644 --- a/packages/@aws-cdk/aws-lambda-python-alpha/lib/function.ts +++ b/packages/@aws-cdk/aws-lambda-python-alpha/lib/function.ts @@ -74,7 +74,7 @@ export class PythonFunction extends Function { entry, runtime, skip: !Stack.of(scope).bundlingRequired, - // define architecture based on the target architecture of the function, possibly overriden in bundling options + // define architecture based on the target architecture of the function, possibly overridden in bundling options architecture: props.architecture, ...props.bundling, }), diff --git a/packages/@aws-cdk/aws-sagemaker-alpha/lib/scalable-instance-count.ts b/packages/@aws-cdk/aws-sagemaker-alpha/lib/scalable-instance-count.ts index 3cbae1594088f..c512adf0efbfd 100644 --- a/packages/@aws-cdk/aws-sagemaker-alpha/lib/scalable-instance-count.ts +++ b/packages/@aws-cdk/aws-sagemaker-alpha/lib/scalable-instance-count.ts @@ -60,7 +60,7 @@ export interface InvocationsScalingProps extends appscaling.BaseTargetTrackingPr */ readonly maxRequestsPerSecond: number; /** - * Safty factor for calculating the target SageMaker variant invocation per instance + * Safety factor for calculating the target SageMaker variant invocation per instance * * More documentation available here: https://docs.aws.amazon.com/sagemaker/latest/dg/endpoint-scaling-loadtest.html * @default 0.5 diff --git a/scripts/@aws-cdk/script-tests/prioritization/assign-priority.test.js b/scripts/@aws-cdk/script-tests/prioritization/assign-priority.test.js deleted file mode 100644 index d77a9eaccc937..0000000000000 --- a/scripts/@aws-cdk/script-tests/prioritization/assign-priority.test.js +++ /dev/null @@ -1,237 +0,0 @@ -const { PRIORITIES, LABELS, STATUS, ...PROJECT_CONFIG} = require('../../../../scripts/prioritization/project-config'); -const { - createMockPR, - createMockGithub, - OPTION_IDS -} = require('./helpers/mock-data'); - -const assignPriority = require('../../../../scripts/prioritization/assign-priority'); - - -describe('Priority Assignment (R1, R3, R4)', () => { - let mockGithub; - let mockContext; - - beforeEach(() => { - mockGithub = createMockGithub(); - jest.clearAllMocks(); - }); - - async function verifyProjectState(expectedPriority, expectedStatus) { - const calls = mockGithub.graphql.mock.calls; - - if (!expectedPriority) { - const priorityUpdateCall = calls.find(call => - call[1].input?.fieldId === PROJECT_CONFIG.priorityFieldId - ); - expect(priorityUpdateCall).toBeUndefined(); - return; - } - - const priorityUpdateCall = calls.find(call => - call[1].input?.fieldId === PROJECT_CONFIG.priorityFieldId - ); - const statusUpdateCall = calls.find(call => - call[1].input?.fieldId === PROJECT_CONFIG.statusFieldId - ); - - // Verify priority was set correctly - expect(priorityUpdateCall[1].input.value.singleSelectOptionId) - .toBe(OPTION_IDS[expectedPriority]); - - // Verify status was set to Ready - expect(statusUpdateCall[1].input.value.singleSelectOptionId) - .toBe(OPTION_IDS[expectedStatus]); - } - - describe('R1 Priority Tests', () => { - test('should assign R1 and Ready status to non-draft PR with contribution/core label', async () => { - const pr = createMockPR({ - draft: false, - labels: [LABELS.CORE] - }); - - mockContext = { payload: { pull_request: pr } }; - - await assignPriority({ github: mockGithub, context: mockContext }); - await verifyProjectState(PRIORITIES.R1, STATUS.READY); - }); - - test('should assign R1 and Ready status to non-draft PR with contribution/core and needs-maintainer-review labels', async () => { - const pr = createMockPR({ - draft: false, - labels: [LABELS.CORE, LABELS.MAINTAINER_REVIEW] - }); - - mockContext = { payload: { pull_request: pr } }; - - await assignPriority({ github: mockGithub, context: mockContext }); - await verifyProjectState(PRIORITIES.R1, STATUS.READY); - }); - - test('should not add draft PR with contribution/core label to project', async () => { - const pr = createMockPR({ - draft: true, - labels: [LABELS.CORE] - }); - - mockContext = { payload: { pull_request: pr } }; - - await assignPriority({ github: mockGithub, context: mockContext }); - await verifyProjectState(null); - }); - }); - - describe('R3 Priority Tests', () => { - test('should assign R3 and Ready status to non-draft PR with needs-maintainer-review label', async () => { - const pr = createMockPR({ - draft: false, - labels: [LABELS.MAINTAINER_REVIEW] - }); - - mockContext = { payload: { pull_request: pr } }; - - await assignPriority({ github: mockGithub, context: mockContext }); - await verifyProjectState(PRIORITIES.R3, STATUS.READY); - }); - - test('should not assign R3 to draft PR with needs-maintainer-review label', async () => { - const pr = createMockPR({ - draft: true, - labels: [LABELS.MAINTAINER_REVIEW] - }); - - mockContext = { payload: { pull_request: pr } }; - - await assignPriority({ github: mockGithub, context: mockContext }); - await verifyProjectState(null); - }); - }); - - describe('R4 Priority Tests', () => { - test('should assign R4 and Ready status to PR with pr/reviewer-clarification-requested and needs-community-review labels', async () => { - const pr = createMockPR({ - draft: true, - labels: [ - LABELS.CLARIFICATION_REQUESTED, - LABELS.COMMUNITY_REVIEW - ] - }); - - mockContext = { payload: { pull_request: pr } }; - - await assignPriority({ github: mockGithub, context: mockContext }); - await verifyProjectState(PRIORITIES.R4, STATUS.READY); - }); - - test('should assign R4 and Ready status to PR with pr-linter/exemption-requested and needs-community-review labels', async () => { - const pr = createMockPR({ - draft: true, - labels: [ - LABELS.EXEMPTION_REQUESTED, - LABELS.COMMUNITY_REVIEW - ] - }); - - mockContext = { payload: { pull_request: pr } }; - - await assignPriority({ github: mockGithub, context: mockContext }); - await verifyProjectState(PRIORITIES.R4, STATUS.READY); - }); - - test('should assign R4 and Ready status to PR with pr/reviewer-clarification-requested and needs-maintainer-review labels', async () => { - const pr = createMockPR({ - draft: true, - labels: [ - LABELS.CLARIFICATION_REQUESTED, - LABELS.MAINTAINER_REVIEW - ] - }); - - mockContext = { payload: { pull_request: pr } }; - - await assignPriority({ github: mockGithub, context: mockContext }); - await verifyProjectState(PRIORITIES.R4, STATUS.READY); - }); - - test('should assign R4 and Ready status to PR with pr-linter/exemption-requested and needs-maintainer-review labels', async () => { - const pr = createMockPR({ - labels: [ - LABELS.EXEMPTION_REQUESTED, - LABELS.MAINTAINER_REVIEW - ] - }); - - mockContext = { payload: { pull_request: pr } }; - - await assignPriority({ github: mockGithub, context: mockContext }); - await verifyProjectState(PRIORITIES.R4, STATUS.READY); - }); - - test('should assign R4 and Ready status to PR with pr/reviewer-clarification-requested label and no review labels', async () => { - const pr = createMockPR({ - labels: [LABELS.CLARIFICATION_REQUESTED] - }); - - mockContext = { payload: { pull_request: pr } }; - - await assignPriority({ github: mockGithub, context: mockContext }); - await verifyProjectState(PRIORITIES.R4, STATUS.READY); - }); - - test('should assign R4 and Ready status to PR with pr-linter/exemption-requested label and no review labels', async () => { - const pr = createMockPR({ - draft: true, - labels: [LABELS.EXEMPTION_REQUESTED] - }); - - mockContext = { payload: { pull_request: pr } }; - - await assignPriority({ github: mockGithub, context: mockContext }); - await verifyProjectState(PRIORITIES.R4, STATUS.READY); - }); - }); - - describe('Priority Precedence Tests', () => { - test('should assign R1 over R3 when PR has both contribution/core and needs-maintainer-review labels', async () => { - const pr = createMockPR({ - draft: false, - labels: [ - LABELS.CORE, - LABELS.MAINTAINER_REVIEW - ] - }); - - mockContext = { payload: { pull_request: pr } }; - - await assignPriority({ github: mockGithub, context: mockContext }); - await verifyProjectState(PRIORITIES.R1, STATUS.READY); - }); - - test('should assign R1 over R4 when PR has both contribution/core and pr/reviewer-clarification-requested labels', async () => { - const pr = createMockPR({ - draft: false, - labels: [ - LABELS.CORE, - LABELS.CLARIFICATION_REQUESTED - ] - }); - - mockContext = { payload: { pull_request: pr } }; - await assignPriority({ github: mockGithub, context: mockContext }); - await verifyProjectState(PRIORITIES.R1, STATUS.READY); - }); - - test('should not assign any priority when no matching labels', async () => { - const pr = createMockPR({ - draft: false, - labels: [] - }); - - mockContext = { payload: { pull_request: pr } }; - - await assignPriority({ github: mockGithub, context: mockContext }); - await verifyProjectState(null); - }); - }); -}); \ No newline at end of file diff --git a/scripts/@aws-cdk/script-tests/prioritization/assign-r2-priority.test.js b/scripts/@aws-cdk/script-tests/prioritization/assign-r2-priority.test.js deleted file mode 100644 index e6c4e1fa309a8..0000000000000 --- a/scripts/@aws-cdk/script-tests/prioritization/assign-r2-priority.test.js +++ /dev/null @@ -1,115 +0,0 @@ -const { PRIORITIES, LABELS, STATUS, ...PROJECT_CONFIG } = require('../../../../scripts/prioritization/project-config'); -const { - createMockGithubForR2, - OPTION_IDS -} = require('./helpers/mock-data'); - -const assignR2Priority = require('../../../../scripts/prioritization/assign-r2-priority'); - -describe('Priority Assignment (R2)', () => { - let mockGithub; - - beforeEach(() => { - jest.clearAllMocks(); - }); - - async function verifyProjectState(expectedPriority, expectedStatus) { - const calls = mockGithub.graphql.mock.calls; - - if (!expectedPriority) { - const priorityUpdateCall = calls.find(call => - call[1].input?.fieldId === PROJECT_CONFIG.priorityFieldId - ); - expect(priorityUpdateCall).toBeUndefined(); - return; - } - - // Get the existing project item data from the mock response - const projectItemResponse = await mockGithub.graphql.mock.results[2].value; - const existingPRData = projectItemResponse.node.projectItems.nodes[0]; - - // Verify priority update - const priorityUpdateCall = calls.find(call => - call[1].input?.fieldId === PROJECT_CONFIG.priorityFieldId - ); - expect(priorityUpdateCall[1].input.value.singleSelectOptionId) - .toBe(OPTION_IDS[expectedPriority]); - - // Find any status update calls - const statusUpdateCall = calls.find(call => - call[1].input?.fieldId === PROJECT_CONFIG.statusFieldId - ); - - if (existingPRData) { - // For existing PR - // Get the existing status - const existingStatus = existingPRData.fieldValues.nodes - .find(node => node.field.name === 'Status')?.name; - - // Verify no status update was made - expect(statusUpdateCall).toBeUndefined(); - - // Verify expected status matches existing status - expect(existingStatus).toBe(expectedStatus); - } else { - // For new PR - expect(statusUpdateCall[1].input.value.singleSelectOptionId) - .toBe(OPTION_IDS[STATUS.READY]); - } - } - - describe('R2 Priority Tests', () => { - test('should assign R2 priority and Ready status to approved PR with failing checks', async () => { - mockGithub = createMockGithubForR2({ - approved: true, - checksState: 'FAILURE' - }); - - await assignR2Priority({ github: mockGithub }); - await verifyProjectState(PRIORITIES.R2, STATUS.READY); - }); - - test('should not assign R2 priority to PR without approval', async () => { - mockGithub = createMockGithubForR2({ - approved: false, - checksState: 'FAILURE' - }); - - await assignR2Priority({ github: mockGithub }); - await verifyProjectState(null); - }); - - test('should not assign R2 priority to PR with passing checks', async () => { - mockGithub = createMockGithubForR2({ - approved: true, - checksState: 'SUCCESS' - }); - - await assignR2Priority({ github: mockGithub }); - await verifyProjectState(null); - }); - - test('should update existing PR to R2 priority', async () => { - mockGithub = createMockGithubForR2({ - approved: true, - checksState: 'FAILURE', - existingPriority: PRIORITIES.R3, - existingStatus: STATUS.IN_PROGRESS - }); - - await assignR2Priority({ github: mockGithub }); - await verifyProjectState(PRIORITIES.R2, STATUS.IN_PROGRESS); - }); - - test('should not update if PR already has R2 priority', async () => { - mockGithub = createMockGithubForR2({ - approved: true, - checksState: 'FAILURE', - existingPriority: PRIORITIES.R2 - }); - - await assignR2Priority({ github: mockGithub }); - await verifyProjectState(null); - }); - }); -}); \ No newline at end of file diff --git a/scripts/@aws-cdk/script-tests/prioritization/assign-r5-priority.test.js b/scripts/@aws-cdk/script-tests/prioritization/assign-r5-priority.test.js deleted file mode 100644 index 1886cc7081b49..0000000000000 --- a/scripts/@aws-cdk/script-tests/prioritization/assign-r5-priority.test.js +++ /dev/null @@ -1,116 +0,0 @@ -const { PRIORITIES, LABELS, STATUS, ...PROJECT_CONFIG } = require('../../../../scripts/prioritization/project-config'); -const { - createMockPR, - createMockGithubForR5, - OPTION_IDS -} = require('./helpers/mock-data'); - -const assignR5Priority = require('../../../../scripts/prioritization/assign-r5-priority'); - -describe('Priority Assignment (R5)', () => { - let mockGithub; - - beforeEach(() => { - jest.clearAllMocks(); - }); - - async function verifyProjectState(expectedPriority, expectedStatus) { - const calls = mockGithub.graphql.mock.calls; - - if (!expectedPriority) { - const priorityUpdateCall = calls.find(call => - call[1].input?.fieldId === PROJECT_CONFIG.priorityFieldId - ); - expect(priorityUpdateCall).toBeUndefined(); - return; - } - - const priorityUpdateCall = calls.find(call => - call[1].input?.fieldId === PROJECT_CONFIG.priorityFieldId - ); - const statusUpdateCall = calls.find(call => - call[1].input?.fieldId === PROJECT_CONFIG.statusFieldId - ); - - expect(priorityUpdateCall[1].input.value.singleSelectOptionId) - .toBe(OPTION_IDS[expectedPriority]); - - expect(statusUpdateCall[1].input.value.singleSelectOptionId) - .toBe(OPTION_IDS[expectedStatus]); - } - - describe('R5 Priority Tests', () => { - test('should assign R5 and Ready status to non-draft PR with needs-community-review label and no updates for 21 days', async () => { - mockGithub = createMockGithubForR5({ - draft: false, - labels: [LABELS.COMMUNITY_REVIEW] - }); - - await assignR5Priority({ github: mockGithub }); - await verifyProjectState(PRIORITIES.R5, STATUS.READY); - }); - - test('should not assign R5 to draft PR with needs-community-review label and no updates for 21 days', async () => { - mockGithub = createMockGithubForR5({ - draft: true, - labels: [LABELS.COMMUNITY_REVIEW] - }); - - await assignR5Priority({ github: mockGithub }); - await verifyProjectState(null); - }); - - test('should not assign R5 if PR updated within 21 days', async () => { - mockGithub = createMockGithubForR5({ - draft: false, - labels: [LABELS.COMMUNITY_REVIEW], - updatedAt: new Date(Date.now() - 20 * 24 * 60 * 60 * 1000).toISOString() - }); - - await assignR5Priority({ github: mockGithub }); - await verifyProjectState(null); - }); - - test('should not assign R5 if PR has needs-community-review and pr/reviewer-clarification-requested labels', async () => { - mockGithub = createMockGithubForR5({ - draft: false, - labels: [LABELS.COMMUNITY_REVIEW, LABELS.CLARIFICATION_REQUESTED] - }); - - await assignR5Priority({ github: mockGithub }); - await verifyProjectState(null); - }); - - test('should not assign R5 if PR has needs-community-review and pr-linter/exemption-requested labels', async () => { - mockGithub = createMockGithubForR5({ - draft: false, - labels: [LABELS.COMMUNITY_REVIEW, LABELS.EXEMPTION_REQUESTED] - }); - - await assignR5Priority({ github: mockGithub }); - await verifyProjectState(null); - }); - - test('should not assign R5 if PR does not have community review label', async () => { - mockGithub = createMockGithubForR5({ - draft: false, - labels: [] - }); - - await assignR5Priority({ github: mockGithub }); - await verifyProjectState(null); - }); - - test('should not update if PR already has R5 priority', async () => { - mockGithub = createMockGithubForR5({ - draft: false, - labels: [LABELS.COMMUNITY_REVIEW], - existingPriority: PRIORITIES.R5 - }); - - await assignR5Priority({ github: mockGithub }); - - await verifyProjectState(null); - }); - }); -}); diff --git a/scripts/@aws-cdk/script-tests/prioritization/helpers/mock-data.js b/scripts/@aws-cdk/script-tests/prioritization/helpers/mock-data.js deleted file mode 100644 index 418194e89ea31..0000000000000 --- a/scripts/@aws-cdk/script-tests/prioritization/helpers/mock-data.js +++ /dev/null @@ -1,257 +0,0 @@ -const { PRIORITIES, LABELS, STATUS, ...PROJECT_CONFIG } = require('../../../../../scripts/prioritization/project-config'); - -const OPTION_IDS = { - [PRIORITIES.R1]: 'r1-option-id', - [PRIORITIES.R2]: 'r2-option-id', - [PRIORITIES.R3]: 'r3-option-id', - [PRIORITIES.R4]: 'r4-option-id', - [PRIORITIES.R5]: 'r5-option-id', - [STATUS.READY]: 'ready-status-id', - [STATUS.IN_PROGRESS]: 'in_progress-status-id', - [STATUS.PAUSED]: 'paused-status-id', - [STATUS.ASSIGNED]: 'assigned-status-id', - [STATUS.DONE]: 'done-status-id' -}; - -const projectFields = { - organization: { - projectV2: { - fields: { - nodes: [ - { - id: PROJECT_CONFIG.priorityFieldId, - name: 'Priority', - options: Object.values(PRIORITIES).map(priority => ({ - id: OPTION_IDS[priority], - name: priority - })) - }, - { - id: PROJECT_CONFIG.statusFieldId, - name: 'Status', - options: Object.values(STATUS).map(status => ({ - id: OPTION_IDS[status], - name: status - })) - } - ] - } - } - } -}; - -const addItemToProject = { - addProjectV2ItemById: { - item: { id: 'new-item-id' } - } -} - -const updateFieldValueInProject = { - updateProjectV2ItemFieldValue: { - projectV2Item: { id: 'new-item-id' } - } -} - -/** - * Creates a mock PR with specified properties - */ -exports.createMockPR = ({ - number = 123, - node_id = 'PR_123', - draft = false, - labels = [], - updatedAt = new Date().toISOString(), - reviews = [], - checksState = 'SUCCESS' -}) => ({ - number, - node_id, - draft, - labels: labels.map(name => ({ name })), - updatedAt, - reviews: { nodes: reviews }, - commits: { - nodes: [{ - commit: { - statusCheckRollup: { state: checksState } - } - }] - } -}); - -/** - * Creates mock GitHub GraphQL client with predefined responses - */ -exports.createMockGithub = () => { - const graphql = jest.fn(); - - graphql - // First call - fetch project fields - .mockResolvedValueOnce(projectFields) - // Second call - add item to project - .mockResolvedValueOnce(addItemToProject) - // Third call - update priority - .mockResolvedValueOnce(updateFieldValueInProject) - // Fourth call - update status - .mockResolvedValueOnce(updateFieldValueInProject); - - return { graphql }; -}; - -/** - * Creates mock GitHub GraphQL client with predefined responses for R5 priority - */ -exports.createMockGithubForR5 = ({ - draft = false, - labels = [], - updatedAt = new Date(Date.now() - 22 * 24 * 60 * 60 * 1000).toISOString(), - existingPriority = null -}) => { - const graphql = jest.fn(); - - // Set up mock responses in sequence - graphql - // First call - fetch open PRs - .mockResolvedValueOnce({ - repository: { - pullRequests: { - nodes: [{ - id: 'PR_123', - number: 123, - draft, - updatedAt, - labels: { - nodes: labels.map(label => ({ name: label })) - } - }], - pageInfo: { - hasNextPage: false, - endCursor: null - } - } - } - }) - // Second call - fetch project fields - .mockResolvedValueOnce(projectFields) - // Third call - fetchProjectItem (check if PR is in project) - .mockResolvedValueOnce({ - node: { - projectItems: { - nodes: existingPriority ? [{ - id: 'existing-item-id', - project: { - id: PROJECT_CONFIG.projectId - }, - fieldValues: { - nodes: [{ - field: { name: 'Priority' }, - name: existingPriority - }] - } - }] : [] - } - } - }) - // Fourth call - add item to project - .mockResolvedValueOnce(addItemToProject) - // Fifth call - update priority - .mockResolvedValueOnce(updateFieldValueInProject) - // Sixth call - update status - .mockResolvedValueOnce(updateFieldValueInProject); - - return { graphql }; -}; - -/** - * Creates mock GitHub GraphQL client with predefined responses for R2 priority - */ -exports.createMockGithubForR2 = ({ - approved = false, - checksState = 'SUCCESS', - existingPriority = null, - existingStatus = STATUS.READY -}) => { - const graphql = jest.fn(); - - // Set up mock responses in sequence - graphql - // First call - fetch open PRs - .mockResolvedValueOnce({ - repository: { - pullRequests: { - nodes: [{ - id: 'PR_123', - number: 123, - reviews: { - nodes: approved ? [ - { state: 'APPROVED' } - ] : [] - }, - commits: { - nodes: [{ - commit: { - statusCheckRollup: { - state: checksState - } - } - }] - } - }], - pageInfo: { - hasNextPage: false, - endCursor: null - } - } - } - }) - // Second call - fetch project fields - .mockResolvedValueOnce(projectFields) - // Third call - check if PR is in project - .mockResolvedValueOnce({ - node: { - projectItems: { - nodes: existingPriority ? [{ - id: 'existing-item-id', - project: { - id: PROJECT_CONFIG.projectId - }, - fieldValues: { - nodes: [ - { - field: { name: 'Priority' }, - name: existingPriority - }, - { - field: { name: 'Status' }, - name: existingStatus - } - ] - } - }] : [] - } - } - }); - - // If PR exists and needs priority update - if (existingPriority && existingPriority !== PRIORITIES.R2) { - // Fourth call - update priority only - graphql.mockResolvedValueOnce({ - updateProjectV2ItemFieldValue: { - projectV2Item: { id: 'existing-item-id' } - } - }); - } - // If PR doesn't exist in project - else if (!existingPriority) { - // Fourth call - add to project - graphql.mockResolvedValueOnce(addItemToProject) - // Fifth call - update priority - .mockResolvedValueOnce(updateFieldValueInProject) - //Sixth call - update status - .mockResolvedValueOnce(updateFieldValueInProject); - } - - return { graphql }; -}; - -exports.OPTION_IDS = OPTION_IDS; \ No newline at end of file diff --git a/scripts/prioritization/README.md b/scripts/prioritization/README.md deleted file mode 100644 index 9d990b0610218..0000000000000 --- a/scripts/prioritization/README.md +++ /dev/null @@ -1,115 +0,0 @@ -# Prioritization Github Action workflow Automation - -## Setup - -Note: This configuration needs to be updated only when project fields are modified. - -### Prerequisites -1. GitHub CLI installed (`gh`) -2. Appropriate permissions to access AWS organization -3. GitHub token with `read:org` and `project` scopes - -### Project Configuration -To set up the prioritization automation, we need to get the field IDs of the project board and update the configuration. Follow these steps: - -1. Add the github token to `GH_TOKEN` environment variable: - - ```bash - export GH_TOKEN="YOUR GITHUB TOKEN" - -2. Retrieve project field IDs for the specific project: - - ```bash - # Get project and field IDs - gh api graphql -f query=' - query { - organization(login: "aws") { - projectV2(number: YOUR PROJECT NUMBER) { - id - fields(first: 20) { - nodes { - ... on ProjectV2SingleSelectField { - id - name - options { - id - name - } - } - } - } - } - } - } - ' | jq '.data.organization.projectV2 as $project | { - projectId: $project.id, - fields: [ - $project.fields.nodes[] | - select(.name == "Priority" or .name == "Status" or .name == "Needs Attention") | - {name: .name, id: .id} - ] - }' - -3. Update configuration with the returned IDs: - - ```javascript - // project-config.js - module.exports = { - ... - projectNumber: 263, // Project Number - projectId: "xxx", // Project ID - priorityFieldId: "xxx", // Priority field ID - statusFieldId: "xxx", // Status field ID - attentionFieldId: "xxx", // Needs Attention field ID - }; - - -## Available Views -1. [Prioritized Backlog](https://github.com/orgs/aws/projects/263/views/1) : Overall view of all PRs with prioritization -2. [My Items](https://github.com/orgs/aws/projects/263/views/6) : Filtered view showing only PRs assigned to you - -## Common Labels and Categories - -### Priority Labels -`R1` -> Non-draft PRs from the team (`contribution/core`) -`R2` -> Approved PRs with failing/pending checks -`R3` -> Non-draft PRs that needs maintainer review (`pr/needs-maintainer-review`) -`R4` -> PRs that needs clarification or exemption (`pr/reviewer-clarification-requested, pr-linter/exemption-requested`), draft state allowed -`R5` -> Non-draft PRs that are in needs-community-review more than 21 days (`pr/needs-community-review`) - -### Work Status Labels -`Ready` -> Means the PR is ready to be picked up for review -`Assigned` -> Means a team member have picked the PR and assigned to themselves -`In progress` -> Currently being reviewed -`Paused` -> PR review is paused for some reason. Eg: security review -`Done` -> PR review is completed and merged or closed - -### Needs Attention Labels -`Extended` -> If the status being in 7-14 days. Taking longer than expected -`Aging` -> If the status being in 14-21 days. Requires immediate attention -`Stalled` -> If the status being in > 21 days. Critical attention required - -These `Needs Attention` states apply to items in the following status labels: -- Ready: Awaiting assignment -- Assigned: Awaiting start -- In Progress: Under review -- Paused: Blocked/On hold - -## Workflows - -### Prioritized Backlog Workflow -1. PRs are automatically categorized by priority (`R1-R5`) -2. Team members can select PRs from the Ready state -3. Status updates flow through: `Assigned` β†’ `In Progress` β†’ `Done/Paused` -4. Time-based monitoring labels are automatically applied in `Needs Attention` based on duration in each state - -### My Items Workflow -1. PRs appear when assigned to you -2. Update status as you progress with reviews -3. Track your active reviews and blocked items -4. Monitor time-based alerts for your assignments - -## Automation -- Priority labels are automatically assigned based on PR labels -- Time-based monitoring states are automatically updated daily -- Status changes trigger automatic label updates \ No newline at end of file diff --git a/scripts/prioritization/assign-priority.js b/scripts/prioritization/assign-priority.js deleted file mode 100644 index b9c232484bfc8..0000000000000 --- a/scripts/prioritization/assign-priority.js +++ /dev/null @@ -1,135 +0,0 @@ -/** - * Handles the initial priority assignment for PRs when labels are added. This script - * processes R1 (team PRs with contribution/core label), R3 (PRs needing maintainer review), - * and R4 (PRs needing clarification or exemption) priorities. When a matching label - * is detected, the PR is added to the project board with appropriate priority and - * set to Ready status. - */ - - -const { PRIORITIES, LABELS, STATUS, ...PROJECT_CONFIG } = require('./project-config'); -const { - updateProjectField, - addItemToProject, - fetchProjectFields, -} = require('./project-api'); - -module.exports = async ({ github, context }) => { - const getPriority = (pr) => { - const labels = pr.labels.map((l) => l.name); - const isDraft = pr.draft === true; - - const hasExemptionOrClarification = labels.some(label => - [LABELS.CLARIFICATION_REQUESTED, LABELS.EXEMPTION_REQUESTED].includes(label) - ); - - // R1: Not draft + contribution/core - if (!isDraft && labels.includes(LABELS.CORE)) { - return PRIORITIES.R1; - } - - // R3: Not draft + needs-maintainer-review + no contribution/core + no exemption/clarification - if (!isDraft && - labels.includes(LABELS.MAINTAINER_REVIEW) && - !labels.includes(LABELS.CORE) && - !hasExemptionOrClarification) { - return PRIORITIES.R3; - } - - // R4: Three conditions (draft allowed) - if (hasExemptionOrClarification && ( - // Condition 1: With community review - labels.includes(LABELS.COMMUNITY_REVIEW) || - // Condition 2: With maintainer review - labels.includes(LABELS.MAINTAINER_REVIEW) || - // Condition 3: No community or maintainer review - (!labels.includes(LABELS.COMMUNITY_REVIEW) && - !labels.includes(LABELS.MAINTAINER_REVIEW)) - )) { - return PRIORITIES.R4; - } - - return null; - }; - - async function addToProject(pr) { - - // Check if PR qualifies for any priority - const priority = getPriority(pr); - if (!priority) { - console.log(`PR #${pr.number} doesn't qualify for any priority. Skipping.`); - return; - } - - console.log(`Processing PR #${pr.number} for ${priority} priority`); - - // Get project fields - const projectFields = await fetchProjectFields({ - github, - org: PROJECT_CONFIG.org, - number: PROJECT_CONFIG.projectNumber - }); - - const priorityField = projectFields.organization.projectV2.fields.nodes.find( - (field) => field.id === PROJECT_CONFIG.priorityFieldId - ); - - const statusField = projectFields.organization.projectV2.fields.nodes.find( - (field) => field.id === PROJECT_CONFIG.statusFieldId - ); - - try { - // Add PR to project - const addResult = await addItemToProject({ - github, - projectId: PROJECT_CONFIG.projectId, - contentId: pr.node_id, - }); - - const itemId = addResult.addProjectV2ItemById.item.id; - - // Set priority - const priorityOptionId = priorityField.options.find( - (option) => option.name === priority - )?.id; - - if (!priorityOptionId) { - console.error(`Priority option ${priority} not found in project settings`); - return; - } - - // Set Ready status - const readyOptionId = statusField.options.find( - (option) => option.name === STATUS.READY - )?.id; - - if (!readyOptionId) { - console.error('Ready status option not found in project settings'); - return; - } - - // Set Priority and Ready Status - await Promise.all([ - updateProjectField({ - github, - projectId: PROJECT_CONFIG.projectId, - itemId: itemId, - fieldId: PROJECT_CONFIG.priorityFieldId, - value: priorityOptionId, - }), - updateProjectField({ - github, - projectId: PROJECT_CONFIG.projectId, - itemId: itemId, - fieldId: PROJECT_CONFIG.statusFieldId, - value: readyOptionId, - }) - ]); - } catch (error) { - console.error(`Error processing PR #${pr.number}:`, error); - } - } - - const pr = context.payload.pull_request; - await addToProject(pr); -}; diff --git a/scripts/prioritization/assign-r2-priority.js b/scripts/prioritization/assign-r2-priority.js deleted file mode 100644 index 0d5379f53a8fa..0000000000000 --- a/scripts/prioritization/assign-r2-priority.js +++ /dev/null @@ -1,152 +0,0 @@ -/** - * Processes open PRs every 6 hours during weekdays to identify and assign R2 priority. A PR qualifies - * for R2 when it has received approval but has failing or pending checks, regardless of its current - * priority or status. These PRs are either added to the project board with R2 priority and Ready status - * (if not already in board) or updated to R2 priority (if already in board with different priority). - */ - -const { PRIORITIES, LABELS, STATUS, ...PROJECT_CONFIG } = require("./project-config"); - -const { - updateProjectField, - addItemToProject, - fetchProjectFields, - fetchOpenPullRequests, - fetchProjectItem, -} = require('./project-api'); - - -module.exports = async ({ github }) => { - let allPRs = []; - let hasNextPage = true; - let cursor = null; - - // Fetch all PRs using pagination - while (hasNextPage) { - const result = await fetchOpenPullRequests({ - github, - owner: PROJECT_CONFIG.owner, - repo: PROJECT_CONFIG.repo, - cursor: cursor, - }); - - const pullRequests = result.repository.pullRequests; - allPRs = allPRs.concat(pullRequests.nodes); - - // Update pagination info - hasNextPage = pullRequests.pageInfo.hasNextPage; - cursor = pullRequests.pageInfo.endCursor; - } - - console.log(`Total PRs fetched: ${allPRs.length}`); - - // Get project fields - const projectFields = await fetchProjectFields({ - github, - org: PROJECT_CONFIG.org, - number: PROJECT_CONFIG.projectNumber - }); - - const priorityField = projectFields.organization.projectV2.fields.nodes.find( - (field) => field.id === PROJECT_CONFIG.priorityFieldId - ); - - const statusField = projectFields.organization.projectV2.fields.nodes.find( - (field) => field.id === PROJECT_CONFIG.statusFieldId - ); - - const r2OptionId = priorityField.options.find( - (option) => option.name === PRIORITIES.R2 - )?.id; - - const readyStatusId = statusField.options.find( - (option) => option.name === STATUS.READY - )?.id; - - for (const pr of allPRs) { - try { - - // Check PR status - const isApproved = pr.reviews.nodes.some( - (review) => review.state === "APPROVED" - ); - - // Skip if PR is not approved - if (!isApproved) { - continue; - } - - // Check status of checks - const checksState = pr.commits.nodes[0]?.commit.statusCheckRollup?.state; - const checksNotPassing = checksState !== "SUCCESS"; - - // Skip if PR checks is not passing - if (!checksNotPassing) { - continue; - } - - console.log(`Processing PR #${pr.number} for ${PRIORITIES.R2} priority consideration`); - - // Get all projects the PR added to - const result = await fetchProjectItem({ - github, - contentId: pr.id - }); - - // Filter our specific project - const projectItem = result.node.projectItems.nodes - .find(item => item.project.id === PROJECT_CONFIG.projectId); - - if (projectItem) { - // PR already in project - const currentPriority = projectItem.fieldValues.nodes - .find(fv => fv.field?.name === 'Priority')?.name; - - if (currentPriority === PRIORITIES.R2) { - console.log(`PR #${pr.number} already has ${PRIORITIES.R2} priority. Skipping.`); - continue; - } - - // Update priority only, maintain existing status - console.log(`Updating PR #${pr.number} from ${currentPriority} to ${PRIORITIES.R2} priority`); - await updateProjectField({ - github, - projectId: PROJECT_CONFIG.projectId, - itemId: projectItem.id, - fieldId: PROJECT_CONFIG.priorityFieldId, - value: r2OptionId, - }); - } else { - // Add new PR to project with R2 priority and Ready status - console.log(`Adding PR #${pr.number} to project with ${PRIORITIES.R2} priority`); - const addResult = await addItemToProject({ - github, - projectId: PROJECT_CONFIG.projectId, - contentId: pr.id, - }); - itemId = addResult.addProjectV2ItemById.item.id; - - // Set both priority and initial status for new items - await Promise.all([ - updateProjectField({ - github, - projectId: PROJECT_CONFIG.projectId, - itemId: itemId, - fieldId: PROJECT_CONFIG.priorityFieldId, - value: r2OptionId, - }), - updateProjectField({ - github, - projectId: PROJECT_CONFIG.projectId, - itemId: itemId, - fieldId: PROJECT_CONFIG.statusFieldId, - value: readyStatusId, - }) - ]); - } - } catch (error) { - console.error(`Error processing PR #${pr.number}:`, error); - continue; - } - } -}; diff --git a/scripts/prioritization/assign-r5-priority.js b/scripts/prioritization/assign-r5-priority.js deleted file mode 100644 index bee4e1921a845..0000000000000 --- a/scripts/prioritization/assign-r5-priority.js +++ /dev/null @@ -1,145 +0,0 @@ -/** - * Monitors open PRs once daily during weekdays to identify stale community review requests. When a PR - * with the community review label hasn't been updated for the specified threshold - * period (default 21 days), it's assigned R5 priority. These PRs are added to the - * project board and set to Ready status to ensure visibility of long-pending - * community reviews. - */ - -const { PRIORITIES, LABELS, STATUS, DAYS_THRESHOLD, ...PROJECT_CONFIG } = require("./project-config"); - -const { - updateProjectField, - addItemToProject, - fetchProjectFields, - fetchOpenPullRequests, - fetchProjectItem, -} = require('./project-api'); - -const MS_PER_DAY = 1000 * 60 * 60 * 24; - -module.exports = async ({ github }) => { - let allPRs = []; - let hasNextPage = true; - let cursor = null; - - // Fetch all PRs using pagination - while (hasNextPage) { - const result = await fetchOpenPullRequests({ - github, - owner: PROJECT_CONFIG.owner, - repo: PROJECT_CONFIG.repo, - cursor: cursor, - }); - - const pullRequests = result.repository.pullRequests; - allPRs = allPRs.concat(pullRequests.nodes); - - // Update pagination info - hasNextPage = pullRequests.pageInfo.hasNextPage; - cursor = pullRequests.pageInfo.endCursor; - } - - console.log(`Total PRs fetched: ${allPRs.length}`); - - // Get project fields - const projectFields = await fetchProjectFields({ - github, - org: PROJECT_CONFIG.org, - number: PROJECT_CONFIG.projectNumber - }); - - const priorityField = projectFields.organization.projectV2.fields.nodes.find( - (field) => field.id === PROJECT_CONFIG.priorityFieldId - ); - - const statusField = projectFields.organization.projectV2.fields.nodes.find( - (field) => field.id === PROJECT_CONFIG.statusFieldId - ); - - const r5OptionId = priorityField.options.find( - (option) => option.name === PRIORITIES.R5 - )?.id; - - const readyStatusId = statusField.options.find( - (option) => option.name === STATUS.READY - )?.id; - - for (const pr of allPRs) { - const labels = pr.labels.nodes.map((l) => l.name); - const isDraft = pr.draft === true; - - // Skip draft PRs - if (isDraft) { - console.log(`Skipping draft PR #${pr.number}`); - continue; - } - - const hasExemptionOrClarification = labels.some(label => - [LABELS.CLARIFICATION_REQUESTED, LABELS.EXEMPTION_REQUESTED].includes(label) - ); - - // Skip if PR doesn't have community review label or has exemption/clarification - if (!labels.includes(LABELS.COMMUNITY_REVIEW) || hasExemptionOrClarification) { - continue; - } - - const lastUpdated = new Date(pr.updatedAt); - const daysSinceUpdate = (Date.now() - lastUpdated) / MS_PER_DAY; - - // Skip if PR update is within the days threshold - if (daysSinceUpdate <= DAYS_THRESHOLD) { - continue; - } - - console.log(`Processing PR #${pr.number} for ${PRIORITIES.R5} priority consideration`); - - try { - // Get all projects the PR added to - const result = await fetchProjectItem({ - github, - contentId: pr.id - }); - - // Filter specific project - const projectItem = result.node.projectItems.nodes - .find(item => item.project.id === PROJECT_CONFIG.projectId); - - // Skip if PR is already in project - if (projectItem) { - console.log(`PR #${pr.number} is already in project. Skipping.`); - continue; - } - - // Add new PR to project with R5 priority - console.log(`Adding PR #${pr.number} to project with ${PRIORITIES.R5} priority`); - - const addResult = await addItemToProject({ - github, - projectId: PROJECT_CONFIG.projectId, - contentId: pr.id, - }); - - // Set initial priority and status - await Promise.all([ - updateProjectField({ - github, - projectId: PROJECT_CONFIG.projectId, - itemId: addResult.addProjectV2ItemById.item.id, - fieldId: PROJECT_CONFIG.priorityFieldId, - value: r5OptionId, - }), - updateProjectField({ - github, - projectId: PROJECT_CONFIG.projectId, - itemId: addResult.addProjectV2ItemById.item.id, - fieldId: PROJECT_CONFIG.statusFieldId, - value: readyStatusId, - }) - ]); - } catch (error) { - console.error(`Error processing PR #${pr.number}:`, error); - continue; - } -} -} diff --git a/scripts/prioritization/project-api.js b/scripts/prioritization/project-api.js deleted file mode 100644 index 7b1d1ce006b7d..0000000000000 --- a/scripts/prioritization/project-api.js +++ /dev/null @@ -1,201 +0,0 @@ -/** - * Updates a field value for an item in a GitHub Project. - * @param {Object} params - The parameters for updating the project field - * @param {Object} params.github - The GitHub API client - * @param {string} params.projectId - The ID of the project - * @param {string} params.itemId - The ID of the item to update - * @param {string} params.fieldId - The ID of the field to update - * @param {string} params.value - The new value for the field - * @returns {Promise} The GraphQL mutation response - */ -const updateProjectField = async ({ - github, - projectId, - itemId, - fieldId, - value, - }) => { - return github.graphql( - ` - mutation($input: UpdateProjectV2ItemFieldValueInput!) { - updateProjectV2ItemFieldValue(input: $input) { - projectV2Item { - id - } - } - } - `, - { - input: { - projectId, - itemId, - fieldId, - value: value ? { singleSelectOptionId: value } : null, - }, - } - ); - }; - -/** - * Adds an item (PR) to a GitHub Project. - * @param {Object} params - The parameters for adding an item to the project - * @param {Object} params.github - The GitHub API client - * @param {string} params.projectId - The ID of the project - * @param {string} params.contentId - The node ID of the PR to add - * @returns {Promise} The GraphQL mutation response with the new item's ID - */ - const addItemToProject = async ({ github, projectId, contentId }) => { - return github.graphql( - ` - mutation($input: AddProjectV2ItemByIdInput!) { - addProjectV2ItemById(input: $input) { - item { - id - } - } - } - `, - { - input: { - projectId, - contentId, - }, - } - ); - }; - -/** - * Fetches fields configuration for a GitHub Project. - * @param {Object} params - The parameters for fetching project fields - * @param {Object} params.github - The GitHub API client - * @param {string} params.org - The organization name - * @param {number} params.number - The project number - * @returns {Promise} The project fields data including field IDs and options - */ - const fetchProjectFields = async ({ github, org, number }) => { - return github.graphql( - ` - query($org: String!, $number: Int!) { - organization(login: $org) { - projectV2(number: $number) { - fields(first: 20) { - nodes { - ... on ProjectV2SingleSelectField { - id - name - options { - id - name - } - } - } - } - } - } - } - `, - { org, number } - ); - }; - - -/** - * Fetches open pull requests from a repository with pagination support. - * Includes data needed for both R2 and R5 priority processing. - * @param {Object} params - The parameters for fetching pull requests - * @param {Object} params.github - The GitHub API client - * @param {string} params.owner - The repository owner - * @param {string} params.repo - The repository name - * @param {string} [params.cursor] - The pagination cursor - * @returns {Promise} The GraphQL mutation response - */ - const fetchOpenPullRequests = async ({ github, owner, repo, cursor }) => { - return github.graphql( - ` - query($owner: String!, $repo: String!, $cursor: String) { - repository(owner: $owner, name: $repo) { - pullRequests(first: 100, after: $cursor, states: OPEN) { - nodes { - id - number - updatedAt - reviews(last: 100) { - nodes { - state - } - } - commits(last: 1) { - nodes { - commit { - statusCheckRollup { - state - } - } - } - } - labels(first: 10) { - nodes { - name - } - } - } - pageInfo { - hasNextPage - endCursor - } - } - } - } - `, - { owner, repo, cursor } - ); - }; - - /** - * Fetches project item details for a specific PR - * @param {Object} params - The parameters for fetching project item - * @param {Object} params.github - The GitHub API client - * @param {string} params.contentId - PR node ID - * @returns {Promise} Project item details if PR is in project - */ - const fetchProjectItem = async ({ github, contentId }) => { - return github.graphql( - ` - query($contentId: ID!) { - node(id: $contentId) { - ... on PullRequest { - projectItems(first: 100) { - nodes { - id - project { - id - } - fieldValues(first: 8) { - nodes { - ... on ProjectV2ItemFieldSingleSelectValue { - name - field { - ... on ProjectV2SingleSelectField { - name - } - } - } - } - } - } - } - } - } - } - `, - { contentId } - ); - }; - - module.exports = { - updateProjectField, - addItemToProject, - fetchProjectFields, - fetchOpenPullRequests, - fetchProjectItem - }; \ No newline at end of file diff --git a/scripts/prioritization/project-config.js b/scripts/prioritization/project-config.js deleted file mode 100644 index de80a31240f77..0000000000000 --- a/scripts/prioritization/project-config.js +++ /dev/null @@ -1,65 +0,0 @@ -const LABELS = { - CORE: 'contribution/core', - MAINTAINER_REVIEW: 'pr/needs-maintainer-review', - COMMUNITY_REVIEW: 'pr/needs-community-review', - CLARIFICATION_REQUESTED: 'pr/reviewer-clarification-requested', - EXEMPTION_REQUESTED: 'pr-linter/exemption-requested' -}; - -const PRIORITIES = { - R1: '🚨 R1', - R2: 'πŸ”₯ R2', - R3: '🎯 R3', - R4: 'πŸ’­ R4', - R5: 'πŸ“† R5' -}; - -const STATUS = { - READY: '⭐ Ready', - IN_PROGRESS: 'πŸ”„ In Progress', - PAUSED: '⏸️ Paused', - ASSIGNED: 'πŸ‘€ Assigned', - DONE: 'βœ… Done' -}; - -// Time threshold for R5 -const DAYS_THRESHOLD = 21; - -const ATTENTION_STATUS = { - STALLED: { - name: '🚨 Stalled', - threshold: 21, - description: 'Critical attention required' - }, - AGING: { - name: '⚠️ Aging', - threshold: 14, - description: 'Requires immediate attention' - }, - EXTENDED: { - name: 'πŸ•’ Extended', - threshold: 7, - description: 'Taking longer than expected' - } -}; - -/** - * Project configuration for GitHub project automation. - * Note: For projectId, priorityFieldId, statusFieldId, and attentionFieldId, - * refer to Setup section in README.md on how to retrieve these values using GraphQL query. - * These IDs need to be updated only when project fields are modified. - */ -module.exports = { - org: 'aws', - repo: 'aws-cdk', - projectNumber: 263, - projectId: 'PVT_kwDOACIPmc4Av_32', - priorityFieldId: 'PVTSSF_lADOACIPmc4Av_32zgmVmPs', - statusFieldId: 'PVTSSF_lADOACIPmc4Av_32zgmVmF8', - attentionFieldId: 'PVTSSF_lADOACIPmc4Av_32zgmZDdo', - LABELS, - PRIORITIES, - STATUS, - ATTENTION_STATUS, - DAYS_THRESHOLD -}; diff --git a/yarn.lock b/yarn.lock index 1e56bb8661401..c448bcffd7ae0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7104,14 +7104,14 @@ chalk "^4.1.2" semver "^7.6.3" -"@jsii/spec@1.104.0", "@jsii/spec@^1.103.1", "@jsii/spec@^1.104.0": +"@jsii/spec@1.104.0": version "1.104.0" resolved "https://registry.npmjs.org/@jsii/spec/-/spec-1.104.0.tgz#9f1206b3712808ad7cbbdbf6cf333a77a8f3df8c" integrity sha512-7jxU8iRowA3O7Dpn8XAsX8o4Y8Fy8plbEVg0CnjvIQsJh3puI3KFHspXur70OOccfGkoL1TWnXBZ+BwCcvhu1g== dependencies: ajv "^8.17.1" -"@jsii/spec@^1.106.0": +"@jsii/spec@^1.103.1", "@jsii/spec@^1.104.0", "@jsii/spec@^1.106.0": version "1.106.0" resolved "https://registry.npmjs.org/@jsii/spec/-/spec-1.106.0.tgz#f40010ec8cde14b7a003dd5b6a46480d9d7e222f" integrity sha512-pAIvqEGf0YLmtzFtUKWNEGkCmXMHENy7k+rzCD147wnM4jHhvEL1mEvxi99aA2VcmvLYaAYNOs/XozT+s+kLqQ== @@ -15866,9 +15866,9 @@ jsii@~5.4.0: yargs "^17.7.2" jsii@~5.7.0: - version "5.7.3" - resolved "https://registry.npmjs.org/jsii/-/jsii-5.7.3.tgz#5f35be6529cf316cd903692112ad8321aa710ab4" - integrity sha512-YhXN1vNHDHbGMkER9CpCFyleeOA4SuFS+J8U0LCyKOAqtmw8DkbBiHP/6kYyJBd/Wl0Z7CdapMoL7UDAQBWUuA== + version "5.7.4" + resolved "https://registry.npmjs.org/jsii/-/jsii-5.7.4.tgz#4d7e7f5d7257789767b7dacab832e0fe7d9cec51" + integrity sha512-L2JWl4ETXjXCe25t8vg0lFQ0ycqtMuGDd0j0kSxKNFO/g19c3mW5wcClPZF1YD1fG5VIHG4t9ZGJ8ZU8H7tZMg== dependencies: "@jsii/check-node" "1.106.0" "@jsii/spec" "^1.106.0" @@ -20534,10 +20534,10 @@ typescript-json-schema@^0.65.1: typescript "~5.5.0" yargs "^17.1.1" -"typescript@>=3 < 6", typescript@~5.6.3: - version "5.6.3" - resolved "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz#5f3449e31c9d94febb17de03cc081dd56d81db5b" - integrity sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw== +"typescript@>=3 < 6", typescript@~5.7: + version "5.7.2" + resolved "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz#3169cf8c4c8a828cde53ba9ecb3d2b1d5dd67be6" + integrity sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg== typescript@^3.9.10, typescript@^3.9.5, typescript@^3.9.7: version "3.9.10" @@ -20559,10 +20559,10 @@ typescript@~5.5.0, typescript@~5.5.2: resolved "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz#d9852d6c82bad2d2eda4fd74a5762a8f5909e9ba" integrity sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q== -typescript@~5.7: - version "5.7.2" - resolved "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz#3169cf8c4c8a828cde53ba9ecb3d2b1d5dd67be6" - integrity sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg== +typescript@~5.6.3: + version "5.6.3" + resolved "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz#5f3449e31c9d94febb17de03cc081dd56d81db5b" + integrity sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw== uc.micro@^1.0.1, uc.micro@^1.0.5: version "1.0.6" @@ -20595,9 +20595,9 @@ undici-types@~6.19.2: integrity sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw== undici@^5.25.4: - version "5.28.4" - resolved "https://registry.npmjs.org/undici/-/undici-5.28.4.tgz#6b280408edb6a1a604a9b20340f45b422e373068" - integrity sha512-72RFADWFqKmUb2hmmvNODKL3p9hcB6Gt2DOQMis1SEBaV6a4MH8soBvzg+95CYhCKPFedut2JY9bMfrDl9D23g== + version "5.28.5" + resolved "https://registry.npmjs.org/undici/-/undici-5.28.5.tgz#b2b94b6bf8f1d919bc5a6f31f2c01deb02e54d4b" + integrity sha512-zICwjrDrcrUE0pyyJc1I2QzBkLM8FINsgOrt6WjA+BgajVq9Nxu2PbFFXUrAggLfDXlZGZBVZYw7WNV5KiBiBA== dependencies: "@fastify/busboy" "^2.0.0"