/** * CDK Utilities for Spicy CDK pipelines * * Provides functions to run AWS CDK commands from Jenkins pipelines. * CDK runs directly on the Jenkins worker (no Docker needed for CDK itself). */ /** * Run a CDK command * * @param args.account Account configuration with region and jenkinsAwsCredentialsId * @param args.command The CDK command to run (e.g., "deploy", "diff", "synth") * @param args.stackName Name of the stack to deploy * @param args.context Map of context values to pass to CDK (-c key=value) * @param args.workDir Working directory (default: current directory) * @param args.requireApproval CDK approval level (default: "never" for CI/CD) */ def runCdk(Map args) { def account = args.account def command = args.command ?: "deploy" def stackName = args.stackName def context = args.context ?: [:] def workDir = args.workDir ?: "cdk-source" def requireApproval = args.requireApproval ?: "never" // Build context arguments def contextArgs = context.collect { k, v -> if (v != null && v != '') { "-c ${k}='${v}'" } }.findAll { it != null }.join(' ') // Build the full CDK command def cdkCommand = "npx cdk ${command}" if (stackName) { cdkCommand += " ${stackName}" } if (contextArgs) { cdkCommand += " ${contextArgs}" } if (command == "deploy") { cdkCommand += " --require-approval ${requireApproval}" } if (command == "destroy") { cdkCommand += " --force" } // Run with AWS credentials withCredentials([[$class: 'AmazonWebServicesCredentialsBinding', credentialsId: account.jenkinsAwsCredentialsId]]) { dir(workDir) { sh """ export AWS_DEFAULT_REGION=${account.region} export CDK_DEFAULT_ACCOUNT=${account.accountId ?: ''} export CDK_DEFAULT_REGION=${account.region} ${cdkCommand} """ } } } /** * Deploy a CDK stack * * @param args.account Account configuration * @param args.stackName Name of the stack * @param args.stackType Type of stack (vpc, ecs-cluster, ecs-service) * @param args.context Additional context values */ def deploy(Map args) { def context = args.context ?: [:] // Add stackType and stackName to context context.stackType = args.stackType ?: "vpc" context.stackName = args.stackName context.region = args.account.region runCdk( account: args.account, command: "deploy", stackName: args.stackName, context: context, workDir: args.workDir ?: "cdk-source" ) } /** * Show diff for a CDK stack */ def diff(Map args) { def context = args.context ?: [:] context.stackType = args.stackType ?: "vpc" context.stackName = args.stackName context.region = args.account.region runCdk( account: args.account, command: "diff", stackName: args.stackName, context: context, workDir: args.workDir ?: "cdk-source" ) } /** * Synthesize CloudFormation template for a CDK stack */ def synth(Map args) { def context = args.context ?: [:] context.stackType = args.stackType ?: "vpc" context.stackName = args.stackName context.region = args.account.region runCdk( account: args.account, command: "synth", stackName: args.stackName, context: context, workDir: args.workDir ?: "cdk-source" ) } /** * Destroy a CDK stack */ def destroy(Map args) { def context = args.context ?: [:] context.stackType = args.stackType ?: "vpc" context.stackName = args.stackName context.region = args.account.region runCdk( account: args.account, command: "destroy", stackName: args.stackName, context: context, workDir: args.workDir ?: "cdk-source" ) } /** * Install CDK workspace files from library resources. * Assumes CDK/TypeScript toolchain is available globally on the agent. */ def install(Map args = [:]) { def workDir = args.workDir ?: "cdk-source" dir(workDir) { // Ensure expected directory layout exists before copying files from resources sh "mkdir -pv bin lib lib/constructs lib/stacks" // Copy CDK project files from shared library resources into the workspace [ [source: "package.json", destination: "package.json"], [source: "pnpm-lock.yaml", destination: "pnpm-lock.yaml"], [source: "tsconfig.json", destination: "tsconfig.json"], [source: "cdk.json", destination: "cdk.json"], [source: "cdk.context.json", destination: "cdk.context.json"], [source: "bin/spicy-cdk.ts", destination: "bin/spicy-cdk.ts"], [source: "lib/index.ts", destination: "lib/index.ts"], [source: "lib/constructs/index.ts", destination: "lib/constructs/index.ts"], [source: "lib/constructs/spicy-alb.ts", destination: "lib/constructs/spicy-alb.ts"], [source: "lib/constructs/spicy-ecs-cluster.ts", destination: "lib/constructs/spicy-ecs-cluster.ts"], [source: "lib/constructs/spicy-ecs-service.ts", destination: "lib/constructs/spicy-ecs-service.ts"], [source: "lib/constructs/spicy-vpc.ts", destination: "lib/constructs/spicy-vpc.ts"], [source: "lib/stacks/index.ts", destination: "lib/stacks/index.ts"], [source: "lib/stacks/spicy-alb-stack.ts", destination: "lib/stacks/spicy-alb-stack.ts"], [source: "lib/stacks/spicy-ecs-cluster-stack.ts", destination: "lib/stacks/spicy-ecs-cluster-stack.ts"], [source: "lib/stacks/spicy-ecs-service-stack.ts", destination: "lib/stacks/spicy-ecs-service-stack.ts"], [source: "lib/stacks/spicy-vpc-stack.ts", destination: "lib/stacks/spicy-vpc-stack.ts"], ].each { entry -> resources.copyResourceFile( source: entry.source, destination: entry.destination, overwrite: true ) } sh "pnpm install --frozen-lockfile" } } /** * Run CDK tests */ def test(Map args = [:]) { def workDir = args.workDir ?: "." dir(workDir) { sh "pnpm run test" } } return this