# Lambda Mono-repo Specifics

Lambda repositories are mono-repos where each subdirectory under `lambda/` is an independent function with its own `Dockerfile`. The pipeline must only build and deploy functions whose files changed in the triggering commit/PR.

## agent none

Lambda pipelines set `agent none` at the top level because each function runs its own Docker container. Individual stages that need an agent specify `agent any`.

## withModifiedFunctions helper

This helper iterates only over functions that have file changes, running the provided closure for each in parallel.

```groovy
def withModifiedFunctions(Map args = [:], Closure steps) {
    getMonorepoUtils().withModifiedProjects(args, steps)
}

def getMonorepoUtils() {
    return library("jenkins-global-libraries@${params.SHARED_LIBRARIES_VERSION}")
        .com.sonymusic.MonorepoUtils.getInstance(
            steps: this,
            projectBasePath: 'lambda',
            projectsToBuild: params.LAMBDA_FUNCTION_NAMES
                ? params.LAMBDA_FUNCTION_NAMES.split(',')
                : null
        )
}
```

The `LAMBDA_FUNCTION_NAMES` parameter (comma-separated) lets engineers manually scope a build to specific functions — useful for hotfixes or forcing a full rebuild.

## Image naming convention

Lambda function images are named `lambda-<function-name>` with underscores replaced by hyphens:

```groovy
imageName: "lambda-${functionName.replaceAll('_', '-')}"
```

## Compliance Checks

Compliance checks run per function:

```groovy
stage('Compliance Checks') {
    steps {
        withModifiedFunctions(checkout: true) { functionName ->
            dir("lambda/${functionName}") {
                complianceChecks()
            }
        }
    }
}
```

## Build Test and Scan — sub-stage overrides

### Validate Software Catalog Definition

```groovy
stage('Validate Software Catalog Definition') {
    steps {
        withModifiedFunctions(checkout: true) { functionName ->
            datadogSoftwareCatalogValidate(servicePath: "lambda/${functionName}")
        }
    }
}
```

### Unit Tests and Style Checks

`COMPOSE_PROJECT_NAME` must be unique per function to avoid collisions when functions run in parallel.

```groovy
stage('Unit Tests and Style Checks') {
    steps {
        withModifiedFunctions(checkout: true) { functionName ->
            dir("lambda/${functionName}") {
                withEnv(["COMPOSE_PROJECT_NAME=${env.BUILD_TAG.toLowerCase()}-${functionName}"]) {
                    try {
                        sh 'docker compose run --rm --build lint-and-test'
                    } finally {
                        sh 'docker compose down -v'
                    }
                }
            }
        }
    }
}
```

### Static Application Security Tests

```groovy
stage('Static Application Security Tests') {
    steps {
        withModifiedFunctions(checkout: true) { functionName ->
            sastTests(projectDir: "lambda/${functionName}")
        }
    }
}
```

### Sonar Scan and Analysis

Exclude test directories from the scan.

```groovy
stage('Sonar Scan and Analysis') {
    agent any
    when {
        branch 'master'
    }
    steps {
        sonarScan project: GITHUB_REPOSITORY, language: 'py', exclusions: 'lambda/**/tests/**'
    }
}
```

### Create and Scan a Release

Each function has its own image. Wrap with `withModifiedFunctions` and use per-function image naming:

```groovy
stage('Create and Scan a Release') {
    steps {
        withModifiedFunctions(checkout: true) { functionName ->
            dockerToEcr(
                awsRegions: AWS_REGIONS,
                ecrAccountId: ECR_ACCOUNT_ID,
                imageName: "lambda-${functionName.replaceAll('_', '-')}",
                imageTag: env.GIT_COMMIT,
                dockerBuildContext: "lambda/${functionName}",
                dockerBuildFile: "lambda/${functionName}/Dockerfile"
            )
            dockerScan(
                awsRegion: AWS_REGIONS[0],
                ecrAccountId: ECR_ACCOUNT_ID,
                imageName: "lambda-${functionName.replaceAll('_', '-')}",
                imageTag: env.GIT_COMMIT,
                vulnerabilitiesToIgnore: VULNERABILITIES_TO_IGNORE
            )
        }
    }
}
```

## Deploy to QA

```groovy
stage('Deploy to QA') {
    when {
        branch 'master'
    }
    steps {
        withModifiedFunctions { functionName ->
            dir("lambda/${functionName}") {
                lambdaDeploy(
                    environment: 'qa',
                    awsRegions: AWS_REGIONS,
                    imageTag: env.GIT_COMMIT,
                    imageName: "lambda-${functionName.replaceAll('_', '-')}",
                    ecrRegistryAccountId: ECR_ACCOUNT_ID,
                    awsDeploymentTargetAccountId: QA_ACCOUNT_ID,
                    awsDeploymentRoleName: QA_DEPLOYMENT_ROLE
                )
            }
        }
    }
}
```

## Deploy to Prod

```groovy
stage('Deploy to Prod') {
    when {
        allOf {
            branch 'master'
            expression { params.DEPLOY_TO_PROD }
        }
    }
    steps {
        withModifiedFunctions(checkout: true) { functionName ->
            dir("lambda/${functionName}") {
                lambdaDeploy(
                    environment: 'prod',
                    awsRegions: AWS_REGIONS,
                    imageTag: env.GIT_COMMIT,
                    imageName: "lambda-${functionName.replaceAll('_', '-')}",
                    ecrRegistryAccountId: ECR_ACCOUNT_ID,
                    awsDeploymentTargetAccountId: PROD_ACCOUNT_ID,
                    awsDeploymentRoleName: PROD_DEPLOYMENT_ROLE
                )
                datadogSoftwareCatalogPublish(servicePath: "lambda/${functionName}")
            }
        }
    }
}
```
