├── .github
├── dependabot.yml
├── release-drafter.yml
└── workflows
│ ├── release-drafter.yml
│ └── test.yml
├── .gitignore
├── Jenkinsfile
├── LICENSE.txt
├── README.md
├── RELEASE.md
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── grapeConfig.xml
└── src
├── main
└── groovy
│ └── com
│ └── lesfurets
│ └── jenkins
│ └── unit
│ ├── BasePipelineTest.groovy
│ ├── BaseRegressionTest.groovy
│ ├── DockerMock.groovy
│ ├── InterceptingGCL.groovy
│ ├── LibClassLoader.groovy
│ ├── MethodCall.groovy
│ ├── MethodSignature.groovy
│ ├── MockPipelineScript.groovy
│ ├── PipelineTestHelper.groovy
│ ├── RegressionTest.groovy
│ ├── RegressionTestHelper.groovy
│ ├── VerificationException.groovy
│ ├── cps
│ ├── BasePipelineTestCPS.groovy
│ ├── BaseRegressionTestCPS.groovy
│ ├── MockClosure.groovy
│ ├── MockPipelineScriptCPS.groovy
│ └── PipelineTestHelperCPS.groovy
│ ├── declarative
│ ├── AgentDeclaration.groovy
│ ├── AllOfDeclaration.groovy
│ ├── AnyOfDeclaration.groovy
│ ├── ChangeRequestDeclaration.groovy
│ ├── ComparatorEnum.groovy
│ ├── DeclarativePipeline.groovy
│ ├── DeclarativePipelineTest.groovy
│ ├── GenericPipelineDeclaration.groovy
│ ├── NotDeclaration.groovy
│ ├── ObjectUtils.groovy
│ ├── ParallelDeclaration.groovy
│ ├── ParametersDeclaration.groovy
│ ├── PostDeclaration.groovy
│ ├── StageDeclaration.groovy
│ ├── WhenDeclaration.groovy
│ ├── agent
│ │ ├── DockerAgentDeclaration.groovy
│ │ ├── DockerfileAgentDeclaration.groovy
│ │ └── KubernetesAgentDeclaration.groovy
│ └── kubernetes
│ │ ├── ContainerLivenessProbeDeclaration.groovy
│ │ ├── ContainerTemplateDeclaration.groovy
│ │ ├── PodTemplateDeclaration.groovy
│ │ ├── PortMappingDeclaration.groovy
│ │ ├── TemplateEnvVarDeclaration.groovy
│ │ └── WorkspaceVolumeDeclaration.groovy
│ └── global
│ └── lib
│ ├── GitSource.groovy
│ ├── Library.groovy
│ ├── LibraryAnnotationTransformer.groovy
│ ├── LibraryConfiguration.groovy
│ ├── LibraryLoader.groovy
│ ├── LibraryRecord.groovy
│ ├── LocalSource.groovy
│ ├── ProjectSource.groovy
│ └── SourceRetriever.groovy
└── test
├── groovy
└── com
│ ├── TestCatchError.groovy
│ └── lesfurets
│ └── jenkins
│ ├── TestAddCredential.groovy
│ ├── TestAddEnvVar.groovy
│ ├── TestAddParam.groovy
│ ├── TestCustomMethodJob.groovy
│ ├── TestCustomMethodJobCPS.groovy
│ ├── TestDeclarativeImmutableParams.groovy
│ ├── TestExampleJob.groovy
│ ├── TestExampleJobCPS.groovy
│ ├── TestFailingJobs.groovy
│ ├── TestHelperInitialization.groovy
│ ├── TestHelperSingleton.groovy
│ ├── TestInlineScript.groovy
│ ├── TestInterceptingGCL.groovy
│ ├── TestInterceptingGCLLazyLoadLibClasses.groovy
│ ├── TestLibraryResourceStep.groovy
│ ├── TestOneArgumentJob.groovy
│ ├── TestParallelJob.groovy
│ ├── TestParallelJobCPS.groovy
│ ├── TestParametersJob.groovy
│ ├── TestRegisterOriginalMethodCallArgs.groovy
│ ├── TestRegression.groovy
│ ├── TestRegressionGlobalVar.groovy
│ ├── TestSerialization.groovy
│ ├── TestSerializationCPS.groovy
│ ├── TestSharedLibraryAccessibleParams.groovy
│ ├── TestSharedLibraryEnvVariable.groovy
│ ├── TestSharedLibraryWithLocalSourceRetriever.groovy
│ ├── TestSharedLibraryWithLocalSourceRetrieverCPS.groovy
│ ├── TestSharedLibraryWithProjectSourceRetriever.groovy
│ ├── TestSharedLibraryWithProjectSourceRetrieverCPS.groovy
│ ├── TestUtilsLib.groovy
│ ├── TestUtilsLibCPS.groovy
│ ├── TestWithCredentialsAndParametersJob.groovy
│ ├── TestWithCredentialsJob.groovy
│ └── unit
│ ├── BasePipelineTestTest.groovy
│ ├── CallStackDumpTest.groovy
│ ├── PipelineTestHelperTest.groovy
│ ├── TestDockerMock.groovy
│ ├── VerifyTest.groovy
│ └── declarative
│ ├── TestDeclaraticeWithCredentials.groovy
│ ├── TestDeclarativePipeline.groovy
│ ├── TestDockerAgentInStep.groovy
│ └── TestMockLocalFunction.groovy
├── java
└── com
│ └── lesfurets
│ └── jenkins
│ └── TestPipelineJava.java
├── jenkins
├── jenkinsfiles
│ ├── AgentEmptyLabel_Jenkinsfile
│ ├── AgentParam_Jenkinsfile
│ ├── AgentStageNoSteps_Jenkinsfile
│ ├── Agent_Jenkinsfile
│ ├── Agent_bindings_Jenkinsfile
│ ├── Agent_env_Jenkinsfile
│ ├── AllOf_Jenkinsfile
│ ├── AnyOf_Jenkinsfile
│ ├── Branch_Jenkinsfile
│ ├── BuildStatus_Failure_Jenkinsfile
│ ├── BuildStatus_Success_Jenkinsfile
│ ├── ChangeRequest_Jenkinsfile
│ ├── ComplexStages_Jenkinsfile
│ ├── Credentials_Jenkinsfile
│ ├── Declarative_Jenkinsfile
│ ├── Docker_Jenkinsfile
│ ├── Docker_agentInStep_JenkinsFile
│ ├── Dockerfile_Agent_Default_Jenkinsfile
│ ├── Dockerfile_Agent_Only_Filename_JenkinsFile
│ ├── Dockerfile_agent_JenkinsFile
│ ├── Environment_Jenkinsfile
│ ├── Kubernetes_Agent_Jenkinsfile
│ ├── Kubernetes_Default_Jenkinsfile
│ ├── Kubernetes_Map_Agent_Jenkinsfile
│ ├── Mock_existing_function_Jenkinsfile
│ ├── Mock_existing_function_Jenkinsfile_params
│ ├── Nested_AllOf_And_AnyOf_Jenkinsfile
│ ├── Nested_AnyOf_And_AllOf_Jenkinsfile
│ ├── Nested_BeforeAgent_Jenkinsfile
│ ├── Non_Valid_Jenkinsfile
│ ├── Parallel_Jenkinsfile
│ ├── Parallel_NestedStages_Jenkinsfile
│ ├── Parallel_When_Jenkinsfile
│ ├── Params_Jenkinsfile
│ ├── StageAndSteps_Jenkinsfile
│ ├── StageFailed_Jenkinsfile
│ ├── Tag_Jenkinsfile
│ ├── ThisScope_Jenkinsfile
│ ├── WithEnvEquals_Jenkinsfile
│ ├── WithEnv_Jenkinsfile
│ ├── not_Jenkinsfile
│ └── withCredentials_Jenkinsfile
├── job
│ ├── callStackDump.jenkins
│ ├── customMethod.jenkins
│ ├── customMethodWithArguments.jenkins
│ ├── exampleJob.jenkins
│ ├── globalVar.jenkins
│ ├── immutableMapArgs.jenkins
│ ├── library
│ │ ├── cross_class
│ │ │ ├── test_annotation.jenkins
│ │ │ ├── test_dynamic.jenkins
│ │ │ └── test_implicit.jenkins
│ │ ├── cross_class_lazy_load
│ │ │ └── test_var_with_lib_class_arg.jenkins
│ │ ├── cross_class_pre_loaded
│ │ │ ├── test_annotation.jenkins
│ │ │ ├── test_dynamic.jenkins
│ │ │ └── test_implicit.jenkins
│ │ ├── cross_class_with_pipeline_ref
│ │ │ ├── test_annotation.jenkins
│ │ │ ├── test_dynamic.jenkins
│ │ │ └── test_implicit.jenkins
│ │ ├── cross_library
│ │ │ ├── test_cross_class_annotation.jenkins
│ │ │ ├── test_cross_class_dynamic.jenkins
│ │ │ └── test_cross_class_implicit.jenkins
│ │ ├── cross_vars
│ │ │ ├── test_annotation.jenkins
│ │ │ ├── test_dynamic.jenkins
│ │ │ └── test_implicit.jenkins
│ │ ├── libraryJob.jenkins
│ │ ├── libraryJob_dynamic_map.jenkins
│ │ ├── libraryJob_feature.jenkins
│ │ ├── libraryJob_feature2.jenkins
│ │ ├── libraryJob_implicit.jenkins
│ │ ├── libraryJob_inline_library.jenkins
│ │ ├── libraryJob_master.jenkins
│ │ ├── params_not_accessible.jenkins
│ │ ├── test_lib_call_shell_annotation.jenkins
│ │ ├── test_lib_call_shell_dynamic.jenkins
│ │ ├── test_lib_call_with_null.jenkins
│ │ ├── test_lib_var_not_defined_in_env.jenkins
│ │ ├── test_libraryResource_as_base64.jenkins
│ │ ├── test_libraryResource_with_encoding.jenkins
│ │ ├── test_libraryResource_with_string.jenkins
│ │ ├── test_libraryResource_without_encoding.jenkins
│ │ ├── test_params_immutable_declarative.jenkins
│ │ └── test_params_not_defined_in_env.jenkins
│ ├── parallelJob.jenkins
│ ├── parameters.jenkins
│ ├── serialize.jenkins
│ ├── serializeCPS.jenkins
│ ├── shouldFail
│ │ ├── forEach.jenkins
│ │ └── nonCpsCallingCps.jenkins
│ ├── verify.jenkins
│ ├── withCredentials.jenkins
│ └── withCredentialsAndParameters.jenkins
└── lib
│ ├── properties.jenkins
│ └── utils.jenkins
└── resources
├── callstacks
├── TestJenkinsFile_develop.txt
├── TestJenkinsFile_feature_.txt
├── TestJenkinsFile_master.txt
├── TestOneArgumentJob_should_run_script_with_one_argument.txt
├── TestParametersJob_parameters.txt
├── TestRegressionGlobalVar_globalVar.txt
├── TestRegression_example.txt
├── TestWithCredentialsAndParametersJob_withCredentialsAndParameters.txt
└── TestWithCredentialsJob_withCredentials.txt
├── libs
├── commons@feature
│ ├── resources
│ │ └── net
│ │ │ └── courtanet
│ │ │ └── jenkins
│ │ │ ├── icon.png
│ │ │ ├── plaintext.iso-8859-1.txt
│ │ │ └── plaintext.utf8.txt
│ ├── src
│ │ └── net
│ │ │ └── courtanet
│ │ │ └── jenkins
│ │ │ └── Utils2.groovy
│ └── vars
│ │ ├── greetings.groovy
│ │ ├── helloMessage.groovy
│ │ ├── oneArg.groovy
│ │ └── sayHello.groovy
├── commons@master
│ ├── resources
│ │ └── net
│ │ │ └── courtanet
│ │ │ └── jenkins
│ │ │ └── pom.xml
│ ├── src
│ │ └── net
│ │ │ └── courtanet
│ │ │ └── jenkins
│ │ │ └── Utils.groovy
│ └── vars
│ │ ├── acme.groovy
│ │ └── sayHello.groovy
├── env_var_not_defined
│ └── vars
│ │ └── envHelper.groovy
├── params_not_accessible
│ └── src
│ │ └── org
│ │ └── test
│ │ └── LibClass.groovy
├── test_cross_class_as_var_arg_1
│ ├── src
│ │ └── org
│ │ │ └── test
│ │ │ └── Monster1.groovy
│ └── vars
│ │ └── monster1.groovy
├── test_cross_class_as_var_arg_2
│ ├── src
│ │ └── org
│ │ │ └── test
│ │ │ └── extra
│ │ │ └── Monster2.groovy
│ └── vars
│ │ └── monster2.groovy
├── test_cross_class_usage
│ └── src
│ │ └── org
│ │ └── test
│ │ ├── ClassA.groovy
│ │ ├── ClassAB.groovy
│ │ └── ClassB.groovy
└── test_cross_vars_usage
│ ├── src
│ └── org
│ │ └── test
│ │ └── LaClass.groovy
│ └── vars
│ ├── methodA.groovy
│ ├── methodAB.groovy
│ └── methodB.groovy
└── test-pom.xml
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | ---
2 | version: 2
3 | updates:
4 | - package-ecosystem: gradle
5 | directory: "/"
6 | schedule:
7 | interval: daily
8 |
--------------------------------------------------------------------------------
/.github/release-drafter.yml:
--------------------------------------------------------------------------------
1 | # See https://github.com/jenkinsci/.github/blob/master/.github/release-drafter.adoc
2 | _extends: jenkinsci/.github
3 | version-template: $MAJOR.$MINOR
4 | tag-template: v$NEXT_MINOR_VERSION
5 | name-template: $NEXT_MINOR_VERSION
6 |
--------------------------------------------------------------------------------
/.github/workflows/release-drafter.yml:
--------------------------------------------------------------------------------
1 | # Note: additional setup is required, see https://github.com/jenkinsci/.github/blob/master/.github/release-drafter.adoc
2 |
3 | name: Release Drafter
4 |
5 | on:
6 | push:
7 | branches:
8 | - master
9 |
10 | jobs:
11 | update_release_draft:
12 | runs-on: ubuntu-latest
13 | steps:
14 | # Drafts your next Release notes as Pull Requests are merged into the default branch
15 | - uses: release-drafter/release-drafter@v5
16 | env:
17 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
18 |
--------------------------------------------------------------------------------
/.github/workflows/test.yml:
--------------------------------------------------------------------------------
1 | name: Tests
2 | on:
3 | pull_request:
4 | branches:
5 | - master
6 | push:
7 | branches:
8 | - master
9 | jobs:
10 | test:
11 | name: macos-17
12 | runs-on: macos-latest
13 | steps:
14 | - name: Setup Java
15 | uses: actions/setup-java@v1
16 | with:
17 | java-version: 17
18 | - name: Checkout
19 | uses: actions/checkout@v2
20 | - name: Run Tests
21 | run: ./gradlew --no-daemon cleanTest build
22 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | target
3 | .idea
4 | build
5 | .gradle
6 | *.swp
7 | out
8 | .DS_store
9 | .classpath
10 | .project
11 | .settings/
12 | bin/
--------------------------------------------------------------------------------
/Jenkinsfile:
--------------------------------------------------------------------------------
1 | buildPluginWithGradle(
2 | configurations: [
3 | [platform: 'linux', jdk: '17'],
4 | [platform: 'windows', jdk: '17'],
5 | ],
6 | )
7 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2016 Courtanet
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/RELEASE.md:
--------------------------------------------------------------------------------
1 | # Release Procedure
2 |
3 | ## Pre-Requirements
4 |
5 | In order to release a new version you need a `jenkins.io` account, push rights to this
6 | repository and access to Jenkins' JFrog artifactory server. See the [Jenkins plugin
7 | adoption procedure][jenkins-adopt-a-plugin].
8 |
9 | ## Overview
10 |
11 | In short, the release procedure could described with the following steps:
12 |
13 | 1. Update the version in `gradle.properties` and create a new commit
14 | 2. Make a new tag
15 | 3. Build and publish artifacts to the [Jenkins plugin repository][jenkins-plugin-repo].
16 | 4. Update the version in `gradle.properties` for the next development cycle
17 | 5. Add release notes to GitHub
18 |
19 | **NOTE:** repo.jenkins-ci.org might have a delay (1-2d) in publishing artifacts to public,
20 | so mark new releases as `pre-release`. Once artifacts are available, the `pre-release`
21 | label can be removed.
22 |
23 | ## Tools
24 |
25 | To automate the above steps, JenkinsPipelineUnit uses a variety of Gradle plugins and
26 | GitHub actions.
27 |
28 | ### Gradle plugins
29 |
30 | #### net.researchgate.release
31 |
32 | This plugin is used to prepare new releases. This Gradle plugin automatically creates a
33 | new commit with the updated release version, makes a new release tag, and then prepares
34 | the repository for the next development cycle.
35 |
36 | #### com.jfrog.artifactory
37 |
38 | Needed to publish artifacts to the repo.jenkins-ci.org/releases maven repository.
39 |
40 | ### GitHub Actions
41 |
42 | There is a [release-drafter][release-drafter] GitHub actions script to prepare release
43 | notes based on issues and PRs which were made to the main branch since the last release.
44 |
45 | ## Example
46 |
47 | Let's say current version is `1.6`, new version is `1.7`, and the next development version
48 | is `1.8-SNAPSHOT`.
49 |
50 | * Checkout release branch
51 |
52 | ```bash
53 | git fetch
54 | git checkout -B master origin/master
55 | ```
56 |
57 | * Create release:
58 |
59 | ```
60 | ./gradlew release
61 | # You will be asked for the new version, and new snapshot version
62 | # (just press enter to use default values)
63 |
64 | ```
65 | * Checkout release tag
66 |
67 | ```bash
68 | git checkout v1.7
69 | ```
70 |
71 | * Publish artifacts
72 |
73 | ```bash
74 | # create ~/.gradle/gradle.properties with the following content:
75 | # artifactory_user=jenkins.io_username
76 | # artifactory_password=s3cr3t
77 |
78 | ./gradlew artifactoryPublish
79 | ```
80 |
81 | * Publish new release notes at https://github.com/jenkinsci/JenkinsPipelineUnit/releases
82 |
83 |
84 | [jenkins-adopt-a-plugin]: https://www.jenkins.io/doc/developer/plugin-governance/adopt-a-plugin/
85 | [jenkins-plugin-repo]: https://repo.jenkins-ci.org/artifactory/releases/com/lesfurets/jenkins-pipeline-unit/
86 | [release-drafter]: https://github.com/jenkinsci/.github/blob/master/.github/release-drafter.adoc
87 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # dummy properties
2 | artifactory_user=deployment
3 | artifactory_password=deployment123
4 | version=1.26
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jenkinsci/JenkinsPipelineUnit/18fbbb546c3dba7faf1da9f1bbfe159ba7e599c1/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.0.2-bin.zip
4 | networkTimeout=10000
5 | zipStoreBase=GRADLE_USER_HOME
6 | zipStorePath=wrapper/dists
7 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%"=="" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%"=="" set DIRNAME=.
29 | @rem This is normally unused
30 | set APP_BASE_NAME=%~n0
31 | set APP_HOME=%DIRNAME%
32 |
33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
35 |
36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
38 |
39 | @rem Find java.exe
40 | if defined JAVA_HOME goto findJavaFromJavaHome
41 |
42 | set JAVA_EXE=java.exe
43 | %JAVA_EXE% -version >NUL 2>&1
44 | if %ERRORLEVEL% equ 0 goto execute
45 |
46 | echo.
47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
48 | echo.
49 | echo Please set the JAVA_HOME variable in your environment to match the
50 | echo location of your Java installation.
51 |
52 | goto fail
53 |
54 | :findJavaFromJavaHome
55 | set JAVA_HOME=%JAVA_HOME:"=%
56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
57 |
58 | if exist "%JAVA_EXE%" goto execute
59 |
60 | echo.
61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
62 | echo.
63 | echo Please set the JAVA_HOME variable in your environment to match the
64 | echo location of your Java installation.
65 |
66 | goto fail
67 |
68 | :execute
69 | @rem Setup the command line
70 |
71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
72 |
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if %ERRORLEVEL% equ 0 goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | set EXIT_CODE=%ERRORLEVEL%
85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1
86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
87 | exit /b %EXIT_CODE%
88 |
89 | :mainEnd
90 | if "%OS%"=="Windows_NT" endlocal
91 |
92 | :omega
93 |
--------------------------------------------------------------------------------
/grapeConfig.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/src/main/groovy/com/lesfurets/jenkins/unit/BaseRegressionTest.groovy:
--------------------------------------------------------------------------------
1 | package com.lesfurets.jenkins.unit
2 |
3 | abstract class BaseRegressionTest extends BasePipelineTest implements RegressionTest {
4 | }
5 |
--------------------------------------------------------------------------------
/src/main/groovy/com/lesfurets/jenkins/unit/DockerMock.groovy:
--------------------------------------------------------------------------------
1 | package com.lesfurets.jenkins.unit
2 |
3 |
4 | @SuppressWarnings(['EmptyMethod', 'MethodReturnTypeRequired', 'UnusedMethodParameter'])
5 | class DockerMock implements Serializable {
6 | class Container implements Serializable {
7 | String id
8 |
9 | Container(String id = 'mock-container') {
10 | this.id = id
11 | }
12 |
13 | def port() {
14 | return '1234'
15 | }
16 |
17 | def stop() {}
18 | }
19 |
20 | class Image implements Serializable {
21 | String id
22 | String tagname
23 |
24 | Image(String id) {
25 | this.id = id
26 | this.tagname = 'latest'
27 | }
28 |
29 | def imageName() {
30 | return id
31 | }
32 |
33 | def inside(String args = '', Closure body) {
34 | return body(new Container())
35 | }
36 |
37 | def pull() {}
38 |
39 | def push(String tagname = '') {
40 | if (tagname) {
41 | tag(tagname)
42 | }
43 | }
44 |
45 | def run(String args = '', String command = '') {
46 | return new Container()
47 | }
48 |
49 | def tag(String tagname = '') {
50 | this.tagname = tagname
51 | }
52 |
53 | def withRun(String args = '', String command = '', Closure body) {
54 | return body(new Container())
55 | }
56 | }
57 |
58 | Image build(String image, String args = '') {
59 | return new Image(image)
60 | }
61 |
62 | Image image(String id) {
63 | return new Image(id)
64 | }
65 |
66 | void withRegistry(String url, String credentialsId = '', Closure body) {
67 | body()
68 | }
69 |
70 | void withServer(String uri, String credentialsId = '', Closure body) {
71 | body()
72 | }
73 |
74 | void withTool(String toolName, Closure body) {
75 | body()
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/src/main/groovy/com/lesfurets/jenkins/unit/LibClassLoader.groovy:
--------------------------------------------------------------------------------
1 | package com.lesfurets.jenkins.unit
2 |
3 | import org.apache.commons.lang3.reflect.ConstructorUtils;
4 | import org.apache.commons.lang3.reflect.MethodUtils;
5 |
6 | /**
7 | * Kind of proxy object to create instances of library's classes
8 | *
9 | * Copied from https://github.com/jenkinsci/workflow-cps-global-lib-plugin/blob/master/src/main/java/org/jenkinsci/plugins/workflow/libs/LibraryStep.java
10 | */
11 | class LibClassLoader extends GroovyObjectSupport {
12 | private String className
13 | private PipelineTestHelper helper
14 | Class loadedClass
15 |
16 | LibClassLoader(helper, String className){
17 | this.helper = helper
18 | this.className = className
19 | this.loadedClass = null
20 | }
21 |
22 | LibClassLoader(helper, String className, loadedClass){
23 | this.helper = helper
24 | this.className = className
25 | this.loadedClass = loadedClass
26 | }
27 |
28 | @Override
29 | Object getProperty(String property) {
30 |
31 | if(loadedClass) {
32 | return loadedClass.getProperties()[property]
33 | }
34 |
35 | if(!this.className) {
36 | return new LibClassLoader(this.helper, property)
37 | }
38 |
39 | if(property =~ /^[A-Z].*/) {
40 |
41 | def gcl = this.helper.getLibLoader().getGroovyClassLoader()
42 | loadedClass = gcl.loadClass( (String) "${this.className}.${property}")
43 | return new LibClassLoader(this.helper, "${this.className}.${property}", loadedClass)
44 | } else {
45 | return new LibClassLoader(this.helper, "${this.className}.${property}")
46 | }
47 | }
48 |
49 | @Override
50 | Object invokeMethod(String name, Object _args) {
51 | Object[] args = _args as Object[]
52 | if(loadedClass) {
53 | if (name.equals("new")) {
54 | return ConstructorUtils.invokeConstructor(loadedClass, args);
55 | } else {
56 | return MethodUtils.invokeStaticMethod(loadedClass, name, args);
57 | }
58 | }
59 |
60 | }
61 | }
--------------------------------------------------------------------------------
/src/main/groovy/com/lesfurets/jenkins/unit/MethodSignature.groovy:
--------------------------------------------------------------------------------
1 | package com.lesfurets.jenkins.unit
2 |
3 | import static org.codehaus.groovy.runtime.MetaClassHelper.isAssignableFrom
4 |
5 | import groovy.transform.CompileStatic
6 |
7 | @CompileStatic
8 | class MethodSignature {
9 | String name
10 | Class[] args
11 |
12 | static MethodSignature method(String name, Class... args = []) {
13 | return new MethodSignature(name, args)
14 | }
15 |
16 | MethodSignature(String name, Class[] args) {
17 | this.name = name
18 | this.args = args
19 | }
20 |
21 | String argsToString() {
22 | return args.collect { Class it ->
23 | if (it != null && Closure.isAssignableFrom(it)) {
24 | Closure.class.getName()
25 | } else {
26 | String.valueOf(it)
27 | }
28 | }.join(', ')
29 | }
30 |
31 | @Override
32 | boolean equals(o) {
33 | if (this.is(o)) return true
34 | if (getClass() != o.class) return false
35 |
36 | MethodSignature that = (MethodSignature) o
37 |
38 | if (name != that.name) return false
39 | if (args == null && that.args == null) return true
40 | if (args.size() != that.args.size()) return false
41 | for (int i = 0; i < args.size(); i++) {
42 | Class thisClazz = this.args[i]
43 | Class thatClazz = that.args[i]
44 | if (!(isAssignableFrom(Closure.class, thatClazz) && isAssignableFrom(Closure.class, thisClazz))) {
45 | if (!isAssignableFrom(thisClazz, thatClazz)) {
46 | return false
47 | }
48 | }
49 | }
50 | return true
51 | }
52 |
53 | @Override
54 | int hashCode() {
55 | int result
56 | result = (name != null ? name.hashCode() : 0)
57 | result = 31 * result + (args != null ? argsToString().hashCode() : 0)
58 | return result
59 | }
60 |
61 |
62 | @Override
63 | String toString() {
64 | return "MethodSignature{ name='$name', args=${Arrays.toString(args)} }"
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/src/main/groovy/com/lesfurets/jenkins/unit/MockPipelineScript.groovy:
--------------------------------------------------------------------------------
1 | package com.lesfurets.jenkins.unit
2 |
3 | abstract class MockPipelineScript extends Script {
4 |
5 | /**
6 | * Override sleep method
7 | */
8 | void sleep(long milliseconds) {
9 | // no-op
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/main/groovy/com/lesfurets/jenkins/unit/RegressionTest.groovy:
--------------------------------------------------------------------------------
1 | package com.lesfurets.jenkins.unit
2 |
3 | trait RegressionTest {
4 |
5 | String callStackPath = "src/test/resources/callstacks/"
6 |
7 | /**
8 | * Checks the current callstack is the same as the reference callstack.
9 | * The reference callstack can be updated into a txt file in the callStackPath
10 | *
11 | * Pattern: /<_subname>.txt
12 | * @param subname optional subname, used in the reference callstack filename
13 | */
14 | void testNonRegression(String subname = '') {
15 | String targetFileName = "${callStackPath}${this.class.simpleName}"
16 | if (subname) {
17 | targetFileName += "_${subname}"
18 | }
19 | RegressionTestHelper.testNonRegression(helper, targetFileName)
20 | }
21 |
22 | }
23 |
--------------------------------------------------------------------------------
/src/main/groovy/com/lesfurets/jenkins/unit/VerificationException.groovy:
--------------------------------------------------------------------------------
1 | package com.lesfurets.jenkins.unit
2 |
3 | /**
4 | * Custom exception class used in the verify method in BasePipelineTest
5 | */
6 | class VerificationException extends Exception {
7 |
8 | VerificationException(String errorMessage) {
9 | super(errorMessage)
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/main/groovy/com/lesfurets/jenkins/unit/cps/BasePipelineTestCPS.groovy:
--------------------------------------------------------------------------------
1 | package com.lesfurets.jenkins.unit.cps
2 |
3 | import com.lesfurets.jenkins.unit.BasePipelineTest
4 | import com.lesfurets.jenkins.unit.PipelineTestHelper
5 |
6 | class BasePipelineTestCPS extends BasePipelineTest {
7 |
8 | BasePipelineTestCPS(PipelineTestHelper helper) {
9 | super(helper)
10 | }
11 |
12 | BasePipelineTestCPS() {
13 | super(new PipelineTestHelperCPS())
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/main/groovy/com/lesfurets/jenkins/unit/cps/BaseRegressionTestCPS.groovy:
--------------------------------------------------------------------------------
1 | package com.lesfurets.jenkins.unit.cps
2 |
3 | import com.lesfurets.jenkins.unit.RegressionTest
4 |
5 | abstract class BaseRegressionTestCPS extends BasePipelineTestCPS implements RegressionTest {
6 |
7 | }
8 |
--------------------------------------------------------------------------------
/src/main/groovy/com/lesfurets/jenkins/unit/cps/MockClosure.groovy:
--------------------------------------------------------------------------------
1 | package com.lesfurets.jenkins.unit.cps
2 |
3 | import org.codehaus.groovy.runtime.InvokerHelper
4 |
5 | import com.cloudbees.groovy.cps.Block
6 | import com.cloudbees.groovy.cps.Env
7 | import com.cloudbees.groovy.cps.impl.CpsClosure
8 |
9 | class MockClosure extends CpsClosure {
10 |
11 | MockClosure(Object owner, Object thisObject, List parameters, Block body, Env capture) {
12 | super(owner, thisObject, parameters, body, capture)
13 | }
14 |
15 | /**
16 | * Override sleep method
17 | */
18 | void sleep(long milliseconds) {
19 | InvokerHelper.invokeMethod(getOwner(), "sleep", milliseconds)
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/main/groovy/com/lesfurets/jenkins/unit/cps/MockPipelineScriptCPS.groovy:
--------------------------------------------------------------------------------
1 | package com.lesfurets.jenkins.unit.cps
2 |
3 | import com.lesfurets.jenkins.unit.MockPipelineScript
4 |
5 | abstract class MockPipelineScriptCPS extends MockPipelineScript implements Serializable {
6 |
7 | private void writeObject(ObjectOutputStream oos) throws IOException {
8 | // binding is defined in non-serializable Script class,
9 | // so we need to persist that here
10 | def variables = getBinding().getVariables().clone()
11 | oos.writeObject(variables)
12 | }
13 |
14 | private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
15 | Map m = (Map)ois.readObject()
16 | getBinding().getVariables().putAll(m)
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/main/groovy/com/lesfurets/jenkins/unit/declarative/AgentDeclaration.groovy:
--------------------------------------------------------------------------------
1 | package com.lesfurets.jenkins.unit.declarative
2 |
3 | import com.lesfurets.jenkins.unit.declarative.agent.DockerAgentDeclaration
4 | import com.lesfurets.jenkins.unit.declarative.agent.DockerfileAgentDeclaration
5 | import com.lesfurets.jenkins.unit.declarative.agent.KubernetesAgentDeclaration
6 | import groovy.transform.ToString
7 |
8 | import static groovy.lang.Closure.DELEGATE_FIRST
9 |
10 | @ToString(includePackage = false, includeNames = true, ignoreNulls = true)
11 | class AgentDeclaration extends GenericPipelineDeclaration {
12 |
13 | String label
14 | DockerAgentDeclaration docker
15 | KubernetesAgentDeclaration kubernetes
16 | DockerfileAgentDeclaration dockerfileAgent
17 | String customWorkspace
18 | def binding = null
19 |
20 | def label(String label) {
21 | this.label = label
22 | }
23 |
24 | def node(@DelegatesTo(AgentDeclaration) Closure closure) {
25 | closure.call()
26 | }
27 |
28 | def customWorkspace(String workspace) {
29 | this.customWorkspace = workspace
30 | }
31 |
32 | def docker(String image) {
33 | this.docker = new DockerAgentDeclaration().with { it.image = image; it }
34 | }
35 |
36 | def docker(@DelegatesTo(strategy = DELEGATE_FIRST, value = DockerAgentDeclaration) Closure closure) {
37 | this.docker = createComponent(DockerAgentDeclaration, closure)
38 | }
39 |
40 | def kubernetes(boolean _) {
41 | kubernetes([:])
42 | }
43 |
44 | def kubernetes(Object kubernetesAgent) {
45 | this.@kubernetes = kubernetesAgent as KubernetesAgentDeclaration
46 | }
47 |
48 | def kubernetes(@DelegatesTo(strategy = DELEGATE_FIRST, value = KubernetesAgentDeclaration) Closure closure) {
49 | this.@kubernetes = createComponent(KubernetesAgentDeclaration, closure)
50 | }
51 |
52 | def dockerfile(boolean _) {
53 | dockerfile([:])
54 | }
55 |
56 | def dockerfile(Object dockerfile) {
57 | this.@dockerfileAgent = dockerfile as DockerfileAgentDeclaration
58 | }
59 |
60 | def dockerfile(@DelegatesTo(strategy = DELEGATE_FIRST, value = DockerfileAgentDeclaration) Closure closure) {
61 | this.@dockerfileAgent = createComponent(DockerfileAgentDeclaration, closure)
62 | }
63 |
64 |
65 | def getCurrentBuild() {
66 | return binding?.currentBuild
67 | }
68 |
69 | def getEnv() {
70 | return binding?.env
71 | }
72 |
73 | def getParams() {
74 | return binding?.params
75 | }
76 |
77 | def execute(Object delegate) {
78 | def agentDesc = null
79 |
80 | if (label != null) {
81 | agentDesc = '[label:' + label.toString() + ']'
82 | }
83 | else if (docker) {
84 | agentDesc = '[docker:' + docker.toString() + ']'
85 | }
86 | else if (dockerfileAgent) {
87 | agentDesc = '[dockerfile:' + dockerfileAgent.toString() + ']'
88 | }
89 | else if (kubernetes) {
90 | agentDesc = '[kubernetes:' + kubernetes.toString() + ']'
91 | }
92 | else {
93 | throw new IllegalStateException("No agent description found")
94 | }
95 | executeWith(delegate, { echo "Executing on agent $agentDesc" })
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/src/main/groovy/com/lesfurets/jenkins/unit/declarative/AllOfDeclaration.groovy:
--------------------------------------------------------------------------------
1 | package com.lesfurets.jenkins.unit.declarative
2 |
3 | import static groovy.lang.Closure.DELEGATE_FIRST
4 |
5 | class AllOfDeclaration extends WhenDeclaration {
6 |
7 | List> tags = []
8 | List> branches = []
9 | List changeRequests = []
10 | List expressions = []
11 | List anyOfs = []
12 |
13 | def tag(String pattern) {
14 | tags.add(new Tuple2(pattern, ComparatorEnum.GLOB))
15 | }
16 |
17 | def tag(Map args) {
18 | if (args.comparator) {
19 | ComparatorEnum comparator = ComparatorEnum.getComparator(args.comparator as String)
20 | this.tags.add(new Tuple2(args.pattern as String, comparator))
21 | }
22 | else {
23 | tag(args.pattern)
24 | }
25 | }
26 |
27 | def branch(String pattern) {
28 | branches.add(new Tuple2(pattern, ComparatorEnum.GLOB))
29 | }
30 |
31 | def branch(Map args) {
32 | if (args.comparator) {
33 | ComparatorEnum comparator = ComparatorEnum.getComparator(args.comparator as String)
34 | this.branches.add(new Tuple2(args.pattern as String, comparator))
35 | }
36 | else {
37 | branch(args.pattern)
38 | }
39 | }
40 |
41 | def changeRequest(Object val) {
42 | this.changeRequests.add(new ChangeRequestDeclaration(val))
43 | }
44 |
45 | def expression(Closure closure) {
46 | this.expressions.add(closure)
47 | }
48 |
49 | def anyOf(@DelegatesTo(strategy = DELEGATE_FIRST, value = AnyOfDeclaration) Closure closure) {
50 | this.anyOfs.add(createComponent(AnyOfDeclaration, closure))
51 | }
52 |
53 | def expressions(Object delegate) {
54 | return this.expressions.collect {executeWith(delegate, it)}.every()
55 | }
56 |
57 | def anyOf(Object delegate) {
58 | return this.anyOfs.collect {it.execute(delegate)}
59 | }
60 |
61 | Boolean execute(Object delegate) {
62 | def results = []
63 |
64 | if (tags) {
65 | tags.each { tag ->
66 | results.add(compareStringToPattern(delegate.env.TAG_NAME, tag))
67 | }
68 | }
69 |
70 | if (branches) {
71 | branches.each { branch ->
72 | results.add(compareStringToPattern(delegate.env.BRANCH_NAME, branch))
73 | }
74 | }
75 |
76 | if (changeRequests) {
77 | changeRequests.each { changeRequest ->
78 | results.add(changeRequest.execute(delegate))
79 | }
80 | }
81 |
82 | if (expressions) {
83 | results.add(expressions(delegate))
84 | }
85 |
86 | if (anyOfs) {
87 | results.addAll(anyOf(delegate))
88 | }
89 |
90 | return results.every()
91 | }
92 |
93 | }
94 |
--------------------------------------------------------------------------------
/src/main/groovy/com/lesfurets/jenkins/unit/declarative/AnyOfDeclaration.groovy:
--------------------------------------------------------------------------------
1 | package com.lesfurets.jenkins.unit.declarative
2 |
3 | import static com.lesfurets.jenkins.unit.declarative.GenericPipelineDeclaration.executeWith
4 |
5 | import static groovy.lang.Closure.DELEGATE_FIRST
6 |
7 | class AnyOfDeclaration extends WhenDeclaration {
8 |
9 | List> tags = []
10 | List> branches = []
11 | List changeRequests = []
12 | List expressions = []
13 | List allOfs = []
14 |
15 | def tag(String pattern) {
16 | this.tags.add(new Tuple2(pattern, ComparatorEnum.GLOB))
17 | }
18 |
19 | def tag(Map args) {
20 | if (args.comparator) {
21 | ComparatorEnum comparator = ComparatorEnum.getComparator(args.comparator as String)
22 | this.tags.add(new Tuple2(args.pattern as String, comparator))
23 | }
24 | else {
25 | tag(args.pattern)
26 | }
27 | }
28 |
29 | def branch(String pattern) {
30 | this.branches.add(new Tuple2(pattern, ComparatorEnum.GLOB))
31 | }
32 |
33 | def branch(Map args) {
34 | if (args.comparator) {
35 | ComparatorEnum comparator = ComparatorEnum.getComparator(args.comparator as String)
36 | this.branches.add(new Tuple2(args.pattern as String, comparator))
37 | }
38 | else {
39 | branch(args.pattern)
40 | }
41 | }
42 |
43 | def changeRequest(Object val) {
44 | this.changeRequests.add(new ChangeRequestDeclaration(val))
45 | }
46 |
47 | def expression(Closure closure) {
48 | this.expressions.add(closure)
49 | }
50 |
51 | def allOf(@DelegatesTo(strategy = DELEGATE_FIRST, value = AllOfDeclaration) Closure closure) {
52 | this.allOfs.add(createComponent(AllOfDeclaration, closure))
53 | }
54 |
55 | def allOf(Object delegate) {
56 | return this.allOfs.collect {it.execute(delegate)}
57 | }
58 |
59 | def expressions(Object delegate) {
60 | return this.expressions.collect {executeWith(delegate, it)}.any()
61 | }
62 |
63 | Boolean execute(Object delegate) {
64 | def results = []
65 |
66 | if (tags) {
67 | tags.each { tag ->
68 | results.add(compareStringToPattern(delegate.env.TAG_NAME, tag))
69 | }
70 | }
71 |
72 | if (branches) {
73 | branches.each { branch ->
74 | results.add(compareStringToPattern(delegate.env.BRANCH_NAME, branch))
75 | }
76 | }
77 |
78 | if (changeRequests) {
79 | changeRequests.each { changeRequest ->
80 | results.add(changeRequest.execute(delegate))
81 | }
82 | }
83 |
84 | if (expressions) {
85 | results.add(expressions(delegate))
86 | }
87 |
88 | if (allOfs) {
89 | results.addAll(allOf(delegate))
90 | }
91 |
92 | return results.any()
93 | }
94 |
95 | }
96 |
--------------------------------------------------------------------------------
/src/main/groovy/com/lesfurets/jenkins/unit/declarative/ComparatorEnum.groovy:
--------------------------------------------------------------------------------
1 | package com.lesfurets.jenkins.unit.declarative
2 |
3 | enum ComparatorEnum {
4 |
5 | EQUALS ('EQUALS'),
6 | GLOB ('GLOB'),
7 | REGEXP ('REGEXP')
8 |
9 | private static Map map
10 |
11 | static {
12 | map = [:]
13 | values().each { comparator ->
14 | map.put(comparator.name, comparator)
15 | }
16 | }
17 |
18 | static ComparatorEnum getComparator(String name) {
19 | return map.get(name)
20 | }
21 |
22 | private final String name
23 |
24 | private ComparatorEnum(String name) {
25 | this.name = name
26 | }
27 |
28 | @Override
29 | String toString() {
30 | return this.name
31 | }
32 |
33 | }
--------------------------------------------------------------------------------
/src/main/groovy/com/lesfurets/jenkins/unit/declarative/DeclarativePipeline.groovy:
--------------------------------------------------------------------------------
1 | package com.lesfurets.jenkins.unit.declarative
2 |
3 | import static groovy.lang.Closure.*
4 |
5 | class DeclarativePipeline extends GenericPipelineDeclaration {
6 |
7 | def properties = [:]
8 | List options = []
9 |
10 | Closure triggers
11 | ParametersDeclaration params = null
12 |
13 | DeclarativePipeline() {
14 | properties.put('any', 'any')
15 | properties.put('none', 'none')
16 | properties.put('scm', 'scm')
17 | }
18 |
19 | def propertyMissing(String name) {
20 | if (properties.containsKey(name)) {
21 | return properties.get(name)
22 | } else {
23 | throw new MissingPropertyException(name)
24 | }
25 | }
26 |
27 | def propertyMissing(String name, arg) {
28 |
29 | }
30 |
31 | def options(@DelegatesTo(DeclarativePipeline) Closure closure) {
32 | options.add(closure)
33 | }
34 |
35 | def triggers(@DelegatesTo(DeclarativePipeline) Closure closure) {
36 | this.triggers = closure
37 | }
38 |
39 | def parameters(Object o) {
40 | this.params = new ParametersDeclaration().with { it.label = o; it }
41 | }
42 |
43 | def parameters(@DelegatesTo(strategy=DELEGATE_FIRST, value=ParametersDeclaration) Closure closure) {
44 | this.params = createComponent(ParametersDeclaration, closure)
45 | }
46 |
47 | def execute(Object delegate) {
48 | super.execute(delegate)
49 | this.options.forEach {
50 | executeOn(delegate, it)
51 | }
52 | this.agent?.execute(delegate)
53 | executeOn(delegate, this.triggers)
54 | this.stages.entrySet().forEach { e ->
55 | e.value.execute(delegate)
56 | }
57 | this.post?.execute(delegate)
58 | }
59 |
60 | }
61 |
--------------------------------------------------------------------------------
/src/main/groovy/com/lesfurets/jenkins/unit/declarative/DeclarativePipelineTest.groovy:
--------------------------------------------------------------------------------
1 | package com.lesfurets.jenkins.unit.declarative
2 |
3 | import com.lesfurets.jenkins.unit.BasePipelineTest
4 |
5 | import static com.lesfurets.jenkins.unit.MethodSignature.method
6 |
7 | @groovy.transform.InheritConstructors
8 | abstract class DeclarativePipelineTest extends BasePipelineTest {
9 |
10 | def pipelineInterceptor = { Closure closure ->
11 | GenericPipelineDeclaration.binding = binding
12 | GenericPipelineDeclaration.createComponent(DeclarativePipeline, closure).execute(delegate)
13 | }
14 |
15 | @Override
16 | void setUp() throws Exception {
17 | super.setUp()
18 | helper.registerAllowedMethod('booleanParam', [Map], paramInterceptor)
19 | helper.registerAllowedMethod('checkout', [Closure])
20 | helper.registerAllowedMethod('credentials', [String], { String credName ->
21 | return binding.getVariable('credentials')[credName]
22 | })
23 | helper.registerAllowedMethod('cron', [String])
24 | helper.registerAllowedMethod('input', [Closure])
25 | helper.registerAllowedMethod('message', [String])
26 | helper.registerAllowedMethod(method("pipeline", Closure), pipelineInterceptor)
27 | helper.registerAllowedMethod('pollSCM', [String])
28 | helper.registerAllowedMethod('script', [Closure])
29 | helper.registerAllowedMethod('skipDefaultCheckout')
30 | helper.registerAllowedMethod('string', [Map], stringInterceptor)
31 | helper.registerAllowedMethod('timeout', [Integer, Closure])
32 | helper.registerAllowedMethod('timestamps')
33 | binding.setVariable('credentials', [:])
34 | binding.setVariable('params', [:].asImmutable())
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/main/groovy/com/lesfurets/jenkins/unit/declarative/NotDeclaration.groovy:
--------------------------------------------------------------------------------
1 | package com.lesfurets.jenkins.unit.declarative
2 |
3 | class NotDeclaration extends WhenDeclaration {
4 |
5 | Boolean execute(Object delegate) {
6 | return !super.execute(delegate)
7 | }
8 | }
9 |
10 |
--------------------------------------------------------------------------------
/src/main/groovy/com/lesfurets/jenkins/unit/declarative/ObjectUtils.groovy:
--------------------------------------------------------------------------------
1 | package com.lesfurets.jenkins.unit.declarative
2 |
3 | class ObjectUtils {
4 |
5 | static String printNonNullProperties(Object obj) {
6 | def props = obj.properties.clone()
7 | props.remove('class')
8 | props.remove('binding')
9 | obj.properties.entrySet().forEach { e ->
10 | if (e.value == null) {
11 | props.remove(e.key)
12 | }
13 | }
14 | return props.toString()
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/main/groovy/com/lesfurets/jenkins/unit/declarative/ParallelDeclaration.groovy:
--------------------------------------------------------------------------------
1 | package com.lesfurets.jenkins.unit.declarative
2 |
3 | import static groovy.lang.Closure.DELEGATE_FIRST
4 | //import static com.lesfurets.jenkins.unit.declarative.DeclarativePipeline.executeOn
5 |
6 | class ParallelDeclaration extends GenericPipelineDeclaration {
7 |
8 | boolean failFast
9 |
10 | ParallelDeclaration(boolean failFast) {
11 | this.failFast = failFast
12 | }
13 |
14 | ParallelDeclaration() {
15 | this.failFast = false
16 | }
17 |
18 | def stage(String name,
19 | @DelegatesTo(strategy = DELEGATE_FIRST, value = StageDeclaration) Closure closure) {
20 | this.stages.put(name, createComponent(StageDeclaration, closure).with{it.name = name;it} )
21 | }
22 |
23 | def execute(Object delegate) {
24 | super.execute(delegate)
25 | this.stages.entrySet().forEach { e ->
26 | e.value.execute(delegate)
27 | }
28 | }
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/src/main/groovy/com/lesfurets/jenkins/unit/declarative/ParametersDeclaration.groovy:
--------------------------------------------------------------------------------
1 | package com.lesfurets.jenkins.unit.declarative
2 |
3 |
4 | class ParametersDeclaration extends GenericPipelineDeclaration {
5 |
6 | void setParams(String name, Object val) {
7 | Map immutableParams = binding.getVariable('params') as Map
8 | if (immutableParams[name] == null) {
9 | Map mutableParams = [:]
10 | immutableParams.each { k, v -> mutableParams[k] = v }
11 | mutableParams[name] = val
12 | binding.setVariable('params', mutableParams.asImmutable())
13 | }
14 | }
15 |
16 | // dereference 'parameters closure
17 | def booleanParam(Map val) {
18 | this.setParams(val.name, val.defaultValue)
19 | }
20 |
21 | def choice(Map val) {
22 | this.setParams(val.name, val.choices[0])
23 | }
24 |
25 | def password(Map val) {
26 | this.setParams(val.name, val.defaultValue)
27 | }
28 |
29 | def string(Map val) {
30 | this.setParams(val.name, val.defaultValue)
31 | }
32 |
33 | def text(Map val) {
34 | this.setParams(val.name, val.defaultValue)
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/main/groovy/com/lesfurets/jenkins/unit/declarative/PostDeclaration.groovy:
--------------------------------------------------------------------------------
1 | package com.lesfurets.jenkins.unit.declarative
2 |
3 | import static com.lesfurets.jenkins.unit.declarative.DeclarativePipeline.executeOn
4 |
5 | class PostDeclaration {
6 |
7 | Closure always
8 | Closure changed
9 | Closure success
10 | Closure unstable
11 | Closure failure
12 | Closure aborted
13 | Closure unsuccessful
14 | Closure cleanup
15 | Closure fixed
16 | Closure regression
17 |
18 | def always(Closure closure) {
19 | this.always = closure
20 | }
21 |
22 | def changed(Closure closure) {
23 | this.changed = closure
24 | }
25 |
26 | def success(Closure closure) {
27 | this.success = closure
28 | }
29 |
30 | def unstable(Closure closure) {
31 | this.unstable = closure
32 | }
33 |
34 | def unsuccessful(Closure closure) {
35 | this.unsuccessful = closure
36 | }
37 |
38 | def failure(Closure closure) {
39 | this.failure = closure
40 | }
41 |
42 | def aborted(Closure closure) {
43 | this.aborted = closure
44 | }
45 |
46 | def cleanup(Closure closure) {
47 | this.cleanup = closure
48 | }
49 |
50 | def fixed(Closure closure) {
51 | this.fixed = closure
52 | }
53 |
54 | def regression(Closure closure){
55 | this.regression = closure
56 | }
57 |
58 | def execute(Object delegate) {
59 | def currentBuild = delegate.currentBuild.result
60 | def previousBuild = delegate.currentBuild?.previousBuild?.result
61 | if (this.always) {
62 | executeOn(delegate, this.always)
63 | }
64 |
65 | switch (currentBuild) {
66 | case 'SUCCESS':
67 | executeOn(delegate, this.success)
68 | break
69 | case 'FAILURE':
70 | executeOn(delegate, this.failure)
71 | break
72 | case 'ABORTED':
73 | executeOn(delegate, this.aborted)
74 | break
75 | case 'UNSTABLE':
76 | executeOn(delegate, this.unstable)
77 | break
78 | }
79 |
80 | if(currentBuild != previousBuild && this.changed)
81 | {
82 | executeOn(delegate, this.changed)
83 | }
84 | if(currentBuild != 'SUCCESS' && this.unsuccessful)
85 | {
86 | executeOn(delegate, this.unsuccessful)
87 | }
88 | if(this.fixed){
89 | if(currentBuild == 'SUCCESS' && (previousBuild == 'FAILURE' || previousBuild == 'UNSTABLE'))
90 | {
91 | executeOn(delegate, this.fixed)
92 | }
93 | }
94 | if(this.regression)
95 | {
96 | if((currentBuild == 'FAILURE' || currentBuild == 'UNSTABLE') && previousBuild == 'SUCCESS'){
97 | executeOn(delegate, this.regression)
98 | }
99 | }
100 |
101 | // Cleanup is always performed last
102 | if(this.cleanup){
103 | executeOn(delegate, this.cleanup)
104 | }
105 | }
106 |
107 | }
108 |
--------------------------------------------------------------------------------
/src/main/groovy/com/lesfurets/jenkins/unit/declarative/StageDeclaration.groovy:
--------------------------------------------------------------------------------
1 | package com.lesfurets.jenkins.unit.declarative
2 |
3 |
4 | import static groovy.lang.Closure.*
5 |
6 | class StageDeclaration extends GenericPipelineDeclaration {
7 |
8 | String name
9 | Closure steps
10 | WhenDeclaration when
11 | ParallelDeclaration parallel
12 | boolean failFast = false
13 | List options = []
14 |
15 | StageDeclaration(String name) {
16 | this.name = name
17 | }
18 |
19 | def steps(Closure closure) {
20 | this.steps = closure
21 | }
22 |
23 | def failFast(boolean failFast) {
24 | this.failFast = failFast
25 | }
26 |
27 | def getBinding_var() {
28 | return binding?.var
29 | }
30 |
31 | def parallel(@DelegatesTo(strategy = DELEGATE_FIRST, value = ParallelDeclaration) Closure closure) {
32 | this.parallel = createComponent(ParallelDeclaration, closure).with { it.failFast = failFast; it }
33 | }
34 |
35 | def when(@DelegatesTo(strategy = DELEGATE_FIRST, value = WhenDeclaration) Closure closure) {
36 | this.when = createComponent(WhenDeclaration, closure)
37 | }
38 |
39 | def options(@DelegatesTo(StageDeclaration) Closure closure) {
40 | options.add(closure)
41 | }
42 |
43 | def execute(Object delegate) {
44 | String name = this.name
45 | def actions = 0
46 | if(parallel) {
47 | actions++
48 | }
49 | if(stages.size()>0) {
50 | actions++
51 | }
52 | if(steps) {
53 | actions++
54 | }
55 | if (actions > 1 ) {
56 | throw new IllegalArgumentException ("""Only one of "matrix", "parallel", "stages", or "steps" allowed for stage "${name}" """)
57 | }
58 |
59 | this.options.each {
60 | executeOn(delegate, it)
61 | }
62 |
63 | if(delegate.binding.variables.currentBuild.result == "FAILURE"){
64 | executeWith(delegate, { echo "Stage \"$name\" skipped due to earlier failure(s)" })
65 | return
66 | }
67 |
68 | if (!when || when.execute(delegate)) {
69 | super.execute(delegate)
70 |
71 | // TODO handle credentials
72 |
73 | if (parallel) {
74 | parallel.execute(delegate)
75 | }
76 |
77 | Closure stageBody = { agent?.execute(delegate) }
78 | Closure cl = { stage("$name", stageBody) }
79 | if (steps) {
80 | stageBody = stageBody >> steps.rehydrate(delegate, this, delegate)
81 | }
82 | executeWith(delegate, cl)
83 |
84 | this.stages.entrySet().forEach { e ->
85 | e.value.execute(delegate)
86 | }
87 |
88 | if (post) {
89 | this.post.execute(delegate)
90 | }
91 | } else {
92 | executeWith(delegate, { echo "Skipping stage $name" })
93 | }
94 | }
95 |
96 | }
97 |
--------------------------------------------------------------------------------
/src/main/groovy/com/lesfurets/jenkins/unit/declarative/agent/DockerAgentDeclaration.groovy:
--------------------------------------------------------------------------------
1 | package com.lesfurets.jenkins.unit.declarative.agent
2 |
3 | import com.lesfurets.jenkins.unit.declarative.GenericPipelineDeclaration
4 | import groovy.transform.Memoized
5 | import groovy.transform.ToString
6 |
7 | import static com.lesfurets.jenkins.unit.declarative.ObjectUtils.printNonNullProperties
8 |
9 | @ToString(includePackage = false, includeNames = true, ignoreNulls = true)
10 | class DockerAgentDeclaration extends GenericPipelineDeclaration {
11 |
12 | String label
13 | String args = ""
14 | String registryUrl
15 | String registryCredentialsId
16 | String customWorkspace
17 | boolean reuseNode
18 | boolean containerPerStageRoot
19 | boolean alwaysPull
20 | String image
21 |
22 | def label(final String label) {
23 | this.label = label
24 | }
25 |
26 | def args(final String args) {
27 | this.args = args
28 | }
29 |
30 | def alwaysPull(final boolean alwaysPull) {
31 | this.alwaysPull = alwaysPull
32 | }
33 |
34 | def registryUrl(final String registryUrl) {
35 | this.registryUrl = registryUrl
36 | }
37 |
38 | def registryCredentialsId(final String registryCredentialsId) {
39 | this.registryCredentialsId = registryCredentialsId
40 | }
41 |
42 | def customWorkspace(final String customWorkspace) {
43 | this.customWorkspace = customWorkspace
44 | }
45 |
46 | def reuseNode(final boolean reuseNode) {
47 | this.reuseNode = reuseNode
48 | }
49 |
50 | def containerPerStageRoot(final boolean containerPerStageRoot) {
51 | this.containerPerStageRoot = containerPerStageRoot
52 | }
53 |
54 | def image(String image) {
55 | this.image = image
56 | }
57 |
58 | @Memoized
59 | String toString() {
60 | return printNonNullProperties(this)
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/src/main/groovy/com/lesfurets/jenkins/unit/declarative/agent/DockerfileAgentDeclaration.groovy:
--------------------------------------------------------------------------------
1 | package com.lesfurets.jenkins.unit.declarative.agent
2 |
3 |
4 | import com.lesfurets.jenkins.unit.declarative.GenericPipelineDeclaration
5 | import groovy.transform.ToString
6 |
7 | @ToString(includePackage = false, includeNames = true, ignoreNulls = true)
8 | class DockerfileAgentDeclaration extends GenericPipelineDeclaration {
9 | String additionalBuildArgs
10 | String args
11 | String customWorkspace
12 | String dockerfileDir
13 | String filename
14 | String label
15 | String registryCredentialsId
16 | String registryUrl
17 | Boolean reuseNode
18 |
19 | def additionalBuildArgs(String additionalBuildArgs) {
20 | this.additionalBuildArgs = additionalBuildArgs
21 | }
22 |
23 | def args(String args) {
24 | this.args = args
25 | }
26 |
27 | def customWorkspace(final String customWorkspace) {
28 | this.customWorkspace = customWorkspace
29 | }
30 |
31 | def dir(String dir) {
32 | this.dockerfileDir = dir
33 | }
34 |
35 | def filename(String filename) {
36 | this.filename = filename
37 | }
38 |
39 | def label(final String label) {
40 | this.label = label
41 | }
42 |
43 | def registryCredentialsId(final String registryCredentialsId) {
44 | this.registryCredentialsId = registryCredentialsId
45 | }
46 |
47 | def registryUrl(final String registryUrl) {
48 | this.registryUrl = registryUrl
49 | }
50 |
51 | def reuseNode(boolean reuse) {
52 | this.reuseNode = reuse
53 | }
54 |
55 | }
56 |
--------------------------------------------------------------------------------
/src/main/groovy/com/lesfurets/jenkins/unit/declarative/kubernetes/ContainerLivenessProbeDeclaration.groovy:
--------------------------------------------------------------------------------
1 | package com.lesfurets.jenkins.unit.declarative.kubernetes
2 |
3 | import com.lesfurets.jenkins.unit.declarative.GenericPipelineDeclaration
4 |
5 | class ContainerLivenessProbeDeclaration extends GenericPipelineDeclaration {
6 |
7 | String execArgs
8 | int timeoutSeconds
9 | int initialDelaySeconds
10 | int failureThreshold
11 | int periodSeconds
12 | int successThreshold
13 |
14 | def execArgs(final String execArgs) {
15 | this.execArgs = execArgs
16 | }
17 |
18 | def timeoutSeconds(final int timeoutSeconds) {
19 | this.timeoutSeconds = timeoutSeconds
20 | }
21 |
22 | def initialDelaySeconds(final int initialDelaySeconds) {
23 | this.initialDelaySeconds = initialDelaySeconds
24 | }
25 |
26 | def failureThreshold(final int failureThreshold) {
27 | this.failureThreshold = failureThreshold
28 | }
29 |
30 | def periodSeconds(final int periodSeconds) {
31 | this.periodSeconds = periodSeconds
32 | }
33 |
34 | def successThreshold(final int successThreshold) {
35 | this.successThreshold = successThreshold
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/main/groovy/com/lesfurets/jenkins/unit/declarative/kubernetes/PodTemplateDeclaration.groovy:
--------------------------------------------------------------------------------
1 | package com.lesfurets.jenkins.unit.declarative.kubernetes
2 |
3 | import com.lesfurets.jenkins.unit.declarative.GenericPipelineDeclaration
4 |
5 |
6 | class PodTemplateDeclaration extends GenericPipelineDeclaration {
7 | }
8 |
--------------------------------------------------------------------------------
/src/main/groovy/com/lesfurets/jenkins/unit/declarative/kubernetes/PortMappingDeclaration.groovy:
--------------------------------------------------------------------------------
1 | package com.lesfurets.jenkins.unit.declarative.kubernetes
2 |
3 | import com.lesfurets.jenkins.unit.declarative.GenericPipelineDeclaration
4 | import groovy.transform.Memoized
5 | import groovy.transform.ToString
6 |
7 | import static com.lesfurets.jenkins.unit.declarative.ObjectUtils.printNonNullProperties
8 |
9 | @ToString(includePackage = false, includeNames = true, ignoreNulls = true)
10 | class PortMappingDeclaration extends GenericPipelineDeclaration {
11 | String name
12 | int containerPort
13 | int hostPort
14 |
15 | def name(final String name) {
16 | this.name = name
17 | }
18 |
19 | def containerPort(final int containerPort) {
20 | this.containerPort = containerPort
21 | }
22 |
23 | def hostPort(final int hostPort) {
24 | this.hostPort = hostPort
25 | }
26 |
27 | @Memoized
28 | String toString() {
29 | return printNonNullProperties(this)
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/main/groovy/com/lesfurets/jenkins/unit/declarative/kubernetes/TemplateEnvVarDeclaration.groovy:
--------------------------------------------------------------------------------
1 | package com.lesfurets.jenkins.unit.declarative.kubernetes
2 |
3 | import com.lesfurets.jenkins.unit.declarative.GenericPipelineDeclaration
4 |
5 | class TemplateEnvVarDeclaration extends GenericPipelineDeclaration {
6 |
7 | KeyValueVar containerEnvVar;
8 | KeyValueVar envVar;
9 | KeyValueVar podEnvVar;
10 | KeyValueVar secretEnvVar;
11 |
12 | def containerEnvVar(final KeyValueVar containerEnvVar) {
13 | this.containerEnvVar = containerEnvVar
14 | }
15 |
16 | def envVar(final KeyValueVar envVar) {
17 | this.envVar = envVar
18 | }
19 |
20 | def podEnvVar(final KeyValueVar podEnvVar) {
21 | this.podEnvVar = podEnvVar
22 | }
23 |
24 | def secretEnvVar(final KeyValueVar secretEnvVar) {
25 | this.secretEnvVar = secretEnvVar
26 | }
27 |
28 | class KeyValueVar {
29 | String key
30 | String value
31 |
32 | def key(final String key) {
33 | this.key = key
34 | }
35 |
36 | def value(final String value) {
37 | this.value = value
38 | }
39 | }
40 |
41 | class SecretVar {
42 | String key
43 | String secretName
44 | String secretKey
45 | boolean optional
46 |
47 | def key(final String key) {
48 | this.key = key
49 | }
50 |
51 | def secretName(final String secretName) {
52 | this.secretName = secretName
53 | }
54 |
55 | def secretKey(final String secretKey) {
56 | this.secretKey = secretKey
57 | }
58 |
59 | def optional(final boolean optional) {
60 | this.optional = optional
61 | }
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/src/main/groovy/com/lesfurets/jenkins/unit/global/lib/GitSource.groovy:
--------------------------------------------------------------------------------
1 | package com.lesfurets.jenkins.unit.global.lib
2 |
3 | import java.util.concurrent.TimeUnit
4 |
5 | import groovy.transform.CompileStatic
6 | import groovy.transform.Immutable
7 |
8 | @Immutable
9 | @CompileStatic
10 | class GitSource implements SourceRetriever {
11 |
12 | String sourceURL
13 |
14 | @Override
15 | List retrieve(String repository, String branch, String targetPath) throws IllegalStateException {
16 | File target = new File(targetPath)
17 | def fetch = target.toPath().resolve("$repository@$branch").toFile()
18 | if (fetch.exists()) {
19 | return [fetch.toURI().toURL()]
20 | } else {
21 | fetch.parentFile.mkdirs()
22 | }
23 | def command = "git clone -b $branch --single-branch $sourceURL $repository@$branch"
24 | println command
25 | def processBuilder = new ProcessBuilder(command.split(' '))
26 | .inheritIO()
27 | .directory(target)
28 | def proc = processBuilder.start()
29 | proc.waitFor(CLONE_TIMEOUT_MIN, TimeUnit.MINUTES)
30 | proc.exitValue()
31 | return [fetch.toURI().toURL()]
32 | }
33 |
34 | static GitSource gitSource(String source) {
35 | new GitSource(source)
36 | }
37 |
38 | @Override
39 | String toString() {
40 | return "GitSource{" +
41 | "sourceURL='" + sourceURL + '\'' +
42 | '}'
43 | }
44 | }
--------------------------------------------------------------------------------
/src/main/groovy/com/lesfurets/jenkins/unit/global/lib/Library.groovy:
--------------------------------------------------------------------------------
1 | package com.lesfurets.jenkins.unit.global.lib
2 |
3 | /**
4 | * Annotation definition to avoid missing import
5 | */
6 | @interface Library {
7 |
8 | /**
9 | * Library names, each optionally followed by {@code @} and a version.
10 | */
11 | String[] value()
12 |
13 | }
--------------------------------------------------------------------------------
/src/main/groovy/com/lesfurets/jenkins/unit/global/lib/LibraryConfiguration.groovy:
--------------------------------------------------------------------------------
1 | package com.lesfurets.jenkins.unit.global.lib
2 |
3 | import groovy.transform.CompileStatic
4 | import groovy.transform.builder.Builder
5 | import groovy.transform.builder.ExternalStrategy
6 |
7 | /**
8 | * Mock for org.jenkinsci.plugins.workflow.libs.LibraryConfiguration
9 | */
10 | @CompileStatic
11 | class LibraryConfiguration {
12 |
13 | String name
14 | String defaultVersion = 'master'
15 | SourceRetriever retriever
16 | boolean implicit = false
17 | boolean allowOverride = true
18 | String targetPath
19 |
20 | LibraryConfiguration validate() {
21 | if (name && defaultVersion && retriever && targetPath)
22 | return this
23 | throw new IllegalStateException("LibraryConfiguration is not properly initialized ${this.toString()}")
24 | }
25 |
26 | static LibraryBuilder library(String libName = null) {
27 | return new LibraryBuilder() {
28 | LibraryConfiguration build() { return super.build().validate() }
29 | }.with { it.name(libName) }
30 | }
31 |
32 | @Builder(builderStrategy = ExternalStrategy, forClass = LibraryConfiguration)
33 | static class LibraryBuilder {
34 |
35 | }
36 |
37 | @Override
38 | String toString() {
39 | return "LibraryConfiguration{" +
40 | "name='" + name + '\'' +
41 | ", defaultVersion='" + defaultVersion + '\'' +
42 | ", retriever=" + retriever +
43 | ", implicit=" + implicit +
44 | ", allowOverride=" + allowOverride +
45 | '}'
46 | }
47 | }
--------------------------------------------------------------------------------
/src/main/groovy/com/lesfurets/jenkins/unit/global/lib/LibraryRecord.groovy:
--------------------------------------------------------------------------------
1 | package com.lesfurets.jenkins.unit.global.lib
2 |
3 | import groovy.transform.CompileStatic
4 |
5 | @CompileStatic
6 | class LibraryRecord {
7 |
8 | LibraryConfiguration configuration
9 | String version
10 | List rootPaths
11 |
12 | Map definedGlobalVars
13 |
14 | LibraryRecord(LibraryConfiguration configuration, String version, List rootPaths) {
15 | this.configuration = configuration
16 | this.version = version
17 | this.rootPaths = rootPaths
18 | }
19 |
20 | String getIdentifier() {
21 | return "$configuration.name@$version"
22 | }
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/src/main/groovy/com/lesfurets/jenkins/unit/global/lib/LocalSource.groovy:
--------------------------------------------------------------------------------
1 | package com.lesfurets.jenkins.unit.global.lib
2 |
3 | import groovy.transform.CompileStatic
4 | import groovy.transform.Immutable
5 |
6 | @Immutable
7 | @CompileStatic
8 | class LocalSource implements SourceRetriever {
9 |
10 | String sourceURL
11 |
12 | @Override
13 | List retrieve(String repository, String branch, String targetPath) {
14 | def sourceDir = new File(sourceURL).toPath().resolve("$repository@$branch").toFile()
15 | if (sourceDir.exists()) {
16 | return [sourceDir.toURI().toURL()]
17 | }
18 | throw new IllegalStateException("Directory $sourceDir.path does not exists")
19 | }
20 |
21 | static LocalSource localSource(String source) {
22 | new LocalSource(source)
23 | }
24 |
25 | @Override
26 | String toString() {
27 | return "LocalSource{" +
28 | "sourceURL='" + sourceURL + '\'' +
29 | '}'
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/main/groovy/com/lesfurets/jenkins/unit/global/lib/ProjectSource.groovy:
--------------------------------------------------------------------------------
1 | package com.lesfurets.jenkins.unit.global.lib
2 |
3 | import com.lesfurets.jenkins.unit.global.lib.SourceRetriever
4 |
5 | import groovy.transform.CompileStatic
6 | import groovy.transform.Immutable
7 |
8 | /**
9 | * Retrieves the shared lib sources of the current project which are expected to be
10 | * at the default location ("./vars"), "./src", "./resources").
11 | * When working with this retriever the LibraryConfiguration which is provided
12 | * with this SourceRetrievers should be configured with allowOverride=false
13 | * since the ProjectSource retriever does not support loading of alternate versions. As
14 | * already outlined above this source retriever always loads the version as it is
15 | * contained at the default location.
16 | */
17 |
18 | @Immutable
19 | @CompileStatic
20 | class ProjectSource implements SourceRetriever {
21 |
22 | String sourceURL
23 |
24 | /*
25 | * None of the parameters provided in the signature are used in the use-case of that retriever.
26 | */
27 | @Override
28 | List retrieve(String repository, String branch, String targetPath) {
29 | def sourceDir = new File(sourceURL)
30 | if (sourceDir.exists()) {
31 | return [sourceDir.getAbsoluteFile().toURI().toURL()]
32 | }
33 | throw new IllegalStateException("Directory $sourceDir.path does not exists")
34 | }
35 |
36 | static ProjectSource projectSource(String sourceDir = '.') {
37 | new ProjectSource(sourceDir)
38 | }
39 |
40 | @Override
41 | String toString() {
42 | return "${getClass().getSimpleName()}{" +
43 | "sourceURL='" + sourceURL + '\'' +
44 | '}'
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/main/groovy/com/lesfurets/jenkins/unit/global/lib/SourceRetriever.groovy:
--------------------------------------------------------------------------------
1 | package com.lesfurets.jenkins.unit.global.lib
2 |
3 | import groovy.transform.CompileStatic
4 |
5 | @CompileStatic
6 | interface SourceRetriever {
7 |
8 | public static final int CLONE_TIMEOUT_MIN = 10
9 |
10 | List retrieve(String repository, String branch, String targetPath) throws IllegalStateException
11 |
12 | }
13 |
--------------------------------------------------------------------------------
/src/test/groovy/com/TestCatchError.groovy:
--------------------------------------------------------------------------------
1 | package com.lesfurets.jenkins
2 |
3 | import com.lesfurets.jenkins.unit.BasePipelineTest
4 | import org.junit.Before
5 | import org.junit.Test
6 |
7 | class TestCatchError extends BasePipelineTest {
8 |
9 | @Override
10 | @Before
11 | void setUp() throws Exception {
12 | super.setUp()
13 | }
14 |
15 | @Test()
16 | void should_fail_with_fail_before_and_SuccesCatch() throws Exception {
17 | def script = runInlineScript("""
18 | node() {
19 | stage('test') {
20 | error 'error'
21 | catchError(buildResult: 'SUCCESS') {
22 | throw new Exception()
23 | }
24 | }
25 | }
26 | """)
27 | assertJobStatusFailure()
28 | }
29 |
30 | @Test()
31 | void should_unstable_with_unstable_before_and_SuccesCatch() throws Exception {
32 | def script = runInlineScript("""
33 | node() {
34 | stage('test') {
35 | unstable 'unstable'
36 | catchError(buildResult: 'SUCCESS') {
37 | throw new Exception()
38 | }
39 | }
40 | }
41 | """)
42 | assertJobStatusUnstable()
43 | }
44 |
45 | @Test()
46 | void should_succes_with_SuccesCatch() throws Exception {
47 | def script = runInlineScript("""
48 | node() {
49 | stage('test') {
50 | catchError(buildResult: 'SUCCESS') {
51 | throw new Exception()
52 | }
53 | }
54 | }
55 | """)
56 | assertJobStatusSuccess()
57 | }
58 |
59 | @Test()
60 | void should_unstable_with_UnstableCatch() throws Exception {
61 | def script = runInlineScript("""
62 | node() {
63 | stage('test') {
64 | catchError(buildResult: 'UNSTABLE') {
65 | throw new Exception()
66 | }
67 | }
68 | }
69 | """)
70 | assertJobStatusUnstable()
71 | }
72 |
73 | @Test()
74 | void should_fail_with_no_parameter() throws Exception {
75 | def script = runInlineScript("""
76 | node() {
77 | stage('test') {
78 | catchError() {
79 | throw new Exception()
80 | }
81 | }
82 | }
83 | """)
84 | assertJobStatusFailure()
85 | }
86 |
87 | }
88 |
--------------------------------------------------------------------------------
/src/test/groovy/com/lesfurets/jenkins/TestAddCredential.groovy:
--------------------------------------------------------------------------------
1 | package com.lesfurets.jenkins
2 |
3 | import com.lesfurets.jenkins.unit.BasePipelineTest
4 | import org.junit.Test
5 |
6 | class TestAddCredential extends BasePipelineTest {
7 |
8 | @Test
9 | void readUndefinedParamNoException() throws Exception {
10 | super.setUp()
11 | addCredential('FOO', 'BAR')
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/test/groovy/com/lesfurets/jenkins/TestAddEnvVar.groovy:
--------------------------------------------------------------------------------
1 | package com.lesfurets.jenkins
2 |
3 | import com.lesfurets.jenkins.unit.BasePipelineTest
4 | import org.junit.Test
5 |
6 | class TestAddEnvVar extends BasePipelineTest {
7 |
8 | @Test
9 | void readUndefinedParamNoException() throws Exception {
10 | super.setUp()
11 | addEnvVar('FOO', 'BAR')
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/test/groovy/com/lesfurets/jenkins/TestAddParam.groovy:
--------------------------------------------------------------------------------
1 | package com.lesfurets.jenkins
2 |
3 | import com.lesfurets.jenkins.unit.BasePipelineTest
4 | import org.junit.Test
5 |
6 | class TestAddParam extends BasePipelineTest {
7 |
8 | @Test
9 | void readUndefinedParamNoException() throws Exception {
10 | super.setUp()
11 | addParam('FOO', 'BAR')
12 | }
13 |
14 | @Test(expected = UnsupportedOperationException)
15 | void addParamImmutable() throws Exception {
16 | super.setUp()
17 | addParam('FOO', 'BAR')
18 |
19 | // We should not be able to modify existing parameters. This would not work on Jenkins.
20 | binding.getVariable('params')['FOO'] = 'NOT-BAR'
21 | }
22 |
23 | @Test(expected = UnsupportedOperationException)
24 | void addNewParamImmutable() throws Exception {
25 | super.setUp()
26 |
27 | // It also is not permitted to add new parameters directly. Instead, addParam must be used.
28 | binding.getVariable('params')['BAZ'] = 'QUX'
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/test/groovy/com/lesfurets/jenkins/TestCustomMethodJob.groovy:
--------------------------------------------------------------------------------
1 | package com.lesfurets.jenkins
2 |
3 | import com.lesfurets.jenkins.unit.BasePipelineTest
4 | import org.junit.Before
5 | import org.junit.Test
6 |
7 | import static org.junit.Assert.assertTrue
8 |
9 | class TestCustomMethodJob extends BasePipelineTest {
10 |
11 | @Override
12 | @Before
13 | void setUp() throws Exception {
14 | scriptRoots += 'src/test/jenkins'
15 | super.setUp()
16 | }
17 |
18 | @Test
19 | void should_run_script_with_custom_method() {
20 | // when:
21 | runScript("job/customMethod.jenkins")
22 |
23 | // then:
24 | assertJobStatusSuccess()
25 | assertTrue(helper.callStack.find { call ->
26 | call.methodName == 'echo'
27 | }.argsToString() == 'executing custom method closure')
28 | }
29 |
30 | @Test
31 | void should_run_script_with_custom_method_mock() {
32 | // given:
33 | Closure customMethodMock = { echo 'executing mock closure' }
34 | helper.registerAllowedMethod('customMethod', [], customMethodMock)
35 |
36 | // when:
37 | runScript("job/customMethod.jenkins")
38 |
39 | // then:
40 | assertJobStatusSuccess()
41 | assertTrue(helper.callStack.find { call ->
42 | call.methodName == 'echo'
43 | }.argsToString() == 'executing mock closure')
44 | }
45 |
46 | @Test
47 | void should_run_script_with_custom_method_with_arguments() {
48 | // when:
49 | runScript("job/customMethodWithArguments.jenkins")
50 |
51 | // then:
52 | assertJobStatusSuccess()
53 | assertTrue(helper.callStack.find { call ->
54 | call.methodName == 'echo'
55 | }.argsToString() == 'executing custom method with arguments closure (arguments: \'stringArg\', \'42\', \'[collectionArg, 42]\')')
56 | }
57 |
58 | @Test
59 | void should_run_script_with_custom_method_with_arguments_mock() {
60 | // given:
61 | Closure customMethodWithArgumentsMock = { String stringArg, int intArg, Collection collectionArg ->
62 | echo "executing mock closure with arguments (arguments: '${stringArg}', '${intArg}', '${collectionArg}')"
63 | }
64 | helper.registerAllowedMethod('customMethodWithArguments', [String, int, Collection], customMethodWithArgumentsMock)
65 |
66 | // when:
67 | runScript("job/customMethodWithArguments.jenkins")
68 |
69 | // then:
70 | assertJobStatusSuccess()
71 | assertTrue(helper.callStack.find { call ->
72 | call.methodName == 'echo'
73 | }.argsToString() == 'executing mock closure with arguments (arguments: \'stringArg\', \'42\', \'[collectionArg, 42]\')')
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/src/test/groovy/com/lesfurets/jenkins/TestCustomMethodJobCPS.groovy:
--------------------------------------------------------------------------------
1 | package com.lesfurets.jenkins
2 |
3 | import com.lesfurets.jenkins.unit.cps.BasePipelineTestCPS
4 | import org.junit.Before
5 | import org.junit.Test
6 |
7 | import static org.junit.Assert.assertTrue
8 |
9 | class TestCustomMethodJobCPS extends BasePipelineTestCPS {
10 |
11 | @Override
12 | @Before
13 | void setUp() throws Exception {
14 | scriptRoots += 'src/test/jenkins'
15 | super.setUp()
16 | }
17 |
18 | @Test
19 | void should_run_script_with_custom_method() {
20 | // when:
21 | runScript("job/customMethod.jenkins")
22 |
23 | // then:
24 | assertJobStatusSuccess()
25 | assertTrue(helper.callStack.find { call ->
26 | call.methodName == 'echo'
27 | }.argsToString() == 'executing custom method closure')
28 | }
29 |
30 | @Test
31 | void should_run_script_with_custom_method_mock() {
32 | // given:
33 | Closure customMethodMock = { echo 'executing mock closure' }
34 | helper.registerAllowedMethod('customMethod', [], customMethodMock)
35 |
36 | // when:
37 | runScript("job/customMethod.jenkins")
38 |
39 | // then:
40 | assertJobStatusSuccess()
41 | assertTrue(helper.callStack.find { call ->
42 | call.methodName == 'echo'
43 | }.argsToString() == 'executing mock closure')
44 | }
45 |
46 | @Test
47 | void should_run_script_with_custom_method_with_arguments() {
48 | // when:
49 | runScript("job/customMethodWithArguments.jenkins")
50 |
51 | // then:
52 | assertJobStatusSuccess()
53 | assertTrue(helper.callStack.find { call ->
54 | call.methodName == 'echo'
55 | }.argsToString() == 'executing custom method with arguments closure (arguments: \'stringArg\', \'42\', \'[collectionArg, 42]\')')
56 | }
57 |
58 | @Test
59 | void should_run_script_with_custom_method_with_arguments_mock() {
60 | // given:
61 | Closure customMethodWithArgumentsMock = { String stringArg, int intArg, Collection collectionArg ->
62 | echo "executing mock closure with arguments (arguments: '${stringArg}', '${intArg}', '${collectionArg}')"
63 | }
64 | helper.registerAllowedMethod('customMethodWithArguments', [String, int, Collection], customMethodWithArgumentsMock)
65 |
66 | // when:
67 | runScript("job/customMethodWithArguments.jenkins")
68 |
69 | // then:
70 | assertJobStatusSuccess()
71 | assertTrue(helper.callStack.find { call ->
72 | call.methodName == 'echo'
73 | }.argsToString() == 'executing mock closure with arguments (arguments: \'stringArg\', \'42\', \'[collectionArg, 42]\')')
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/src/test/groovy/com/lesfurets/jenkins/TestDeclarativeImmutableParams.groovy:
--------------------------------------------------------------------------------
1 | package com.lesfurets.jenkins
2 |
3 | import com.lesfurets.jenkins.unit.declarative.DeclarativePipelineTest
4 | import org.junit.Before
5 | import org.junit.Test
6 |
7 | import static org.junit.Assert.assertEquals
8 |
9 | class TestDeclarativeImmutableParams extends DeclarativePipelineTest {
10 |
11 | @Override
12 | @Before
13 | void setUp() throws Exception {
14 | scriptRoots += 'src/test/jenkins'
15 |
16 | super.setUp()
17 | }
18 |
19 | @Test(expected = UnsupportedOperationException)
20 | void "test immutable params in declarative pipeline"() {
21 | runScript("job/library/test_params_immutable_declarative.jenkins")
22 | assertEquals('original', binding.params['new'].toString())
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/test/groovy/com/lesfurets/jenkins/TestExampleJob.groovy:
--------------------------------------------------------------------------------
1 | package com.lesfurets.jenkins
2 |
3 | import org.junit.Before
4 | import org.junit.Test
5 |
6 | import com.lesfurets.jenkins.unit.BasePipelineTest
7 |
8 | import static com.lesfurets.jenkins.unit.MethodCall.callArgsToString
9 | import static org.junit.Assert.assertTrue
10 |
11 | class TestExampleJob extends BasePipelineTest {
12 |
13 | @Override
14 | @Before
15 | void setUp() throws Exception {
16 | scriptRoots += 'src/test/jenkins'
17 | super.setUp()
18 | def scmBranch = "feature_test"
19 | helper.registerAllowedMethod("sh", [Map.class], {c -> 'bcc19744'})
20 | binding.setVariable('scm', [
21 | $class : 'GitSCM',
22 | branches : [[name: scmBranch]]
23 | ])
24 | }
25 |
26 | @Test
27 | void should_execute_without_errors() throws Exception {
28 | def script = runScript("job/exampleJob.jenkins")
29 | script.execute()
30 | printCallStack()
31 | assertJobStatusSuccess()
32 | }
33 |
34 | @Test
35 | void should_print_property_value() {
36 | def script = runScript('job/exampleJob.jenkins')
37 | script.execute()
38 |
39 | def value = 'value'
40 | assertTrue(helper.callStack.findAll { call ->
41 | call.methodName == 'println'
42 | }.any { call ->
43 | callArgsToString(call).contains(value)
44 | })
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/test/groovy/com/lesfurets/jenkins/TestExampleJobCPS.groovy:
--------------------------------------------------------------------------------
1 | package com.lesfurets.jenkins
2 |
3 | import static com.lesfurets.jenkins.unit.MethodCall.callArgsToString
4 | import static org.junit.Assert.assertTrue
5 |
6 | import org.junit.Before
7 | import org.junit.Test
8 |
9 | import com.lesfurets.jenkins.unit.cps.BasePipelineTestCPS
10 |
11 | class TestExampleJobCPS extends BasePipelineTestCPS {
12 |
13 | @Override
14 | @Before
15 | void setUp() throws Exception {
16 | scriptRoots += 'src/test/jenkins'
17 | super.setUp()
18 | def scmBranch = "feature_test"
19 | helper.registerAllowedMethod("sh", [Map.class], {c -> "bcc19744fc4876848f3a21aefc92960ea4c716cf"})
20 | binding.setVariable('scm', [
21 | $class : 'GitSCM',
22 | branches : [[name: scmBranch]]
23 | ])
24 | }
25 |
26 | @Test
27 | void should_execute_without_errors() throws Exception {
28 | def script = runScript("job/exampleJob.jenkins")
29 | script.execute()
30 | printCallStack()
31 | assertJobStatusSuccess()
32 | }
33 |
34 | @Test
35 | void should_print_property_value() {
36 | def script = runScript('job/exampleJob.jenkins')
37 | script.execute()
38 |
39 | def value = 'value'
40 | assertTrue(helper.callStack.findAll { call ->
41 | call.methodName == 'println'
42 | }.any { call ->
43 | callArgsToString(call).contains(value)
44 | })
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/test/groovy/com/lesfurets/jenkins/TestFailingJobs.groovy:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) by Courtanet, All Rights Reserved.
3 | */
4 | package com.lesfurets.jenkins
5 |
6 | import org.codehaus.groovy.runtime.typehandling.GroovyCastException
7 | import org.junit.Before
8 | import org.junit.Ignore
9 | import org.junit.Test
10 |
11 | import com.lesfurets.jenkins.unit.cps.BasePipelineTestCPS;
12 |
13 | class TestFailingJobs extends BasePipelineTestCPS {
14 |
15 | @Override
16 | @Before
17 | void setUp() throws Exception {
18 | scriptRoots += 'src/test/jenkins'
19 | super.setUp()
20 | }
21 |
22 | @Test(expected = GroovyCastException)
23 | void should_fail_nonCpsCallingCps() throws Exception {
24 | def script = runScript("job/shouldFail/nonCpsCallingCps.jenkins")
25 | printCallStack()
26 | }
27 |
28 | /**
29 | * java.lang.UnsupportedOperationException: Calling public static java.util.List
30 | * org.codehaus.groovy.runtime.DefaultGroovyMethods.each(java.util.List,groovy.lang.Closure)
31 | * on a CPS-transformed closure is not yet supported (JENKINS-26481);
32 | * encapsulate in a @NonCPS method, or use Java-style loops
33 | */
34 | @Test(expected = UnsupportedOperationException)
35 | @Ignore
36 | void should_fail_forEach() throws Exception {
37 | def script = runScript("job/shouldFail/forEach.jenkins")
38 | printCallStack()
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/test/groovy/com/lesfurets/jenkins/TestHelperInitialization.groovy:
--------------------------------------------------------------------------------
1 | package com.lesfurets.jenkins
2 |
3 | import org.junit.Test
4 |
5 | import com.lesfurets.jenkins.unit.BasePipelineTest
6 |
7 | class TestHelperInitialization extends BasePipelineTest {
8 |
9 | @Test(expected = IllegalStateException)
10 | void non_initialized_helper() throws Exception {
11 | runScript('jobs/exampleJob.jenkins')
12 | }
13 |
14 | @Test(expected = NullPointerException)
15 | void non_initialized_gse() throws Exception {
16 | helper.loadScript('jobs/exampleJob.jenkins')
17 | }
18 |
19 | @Test
20 | void initialized_helper() throws Exception {
21 | scriptRoots += 'src/test/jenkins'
22 | super.setUp()
23 | helper.loadScript('job/exampleJob.jenkins')
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/test/groovy/com/lesfurets/jenkins/TestHelperSingleton.groovy:
--------------------------------------------------------------------------------
1 | package com.lesfurets.jenkins
2 |
3 | import com.lesfurets.jenkins.unit.BasePipelineTest
4 | import com.lesfurets.jenkins.unit.PipelineTestHelper
5 | import org.junit.Before
6 | import org.junit.BeforeClass
7 | import org.junit.Test
8 |
9 | import static com.lesfurets.jenkins.unit.global.lib.LibraryConfiguration.library
10 | import static com.lesfurets.jenkins.unit.global.lib.LocalSource.localSource
11 | import static org.assertj.core.api.Assertions.assertThat
12 |
13 | class TestHelperSingleton extends BasePipelineTest {
14 |
15 | static PipelineTestHelper HELPER = new PipelineTestHelper()
16 |
17 | TestHelperSingleton() {
18 | super(HELPER)
19 | }
20 |
21 | @Override
22 | @Before
23 | void setUp() throws Exception {
24 | scriptRoots += 'src/test/jenkins'
25 |
26 | String sharedLibs = this.class.getResource('/libs').getFile()
27 |
28 | def library = library().name('commons')
29 | .defaultVersion("master")
30 | .allowOverride(true)
31 | .implicit(true)
32 | .targetPath(sharedLibs)
33 | .retriever(localSource(sharedLibs))
34 | .build()
35 |
36 | HELPER.registerSharedLibrary(library)
37 |
38 | super.setUp()
39 | }
40 |
41 | @Test
42 | void staticHelperTestRunScript() throws Exception {
43 |
44 | assertThat(helper.isInitialized())
45 |
46 | assertThat(helper == HELPER)
47 |
48 | boolean exception = false
49 | try {
50 | def script = runScript("job/library/libraryJob.jenkins")
51 | script.execute()
52 | printCallStack()
53 | } catch (e) {
54 | e.printStackTrace()
55 | exception = true
56 | }
57 | assertThat(false).isEqualTo(exception)
58 | }
59 |
60 | }
61 |
--------------------------------------------------------------------------------
/src/test/groovy/com/lesfurets/jenkins/TestInlineScript.groovy:
--------------------------------------------------------------------------------
1 | package com.lesfurets.jenkins
2 |
3 | import static com.lesfurets.jenkins.unit.global.lib.LibraryConfiguration.library
4 | import static com.lesfurets.jenkins.unit.global.lib.LocalSource.localSource
5 |
6 | import org.junit.Before
7 | import org.junit.Test
8 |
9 | import com.lesfurets.jenkins.unit.BasePipelineTest
10 |
11 | class TestInlineScript extends BasePipelineTest {
12 |
13 | String sharedLibs = this.class.getResource('/libs').getFile()
14 |
15 | @Override
16 | @Before
17 | void setUp() throws Exception {
18 | scriptRoots += 'src/test/jenkins'
19 | super.setUp()
20 |
21 | def library = library()
22 | .name('commons')
23 | .defaultVersion('master')
24 | .allowOverride(true)
25 | .implicit(false)
26 | .targetPath(sharedLibs)
27 | .retriever(localSource(sharedLibs))
28 | .build()
29 |
30 | helper.registerSharedLibrary(library)
31 | }
32 |
33 | @Test
34 | void load_inline_script_with_simple_commands() {
35 | def script = loadInlineScript('''
36 | node {
37 | echo 'Test'
38 | }
39 | ''')
40 |
41 | script.run()
42 |
43 | printCallStack()
44 | assertJobStatusSuccess()
45 | }
46 |
47 | @Test
48 | void run_inline_script_with_simple_commands() {
49 | runInlineScript('''
50 | node {
51 | echo 'Test'
52 | }
53 | ''')
54 |
55 | printCallStack()
56 | assertJobStatusSuccess()
57 | }
58 |
59 | @Test
60 | void load_inline_script_with_shared_library() {
61 | def script = loadInlineScript('''
62 | @Library('commons') _
63 |
64 | node {
65 | sayHello()
66 | }
67 | ''')
68 |
69 | script.run()
70 |
71 | printCallStack()
72 | assertJobStatusSuccess()
73 | }
74 |
75 | @Test
76 | void run_inline_script_with_shared_library() {
77 | runInlineScript('''
78 | @Library('commons') _
79 |
80 | node {
81 | sayHello()
82 | }
83 | ''')
84 |
85 | printCallStack()
86 | assertJobStatusSuccess()
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/src/test/groovy/com/lesfurets/jenkins/TestInterceptingGCLLazyLoadLibClasses.groovy:
--------------------------------------------------------------------------------
1 | package com.lesfurets.jenkins
2 |
3 | import com.lesfurets.jenkins.unit.LibClassLoader
4 | import com.lesfurets.jenkins.unit.BasePipelineTest
5 |
6 | import org.junit.Before
7 | import org.junit.Test
8 |
9 | import static com.lesfurets.jenkins.unit.global.lib.LibraryConfiguration.library
10 | import static com.lesfurets.jenkins.unit.global.lib.ProjectSource.projectSource
11 |
12 | class TestInterceptingGCLLazyLoadLibClasses extends BasePipelineTest {
13 | @Override
14 | @Before
15 | void setUp() throws Exception {
16 | scriptRoots += 'src/test/jenkins'
17 | super.setUp()
18 | helper.libLoader.preloadLibraryClasses = false
19 | }
20 |
21 | /**
22 | * 1. Load two libraries--one dependent on the other--with implicity
23 | * 2. Create instance of library class and pass instance to library vars step
24 | * 3. That vars step in turn creates instance of another library class
25 | * and passes it to another library step
26 | * 4. Make sure interception of pipeline methods works propertly
27 | */
28 | @Test
29 | void test_cross_class_as_var_arg_implicit_lazy_load() throws Exception {
30 | //This does not factor much in the current test but does replicate the
31 | //use case in which the lazy load feature originated.
32 | helper.cloneArgsOnMethodCallRegistration = false
33 |
34 | //test_cross_class_as_var_arg_1 uses vars and classes in
35 | //test_cross_class_as_var_arg_2 so the latter has to be loaded first
36 | [
37 | "test_cross_class_as_var_arg_2",
38 | "test_cross_class_as_var_arg_1",
39 | ].each { libName ->
40 | final libDir = this.class.getResource("/libs/$libName").file
41 | final library = library().name(libName)
42 | .defaultVersion("master")
43 | .allowOverride(false)
44 | .implicit(true)
45 | .targetPath(libDir)
46 | .retriever(projectSource(libDir))
47 | .build()
48 | helper.registerSharedLibrary(library)
49 | }
50 |
51 | final pipeline = "test_var_with_lib_class_arg"
52 | runScript("job/library/cross_class_lazy_load/${pipeline}.jenkins")
53 | printCallStack()
54 | assertCallStackContains("""${pipeline}.monster1(org.test.Monster1""")
55 | assertCallStackContains("""monster1.monster2(org.test.extra.Monster2""")
56 | assertCallStackContains("""monster2.echo(Frankenstein's Monster all by itself is frightening)""")
57 | assertCallStackContains("""monster1.echo(Dracula and Frankenstein's Monster make quite a scary team)""")
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/src/test/groovy/com/lesfurets/jenkins/TestOneArgumentJob.groovy:
--------------------------------------------------------------------------------
1 | package com.lesfurets.jenkins
2 |
3 | import static com.lesfurets.jenkins.unit.global.lib.LibraryConfiguration.library
4 | import static com.lesfurets.jenkins.unit.global.lib.LocalSource.localSource
5 |
6 | import com.lesfurets.jenkins.unit.LibClassLoader
7 | import com.lesfurets.jenkins.unit.BaseRegressionTest
8 | import org.junit.Before
9 | import org.junit.Test
10 |
11 | import static org.junit.Assert.assertTrue
12 |
13 | class TestOneArgumentJob extends BaseRegressionTest {
14 |
15 | String sharedLibs = this.class.getResource('/libs').getFile()
16 |
17 | @Override
18 | @Before
19 | void setUp() throws Exception {
20 | scriptRoots += 'src/test/jenkins'
21 | super.setUp()
22 | binding.setVariable('scm', [branch: 'master'])
23 | }
24 |
25 | @Test
26 | void should_run_script_with_one_argument() {
27 | def library = library().name('commons')
28 | .defaultVersion("master")
29 | .allowOverride(true)
30 | .implicit(false)
31 | .targetPath(sharedLibs)
32 | .retriever(localSource(sharedLibs))
33 | .build()
34 | helper.registerSharedLibrary(library)
35 |
36 | // when:
37 | runScript("job/library/test_lib_call_with_null.jenkins")
38 |
39 | // then:
40 | assertJobStatusSuccess()
41 | testNonRegression("should_run_script_with_one_argument")
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/test/groovy/com/lesfurets/jenkins/TestParallelJob.groovy:
--------------------------------------------------------------------------------
1 | package com.lesfurets.jenkins
2 |
3 | import com.lesfurets.jenkins.unit.BasePipelineTest
4 | import org.junit.Before
5 | import org.junit.Test
6 |
7 | class TestParallelJob extends BasePipelineTest {
8 |
9 | @Override
10 | @Before
11 | void setUp() throws Exception {
12 | scriptRoots += 'src/test/jenkins'
13 | super.setUp()
14 | def scmBranch = "feature_test"
15 | binding.setVariable('scm', [
16 | $class : 'GitSCM',
17 | branches : [[name: scmBranch]],
18 | extensions : [],
19 | userRemoteConfigs : [[
20 | credentialsId: 'gitlab_git_ssh',
21 | url : 'github.com/lesfurets/JenkinsPipelineUnit.git'
22 | ]]
23 | ])
24 | }
25 |
26 | @Test
27 | void should_execute_parallel_with_errors() throws Exception {
28 | def script = runScript("job/parallelJob.jenkins")
29 | try{
30 | script.execute()
31 | } finally {
32 | printCallStack()
33 | }
34 | assertJobStatusFailure()
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/test/groovy/com/lesfurets/jenkins/TestParallelJobCPS.groovy:
--------------------------------------------------------------------------------
1 | package com.lesfurets.jenkins
2 |
3 | import org.junit.Before
4 | import org.junit.Test
5 |
6 | import com.lesfurets.jenkins.unit.cps.BasePipelineTestCPS
7 |
8 | class TestParallelJobCPS extends BasePipelineTestCPS {
9 |
10 | @Override
11 | @Before
12 | void setUp() throws Exception {
13 | scriptRoots += 'src/test/jenkins'
14 | super.setUp()
15 | def scmBranch = "feature_test"
16 | binding.setVariable('scm', [
17 | $class : 'GitSCM',
18 | branches : [[name: scmBranch]],
19 | extensions : [],
20 | userRemoteConfigs : [[
21 | credentialsId: 'gitlab_git_ssh',
22 | url : 'github.com/lesfurets/JenkinsPipelineUnit.git'
23 | ]]
24 | ])
25 | }
26 |
27 | @Test
28 | void should_execute_parallel_with_errors() throws Exception {
29 | def script = runScript("job/parallelJob.jenkins")
30 | try{
31 | script.execute()
32 | } finally {
33 | printCallStack()
34 | }
35 | assertJobStatusFailure()
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/test/groovy/com/lesfurets/jenkins/TestParametersJob.groovy:
--------------------------------------------------------------------------------
1 | package com.lesfurets.jenkins
2 |
3 | import com.lesfurets.jenkins.unit.BaseRegressionTest
4 | import org.junit.Before
5 | import org.junit.Test
6 |
7 | class TestParametersJob extends BaseRegressionTest {
8 |
9 | @Override
10 | @Before
11 | void setUp() throws Exception {
12 | scriptRoots += 'src/test/jenkins'
13 | super.setUp()
14 | }
15 |
16 | @Test
17 | void should_run_script_parameters() {
18 | // when:
19 | runScript("job/parameters.jenkins")
20 |
21 | // then:
22 | assertJobStatusSuccess()
23 | testNonRegression("parameters")
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/test/groovy/com/lesfurets/jenkins/TestRegisterOriginalMethodCallArgs.groovy:
--------------------------------------------------------------------------------
1 | package com.lesfurets.jenkins
2 |
3 | import org.junit.Before
4 | import org.junit.Test
5 |
6 | import com.lesfurets.jenkins.unit.BasePipelineTest
7 |
8 | import static org.junit.Assert.*
9 |
10 | class TestRegisterOriginalMethodCallArgs extends BasePipelineTest {
11 |
12 | @Override
13 | @Before
14 | void setUp() throws Exception {
15 | scriptRoots += "src/test/jenkins"
16 | super.setUp()
17 | }
18 |
19 | @Test
20 | void should_not_always_clone_args() {
21 | helper.cloneArgsOnMethodCallRegistration = false
22 |
23 | runScript("job/immutableMapArgs.jenkins")
24 |
25 | def arg = helper.callStack.find { call ->
26 | call.methodName == "writeFile"
27 | }.args.first()
28 |
29 | //Ensure that the arg is the original uncloned binding variable, and
30 | //that we can inspect it in detail using all the normal Map interfaces.
31 | assertTrue(arg.is(binding.pretendArgsFromFarUpstream))
32 |
33 | assertEquals(arg.getClass().simpleName, "UnmodifiableMap")
34 | assertEquals(arg.size(), 2)
35 |
36 | assertEquals(arg.file, "foo.txt")
37 | assertEquals(arg.text, "All bar, all the time")
38 | }
39 |
40 | @Test
41 | void should_usually_clone_args() {
42 | //By default the helper clones args on registering calls.
43 |
44 | runScript("job/immutableMapArgs.jenkins")
45 |
46 | def arg = helper.callStack.find { call ->
47 | call.methodName == "writeFile"
48 | }.args.first()
49 |
50 | //Ensure that the arg is not the original binding variable, and not
51 | //even the same type, because that variable was an uncloneable
52 | //UnmodifiableMap. The cloning logic turns uncloneables into Strings.
53 | assertFalse(arg.is(binding.pretendArgsFromFarUpstream))
54 |
55 | assertEquals(arg.getClass().simpleName, "String")
56 |
57 | assertTrue(arg.contains("file:foo.txt") || arg.contains("file=foo.txt"))
58 | assertTrue(arg.contains("text:All bar, all the time") || arg.contains("text=All bar, all the time"))
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/test/groovy/com/lesfurets/jenkins/TestRegression.groovy:
--------------------------------------------------------------------------------
1 | package com.lesfurets.jenkins
2 |
3 | import org.junit.Before
4 | import org.junit.Test
5 |
6 | import com.lesfurets.jenkins.unit.cps.BaseRegressionTestCPS
7 |
8 | class TestRegression extends BaseRegressionTestCPS {
9 |
10 | @Override
11 | @Before
12 | void setUp() throws Exception {
13 | scriptRoots += 'src/test/jenkins'
14 | super.setUp()
15 | def scmBranch = "feature_test"
16 | helper.registerAllowedMethod("sh", [Map.class], {c -> 'bcc19744'})
17 | binding.setVariable('scm', [
18 | $class : 'GitSCM',
19 | branches : [[name: scmBranch]],
20 | extensions : [],
21 | userRemoteConfigs : [[
22 | credentialsId: 'gitlab_git_ssh',
23 | url : 'github.com/lesfurets/JenkinsPipelineUnit.git'
24 | ]]
25 | ])
26 | }
27 |
28 | @Test
29 | void testNonReg() throws Exception {
30 | def script = runScript("job/exampleJob.jenkins")
31 | script.execute()
32 | super.testNonRegression("example")
33 | }
34 |
35 | }
36 |
--------------------------------------------------------------------------------
/src/test/groovy/com/lesfurets/jenkins/TestRegressionGlobalVar.groovy:
--------------------------------------------------------------------------------
1 | package com.lesfurets.jenkins
2 |
3 | import com.lesfurets.jenkins.unit.cps.BaseRegressionTestCPS
4 | import org.junit.Before
5 | import org.junit.Test
6 |
7 | class TestRegressionGlobalVar extends BaseRegressionTestCPS {
8 |
9 | @Override
10 | @Before
11 | void setUp() throws Exception {
12 | scriptRoots += 'src/test/jenkins'
13 | super.setUp()
14 | helper.registerAllowedMethod("doWithProperties", [TreeMap.class], null)
15 | }
16 |
17 | @Test
18 | void testGlobalVarRegression() throws Exception {
19 | runScript("job/globalVar.jenkins")
20 | super.testNonRegression("globalVar")
21 | }
22 |
23 | }
24 |
--------------------------------------------------------------------------------
/src/test/groovy/com/lesfurets/jenkins/TestSerialization.groovy:
--------------------------------------------------------------------------------
1 | package com.lesfurets.jenkins
2 |
3 | import org.junit.Before
4 | import org.junit.Test
5 |
6 | import com.lesfurets.jenkins.unit.BasePipelineTest
7 |
8 | class TestSerialization extends BasePipelineTest {
9 |
10 | @Override
11 | @Before
12 | void setUp() throws Exception {
13 | scriptRoots += 'src/test/jenkins'
14 | super.setUp()
15 | def scmBranch = "feature_test"
16 | binding.setVariable('scm', [
17 | $class : 'GitSCM',
18 | branches : [[name: scmBranch]],
19 | extensions : [],
20 | userRemoteConfigs : [[
21 | credentialsId: 'gitlab_git_ssh',
22 | url : 'github.com/lesfurets/JenkinsPipelineUnit.git'
23 | ]]
24 | ])
25 | }
26 |
27 | /**
28 | * This exception should have thrown an exception because of the bad usage of NonCPS method.
29 | * @throws Exception
30 | */
31 | @Test
32 | // (expected = Exception.class)
33 | void testException() throws Exception {
34 | def script = runScript('job/serialize.jenkins')
35 | try {
36 | script.execute()
37 | } catch (e) {
38 | throw e
39 | } finally {
40 | printCallStack()
41 | }
42 | }
43 |
44 | }
45 |
--------------------------------------------------------------------------------
/src/test/groovy/com/lesfurets/jenkins/TestSerializationCPS.groovy:
--------------------------------------------------------------------------------
1 | package com.lesfurets.jenkins
2 |
3 | import org.junit.Before
4 | import org.junit.Test
5 |
6 | import com.lesfurets.jenkins.unit.cps.BasePipelineTestCPS
7 |
8 | class TestSerializationCPS extends BasePipelineTestCPS {
9 |
10 |
11 |
12 | @Override
13 | @Before
14 | void setUp() throws Exception {
15 | scriptRoots += 'src/test/jenkins'
16 | super.setUp()
17 | def scmBranch = "feature_test"
18 | binding.setVariable('scm', [
19 | $class : 'GitSCM',
20 | branches : [[name: scmBranch]],
21 | extensions : [],
22 | userRemoteConfigs : [[
23 | credentialsId: 'gitlab_git_ssh',
24 | url : 'github.com/lesfurets/JenkinsPipelineUnit.git'
25 | ]]
26 | ])
27 | }
28 |
29 | @Test(expected = Exception.class)
30 | void testException() throws Exception {
31 | def script = loadScript('job/serialize.jenkins')
32 | try {
33 | script.execute()
34 | } catch (e) {
35 | throw e
36 | } finally {
37 | printCallStack()
38 | }
39 | }
40 |
41 | @Test
42 | void testSerialization() throws Exception {
43 | def script = runScript('job/serializeCPS.jenkins')
44 | try {
45 | script.execute()
46 | } catch (e) {
47 | throw e
48 | } finally {
49 | printCallStack()
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/test/groovy/com/lesfurets/jenkins/TestSharedLibraryAccessibleParams.groovy:
--------------------------------------------------------------------------------
1 | package com.lesfurets.jenkins
2 |
3 | import com.lesfurets.jenkins.unit.declarative.DeclarativePipelineTest
4 | import org.junit.Before
5 | import org.junit.Test
6 |
7 | import static com.lesfurets.jenkins.unit.global.lib.LibraryConfiguration.library
8 | import static com.lesfurets.jenkins.unit.global.lib.ProjectSource.projectSource
9 | import static org.assertj.core.api.Assertions.assertThat
10 |
11 | class TestSharedLibraryAccessibleParams extends DeclarativePipelineTest {
12 |
13 | private final String JOB_NAME = "params_not_accessible"
14 | private final String JOB_PATH = "job/library/${JOB_NAME}.jenkins"
15 | private final String LIB_DIR = this.class.getResource("/libs/$JOB_NAME").getFile()
16 | private final String BINDING_VAR = "testVar"
17 | private final String BINDING_VAL = "notBroken"
18 |
19 | @Override
20 | @Before
21 | void setUp() throws Exception {
22 | scriptRoots += 'src/test/jenkins'
23 | super.setUp()
24 | def library = library().name(JOB_NAME)
25 | .retriever(projectSource(LIB_DIR))
26 | .defaultVersion("master")
27 | .targetPath(LIB_DIR)
28 | .allowOverride(true)
29 | .implicit(false)
30 | .build()
31 | helper.registerSharedLibrary(library)
32 | }
33 |
34 | @Test
35 | void accessible_params_test() {
36 | run_test_with_bindings {assertJobStatusSuccess()}
37 | }
38 |
39 | @Test
40 | void change_binding_test() {
41 | run_test_with_bindings {assertThat(binding.getVariable(BINDING_VAR)).isNotEqualTo(BINDING_VAL)}
42 | }
43 |
44 | @Test(expected = MissingPropertyException.class)
45 | void not_accessible_params_test() {
46 | runScript(JOB_PATH)
47 | }
48 |
49 | private void run_test_with_bindings(Closure assertion) {
50 | binding.setVariable(BINDING_VAR, BINDING_VAL)
51 | runScript(JOB_PATH)
52 | assertion()
53 | }
54 |
55 | }
56 |
--------------------------------------------------------------------------------
/src/test/groovy/com/lesfurets/jenkins/TestSharedLibraryEnvVariable.groovy:
--------------------------------------------------------------------------------
1 | package com.lesfurets.jenkins
2 |
3 | import com.lesfurets.jenkins.unit.declarative.DeclarativePipelineTest
4 | import org.junit.Assert
5 | import org.junit.Before
6 | import org.junit.Test
7 |
8 | import static com.lesfurets.jenkins.unit.global.lib.LibraryConfiguration.library
9 | import static com.lesfurets.jenkins.unit.global.lib.ProjectSource.projectSource
10 |
11 | class TestSharedLibraryEnvVariable extends DeclarativePipelineTest {
12 |
13 | String sharedLibVars = this.class.getResource("/libs/env_var_not_defined").getFile()
14 |
15 | @Override
16 | @Before
17 | void setUp() throws Exception {
18 | scriptRoots += 'src/test/jenkins'
19 |
20 | super.setUp()
21 |
22 | def library = library().name("env_var_not_defined")
23 | .retriever(projectSource(sharedLibVars))
24 | .defaultVersion("master")
25 | .targetPath(sharedLibVars)
26 | .allowOverride(true)
27 | .implicit(false)
28 | .build()
29 | helper.registerSharedLibrary(library)
30 | }
31 |
32 | @Test
33 | void "test lib var not defined in env"() {
34 |
35 | runScript("job/library/test_lib_var_not_defined_in_env.jenkins")
36 |
37 | def prop1 = binding.env["prop1"]
38 | Assert.assertEquals("magic", prop1)
39 | }
40 |
41 | @Test
42 | void "test params not defined in env"() {
43 | runScript("job/library/test_params_not_defined_in_env.jenkins")
44 |
45 | def versionFromEnv = binding.env["VERSION"]
46 | def versionFromParams = binding.params["VERSION"]
47 | Assert.assertEquals(versionFromParams.toString(), versionFromEnv.toString())
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/test/groovy/com/lesfurets/jenkins/TestSharedLibraryWithLocalSourceRetriever.groovy:
--------------------------------------------------------------------------------
1 | package com.lesfurets.jenkins
2 |
3 | import com.lesfurets.jenkins.unit.LibClassLoader
4 |
5 | import static com.lesfurets.jenkins.unit.global.lib.LibraryConfiguration.library
6 | import static com.lesfurets.jenkins.unit.global.lib.LocalSource.localSource
7 | import static org.assertj.core.api.Assertions.assertThat
8 |
9 | import org.junit.Before
10 | import org.junit.Rule
11 | import org.junit.Test
12 | import org.junit.rules.TemporaryFolder
13 | import org.junit.runner.RunWith
14 | import org.junit.runners.Parameterized
15 | import org.junit.runners.Parameterized.Parameter
16 | import org.junit.runners.Parameterized.Parameters
17 |
18 | import com.lesfurets.jenkins.unit.BasePipelineTest
19 |
20 | @RunWith(Parameterized.class)
21 | class TestSharedLibrary extends BasePipelineTest {
22 |
23 | @Rule
24 | public TemporaryFolder folder = new TemporaryFolder()
25 |
26 | String sharedLibs = this.class.getResource('/libs').getFile()
27 |
28 | @Parameter(0)
29 | public String script
30 | @Parameter(1)
31 | public boolean allowOverride
32 | @Parameter(2)
33 | public boolean implicit
34 | @Parameter(3)
35 | public boolean expected
36 |
37 | @Override
38 | @Before
39 | void setUp() throws Exception {
40 | scriptRoots += 'src/test/jenkins'
41 | super.setUp()
42 | binding.setVariable('scm', [branch: 'master'])
43 | }
44 |
45 | @Parameters(name = "Test {0} allowOverride:{1} implicit:{2} expected:{3}")
46 | static Collection