github.com/jaylevin/jenkins-library@v1.230.4/documentation/docs/extensibility.md (about)

     1  # Extensibility
     2  
     3  When using one of the ready-made pipelines of project "Piper", you don't have to write custom pipeline code.
     4  The pipelines are centrally maintained and can be used with only a small amount of declarative configuration as documented [here](configuration.md).
     5  
     6  For the vast majority of _standard_ projects, the features of the ready-made pipelines should be enough to implement [Continuous Delivery](https://martinfowler.com/bliki/ContinuousDelivery.html) with little effort in a best-practice compliant way.
     7  If you miss a feature or discover a bug in one of our pipelines, please see if there is already an [open issue in our GitHub repository](https://github.com/SAP/jenkins-library/issues) and if not, open a new one.
     8  
     9  In some cases, it's not desirable to include specialized features in the ready-made pipelines.
    10  However, you can still benefit from their qualities, if you address your requirements through an **extension**.
    11  Extensions are custom bits of pipeline coding, which you can use to implement special requirements.
    12  
    13  This page explains extensibility options in project "Piper".
    14  For a high level overview of available options and how to interface with them, see this figure:
    15  
    16  ![Library Setup](images/levels-of-extensibility.png "Extensibility Options")
    17  
    18  Before building extensions, please make sure that there is no alternative that works better for you.
    19  
    20  Options for extensibility, in the order in which we recommend considering them:
    21  
    22  ## 1. Extend individual stages
    23  
    24  In this option, you use the centrally maintained pipeline but can change individual stages, if required.
    25  
    26  To do so, create a file called `<StageName>.groovy` (for example, `Acceptance.groovy` or `lint.groovy`) in `.pipeline/extensions/` in the source code repository of your application.
    27  
    28  For this, you need to know the technical identifiers for stage names.
    29  
    30  * For the general purpose pipeline, you can find them in [the pipeline source file](https://github.com/SAP/jenkins-library/blob/master/vars/piperPipeline.groovy).
    31  
    32  The centrally maintained pipeline checks if such a file exists and if it does, executes it.
    33  A parameter of type `Map` that contains the following keys is passed to the extension:
    34  
    35  * `script`: Defines the global script environment of the `Jenkinsfile` run. This makes sure that the correct configuration environment can be passed to project "Piper" steps and allows access to the `commonPipelineEnvironment`, for example. When calling a piper step in an extension, the script object has to be passed using `script: params.script`.
    36  * `originalStage`: Allows you to execute the "original" stage at any place in your script. If omitting a call to `originalStage()`, only your code is executed.
    37  * `stageName`: Name of the current stage
    38  * `config`: Configuration of the stage and general config (including all defaults)
    39  
    40  Here is a simple example for such an extension, which you can use as a starting point:
    41  
    42  ```groovy
    43  void call(Map params) {
    44    //access stage name
    45    echo "Start - Extension for stage: ${params.stageName}"
    46  
    47    //access config
    48    echo "Current stage config: ${params.config}"
    49  
    50    //execute original stage as defined in the template
    51    params.originalStage()
    52  
    53    //access overall pipeline script object
    54    echo "Branch: ${params.script.commonPipelineEnvironment.gitBranch}"
    55  
    56    echo "End - Extension for stage: ${params.stageName}"
    57  }
    58  return this
    59  ```
    60  
    61  !!! note "`return this`"
    62      Don't forget the `return this`, which is required at the end of _all_ extension scripts.
    63      This is due to how Groovy loads scripts internally.
    64  
    65  !!! note "Init stage cannot be extended"
    66      Please note that the `Init` stage also checks out your current repository including your extensions.<br />
    67      Therefore, it is not possible to use extensions on this stage.
    68  
    69  !!! note "Disable Extensions Execution"
    70      By default, there is a possibility for extensions to get executed. In case of disabling it, please ensure to set `PIPER_DISABLE_EXTENSIONS` to `true`.
    71      Setting this parameter to `true` excludes the execution of extension files in `.pipeline/extensions/<StageName>.groovy`.
    72  
    73  ### Practical example
    74  
    75  For a more practical example, you can use extensions in the general purpose pipeline to add custom linters to your pipeline.
    76  
    77  A linter is a tool that can check the source code for certain stylistic criteria. Many teams choose to use a linter to ensure a common programming style.
    78  
    79  For example, if you want to use [Checkstyle](https://checkstyle.sourceforge.io/) in your codebase, you might use an extension similar to this one in a file called `.pipeline/extensions/Build.groovy` in your project:
    80  
    81  ```groovy
    82  def call(Map parameters) {
    83  
    84      parameters.originalStage() // Runs the build stage with built-in linters
    85  
    86      mavenExecute(
    87          script: parameters.script,
    88          flags: ['--batch-mode'],
    89          pomPath: 'application/pom.xml',
    90          m2Path: s4SdkGlobals.m2Directory,
    91          goals: ['checkstyle:checkstyle'],
    92      )
    93  
    94      recordIssues blameDisabled: true,
    95          enabledForFailure: true,
    96          aggregatingResults: false,
    97          tool: checkStyle()
    98  }
    99  
   100  return this
   101  ```
   102  
   103  This code snippet has three components, let's see what is happening here:
   104  
   105  Firstly, we run the original stage.
   106  This builds the application and optionally runs ESLint on JavaScript/TypeScript source files and static checks using PMD and SpotBugs tools as these are standard features of General Purpose Pipeline.
   107  
   108  Secondly, we run the checkstyle maven plugin using the `mavenExecute` Jenkins library step as provided by project "Piper".
   109  This serves as an example for how flexible you can re-use what project "Piper" already provides in your extension.
   110  
   111  Finally, we use the Jenkins [Warnings NG plugin](https://plugins.jenkins.io/warnings-ng/) and its step `recordIssues` to make the findings visible in the Jenkins user interface.
   112  
   113  This example can be adapted for other linters of your choice.
   114  Be sure to checkout the _Library steps_ section of this documentation if you want to do this.
   115  Project "Piper" provides some basic building blocks such as `dockerExecute` and the already mentioned `mavenExecute` which might be helpful.
   116  
   117  ## 2. Modified Ready-Made Pipeline
   118  
   119  This option describes how you can copy and paste one of the centrally maintained pipelines to make changes to it that are not possible otherwise.
   120  For example, you can't change the order of stages and the stages that run in parallel or add new stages to a centrally maintained pipeline.
   121  
   122  A _modified Ready-Made Pipeline_ allows you to modify your [declarative pipeline based on the syntax Jenkins provides](https://jenkins.io/doc/book/pipeline/syntax/#declarative-pipeline).
   123  
   124  This might be done for an individual project (in the `Jenkinsfile`), or in a separate Git repository so it can be used for multiple projects.
   125  
   126  ### Single project
   127  
   128  The default `Jenkinsfile` of centrally maintained pipelines does nothing else but loading the pipeline and running it.
   129  This is convenient but limits the modifiable aspects of the pipeline.
   130  
   131  If one of your projects uses the pipeline, the easiest way to do this modification is to copy the pipeline into your `Jenkinsfile`.
   132  
   133  The basic structure of your `Jenkinsfile` should be the following:
   134  
   135  ```groovy
   136  @Library(/* Shared library definition, see below */) _
   137  
   138  call script: this
   139  
   140  void call(parameters) {
   141    // Your pipeline code based on our ready-made pipelines
   142  }
   143  ```
   144  
   145  The actual pipeline code (the `call` method in the listing above) can be found here:
   146  
   147  * [General purpose pipeline](https://github.com/SAP/jenkins-library/blob/master/vars/piperPipeline.groovy)
   148  
   149  !!! note "Use the correct shared library definition"
   150      Which shared library you need depends on the pipeline you're using.<br />
   151      For the [general purpose pipeline](https://github.com/SAP/jenkins-library/blob/master/vars/piperPipeline.groovy), you need `'piper-lib-os'`.
   152  
   153  For the version identifier, please see the section _How to stay up-to-date_ in this document.
   154  
   155  ### Multiple projects
   156  
   157  If you have multiple projects that share a similar architecture, it might be desirable to share one modified pipeline amongst them.
   158  Similar to what you can do in an individual `Jenkinsfile`, you can copy the pipeline to your own shared library and modify it.
   159  
   160  To do this, create a new Git repository in your preferred Git hosting service.
   161  It must be compliant to [how Jenkins shared libraries are built](https://jenkins.io/doc/book/pipeline/shared-libraries/).
   162  In a nutshell, this means that you need a `vars` directory inside which you can place a copy of your preferred pipeline.
   163  
   164  A minimal example of such a library could have the following directory structure:
   165  
   166  ```
   167  ./vars/myCustomPipeline.groovy
   168  ./README.md
   169  ```
   170  
   171  `myCustomPipeline.groovy` contains the modified pipeline code of the [general purpose pipeline](https://github.com/SAP/jenkins-library/blob/master/vars/piperPipeline.groovy).
   172  
   173  !!! note
   174      The name of your custom pipeline _must_ differ from the other pipelines provided by project "Piper" because Jenkins requires names across multiple libraries to be unique.
   175  
   176  This library must be placed in a Git repository, which is available for Jenkins and must be configured in Jenkins [as documented here](https://jenkins.io/doc/book/pipeline/shared-libraries/#using-libraries).
   177  
   178  The following screenshot shows an example of the configuration in Jenkins.
   179  Note that the name (1) must be the same as the one you use in your `Jenkinsfile`.
   180  
   181  ![Library Setup](images/customPipelineLib.png "Library Setup")
   182  
   183  The `Jenkinsfile` of your individual projects would look similar to the following:
   184  
   185  ```groovy
   186  @Library(['piper-lib-os','my-own-pipeline']) _
   187  
   188  myCustomPipeline script: this
   189  ```
   190  
   191  Be sure to adapt the names and version identifiers accordingly, as described in _How to stay up-to-date_.
   192  
   193  ### How to stay up-to-date
   194  
   195  Regardless of which of the above options you choose, one downside of this approach is that your pipeline will be out of sync with the centrally maintained pipelines at some point in time.
   196  We strongly recommend doing _as little modification as possible_ to fulfil your requirements.
   197  Please be aware that stages may have dependencies on each other.
   198  
   199  !!! warning "Don't depend on stage implementation details"
   200      Your pipeline should treat _stages_ as a black box, the stage implementations are not a published API and may be subject to change at any time.
   201  
   202  !!! tip "Avoid accidental breaking changes"
   203      By default, Jenkins uses the `master` branch of shared libraries.
   204      This way, you're always automatically using the latest and greatest version.
   205      The downside is that in rare cases, breaking changes might happen.
   206      Another potential issue is that your builds are not _repeatable_, that means building the same version of your application twice _might_ have a different result.
   207      For those reasons, you might want to consider to fix versions to a released version like in this example: `@Library('my-shared-library@v1.0') _`<br />
   208      Find the most recent release for the [jenkins-library](https://github.com/SAP/jenkins-library/releases) on GitHub.
   209      To stay up to date with the latest releases, you can ["watch" releases for those repositories on GitHub](https://help.github.com/en/github/receiving-notifications-about-activity-on-github/watching-and-unwatching-releases-for-a-repository).
   210  
   211  !!! note "When to go with a modified ready-made pipeline"
   212      This option is right for you if none of the provided ready-made pipelines serves your purpose, and individual stage extensions don't provide enough flexibility.
   213  
   214  ### Advanced tips and information
   215  
   216  When you consider adding additional capabilities, your first stop should be the [Jenkins Pipeline Steps Reference](https://jenkins.io/doc/pipeline/steps/).
   217  Here you get an overview of what kind of capabilities are already available and a list of related parameters, which you can use to customize the existing implementation.
   218  The provided information should help you understand and extend the functionality of your pipeline.
   219  
   220  ## 3. New Pipeline from Scratch
   221  
   222  Since project "Piper" fully builds on [Jenkins Pipelines as Code](https://jenkins.io/doc/book/pipeline-as-code/), you can also go with your own pipeline from scratch in a `Jenkinsfile`.
   223  
   224  !!! danger "Decoupling"
   225      If you choose this option, you will be decoupled from the innovations provided with project "Piper", unless you re-use stages (as indicated above under [_2. Modified ready-made pipelines_](/extensibility.md#2-modified-ready-made-pipeline)), for example.
   226  
   227      **We recommend using this only when none of the other provided options suit your use case.**