# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## What this is

A [Jenkins Shared Library](https://www.jenkins.io/doc/book/pipeline/shared-libraries) used by `theorchard`/Sony Music pipelines. The "global vars" in `vars/` are the public API — each becomes a step callable from any consuming pipeline (e.g. `cdnInvalidate(appName: 'x', env: 'qa')`). Changes here affect every pipeline that loads the library, so backward compatibility matters (see Breaking changes below).

## Commands

```sh
./gradlew test                          # run all tests
./gradlew test --tests "YarnRunTest"    # run a single test class
./gradlew test -t --tests "YarnRunTest" # watch mode: rerun on change
```

The CI `Jenkinsfile` only loads the library (to regenerate the Jenkins Global Variable Reference docs) and runs `./gradlew test`. There is no separate lint/build step — Groovy compilation happens as part of the test task.

## Architecture

- **`vars/*.groovy`** — global pipeline steps. Each is a script (not a class) exposing a `call(Map args)` method, optionally `call(Map args, Closure body)` for steps that wrap a block (the `withX` convention, e.g. `withSecrets`, `withEcr`, `withBackoff`). Constants go in `@Field` properties. These run in Jenkins' CPS-transformed sandbox and may call any pipeline step (`sh`, `withAWS`, `dir`, `checkout`, …) without importing it.
- **`src/com/sonymusic/*.groovy`** — plain Groovy/Java classes (`Utils`, `ECRImage`, `MetadataUtils`, `MonorepoUtils`) holding reusable logic. Classes that need pipeline steps take them in the constructor (`new Utils(this)`); static helpers (`Utils.validateParams`, `Utils.stripPrefix`) take none. Keep non-trivial logic here so it's unit-testable.
- **`resources/`** — non-Groovy files (e.g. `retagEcrImage.sh`) loaded at runtime via `libraryResource`.
- **`test/com/sonymusic/`** — one `*Test.groovy` per var/class.

## Conventions (enforced by review, not tooling)

**Each `vars/` file has three siblings**, all required:
- `name.groovy` — implementation
- `name.md` — full parameter/usage docs (source of truth for humans)
- `name.txt` — HTML stub that links to the `.md` on GitHub; rendered in the Jenkins UI's Global Variable Reference. Copy an existing `.txt` and swap the name.

**Validate inputs with `Utils.validateParams`** rather than ad-hoc checks, so callers get early, clear errors:

```groovy
import com.sonymusic.Utils

def call(Map args) {
    def params = Utils.validateParams('myVar', args, [   // first arg is the var name, used in error messages
        appName: [type: String, required: true],
        envVars: [type: Map, required: false],
        owner:   [type: String, required: false, defaultValue: 'theorchard'],
        regions: [type: List, itemType: String, required: false]  // itemType only valid when type is List
    ])
}
```

It rejects unknown keys, missing required params, and type mismatches (throwing `IllegalArgumentException`), and applies `defaultValue`s. Older/simpler vars use bare `assert args.x: 'message'` (throws `AssertionError`) — both patterns exist; prefer `validateParams` for anything with more than one or two params.

**Do not pass data into a library via environment variables.** Take everything through the args map (implicit coupling otherwise). You *may* read Jenkins-injected env vars like `BRANCH_NAME`, `BUILD_TAG` to infer pipeline context.

**Breaking changes break every consuming pipeline pinned to the latest version.** Adding a new `required` param, renaming a param, or removing one is breaking. Prefer optional params with `defaultValue`.

## Testing

Tests use JUnit 5 + [jenkins-pipeline-unit](https://github.com/jenkinsci/JenkinsPipelineUnit) and **must extend `BaseGlobalVarTest`** (`test/com/sonymusic/BaseGlobalVarTest.groovy`), which wraps the pipeline-unit helper with assertions like `assertMethodCalledOnceWith`, `assertMethodCalledNthWith`, `assertMethodCalledTimes`, `assertThrowsWithMessage`, and `assertMethodNotCalled`.

Pattern:
1. In `@BeforeEach`, call `super.setUp()`, then `loadScript('vars/myVar.groovy')`.
2. **Register every pipeline step the var calls** via `helper.registerAllowedMethod('stepName', [argTypes...])` — unregistered steps throw. Match the signature you actually use, e.g. `[Map, Closure]` for `withAWS(...) { }`.
3. Assert the var invoked the right steps with the right arguments (the library's job is orchestration; verify the calls). String-match assertions compare against `argsToString()`, so arguments render as `key=value`.

Cover invalid/missing args and each meaningful branch. Naming: `dockerBuild.groovy` → `DockerBuildTest.groovy`.

## Notes

- Java 17 toolchain, Groovy plugin, Gradle wrapper. `jenkinsVersion` and `groovy-cps` are pinned in `build.gradle`.
- Test reports land in `build/test-results/test/*.xml`.
