# jenkins-global-libraries

Jenkins Global Libraries

A canonical Jenkins reference can be found [here](https://www.jenkins.io/doc/book/pipeline/shared-libraries)

## Builds

Gradle is the build system of choice for this project and is used primarily to compile and run tests. Gradle wrapper is also utilized to manage Gradle versions.

## Library Conventions

### Naming

Following the [Jenkins documentation](https://www.jenkins.io/doc/book/pipeline/shared-libraries/#directory-structure), libraries that include only methods (and do not define classes) should be added to the `./vars` directory. Classes and other code should follow standard Java conventions with adherence to the Jenkins docs.

### Structure

Each library should define a `call()` method that accepts an arbitrary map, which defaults to an empty map. If a library provides a context in which a given set of steps are executed, it may also accept a Closure.

All inputs which must be explicitly provided by the calling pipeline should be contained in the parameter map. In particular, do not use environment variables as a means of passing information from a pipeline to a library as this can lead to implicit coupling and make things harder to understand.

However, you may references environment variables which are automatically injected into Jenkins pipelines (e.g. `BRANCH_NAME`) to infer information about the context in which a library method has been invoked.

### Documentation

Each library should have a corresponding `.md` file containing the documentation for that library, including details of the available parameters.

Each library should also have a corresponding `.txt` file which is used to generate documentation in the [Global Variable Reference](https://www.jenkins.io/doc/book/pipeline/getting-started/#global-variable-reference) in the Jenkins UI. This file is processed as HTML and should simply link to the corresponding `.md` file in GitHub.

### Input validation

Libraries should validate their input parameters to be of correct type and that required params are provided.
This helps callers of the libraries to identify issues early and to be aware about typos, renamed or removed parameters.

**Note, introducing breaking changes, like adding a new required parameter will cause all pipelines targeting the latest version to fail.**

#### Example

The repo includes the `Utils.validateParams` util to validate params in a declarative way.

```groovy
import com.sonymusic.Utils

def call(Map args){
    def params = Utils.validateParams(args, [
        appName: [type: String, required: true],
        envVars: [type: Map, required: false],
        owner: [type: String, required: false, defaultValue: 'theorchard']
    ])
}
```

Calling the function above with invalid or unknown parameters will throw an "IllegalArgumentException" exception. [More on the `Utils` class](./src/com/sonymusic/Utils.md).

## Testing

Tests should utilize Junit 5+ syntax, and should be added to [`./test/com/sonymusic`](./test/com/sonymusic) with the naming convention of `LibNameTest.groovy`. In the case of a library named dockerBuild.groovy, the corresponding test should be named `DockerBuildTest.groovy`.

### Writing tests

Each test file should provide proper test coverage for all the input params and internal logic.
Assert that your global var is calling other functions with the correct parameters. To help achieve this, we have a [`BaseGlobalVarTest` base class](./test/com/sonymusic/BaseGlobalVarTest.md) you should inherit from.

e.g

```groovy
import org.junit.jupiter.api.Test
import com.sonymusic.*

class MyGlobalVarTest extends BaseGlobalVarTest {
    def myGlobalVar

    @BeforeEach
    void setUp() {
        super.setUp()
        cdnInvalidate = loadScript('../vars/myGlobalVar.groovy')
        helper.registerAllowedMethod('sh', [String])
        helper.registerAllowedMethod('withEnv', [Map])
    }

    @Test
    void testCallWithSuccess() {
        myGlobalVar()

        // Assert that a method has been called a certain number of times
        assertMethodCalledTimes('withEnv', 2)

        // Assert that a method has been called one time with string match
        assertMethodCalledOnceWith('sh', "echo hi")
    }
}
```

### Running tests

Run tests using the gradle wrapper command:

```sh
./gradlew test
```

### Watch mode with filter

Run tests for a single class/file and watch for changes:

```sh
./gradlew test -t --tests "YarnRunTest"
```

