Initial commit: Spicy CDK automation framework

Jenkins shared library and CDK constructs for AWS infrastructure.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2025-11-18 22:21:00 -08:00
commit 68684df471
51 changed files with 15587 additions and 0 deletions

197
vars/cdkUtils.groovy Normal file
View File

@@ -0,0 +1,197 @@
/**
* 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