From 90ba73b86d5d938d236169f4aa495f929e4af573 Mon Sep 17 00:00:00 2001 From: Jan Stastny Date: Wed, 6 Dec 2023 11:23:06 +0100 Subject: [PATCH] kie-issues#744: Move jenkins pipeline shared libraries (#1131) * Move jenkins pipeline library * tweak GHA PR check * tweak pom.xml to allow running from different dir * remove tmp files --------- Co-authored-by: jstastny-cz --- ...pipeline-shared-library-maven-tests-PR.yml | 26 + jenkins-pipeline-shared-libraries/README.md | 20 + jenkins-pipeline-shared-libraries/pom.xml | 1235 ++++++++++++ .../src/org/kie/jenkins/MavenCommand.groovy | 208 ++ .../kie/jenkins/MavenSettingsConfig.groovy | 25 + .../jenkins/MavenSettingsConfigBuilder.groovy | 67 + .../kie/jenkins/MavenSettingsService.groovy | 44 + .../org/kie/jenkins/MavenSettingsUtils.groovy | 26 + .../org/kie/jenkins/MavenStagingHelper.groovy | 146 ++ .../src/org/kie/jenkins/Utils.groovy | 25 + .../kie/jenkins/shell/AbstractShell.groovy | 116 ++ .../org/kie/jenkins/shell/LocalShell.groovy | 42 + .../src/org/kie/jenkins/shell/SSHShell.groovy | 42 + .../src/org/kie/jenkins/shell/Shell.groovy | 33 + .../installation/AbstractInstallation.groovy | 43 + .../shell/installation/CRCInstallation.groovy | 32 + .../installation/GolangInstallation.groovy | 54 + .../installation/GroovyInstallation.groovy | 31 + .../shell/installation/Installation.groovy | 14 + .../OpenshiftClientInstallation.groovy | 33 + .../resources/build-chain-action-single.yml | 39 + .../test/resources/build-chain-action.yml | 47 + .../test/resources/forked_projects.json | 202 ++ .../test/resources/forked_projects_empty.json | 3 + .../forked_projects_missing_owner.json | 3 + .../test/resources/forked_projects_page3.json | 1002 ++++++++++ .../test/resources/logback-test.xml | 13 + .../test/resources/logback.xml | 13 + .../project-branches-mapping.properties | 2 + .../test/resources/pull_request_empty.json | 3 + .../resources/pull_request_not_empty.json | 1023 ++++++++++ .../test/vars/AbstractShellSpec.groovy | 172 ++ .../test/vars/BuildChainSpec.groovy | 37 + .../test/vars/CloudSpec.groovy | 954 +++++++++ .../test/vars/CloudSpec.groovy.orig | 980 +++++++++ .../test/vars/GithubScmSpec.groovy | 1763 +++++++++++++++++ .../test/vars/LocalShellSpec .groovy | 101 + .../test/vars/MailerSpec.groovy | 233 +++ .../test/vars/MavenCommandSpec.groovy | 472 +++++ .../test/vars/MavenSettingsServiceSpec.groovy | 208 ++ .../test/vars/MavenSpec.groovy | 370 ++++ .../test/vars/MavenStagingHelperSpec.groovy | 98 + .../test/vars/PullRequestSpec.groovy | 35 + .../test/vars/SSHShellSpec.groovy | 128 ++ .../test/vars/UtilSpec.groovy | 1609 +++++++++++++++ .../vars/buildChain.groovy | 15 + .../vars/cloud.groovy | 331 ++++ .../vars/githubscm.groovy | 670 +++++++ .../vars/mailer.groovy | 68 + .../vars/maven.groovy | 196 ++ .../vars/pullrequest.groovy | 33 + .../vars/util.groovy | 549 +++++ 52 files changed, 13634 insertions(+) create mode 100644 .github/workflows/jenkins-pipeline-shared-library-maven-tests-PR.yml create mode 100755 jenkins-pipeline-shared-libraries/README.md create mode 100644 jenkins-pipeline-shared-libraries/pom.xml create mode 100644 jenkins-pipeline-shared-libraries/src/org/kie/jenkins/MavenCommand.groovy create mode 100644 jenkins-pipeline-shared-libraries/src/org/kie/jenkins/MavenSettingsConfig.groovy create mode 100644 jenkins-pipeline-shared-libraries/src/org/kie/jenkins/MavenSettingsConfigBuilder.groovy create mode 100644 jenkins-pipeline-shared-libraries/src/org/kie/jenkins/MavenSettingsService.groovy create mode 100644 jenkins-pipeline-shared-libraries/src/org/kie/jenkins/MavenSettingsUtils.groovy create mode 100644 jenkins-pipeline-shared-libraries/src/org/kie/jenkins/MavenStagingHelper.groovy create mode 100644 jenkins-pipeline-shared-libraries/src/org/kie/jenkins/Utils.groovy create mode 100644 jenkins-pipeline-shared-libraries/src/org/kie/jenkins/shell/AbstractShell.groovy create mode 100644 jenkins-pipeline-shared-libraries/src/org/kie/jenkins/shell/LocalShell.groovy create mode 100644 jenkins-pipeline-shared-libraries/src/org/kie/jenkins/shell/SSHShell.groovy create mode 100644 jenkins-pipeline-shared-libraries/src/org/kie/jenkins/shell/Shell.groovy create mode 100644 jenkins-pipeline-shared-libraries/src/org/kie/jenkins/shell/installation/AbstractInstallation.groovy create mode 100644 jenkins-pipeline-shared-libraries/src/org/kie/jenkins/shell/installation/CRCInstallation.groovy create mode 100644 jenkins-pipeline-shared-libraries/src/org/kie/jenkins/shell/installation/GolangInstallation.groovy create mode 100644 jenkins-pipeline-shared-libraries/src/org/kie/jenkins/shell/installation/GroovyInstallation.groovy create mode 100644 jenkins-pipeline-shared-libraries/src/org/kie/jenkins/shell/installation/Installation.groovy create mode 100644 jenkins-pipeline-shared-libraries/src/org/kie/jenkins/shell/installation/OpenshiftClientInstallation.groovy create mode 100644 jenkins-pipeline-shared-libraries/test/resources/build-chain-action-single.yml create mode 100644 jenkins-pipeline-shared-libraries/test/resources/build-chain-action.yml create mode 100644 jenkins-pipeline-shared-libraries/test/resources/forked_projects.json create mode 100644 jenkins-pipeline-shared-libraries/test/resources/forked_projects_empty.json create mode 100644 jenkins-pipeline-shared-libraries/test/resources/forked_projects_missing_owner.json create mode 100644 jenkins-pipeline-shared-libraries/test/resources/forked_projects_page3.json create mode 100644 jenkins-pipeline-shared-libraries/test/resources/logback-test.xml create mode 100644 jenkins-pipeline-shared-libraries/test/resources/logback.xml create mode 100644 jenkins-pipeline-shared-libraries/test/resources/project-branches-mapping.properties create mode 100644 jenkins-pipeline-shared-libraries/test/resources/pull_request_empty.json create mode 100644 jenkins-pipeline-shared-libraries/test/resources/pull_request_not_empty.json create mode 100644 jenkins-pipeline-shared-libraries/test/vars/AbstractShellSpec.groovy create mode 100644 jenkins-pipeline-shared-libraries/test/vars/BuildChainSpec.groovy create mode 100644 jenkins-pipeline-shared-libraries/test/vars/CloudSpec.groovy create mode 100644 jenkins-pipeline-shared-libraries/test/vars/CloudSpec.groovy.orig create mode 100644 jenkins-pipeline-shared-libraries/test/vars/GithubScmSpec.groovy create mode 100644 jenkins-pipeline-shared-libraries/test/vars/LocalShellSpec .groovy create mode 100644 jenkins-pipeline-shared-libraries/test/vars/MailerSpec.groovy create mode 100644 jenkins-pipeline-shared-libraries/test/vars/MavenCommandSpec.groovy create mode 100644 jenkins-pipeline-shared-libraries/test/vars/MavenSettingsServiceSpec.groovy create mode 100644 jenkins-pipeline-shared-libraries/test/vars/MavenSpec.groovy create mode 100644 jenkins-pipeline-shared-libraries/test/vars/MavenStagingHelperSpec.groovy create mode 100644 jenkins-pipeline-shared-libraries/test/vars/PullRequestSpec.groovy create mode 100644 jenkins-pipeline-shared-libraries/test/vars/SSHShellSpec.groovy create mode 100644 jenkins-pipeline-shared-libraries/test/vars/UtilSpec.groovy create mode 100644 jenkins-pipeline-shared-libraries/vars/buildChain.groovy create mode 100644 jenkins-pipeline-shared-libraries/vars/cloud.groovy create mode 100644 jenkins-pipeline-shared-libraries/vars/githubscm.groovy create mode 100644 jenkins-pipeline-shared-libraries/vars/mailer.groovy create mode 100644 jenkins-pipeline-shared-libraries/vars/maven.groovy create mode 100644 jenkins-pipeline-shared-libraries/vars/pullrequest.groovy create mode 100644 jenkins-pipeline-shared-libraries/vars/util.groovy diff --git a/.github/workflows/jenkins-pipeline-shared-library-maven-tests-PR.yml b/.github/workflows/jenkins-pipeline-shared-library-maven-tests-PR.yml new file mode 100644 index 000000000..54739167a --- /dev/null +++ b/.github/workflows/jenkins-pipeline-shared-library-maven-tests-PR.yml @@ -0,0 +1,26 @@ +# This workflow will build a Java project with Maven +# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven + +name: Jenkins Pipeline Shared Libraries Maven Tests + +on: + pull_request: + branches: [ main ] + paths: + - 'jenkins-pipeline-shared-libraries/**' + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + - name: Set up JDK 1.8 + uses: actions/setup-java@v3 + with: + java-version: 8 + distribution: temurin + cache: maven + - name: Build with Maven + run: mvn -B test --file jenkins-pipeline-shared-libraries/pom.xml diff --git a/jenkins-pipeline-shared-libraries/README.md b/jenkins-pipeline-shared-libraries/README.md new file mode 100755 index 000000000..09df0075e --- /dev/null +++ b/jenkins-pipeline-shared-libraries/README.md @@ -0,0 +1,20 @@ +# Jenkins Pipeline Shared Libraries + +This repository contains shared libraries used across different KIE Jenkins pipeline scripts. + +# Development +Your scripts should be located in `/vars` folder. + +## How to cover with unit tests +Once your groovy methods/script files are implemented you should cover them with Spock tests. +Those tests are located in `/test/vars` folder. Remember you should add Jenkins plugins used by the script as a dependency in the `pom.xml` file in case it is not present, like + +```xml + + + org.jenkins-ci.plugins + config-file-provider + 3.6.2 + test + +``` diff --git a/jenkins-pipeline-shared-libraries/pom.xml b/jenkins-pipeline-shared-libraries/pom.xml new file mode 100644 index 000000000..55d6b4133 --- /dev/null +++ b/jenkins-pipeline-shared-libraries/pom.xml @@ -0,0 +1,1235 @@ + + + 4.0.0 + org.kie + jenkins-pipeline-shared-libraries + jar + 1.0.1 + Jenkins Pipeline Shared Libraries Testing Project + + + + jenkins-releases + Jenkins Releases + https://repo.jenkins-ci.org/releases + + + + repo.jenkins-ci.org + Jenkins Public + https://repo.jenkins-ci.org/public + + + + + 1.8 + 1.8 + + 2.4.11 + 1.6.1 + 29.0-jre + + 2.0.0 + 2.150.2 + 3.1.0 + 2.72 + 2.18 + + 4.13.1 + 1.28 + 2.22.0 + + logback-test.xml + ${project.build.directory}/log + ERROR + 1.2.3 + 1.7.25 + 1.9.1 + + + + + + com.google.guava + guava + ${google.guava.version} + + + + + + com.homeaway.devtools.jenkins + jenkins-spock + ${jenkins-spock.version} + test + + + ch.qos.logback + logback-core + ${log.logback.version} + runtime + + + ch.qos.logback + logback-classic + ${log.logback.version} + runtime + + + junit + junit + ${junit.version} + test + + + org.jenkins-ci.main + jenkins-core + ${jenkins.version} + + + org.jenkins-ci.plugins.workflow + workflow-basic-steps + ${jenkins.workflow.basic.steps.version} + test + + + org.jenkins-ci.plugins.workflow + workflow-cps + ${jenkins.workflow.cps.version} + test + + + + javax.servlet + javax.servlet-api + ${jenkins.servlet.version} + test + + + org.jenkins-ci.plugins + junit + ${junit.plugin.version} + + + org.codehaus.groovy + groovy-all + ${groovy.core.version} + + + org.slf4j + jcl-over-slf4j + ${log.slf4j.version} + runtime + + + org.slf4j + log4j-over-slf4j + ${log.slf4j.version} + runtime + + + + org.yaml + snakeyaml + 1.26 + + + + + + org.jenkins-ci.plugins.workflow + workflow-durable-task-step + 1.3 + test + + + + org.jenkins-ci.plugins + pipeline-stage-step + 2.3 + test + + + + org.jenkins-ci.plugins + git + 3.0.0 + test + + + + org.jenkins-ci.plugins + config-file-provider + 3.6.2 + test + + + + org.jenkins-ci.plugins + credentials-binding + 1.19 + test + + + + org.jenkins-ci.plugins.workflow + workflow-step-api + 2.21 + + + + org.jenkins-ci.plugins + pipeline-utility-steps + 2.3.0 + test + + + + org.jenkins-ci.plugins + email-ext + 2.66 + test + + + + org.jenkins-ci.plugins.workflow + workflow-multibranch + 2.21 + test + + + + org.jenkins-ci.plugins + github-branch-source + 2.5.4 + + + + org.jenkins-ci.plugins + github-api + 1.95 + test + + + org.eclipse.hudson.plugins + git + 3.0.1 + test + + + + com.coravy.hudson.plugins.github + github + 1.29.4 + test + + + + org.jenkins-ci.plugins.workflow + workflow-cps-global-lib + 2.14 + test + + + org.jenkins-ci.plugins + groovy + 2.2 + test + + + org.jenkins-ci.plugins + adaptive-disconnector + 0.2 + test + + + org.jenkins-ci.plugins + ansicolor + 0.6.2 + test + + + org.jenkins-ci.plugins + ant + 1.9 + test + + + org.jenkins-ci.plugins + apache-httpcomponents-client-4-api + 4.5.10-2.0 + test + + + org.jenkins-ci.plugins + authentication-tokens + 1.3 + test + + + org.jenkins-ci.plugins + authorize-project + 1.3.0 + test + + + org.jenkins-ci.plugins + blueocean-autofavorite + 1.2.4 + test + + + org.jenkins-ci.plugins + cloudbees-bitbucket-branch-source + 2.4.5 + test + + + org.jenkins-ci.plugins + bouncycastle-api + 2.17 + test + + + org.jenkins-ci.plugins + branch-api + 2.5.3 + test + + + com.cloudbees.plugins + build-flow-plugin + 0.20 + test + + + org.jenkins-ci.plugins + build-name-setter + 2.0.1 + test + + + org.jenkins-ci.plugins + build-timeout + 1.19 + test + + + org.jenkins-ci.plugins + build-with-parameters + 1.4 + test + + + org.jenkins-ci.plugins + built-on-column + 1.1 + test + + + org.jvnet.hudson.plugins + checkstyle + 4.0.0 + test + + + org.jenkins-ci.plugins + cloud-stats + 0.25 + test + + + org.jenkins-ci.plugins + command-launcher + 1.3 + test + + + org.jenkins-ci.plugins + compress-buildlog + 1.2 + test + + + jenkins.ci.plugins.computerqueue + computer-queue-plugin + 1.4 + test + + + org.jenkins-ci.plugins + conditional-buildstep + 1.3.6 + test + + + org.jenkins-ci.plugins + copyartifact + 1.42.1 + test + + + org.jenkins-ci.plugins + copy-project-link + 1.5 + test + + + org.jenkins-ci.plugins + credentials + 1.19 + test + + + org.jenkins-ci.plugins + cvs + 2.14 + test + + + org.jenkins-ci.plugins + description-setter + 1.10 + test + + + org.jenkins-ci.plugins + display-url-api + 2.3.2 + test + + + org.jenkins-ci.plugins + blueocean-display-url + 2.3.0 + test + + + org.jenkins-ci.plugins + docker-commons + 1.15 + test + + + org.jenkins-ci.plugins + docker-workflow + 1.18 + test + + + org.jenkins-ci.plugins + downstream-ext + 1.8 + test + + + org.jenkins-ci.plugins + dtkit-api + 2.1.1-1 + test + + + org.jenkins-ci.plugins + durable-task + 1.30 + test + + + org.jenkins-ci.plugins + envinject-api + 1.6 + test + + + org.jenkins-ci.plugins + envinject + 2.2.0 + test + + + org.jenkins-ci.plugins + environment-labels-setter + 1.1 + test + + + hudson.plugins.excludeMatrixParent + excludeMatrixParent + 1.1 + test + + + org.jenkins-ci.plugins + export-params + 1.9 + test + + + org.jvnet.hudson.plugins + extended-read-permission + 2.0 + test + + + org.jenkins-ci.plugins + external-monitor-job + 1.7 + test + + + org.jenkins-ci.plugins + fail-the-build-plugin + 1.0 + test + + + org.jvnet.hudson.plugins + favorite + 2.3.2 + test + + + org.jvnet.hudson.plugins + findbugs + 5.0.0 + test + + + org.jenkins-ci.plugins + cloudbees-folder + 6.9 + test + + + com.sonyericsson.hudson.plugins.gerrit + gerrit-trigger + 2.29.0 + test + + + org.jenkins-ci.plugins + git-client + 3.0.0 + test + + + org.jenkins-ci.plugins + git-server + 1.7 + test + + + org.jenkins-ci.plugins + github-pr-comment-build + 2.1 + test + + + org.jenkins-ci.plugins + gitlab-plugin + 1.5.12 + test + + + org.jenkins-ci.plugins + greenballs + 1.15 + test + + + io.jenkins.plugins + h2-api + 1.4.199 + test + + + org.jenkins-ci.plugins + handy-uri-templates-2-api + 2.1.7-1.0 + test + + + org.jenkins-ci.plugins + hidden-parameter + 0.0.4 + test + + + org.jenkins-ci.plugins + htmlpublisher + 1.18 + test + + + org.jenkins-ci.plugins + publish-over + 0.22 + test + + + org.jvnet.hudson.plugins + instant-messaging + 1.38 + test + + + org.jvnet.hudson.plugins + ircbot + 2.31 + test + + + org.jenkins-ci.plugins + jackson2-api + 2.9.9.1 + test + + + org.jenkins-ci.plugins + tmpcleaner + 1.3 + test + + + org.jenkins-ci.plugins + javadoc + 1.5 + test + + + org.jenkins-ci.ui + ace-editor + 1.1 + test + + + org.jenkins-ci.ui + handlebars + 1.1.1 + test + + + org.jenkins-ci.ui + jquery-detached + 1.2.1 + test + + + org.jenkins-ci.ui + momentjs + 1.1.1 + test + + + org.jenkins-ci.plugins + jira + 3.0.8 + test + + + org.jenkins-ci.plugins + jms-messaging + 1.1.18 + test + + + com.synopsys.jenkinsci + ownership + 0.12.1 + test + + + org.jenkins-ci.plugins + jobConfigHistory + 2.23 + test + + + org.jenkins-ci.plugins + job-dsl + 1.76 + test + + + org.jenkins-ci.plugins + job-import-plugin + 3.2 + test + + + org.jenkins-ci.plugins + job-parameter-summary + 0.5 + test + + + jp.ikedam.jenkins.plugins + jobcopy-builder + 1.4.0 + test + + + org.jenkins-ci.plugins + jquery + 1.12.4-1 + test + + + org.jenkins-ci.plugins + jquery-ui + 1.0.2 + test + + + org.jenkins-ci.plugins + jsch + 0.1.55.1 + test + + + com.sonymobile.jenkins.plugins.kerberos-sso + kerberos-sso + 1.5 + test + + + org.jenkins-ci.plugins + ldap + 1.20 + test + + + org.6wind.jenkins + lockable-resources + 2.5 + test + + + org.jenkins-ci.plugins + mailer + 1.29 + test + + + org.jenkins-ci.plugins + mapdb-api + 1.0.9.0 + test + + + org.jenkins-ci.plugins + mask-passwords + 2.12.0 + test + + + org.jenkins-ci.plugins + matrix-auth + 2.4.2 + test + + + org.jenkins-ci.plugins + matrix-project + 1.14 + test + + + net.praqma + matrix-reloaded + 1.1.3 + test + + + org.jenkins-ci.main + maven-plugin + 3.4 + test + + + org.jenkins-ci.plugins + mercurial + 2.7 + test + + + org.jenkins-ci.plugins + jenkins-multijob-plugin + 1.32 + test + + + org.jenkins-ci.plugins + multiple-scms + 0.6 + test + + + org.jenkins-ci.plugins + naginator + 1.18 + test + + + org.jenkins-ci.plugins + nodelabelparameter + 1.7.2 + test + + + org.jenkins-ci.plugins + nodejs + 1.3.3 + test + + + org.jenkins-ci.plugins + openstack-cloud + 2.51 + test + + + org.jenkins-ci.plugins + jdk-tool + 1.3 + test + + + org.jenkins-ci.plugins + antisamy-markup-formatter + 1.5 + test + + + org.jenkins-ci.plugins + pam-auth + 1.5.1 + test + + + org.jenkins-ci.plugins + parameterized-trigger + 2.35.2 + test + + + + + + + + + + + + + + + + + org.jenkins-ci.plugins + pipeline-graph-analysis + 1.10 + test + + + org.jenkins-ci.plugins + pipeline-maven + 3.8.0 + test + + + org.jenkins-ci.plugins.workflow + workflow-api + 2.37 + test + + + org.jenkins-ci.plugins + pipeline-build-step + 2.9 + test + + + org.jenkinsci.plugins + pipeline-model-definition + 1.3.9 + test + + + org.jenkinsci.plugins + pipeline-model-api + 1.1.1 + test + + + org.jenkinsci.plugins + pipeline-model-extensions + 1.3.9 + test + + + org.jenkins-ci.plugins + pipeline-input-step + 2.10 + test + + + org.jenkins-ci.plugins.pipeline-stage-view + pipeline-stage-view + 2.11 + test + + + org.jenkins-ci.plugins.workflow + workflow-scm-step + 2.9 + test + + + org.jenkinsci.plugins + pipeline-stage-tags-metadata + 1.3.9 + test + + + org.jenkins-ci.plugins.workflow + workflow-support + 3.3 + test + + + org.jenkins-ci.plugins + plain-credentials + 1.5 + test + + + org.jenkins-ci.plugins + plot + 2.1.1 + test + + + org.jenkins-ci.plugins + postbuildscript + 2.8.1 + test + + + org.jenkins-ci.plugins + PrioritySorter + 3.6.0 + test + + + org.jenkins-ci.plugins + proc-cleaner-plugin + 2.2 + test + + + org.jenkins-ci.plugins + project-description-setter + 1.2 + test + + + org.jenkins-ci.plugins + pubsub-light + 1.12 + test + + + org.jenkins-ci.plugins + publish-over-ssh + 1.20.1 + test + + + org.jenkins-ci.plugins + read-only-configurations + 1.10 + test + + + com.sonyericsson.hudson.plugins.rebuild + rebuild + 1.31 + test + + + org.jenkins-ci.plugins + resource-disposer + 0.14 + test + + + org.jenkins-ci.plugins + role-strategy + 2.13 + test + + + org.jenkins-ci.plugins + run-condition + 1.2 + test + + + org.jenkins-ci.plugins + saferestart + 0.3 + test + + + org.jenkins-ci.plugins + scm-api + 2.6.3 + test + + + org.jenkins-ci.plugins + script-security + 1.68 + test + + + org.jenkins-ci.plugins + sse-gateway + 1.18 + test + + + org.jenkins-ci.plugins + short-workspace-path + 0.3 + test + + + org.jenkins-ci.plugins + simple-theme-plugin + 0.5.1 + test + + + org.jenkins-ci.plugins + ssh-agent + 1.17 + test + + + org.jenkins-ci.plugins + ssh-credentials + 1.17.3 + test + + + org.jenkins-ci.plugins + ssh-slaves + 1.30.2 + test + + + org.jvnet.hudson.plugins + analysis-core + 1.96 + test + + + org.jenkins-ci.plugins + structs + 1.20 + test + + + org.jenkins-ci.plugins + subversion + 2.12.2 + test + + + de.esailors.jenkins + test-stability + 2.3 + test + + + org.jvnet.hudson.plugins + thinBackup + 1.9 + test + + + org.jenkins-ci.plugins + throttle-concurrents + 2.0.1 + test + + + org.jenkins-ci.plugins + timestamper + 1.9 + test + + + org.jenkins-ci.plugins + token-macro + 2.8 + test + + + org.jenkins-ci.plugins + toolenv + 1.1 + test + + + org.jenkins-ci.plugins + build-user-vars-plugin + 1.5 + test + + + org.jenkins-ci.plugins + variant + 1.3 + test + + + org.jenkins-ci.plugins + windows-slaves + 1.4 + test + + + org.jenkins-ci.plugins + ws-cleanup + 0.37 + test + + + org.jenkins-ci.plugins + xunit + 2.3.5 + test + + + org.jenkins-ci.plugins + xvfb + 1.1.3 + test + + + org.jenkins-ci.plugins + xvnc + 1.24 + test + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + ${surefire.pluginVersion} + + + default-test + + test + + + 1 + + ${test.pattern} + + false + + ${test.loglevel} + Stdout + ${test.loglevel} + ${logdir} + + + + + + + org.codehaus.gmavenplus + gmavenplus-plugin + ${groovy.gmaven.pluginVersion} + + + groovy + + addSources + addTestSources + generateStubs + generateTestStubs + compile + compileTests + removeStubs + removeTestStubs + + + config.groovy + + + ${project.basedir}/vars + + **/*.groovy + + + + ${project.basedir}/src + + **/*.groovy + + + + + + ${project.basedir}/test + + **/*.groovy + + + + + + + + + + + + org.codehaus.gmavenplus + gmavenplus-plugin + + + org.apache.maven.plugins + maven-surefire-plugin + + + ${project.basedir}/vars + ${project.basedir}/test/vars + + + + vars/**/*.groovy + src/**/*.groovy + + ${project.basedir} + + + ${project.basedir}/test/resources + + + ${project.basedir}/resources + + + + diff --git a/jenkins-pipeline-shared-libraries/src/org/kie/jenkins/MavenCommand.groovy b/jenkins-pipeline-shared-libraries/src/org/kie/jenkins/MavenCommand.groovy new file mode 100644 index 000000000..3fbf9efb2 --- /dev/null +++ b/jenkins-pipeline-shared-libraries/src/org/kie/jenkins/MavenCommand.groovy @@ -0,0 +1,208 @@ +package org.kie.jenkins + +class MavenCommand { + + def steps + + String directory = '' + boolean mavenWrapper = false + + MavenSettingsConfigBuilder settingsConfigBuilder + + List mavenOptions = [] + Map properties = [:] + String logFileName = '' + List profiles = [] + + boolean returnStdout = false + boolean printSettings = false + + MavenCommand(steps) { + this.steps = steps + settingsConfigBuilder = new MavenSettingsConfigBuilder() + } + + MavenCommand(steps, List defaultOpts) { + this(steps) + this.mavenOptions.addAll(defaultOpts) + } + + def run(String goals) { + String cmd = getFullRunCommand(goals) + if (directory) { + steps.dir(directory) { + return runCommand(cmd) + } + } else { + return runCommand(cmd) + } + } + + String getFullRunCommand(String goals) { + def cmdBuilder = new StringBuilder(this.mavenWrapper ? './mnw' : 'mvn') + cmdBuilder.append(' -B') + + // Retrieve settings file from id if given + String settingsFile = new MavenSettingsService(this.steps, this.settingsConfigBuilder.build()).createSettingsFile() + if (settingsFile) { + if (this.printSettings) { + steps.sh "cat ${settingsFile}" + } + cmdBuilder.append(" -s ${settingsFile}") + } + + if (this.mavenOptions.size()) { + cmdBuilder.append(' ').append(this.mavenOptions.join(' ')) + } + cmdBuilder.append(' ').append(goals) + if (this.profiles.size()) { + cmdBuilder.append(' -P').append(this.profiles.join(',')) + } + if (this.properties.size()) { + cmdBuilder.append(' ').append(this.properties.collect { it.value != '' ? "-D${it.key}=${it.value}" : "-D${it.key}" }.join(' ')) + } + if (this.logFileName) { + cmdBuilder.append(" | tee \$WORKSPACE/${this.logFileName} ; test \${PIPESTATUS[0]} -eq 0") + } + return cmdBuilder.toString() + } + + private def runCommand(String cmd) { + return steps.sh(script: cmd, returnStdout: this.returnStdout) + } + + MavenCommand inDirectory(String directory) { + this.directory = directory + return this + } + + MavenCommand useMavenWrapper(boolean mavenWrapper = true) { + this.mavenWrapper = mavenWrapper + return this + } + + /** + * Overwrites all current settings config done. + */ + MavenCommand withSettingsConfigBuilder(MavenSettingsConfigBuilder settingsConfigBuilder) { + this.settingsConfigBuilder = settingsConfigBuilder + return this + } + + /** + * IF set, override `withSettingsXmlFile` + **/ + MavenCommand withSettingsXmlId(String settingsXmlId) { + settingsConfigBuilder.settingsXmlConfigFileId(settingsXmlId) + return this + } + + MavenCommand withSettingsXmlFile(String settingsXmlPath) { + assert settingsXmlPath: 'Trying to set an empty settings xml path' + settingsConfigBuilder.settingsXmlPath(settingsXmlPath) + return this + } + + MavenCommand withDependencyRepositoryInSettings(String repoId, String repoUrl) { + settingsConfigBuilder.dependenciesRepositoriesInSettings([(repoId) : repoUrl]) + withProperty('enforcer.skip', true) + return this + } + + MavenCommand withDependencyRepositoriesInSettings(Map repositories = [:]) { + settingsConfigBuilder.dependenciesRepositoriesInSettings(repositories) + withProperty('enforcer.skip', true) + return this + } + + MavenCommand withMirrorDisabledForRepoInSettings(String repoId) { + settingsConfigBuilder.disabledMirrorRepoInSettings([repoId] as Set) + return this + } + + MavenCommand withSnapshotsDisabledInSettings(boolean disabled = true) { + settingsConfigBuilder.disableSnapshotsInSettings(disabled) + return this + } + + MavenCommand withOptions(List opts) { + this.mavenOptions.addAll(opts) + return this + } + + MavenCommand skipTests(boolean skipTests=true) { + return withProperty('skipTests', skipTests) + } + + MavenCommand withProfiles(List profiles) { + this.profiles.addAll(profiles) + return this + } + + MavenCommand withProperty(String key, Object value = '') { + this.properties.put(key, value) + return this + } + + MavenCommand withProperties(Properties properties) { + withPropertyMap(properties ?: [:]) + return this + } + + MavenCommand withPropertyMap(Map properties) { + this.properties.putAll(properties) + return this + } + + MavenCommand withLogFileName(String logFileName) { + this.logFileName = logFileName + return this + } + + MavenCommand withDeployRepository(String deployRepository) { + assert deployRepository: 'Trying to add an empty deploy repository' + withProperty('altDeploymentRepository', "runtimes-artifacts::default::${deployRepository}") + withProperty('enforcer.skip', true) + return this + } + + MavenCommand withLocalDeployFolder(String localDeployFolder) { + assert localDeployFolder: 'Trying to add an empty local deploy folder' + withProperty('altDeploymentRepository', "local::default::file://${localDeployFolder}") + return this + } + + MavenCommand withServerInSettings(String serverId, String username, String password) { + settingsConfigBuilder.servers([ [ id: serverId, username: username, password: password ] ]) + return this + } + + MavenCommand clone() { + def newCmd = new MavenCommand(this.steps) + .withOptions(this.mavenOptions) + .withPropertyMap(this.properties) + .withProfiles(this.profiles) + .withSettingsConfigBuilder(this.settingsConfigBuilder.clone()) + if (this.logFileName) { + newCmd.withLogFileName(this.logFileName) + } + if (this.directory) { + newCmd.inDirectory(this.directory) + } + if (this.returnStdout) { + newCmd.returnOutput() + } + return newCmd + } + + MavenCommand returnOutput(boolean returnStdout = true) { + this.returnStdout = returnStdout + return this + } + + MavenCommand printSettings(boolean printSettings = true) { + this.printSettings = printSettings + return this + } + +} diff --git a/jenkins-pipeline-shared-libraries/src/org/kie/jenkins/MavenSettingsConfig.groovy b/jenkins-pipeline-shared-libraries/src/org/kie/jenkins/MavenSettingsConfig.groovy new file mode 100644 index 000000000..7c3b37102 --- /dev/null +++ b/jenkins-pipeline-shared-libraries/src/org/kie/jenkins/MavenSettingsConfig.groovy @@ -0,0 +1,25 @@ +package org.kie.jenkins + +class MavenSettingsConfig { + + /** + * If set, override `withSettingsXmlFile` + **/ + String settingsXmlConfigFileId = '' + String settingsXmlPath = '' + Map dependenciesRepositoriesInSettings = [:] + Set disabledMirrorRepoInSettings = [] + boolean disableSnapshotsInSettings = false + List servers = [] + + MavenSettingsConfig clone() { + MavenSettingsConfig mavenSettingsConfig = new MavenSettingsConfig() + mavenSettingsConfig.setSettingsXmlConfigFileId(this.settingsXmlConfigFileId) + mavenSettingsConfig.setSettingsXmlPath(this.settingsXmlPath) + mavenSettingsConfig.setDependenciesRepositoriesInSettings(this.dependenciesRepositoriesInSettings) + mavenSettingsConfig.setDisabledMirrorRepoInSettings(this.disabledMirrorRepoInSettings) + mavenSettingsConfig.setDisableSnapshotsInSettings(this.disableSnapshotsInSettings) + mavenSettingsConfig.setServers(this.servers) + return mavenSettingsConfig + } +} diff --git a/jenkins-pipeline-shared-libraries/src/org/kie/jenkins/MavenSettingsConfigBuilder.groovy b/jenkins-pipeline-shared-libraries/src/org/kie/jenkins/MavenSettingsConfigBuilder.groovy new file mode 100644 index 000000000..b9dd28031 --- /dev/null +++ b/jenkins-pipeline-shared-libraries/src/org/kie/jenkins/MavenSettingsConfigBuilder.groovy @@ -0,0 +1,67 @@ +package org.kie.jenkins + +class MavenSettingsConfigBuilder { + + String settingsXmlConfigFileId = '' + String settingsXmlPath = '' + Map dependenciesRepositoriesInSettings = [:] + Set disabledMirrorRepoInSettings = [] + boolean disableSnapshotsInSettings = false + List servers = [] + + static MavenSettingsConfigBuilder from(MavenSettingsConfig settingsConfig) { + return new MavenSettingsConfigBuilder() + .settingsXmlConfigFileId(settingsConfig.settingsXmlConfigFileId) + .settingsXmlPath(settingsConfig.settingsXmlPath) + .dependenciesRepositoriesInSettings(settingsConfig.dependenciesRepositoriesInSettings) + .disabledMirrorRepoInSettings(settingsConfig.disabledMirrorRepoInSettings) + .disableSnapshotsInSettings(settingsConfig.disableSnapshotsInSettings) + .servers(settingsConfig.servers) + } + + MavenSettingsConfig build() { + MavenSettingsConfig mavenSettingsConfig = new MavenSettingsConfig() + mavenSettingsConfig.setSettingsXmlConfigFileId(this.settingsXmlConfigFileId) + mavenSettingsConfig.setSettingsXmlPath(this.settingsXmlPath) + mavenSettingsConfig.setDependenciesRepositoriesInSettings(this.dependenciesRepositoriesInSettings) + mavenSettingsConfig.setDisabledMirrorRepoInSettings(this.disabledMirrorRepoInSettings) + mavenSettingsConfig.setDisableSnapshotsInSettings(this.disableSnapshotsInSettings) + mavenSettingsConfig.setServers(this.servers) + return mavenSettingsConfig + } + + MavenSettingsConfigBuilder clone() { + return MavenSettingsConfigBuilder.from(this.build()) + } + + MavenSettingsConfigBuilder settingsXmlConfigFileId(String settingsXmlConfigFileId) { + this.settingsXmlConfigFileId = settingsXmlConfigFileId + return this + } + + MavenSettingsConfigBuilder settingsXmlPath(String settingsXmlPath) { + this.settingsXmlPath = settingsXmlPath + return this + } + + MavenSettingsConfigBuilder dependenciesRepositoriesInSettings(Map dependenciesRepositoriesInSettings) { + this.dependenciesRepositoriesInSettings.putAll(dependenciesRepositoriesInSettings) + return this + } + + MavenSettingsConfigBuilder disabledMirrorRepoInSettings(Set disabledMirrorRepoInSettings) { + this.disabledMirrorRepoInSettings.addAll(disabledMirrorRepoInSettings) + return this + } + + MavenSettingsConfigBuilder disableSnapshotsInSettings(boolean disableSnapshotsInSettings) { + this.disableSnapshotsInSettings = disableSnapshotsInSettings + return this + } + + MavenSettingsConfigBuilder servers(List servers) { + this.servers.addAll(servers) + return this + } + +} diff --git a/jenkins-pipeline-shared-libraries/src/org/kie/jenkins/MavenSettingsService.groovy b/jenkins-pipeline-shared-libraries/src/org/kie/jenkins/MavenSettingsService.groovy new file mode 100644 index 000000000..a4e9dee1c --- /dev/null +++ b/jenkins-pipeline-shared-libraries/src/org/kie/jenkins/MavenSettingsService.groovy @@ -0,0 +1,44 @@ +package org.kie.jenkins + +class MavenSettingsService { + + def steps + + MavenSettingsConfig mavenSettingsConfig + + MavenSettingsService(def steps) { + this(steps, new MavenSettingsConfig(steps)) + } + + MavenSettingsService(def steps, MavenSettingsConfig mavenSettingsConfig) { + this.steps = steps + this.mavenSettingsConfig = mavenSettingsConfig + } + + String createSettingsFile() { + String settingsFile = this.mavenSettingsConfig.settingsXmlPath + if (this.mavenSettingsConfig.settingsXmlConfigFileId) { + String settingsFilePath = steps.sh(returnStdout: true, script: 'mktemp --suffix -settings.xml').trim() + steps.configFileProvider([steps.configFile(fileId: this.mavenSettingsConfig.settingsXmlConfigFileId, targetLocation: settingsFilePath, variable: 'MAVEN_SETTINGS_XML')]) { + settingsFile = steps.env['MAVEN_SETTINGS_XML'] + } + } + if (settingsFile) { + this.mavenSettingsConfig.dependenciesRepositoriesInSettings.each { MavenSettingsUtils.setRepositoryInSettings(steps, settingsFile, it.key, it.value) } + + this.mavenSettingsConfig.disabledMirrorRepoInSettings.each { + MavenSettingsUtils.disableMirrorForRepoInSettings(steps, settingsFile, it) + } + + if (this.mavenSettingsConfig.disableSnapshotsInSettings) { + MavenSettingsUtils.disableSnapshotsInSettings(steps, settingsFile) + } + + this.mavenSettingsConfig.servers.each { MavenSettingsUtils.addServer(steps, settingsFile, it.id, it.username, it.password) } + return settingsFile + } else { + return '' + } + } + +} diff --git a/jenkins-pipeline-shared-libraries/src/org/kie/jenkins/MavenSettingsUtils.groovy b/jenkins-pipeline-shared-libraries/src/org/kie/jenkins/MavenSettingsUtils.groovy new file mode 100644 index 000000000..939f33fad --- /dev/null +++ b/jenkins-pipeline-shared-libraries/src/org/kie/jenkins/MavenSettingsUtils.groovy @@ -0,0 +1,26 @@ +package org.kie.jenkins + +class MavenSettingsUtils { + + static void setRepositoryInSettings(def steps, String settingsFilePath, String repoId, String repoUrl) { + def depsRepositoryContent = "${repoId}${repoId}${repoUrl}defaulttruetrue" + steps.sh """ + sed -i 's||${depsRepositoryContent}|g' ${settingsFilePath} + sed -i 's||${depsRepositoryContent}|g' ${settingsFilePath} + """ + disableMirrorForRepoInSettings(steps, settingsFilePath, repoId) + } + + static void disableMirrorForRepoInSettings(def steps, String settingsFilePath, String repoId) { + steps.sh "sed -i 's||,!${repoId}|g' ${settingsFilePath}" + } + + static void disableSnapshotsInSettings(def steps, String settingsFilePath) { + steps.sh "sed -i '//,/<\\/repository>/ { //,/<\\/snapshots>/ { s|true|false|; }}' ${settingsFilePath}" + steps.sh "sed -i '//,/<\\/pluginRepository>/ { //,/<\\/snapshots>/ { s|true|false|; }}' ${settingsFilePath}" + } + + static void addServer(def steps, String settingsFilePath, String serverId, String username, String password) { + steps.sh "sed -i 's||${serverId}${username}${password}|g' ${settingsFilePath}" + } +} diff --git a/jenkins-pipeline-shared-libraries/src/org/kie/jenkins/MavenStagingHelper.groovy b/jenkins-pipeline-shared-libraries/src/org/kie/jenkins/MavenStagingHelper.groovy new file mode 100644 index 000000000..f85d39a18 --- /dev/null +++ b/jenkins-pipeline-shared-libraries/src/org/kie/jenkins/MavenStagingHelper.groovy @@ -0,0 +1,146 @@ +package org.kie.jenkins + +/** +* Helper to stage and promote artifacts to Nexus +* +* Prerequisites: Your artifacts should be deployed locally before using this helper +* +* Actions: +* 1 - stageLocalArtifacts will create a staging repository and close it once done so your artifacts can be accessible. +* In the return properties, you can find the staging properties (id and link). +* 2 - promoteStagingRepository will promote a given staging repository to a build promotion profile. +* This is useful if you have many staging repositories, so that all of them can be accessible in one URL. +* +* More information +* - https://help.sonatype.com/repomanager2/staging-releases +* - https://github.com/sonatype/nexus-maven-plugins/tree/master/staging/maven-plugin#deploy-staged-repository +*/ +def class MavenStagingHelper { + + static String nexusPluginGroupId = 'org.sonatype.plugins' + static String nexusStagingPluginArtifactId = 'nexus-staging-maven-plugin' + static String nexusStagingPluginVersion = '1.6.13' + + def steps + + MavenCommand mvnCommand + + String nexusReleaseUrl = 'https://repository.jboss.org/nexus' + String nexusReleaseRepositoryId = 'jboss-releases-repository' + + // If not defined, will retrieve default from project artifactId & version + String stagingDescription + + // Will be filled once `stageLocalArtifacts` is called or via the `withStagingRepositoryId` method + String stagingRepositoryId + + MavenStagingHelper(steps){ + this(steps, new MavenCommand(steps)) + } + + MavenStagingHelper(steps, mvnCommand){ + this.steps = steps + this.mvnCommand = mvnCommand + } + + /** + * Use this method to stage artifacts that have been deployed locally (in given `localArtifactsFolder` parameter) + * It returns a Map of staging properties + */ + Map stageLocalArtifacts(String stagingProfileId, String localArtifactsFolder){ + assert stagingProfileId: 'Please provide a stagingProfileId' + assert localArtifactsFolder: 'Please provide a local folder where artifacts are stored' + + steps.println "[INFO] Staging artifacts to staging profile ID ${stagingProfileId}" + + getDefaultMavenCommand() + .withProperty('keepStagingRepositoryOnCloseRuleFailure', true) + .withProperty('stagingProgressTimeoutMinutes', 10) + .withProperty('repositoryDirectory', localArtifactsFolder) + .withProperty('stagingProfileId', stagingProfileId) + .run(getNexusStagingRunCommand('deploy-staged-repository')) + + // Retrieve `stagingRepositoryId` and fill it + Map stagingProps = retrieveStagingProperties(localArtifactsFolder) + withStagingRepositoryId(stagingProps['stagingRepository.id']) + + return stagingProps + } + + /** + * Use this method to promote a staging repository to a specific build promotion profile. + * Note that if you did not execute `stageLocalArtifacts` first, you will need to provide the staging repository id via `withStagingRepositoryId` + */ + def promoteStagingRepository(String buildPromotionProfileId) { + assert this.stagingRepositoryId: 'Please provide a stagingRepositoryId via staging local artifacts or via withStagingRepositoryId method' + assert buildPromotionProfileId: 'Please provide a buildPromotionProfileId' + + steps.println "[INFO] Promote artifacts from staging repository ${this.stagingRepositoryId} to build promotion profile ID ${buildPromotionProfileId}" + + getDefaultMavenCommand() + .withProperty('buildPromotionProfileId', buildPromotionProfileId) + .withProperty('stagingRepositoryId', this.stagingRepositoryId) + .run(getNexusStagingRunCommand('promote')) + } + + private MavenCommand getDefaultMavenCommand(){ + String projectName = getProjectArtifactId() + String projectVersion = getProjectVersion() + String description = this.stagingDescription ?: "${projectName} ${projectVersion}" + + return this.mvnCommand.clone() + .withOptions(["--projects :${projectName}"]) + .withProperty('nexusUrl', this.nexusReleaseUrl) + .withProperty('serverId', this.nexusReleaseRepositoryId) + .withProperty('stagingDescription', "'${description}'") + } + + MavenStagingHelper withNexusReleaseUrl(String nexusReleaseUrl) { + this.nexusReleaseUrl = nexusReleaseUrl + return this + } + + MavenStagingHelper withNexusReleaseRepositoryId(String nexusReleaseRepositoryId) { + this.nexusReleaseRepositoryId = nexusReleaseRepositoryId + return this + } + + MavenStagingHelper withStagingDescription(String stagingDescription) { + this.stagingDescription = stagingDescription + return this + } + + MavenStagingHelper withStagingRepositoryId(String stagingRepositoryId) { + this.stagingRepositoryId = stagingRepositoryId + return this + } + + private Properties retrieveStagingProperties(String folder){ + String repositoryPropsFile = steps.sh(script: "find ${folder} -name *.properties", returnStdout: true).trim() + steps.println "[INFO] Got staging properties file ${repositoryPropsFile}" + assert repositoryPropsFile: 'No staging properties file has been found' + + return steps.readProperties(file: repositoryPropsFile) + } + + private String getProjectArtifactId() { + return executeMavenHelpEvaluateCommand('project.artifactId') + } + private String getProjectVersion() { + return executeMavenHelpEvaluateCommand('project.version') + } + private String executeMavenHelpEvaluateCommand(String expression){ + return this.mvnCommand.clone() + .withOptions(['-q']) + .withProperty('expression', expression) + .withProperty('forceStdout') + .returnOutput() + .run('help:evaluate') + .trim() + } + + private static String getNexusStagingRunCommand(String target){ + return "${nexusPluginGroupId}:${nexusStagingPluginArtifactId}:${nexusStagingPluginVersion}:${target}" + } + +} diff --git a/jenkins-pipeline-shared-libraries/src/org/kie/jenkins/Utils.groovy b/jenkins-pipeline-shared-libraries/src/org/kie/jenkins/Utils.groovy new file mode 100644 index 000000000..333f9b5a4 --- /dev/null +++ b/jenkins-pipeline-shared-libraries/src/org/kie/jenkins/Utils.groovy @@ -0,0 +1,25 @@ +package org.kie.jenkins + +/* + * Utils class with some common utils methods for Jenkins pipelines. + */ +class Utils { + + /* + * Create a temporary dir on the machine (linux) + */ + static String createTempDir(def script) { + return script.sh(returnStdout: true, script: 'mktemp -d').trim() + } + + /* + * Create a temporary file on the machine (linux) + */ + static String createTempFile(def script, String content = '') { + String tmpFile = script.sh(returnStdout: true, script: 'mktemp').trim() + if (content) { + script.writeFile(file: tmpFile, text: content) + } + return tmpFile + } +} diff --git a/jenkins-pipeline-shared-libraries/src/org/kie/jenkins/shell/AbstractShell.groovy b/jenkins-pipeline-shared-libraries/src/org/kie/jenkins/shell/AbstractShell.groovy new file mode 100644 index 000000000..3891e67e5 --- /dev/null +++ b/jenkins-pipeline-shared-libraries/src/org/kie/jenkins/shell/AbstractShell.groovy @@ -0,0 +1,116 @@ +package org.kie.jenkins.shell + +import org.kie.jenkins.shell.installation.Installation +import org.kie.jenkins.Utils + +/** + * Common methods for a shell +*/ +abstract class AbstractShell implements Shell { + + def script + String installationDir + String cpuArchitecture + List installations = [] + Map envVars = [:] + boolean debug = false + + AbstractShell(def script, String installationDir = '', String cpuArchitecture = '') { + this.script = script + this.installationDir = installationDir + this.cpuArchitecture = cpuArchitecture ?: 'amd64' + } + + @Override + void enableDebug() { + this.debug = true + this.installations.each { installation -> installation.enableDebug() } + } + + @Override + void install(Installation installation) { + installation.setCpuArchitecture(this.cpuArchitecture) + installation.install(getInstallationDir()) + if (debug) { + installation.enableDebug() + } + this.installations.add(installation) + } + + @Override + void execute(String command) { + execute(command, '') + } + + @Override + void execute(String command, String directory) { + String fullCommand = getFullCommand(command, directory) + if (debug) { + println "[DEBUG] Run command: ${fullCommand}" + } + this.script.sh(fullCommand) + } + + @Override + String executeWithOutput(String command) { + return executeWithOutput(command, '') + } + + @Override + String executeWithOutput(String command, String directory) { + String fullCommand = getFullCommand(command, directory) + if (debug) { + println "[DEBUG] Run command: ${fullCommand}" + } + return this.script.sh(returnStdout: true, script: fullCommand).trim() + } + + @Override + def executeWithStatus(String command) { + return executeWithStatus(command, '') + } + + @Override + def executeWithStatus(String command, String directory) { + String fullCommand = getFullCommand(command, directory) + if (debug) { + println "[DEBUG] Run command: ${fullCommand}" + } + return this.script.sh(returnStatus: true, script: fullCommand) + } + + String getFullCommand(String command) { + return getFullCommand(command, '') + } + /* + * Return the full text command with additions for the shell + */ + abstract String getFullCommand(String command, String directory) + + @Override + void addEnvironmentVariable(String key, String value) { + this.envVars.put(key, value) + } + + @Override + Map getEnvironmentVariables() { + Map vars = [:] + this.installations.each { installation -> + vars.putAll(installation.getExtraEnvVars()) + } + vars.putAll(this.envVars) + return vars + } + + String getInstallationDir() { + if (!this.installationDir) { + // Cannot put this in constructor due to https://www.jenkins.io/doc/book/pipeline/cps-method-mismatches/ + this.installationDir = Utils.createTempDir(script) + } + return this.installationDir + } + + def getJenkinsScript() { + return this.script + } +} diff --git a/jenkins-pipeline-shared-libraries/src/org/kie/jenkins/shell/LocalShell.groovy b/jenkins-pipeline-shared-libraries/src/org/kie/jenkins/shell/LocalShell.groovy new file mode 100644 index 000000000..e2ced5c88 --- /dev/null +++ b/jenkins-pipeline-shared-libraries/src/org/kie/jenkins/shell/LocalShell.groovy @@ -0,0 +1,42 @@ +package org.kie.jenkins.shell + +import org.kie.jenkins.shell.installation.Installation +import org.kie.jenkins.Utils + +/** + * LocalShell allow to manage local installations of binaries on the local system. + * + * Use `install` method to add new binaries to local shell. + * + * All commands using local binaries should be done via the `execute*` methods. + * + * Example: + * LocalShell localShell = new LocalShell(this).install(new OpenshiftClientInstallation(this)) + * localShell.execute('oc version --client') +*/ +class LocalShell extends AbstractShell { + + LocalShell(def script, String installationDir = '', String cpuArchitecture = '') { + super(script, installationDir, cpuArchitecture) + } + + @Override + String getFullCommand(String command, String directory) { + String fullCommand = '' + if (directory) { + fullCommand += "mkdir -p ${directory} && cd ${directory}" + fullCommand += "\n" + } + if (installations) { + fullCommand += "export PATH=\${PATH}:${installations.collect { it.getBinaryPaths().join(':') }.join(':')}" + fullCommand += "\n" + } + Map envVars = getEnvironmentVariables() + if (envVars) { + fullCommand += envVars.collect { key, value -> "export ${key}=${value}" }.join('\n') + fullCommand += "\n" + } + fullCommand += "${command}" + } + +} diff --git a/jenkins-pipeline-shared-libraries/src/org/kie/jenkins/shell/SSHShell.groovy b/jenkins-pipeline-shared-libraries/src/org/kie/jenkins/shell/SSHShell.groovy new file mode 100644 index 000000000..9a58640cf --- /dev/null +++ b/jenkins-pipeline-shared-libraries/src/org/kie/jenkins/shell/SSHShell.groovy @@ -0,0 +1,42 @@ +package org.kie.jenkins.shell + +import org.kie.jenkins.Utils + +/** + * SSHShell allow to manage local installations of binaries on a remote system via SSH. + * + * Use `install` method to add new binaries to local shell. + * + * All commands using local binaries should be done via the `execute*` methods. + * + * Example: + * SSHShell sshShell = SSHShell.create(this).install(new OpenshiftClientInstallation(this)) + * sshShell.execute('oc version --client') +*/ +class SSHShell extends LocalShell { + + String sshServer + String sshOptions + + SSHShell(def script, String sshServer, String sshOptions = '', String installationDir = '', String cpuArchitecture = '') { + super(script, installationDir, cpuArchitecture) + this.sshServer = sshServer + this.sshOptions = sshOptions + } + + @Override + String getFullCommand(String command, String directory) { + String shellCommand = super.getFullCommand(command, directory) + + return "ssh ${sshOptions} ${sshServer} \"${shellCommand}\"" + } + + void copyFilesFromRemote(String remotePath, String localPath) { + this.getJenkinsScript().sh("scp -r -p ${sshOptions} ${sshServer}:${remotePath} ${localPath}") + } + + void copyFilesToRemote(String localPath, String remotePath) { + this.getJenkinsScript().sh("scp -r -p ${sshOptions} ${localPath} ${sshServer}:${remotePath}") + } + +} diff --git a/jenkins-pipeline-shared-libraries/src/org/kie/jenkins/shell/Shell.groovy b/jenkins-pipeline-shared-libraries/src/org/kie/jenkins/shell/Shell.groovy new file mode 100644 index 000000000..15cd39935 --- /dev/null +++ b/jenkins-pipeline-shared-libraries/src/org/kie/jenkins/shell/Shell.groovy @@ -0,0 +1,33 @@ +package org.kie.jenkins.shell + +import org.kie.jenkins.shell.installation.Installation + +/** + * Shell interface defines the methods that a shell should implement + * It allows to manage installations of binaries on the a system (local, remote or whatever could be useful). + * + * Use `install` method to add new binaries to local shell. + * + * All commands using local binaries should be done via the `execute*` methods. + * +*/ +interface Shell { + + void enableDebug() + + void execute(String command) + void execute(String command, String directory) + + String executeWithOutput(String command) + String executeWithOutput(String command, String directory) + + def executeWithStatus(String command) + def executeWithStatus(String command, String directory) + + void addEnvironmentVariable(String key, String value) + + Map getEnvironmentVariables() + + void install(Installation installation) + +} diff --git a/jenkins-pipeline-shared-libraries/src/org/kie/jenkins/shell/installation/AbstractInstallation.groovy b/jenkins-pipeline-shared-libraries/src/org/kie/jenkins/shell/installation/AbstractInstallation.groovy new file mode 100644 index 000000000..fc520357f --- /dev/null +++ b/jenkins-pipeline-shared-libraries/src/org/kie/jenkins/shell/installation/AbstractInstallation.groovy @@ -0,0 +1,43 @@ +package org.kie.jenkins.shell.installation + +abstract class AbstractInstallation implements Installation { + + def script + + public String installedVersion + + public String cpuArchitecture = '' + + List binaryPaths = [] + + boolean debug = false + + AbstractInstallation(def script, String installedVersion) { + this.script = script + this.installedVersion = installedVersion + } + + @Override + void install(String installDir) { + this.binaryPaths.addAll(this.installInDir(installDir)) + } + + /** + * Should return a list of binary paths to add + **/ + abstract List installInDir(String installDir) + + @Override + void enableDebug() { + this.debug = true + } + + def getJenkinsScript() { + return this.script + } + + @Override + void setCpuArchitecture(String cpuArchitecture) { + this.cpuArchitecture = cpuArchitecture + } +} diff --git a/jenkins-pipeline-shared-libraries/src/org/kie/jenkins/shell/installation/CRCInstallation.groovy b/jenkins-pipeline-shared-libraries/src/org/kie/jenkins/shell/installation/CRCInstallation.groovy new file mode 100644 index 000000000..e7abfce7b --- /dev/null +++ b/jenkins-pipeline-shared-libraries/src/org/kie/jenkins/shell/installation/CRCInstallation.groovy @@ -0,0 +1,32 @@ +package org.kie.jenkins.shell.installation + +/** + * CodeReadyContainers local installation. Recommended to use with org.kie.jenkins.shell.LocalShell class. + * + * Binaries will be installed from https://mirror.openshift.com/pub/openshift-v4/clients/crc/ repository. + */ +class CRCInstallation extends AbstractInstallation { + + CRCInstallation(def script, String installedVersion = 'latest') { + super(script, installedVersion) + } + + List installInDir(String installDir) { + getJenkinsScript().dir(installDir) { + getJenkinsScript().sh """ + wget --no-verbose https://mirror.openshift.com/pub/openshift-v4/clients/crc/${installedVersion}/crc-linux-${cpuArchitecture}.tar.xz + tar -xf crc-linux-amd64.tar.xz + """ + } + + String crcDir = getJenkinsScript().sh(returnStdout: true, script: "find ${installDir} -name 'crc-linux-*-${cpuArchitecture}' -type d").trim() + getJenkinsScript().echo "CRC installed in path '${crcDir}'" + getJenkinsScript().sh "ls -al ${crcDir}" + return [ crcDir ] + } + + Map getExtraEnvVars() { + return [:] + } + +} diff --git a/jenkins-pipeline-shared-libraries/src/org/kie/jenkins/shell/installation/GolangInstallation.groovy b/jenkins-pipeline-shared-libraries/src/org/kie/jenkins/shell/installation/GolangInstallation.groovy new file mode 100644 index 000000000..cf2536a54 --- /dev/null +++ b/jenkins-pipeline-shared-libraries/src/org/kie/jenkins/shell/installation/GolangInstallation.groovy @@ -0,0 +1,54 @@ +package org.kie.jenkins.shell.installation + +/** + * Golang binaries local installation. Recommended to use with org.kie.jenkins.shell.LocalShell class. + * + * Binaries will be installed from https://golang.org/dl/ repository. + * + * This installation will also set GOENV, GOPATH and GOCACHE environement variables. + */ +class GolangInstallation extends AbstractInstallation { + + String goPath = '' + String goEnv = '' + String goCache = '' + + GolangInstallation(def script, String installedVersion) { + super(script, installedVersion) + } + + List installInDir(String installDir) { + getJenkinsScript().dir(installDir) { + getJenkinsScript().sh """ + wget --no-verbose https://golang.org/dl/go${installedVersion}.linux-${cpuArchitecture}.tar.gz + tar -xzf go${installedVersion}.linux-${cpuArchitecture}.tar.gz + """ + } + + setupExtraPaths(installDir) + + String golangBinDir = "${installDir}/go/bin" + getJenkinsScript().echo "Golang installed in path '${golangBinDir}'" + return [ golangBinDir, "${this.goPath}/bin" ] + } + + void setupExtraPaths(String installDir) { + this.goPath = "${installDir}/gopath" + this.getJenkinsScript().sh "mkdir -p ${this.goPath}" + + this.goEnv = "${installDir}/goenv" + this.getJenkinsScript().sh "mkdir -p ${this.goEnv}" + + this.goCache = "${installDir}/gocache" + this.getJenkinsScript().sh "mkdir -p ${this.goCache}" + } + + Map getExtraEnvVars() { + return [ + 'GOPATH': this.goPath, + 'GOENV': this.goEnv, + 'GOCACHE': this.goCache + ] + } + +} diff --git a/jenkins-pipeline-shared-libraries/src/org/kie/jenkins/shell/installation/GroovyInstallation.groovy b/jenkins-pipeline-shared-libraries/src/org/kie/jenkins/shell/installation/GroovyInstallation.groovy new file mode 100644 index 000000000..b8c4983d2 --- /dev/null +++ b/jenkins-pipeline-shared-libraries/src/org/kie/jenkins/shell/installation/GroovyInstallation.groovy @@ -0,0 +1,31 @@ +package org.kie.jenkins.shell.installation + +/** + * Groovy binary local installation. Recommended to use with org.kie.jenkins.shell.LocalShell class. + * + * Binaries will be installed from https://dl.bintray.com/groovy/maven/ repository. + */ +class GroovyInstallation extends AbstractInstallation { + + GroovyInstallation(def script, String installedVersion) { + super(script, installedVersion) + } + + List installInDir(String installDir) { + getJenkinsScript().dir(installDir) { + getJenkinsScript().sh """ + wget --no-verbose https://archive.apache.org/dist/groovy/${installedVersion}/distribution/apache-groovy-binary-${installedVersion}.zip + unzip apache-groovy-binary-${installedVersion}.zip + """ + } + + String groovyDir = "${installDir}/groovy-${installedVersion}/bin" + getJenkinsScript().echo "Groovy installed in path '${groovyDir}'" + getJenkinsScript().sh "ls -al ${groovyDir}" + return [ groovyDir ] + } + + Map getExtraEnvVars() { + return [:] + } +} diff --git a/jenkins-pipeline-shared-libraries/src/org/kie/jenkins/shell/installation/Installation.groovy b/jenkins-pipeline-shared-libraries/src/org/kie/jenkins/shell/installation/Installation.groovy new file mode 100644 index 000000000..23c699d73 --- /dev/null +++ b/jenkins-pipeline-shared-libraries/src/org/kie/jenkins/shell/installation/Installation.groovy @@ -0,0 +1,14 @@ +package org.kie.jenkins.shell.installation + +interface Installation { + + void install(String installDir) + + List getBinaryPaths() + + void enableDebug() + + Map getExtraEnvVars() + + void setCpuArchitecture(String cpuArchitecture) +} diff --git a/jenkins-pipeline-shared-libraries/src/org/kie/jenkins/shell/installation/OpenshiftClientInstallation.groovy b/jenkins-pipeline-shared-libraries/src/org/kie/jenkins/shell/installation/OpenshiftClientInstallation.groovy new file mode 100644 index 000000000..195f6f9db --- /dev/null +++ b/jenkins-pipeline-shared-libraries/src/org/kie/jenkins/shell/installation/OpenshiftClientInstallation.groovy @@ -0,0 +1,33 @@ +package org.kie.jenkins.shell.installation + +/** + * Openshift client local installation. Recommended to use with org.kie.jenkins.shell.LocalShell class. + * + * Binaries will be installed from https://mirror.openshift.com/pub/openshift-v4/clients/ocp/ repository. + */ +class OpenshiftClientInstallation extends AbstractInstallation { + + OpenshiftClientInstallation(def script, String installedVersion = 'stable') { + super(script, installedVersion) + } + + List installInDir(String installDir) { + String ocDir = "${installDir}/openshiftclient" + + getJenkinsScript().dir(ocDir) { + getJenkinsScript().sh """ + wget --no-verbose https://mirror.openshift.com/pub/openshift-v4/clients/ocp/${installedVersion}/openshift-client-linux.tar.gz + tar -xzf openshift-client-linux.tar.gz + """ + } + + getJenkinsScript().echo "Openshift client installed in path '${ocDir}'" + getJenkinsScript().sh "ls -al ${ocDir}" + return [ ocDir ] + } + + Map getExtraEnvVars() { + return [:] + } + +} diff --git a/jenkins-pipeline-shared-libraries/test/resources/build-chain-action-single.yml b/jenkins-pipeline-shared-libraries/test/resources/build-chain-action-single.yml new file mode 100644 index 000000000..8718597e5 --- /dev/null +++ b/jenkins-pipeline-shared-libraries/test/resources/build-chain-action-single.yml @@ -0,0 +1,39 @@ +name: 'Build Chain' +description: 'Executes build-chain tool' +inputs: + github-token: + description: "the github token" + required: true + definition-file: + description: 'The `definition-file` input for the build-chain' + required: false + default: 'https://raw.githubusercontent.com/${GROUP}/droolsjbpm-build-bootstrap/${BRANCH:main}/.ci/pull-request-config.yaml' + flow-type: + description: "the flow to execute, it can be 'pull-request', 'full-downstream', 'single' or 'branch'" + default: "pull-request" + required: false + starting-project: + description: "the project to start flow from. By default is the project triggering the job" + required: false + logger-level: + description: "the log level. 'info' (default) | 'trace' | 'debug'" + default: "info" + required: false + annotations-prefix: + description: "The prefix for the annotations title" + default: '' + required: false +runs: + using: "composite" + steps: + - name: Build Chain Tool Execution + id: build-chain + uses: kiegroup/github-action-build-chain@v2.6.17 + with: + definition-file: ${{ inputs.definition-file }} + flow-type: ${{ inputs.flow-type }} + starting-project: ${{ inputs.starting-project }} + logger-level: ${{ inputs.logger-level }} + annotations-prefix: ${{ inputs.annotations-prefix }} + env: + GITHUB_TOKEN: ${{ inputs.github-token }}; \ No newline at end of file diff --git a/jenkins-pipeline-shared-libraries/test/resources/build-chain-action.yml b/jenkins-pipeline-shared-libraries/test/resources/build-chain-action.yml new file mode 100644 index 000000000..0e5f7e8b6 --- /dev/null +++ b/jenkins-pipeline-shared-libraries/test/resources/build-chain-action.yml @@ -0,0 +1,47 @@ +name: 'Build Chain' +description: 'Executes buildChain tool' +inputs: + github-token: + description: "the github token" + required: true + definition-file: + description: 'The `definition-file` input for the buildChain' + required: false + default: 'https://raw.githubusercontent.com/${GROUP}/kogito-pipelines/${BRANCH:main}/.ci/pull-request-config.yaml' + flow-type: + description: "the flow to execute, it can be 'pull-request', 'full-downstream', 'single' or 'branch'" + default: "pull-request" + required: false + starting-project: + description: "the project to start flow from. By default is the project triggering the job" + required: false + logger-level: + description: "the log level. 'info' (default) | 'trace' | 'debug'" + default: "info" + required: false + annotations-prefix: + description: "The prefix for the annotations title" + default: '' + required: false + + +runs: + using: "composite" + steps: + - name: Build Chain Tool Execution + id: buildChain + uses: kiegroup/github-action-build-chain@smcVersionString + with: + definition-file: ${{ inputs.definition-file }} + flow-type: ${{ inputs.flow-type }} + starting-project: ${{ inputs.starting-project }} + logger-level: ${{ inputs.logger-level }} + annotations-prefix: ${{ inputs.annotations-prefix }} + env: + GITHUB_TOKEN: ${{ inputs.github-token }} + - name: Cache Maven packages + uses: actions/cache@v2 + with: + path: ~/.m2 + key: ${{ inputs.key-prefix }}-m2-${{ hashFiles('**/pom.xml') }} + restore-keys: ${{ inputs.key-prefix }}-m2 diff --git a/jenkins-pipeline-shared-libraries/test/resources/forked_projects.json b/jenkins-pipeline-shared-libraries/test/resources/forked_projects.json new file mode 100644 index 000000000..d519300af --- /dev/null +++ b/jenkins-pipeline-shared-libraries/test/resources/forked_projects.json @@ -0,0 +1,202 @@ +[ + { + "id": 285831150, + "node_id": "MDEwOlJlcG9zaXRvcnkyODU4MzExNTA=", + "name": "github-action-build-chain", + "full_name": "irtyamine/github-action-build-chain", + "private": false, + "owner": { + "login": "irtyamine", + "id": 47329410, + "node_id": "MDQ6VXNlcjQ3MzI5NDEw", + "avatar_url": "https://avatars0.githubusercontent.com/u/47329410?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/irtyamine", + "html_url": "https://github.com/irtyamine", + "followers_url": "https://api.github.com/users/irtyamine/followers", + "following_url": "https://api.github.com/users/irtyamine/following{/other_user}", + "gists_url": "https://api.github.com/users/irtyamine/gists{/gist_id}", + "starred_url": "https://api.github.com/users/irtyamine/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/irtyamine/subscriptions", + "organizations_url": "https://api.github.com/users/irtyamine/orgs", + "repos_url": "https://api.github.com/users/irtyamine/repos", + "events_url": "https://api.github.com/users/irtyamine/events{/privacy}", + "received_events_url": "https://api.github.com/users/irtyamine/received_events", + "type": "User", + "site_admin": false + }, + "html_url": "https://github.com/irtyamine/github-action-build-chain", + "description": null, + "fork": true, + "url": "https://api.github.com/repos/irtyamine/github-action-build-chain", + "forks_url": "https://api.github.com/repos/irtyamine/github-action-build-chain/forks", + "keys_url": "https://api.github.com/repos/irtyamine/github-action-build-chain/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/irtyamine/github-action-build-chain/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/irtyamine/github-action-build-chain/teams", + "hooks_url": "https://api.github.com/repos/irtyamine/github-action-build-chain/hooks", + "issue_events_url": "https://api.github.com/repos/irtyamine/github-action-build-chain/issues/events{/number}", + "events_url": "https://api.github.com/repos/irtyamine/github-action-build-chain/events", + "assignees_url": "https://api.github.com/repos/irtyamine/github-action-build-chain/assignees{/user}", + "branches_url": "https://api.github.com/repos/irtyamine/github-action-build-chain/branches{/branch}", + "tags_url": "https://api.github.com/repos/irtyamine/github-action-build-chain/tags", + "blobs_url": "https://api.github.com/repos/irtyamine/github-action-build-chain/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/irtyamine/github-action-build-chain/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/irtyamine/github-action-build-chain/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/irtyamine/github-action-build-chain/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/irtyamine/github-action-build-chain/statuses/{sha}", + "languages_url": "https://api.github.com/repos/irtyamine/github-action-build-chain/languages", + "stargazers_url": "https://api.github.com/repos/irtyamine/github-action-build-chain/stargazers", + "contributors_url": "https://api.github.com/repos/irtyamine/github-action-build-chain/contributors", + "subscribers_url": "https://api.github.com/repos/irtyamine/github-action-build-chain/subscribers", + "subscription_url": "https://api.github.com/repos/irtyamine/github-action-build-chain/subscription", + "commits_url": "https://api.github.com/repos/irtyamine/github-action-build-chain/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/irtyamine/github-action-build-chain/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/irtyamine/github-action-build-chain/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/irtyamine/github-action-build-chain/issues/comments{/number}", + "contents_url": "https://api.github.com/repos/irtyamine/github-action-build-chain/contents/{+path}", + "compare_url": "https://api.github.com/repos/irtyamine/github-action-build-chain/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/irtyamine/github-action-build-chain/merges", + "archive_url": "https://api.github.com/repos/irtyamine/github-action-build-chain/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/irtyamine/github-action-build-chain/downloads", + "issues_url": "https://api.github.com/repos/irtyamine/github-action-build-chain/issues{/number}", + "pulls_url": "https://api.github.com/repos/irtyamine/github-action-build-chain/pulls{/number}", + "milestones_url": "https://api.github.com/repos/irtyamine/github-action-build-chain/milestones{/number}", + "notifications_url": "https://api.github.com/repos/irtyamine/github-action-build-chain/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/irtyamine/github-action-build-chain/labels{/name}", + "releases_url": "https://api.github.com/repos/irtyamine/github-action-build-chain/releases{/id}", + "deployments_url": "https://api.github.com/repos/irtyamine/github-action-build-chain/deployments", + "created_at": "2020-08-07T13:05:44Z", + "updated_at": "2020-08-07T13:05:46Z", + "pushed_at": "2020-08-07T12:17:17Z", + "git_url": "git://github.com/irtyamine/github-action-build-chain.git", + "ssh_url": "git@github.com:irtyamine/github-action-build-chain.git", + "clone_url": "https://github.com/irtyamine/github-action-build-chain.git", + "svn_url": "https://github.com/irtyamine/github-action-build-chain", + "homepage": null, + "size": 250, + "stargazers_count": 0, + "watchers_count": 0, + "language": null, + "has_issues": false, + "has_projects": true, + "has_downloads": true, + "has_wiki": true, + "has_pages": false, + "forks_count": 0, + "mirror_url": null, + "archived": false, + "disabled": false, + "open_issues_count": 0, + "license": { + "key": "apache-2.0", + "name": "Apache License 2.0", + "spdx_id": "Apache-2.0", + "url": "https://api.github.com/licenses/apache-2.0", + "node_id": "MDc6TGljZW5zZTI=" + }, + "forks": 0, + "open_issues": 0, + "watchers": 0, + "default_branch": "main" + }, + { + "id": 286916753, + "node_id": "MDEwOlJlcG9zaXRvcnkyODY5MTY3NTM=", + "name": "drools", + "full_name": "deadzq/drools", + "private": false, + "owner": { + "login": "deadzq", + "id": 21271797, + "node_id": "MDQ6VXNlcjIxMjcxNzk3", + "avatar_url": "https://avatars3.githubusercontent.com/u/21271797?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/deadzq", + "html_url": "https://github.com/deadzq", + "followers_url": "https://api.github.com/users/deadzq/followers", + "following_url": "https://api.github.com/users/deadzq/following{/other_user}", + "gists_url": "https://api.github.com/users/deadzq/gists{/gist_id}", + "starred_url": "https://api.github.com/users/deadzq/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/deadzq/subscriptions", + "organizations_url": "https://api.github.com/users/deadzq/orgs", + "repos_url": "https://api.github.com/users/deadzq/repos", + "events_url": "https://api.github.com/users/deadzq/events{/privacy}", + "received_events_url": "https://api.github.com/users/deadzq/received_events", + "type": "User", + "site_admin": false + }, + "html_url": "https://github.com/deadzq/drools", + "description": "Drools is a rule engine, DMN engine and complex event processing (CEP) engine for Java.", + "fork": true, + "url": "https://api.github.com/repos/deadzq/drools", + "forks_url": "https://api.github.com/repos/deadzq/drools/forks", + "keys_url": "https://api.github.com/repos/deadzq/drools/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/deadzq/drools/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/deadzq/drools/teams", + "hooks_url": "https://api.github.com/repos/deadzq/drools/hooks", + "issue_events_url": "https://api.github.com/repos/deadzq/drools/issues/events{/number}", + "events_url": "https://api.github.com/repos/deadzq/drools/events", + "assignees_url": "https://api.github.com/repos/deadzq/drools/assignees{/user}", + "branches_url": "https://api.github.com/repos/deadzq/drools/branches{/branch}", + "tags_url": "https://api.github.com/repos/deadzq/drools/tags", + "blobs_url": "https://api.github.com/repos/deadzq/drools/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/deadzq/drools/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/deadzq/drools/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/deadzq/drools/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/deadzq/drools/statuses/{sha}", + "languages_url": "https://api.github.com/repos/deadzq/drools/languages", + "stargazers_url": "https://api.github.com/repos/deadzq/drools/stargazers", + "contributors_url": "https://api.github.com/repos/deadzq/drools/contributors", + "subscribers_url": "https://api.github.com/repos/deadzq/drools/subscribers", + "subscription_url": "https://api.github.com/repos/deadzq/drools/subscription", + "commits_url": "https://api.github.com/repos/deadzq/drools/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/deadzq/drools/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/deadzq/drools/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/deadzq/drools/issues/comments{/number}", + "contents_url": "https://api.github.com/repos/deadzq/drools/contents/{+path}", + "compare_url": "https://api.github.com/repos/deadzq/drools/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/deadzq/drools/merges", + "archive_url": "https://api.github.com/repos/deadzq/drools/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/deadzq/drools/downloads", + "issues_url": "https://api.github.com/repos/deadzq/drools/issues{/number}", + "pulls_url": "https://api.github.com/repos/deadzq/drools/pulls{/number}", + "milestones_url": "https://api.github.com/repos/deadzq/drools/milestones{/number}", + "notifications_url": "https://api.github.com/repos/deadzq/drools/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/deadzq/drools/labels{/name}", + "releases_url": "https://api.github.com/repos/deadzq/drools/releases{/id}", + "deployments_url": "https://api.github.com/repos/deadzq/drools/deployments", + "created_at": "2020-08-12T04:34:04Z", + "updated_at": "2020-08-12T04:34:07Z", + "pushed_at": "2020-08-11T16:41:31Z", + "git_url": "git://github.com/deadzq/drools.git", + "ssh_url": "git@github.com:deadzq/drools.git", + "clone_url": "https://github.com/deadzq/drools.git", + "svn_url": "https://github.com/deadzq/drools", + "homepage": "http://www.drools.org", + "size": 168289, + "stargazers_count": 0, + "watchers_count": 0, + "language": null, + "has_issues": false, + "has_projects": true, + "has_downloads": true, + "has_wiki": false, + "has_pages": false, + "forks_count": 0, + "mirror_url": null, + "archived": false, + "disabled": false, + "open_issues_count": 0, + "license": { + "key": "apache-2.0", + "name": "Apache License 2.0", + "spdx_id": "Apache-2.0", + "url": "https://api.github.com/licenses/apache-2.0", + "node_id": "MDc6TGljZW5zZTI=" + }, + "forks": 0, + "open_issues": 0, + "watchers": 0, + "default_branch": "main" + } +] \ No newline at end of file diff --git a/jenkins-pipeline-shared-libraries/test/resources/forked_projects_empty.json b/jenkins-pipeline-shared-libraries/test/resources/forked_projects_empty.json new file mode 100644 index 000000000..c44dc44f3 --- /dev/null +++ b/jenkins-pipeline-shared-libraries/test/resources/forked_projects_empty.json @@ -0,0 +1,3 @@ +[ + +] \ No newline at end of file diff --git a/jenkins-pipeline-shared-libraries/test/resources/forked_projects_missing_owner.json b/jenkins-pipeline-shared-libraries/test/resources/forked_projects_missing_owner.json new file mode 100644 index 000000000..63d1a4a35 --- /dev/null +++ b/jenkins-pipeline-shared-libraries/test/resources/forked_projects_missing_owner.json @@ -0,0 +1,3 @@ +{ + "error": "error" +} \ No newline at end of file diff --git a/jenkins-pipeline-shared-libraries/test/resources/forked_projects_page3.json b/jenkins-pipeline-shared-libraries/test/resources/forked_projects_page3.json new file mode 100644 index 000000000..9e2c6054d --- /dev/null +++ b/jenkins-pipeline-shared-libraries/test/resources/forked_projects_page3.json @@ -0,0 +1,1002 @@ +[ + { + "id": 292531782, + "node_id": "MDEwOlJlcG9zaXRvcnkyOTI1MzE3ODI=", + "name": "appformer", + "full_name": "LeonidLapshin/appformer", + "private": false, + "owner": { + "login": "LeonidLapshin", + "id": 2534109, + "node_id": "MDQ6VXNlcjI1MzQxMDk=", + "avatar_url": "https://avatars0.githubusercontent.com/u/2534109?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/LeonidLapshin", + "html_url": "https://github.com/LeonidLapshin", + "followers_url": "https://api.github.com/users/LeonidLapshin/followers", + "following_url": "https://api.github.com/users/LeonidLapshin/following{/other_user}", + "gists_url": "https://api.github.com/users/LeonidLapshin/gists{/gist_id}", + "starred_url": "https://api.github.com/users/LeonidLapshin/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/LeonidLapshin/subscriptions", + "organizations_url": "https://api.github.com/users/LeonidLapshin/orgs", + "repos_url": "https://api.github.com/users/LeonidLapshin/repos", + "events_url": "https://api.github.com/users/LeonidLapshin/events{/privacy}", + "received_events_url": "https://api.github.com/users/LeonidLapshin/received_events", + "type": "User", + "site_admin": false + }, + "html_url": "https://github.com/LeonidLapshin/appformer", + "description": "A web platform to rapidly build forms for data management and business automation.", + "fork": true, + "url": "https://api.github.com/repos/LeonidLapshin/appformer", + "forks_url": "https://api.github.com/repos/LeonidLapshin/appformer/forks", + "keys_url": "https://api.github.com/repos/LeonidLapshin/appformer/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/LeonidLapshin/appformer/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/LeonidLapshin/appformer/teams", + "hooks_url": "https://api.github.com/repos/LeonidLapshin/appformer/hooks", + "issue_events_url": "https://api.github.com/repos/LeonidLapshin/appformer/issues/events{/number}", + "events_url": "https://api.github.com/repos/LeonidLapshin/appformer/events", + "assignees_url": "https://api.github.com/repos/LeonidLapshin/appformer/assignees{/user}", + "branches_url": "https://api.github.com/repos/LeonidLapshin/appformer/branches{/branch}", + "tags_url": "https://api.github.com/repos/LeonidLapshin/appformer/tags", + "blobs_url": "https://api.github.com/repos/LeonidLapshin/appformer/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/LeonidLapshin/appformer/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/LeonidLapshin/appformer/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/LeonidLapshin/appformer/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/LeonidLapshin/appformer/statuses/{sha}", + "languages_url": "https://api.github.com/repos/LeonidLapshin/appformer/languages", + "stargazers_url": "https://api.github.com/repos/LeonidLapshin/appformer/stargazers", + "contributors_url": "https://api.github.com/repos/LeonidLapshin/appformer/contributors", + "subscribers_url": "https://api.github.com/repos/LeonidLapshin/appformer/subscribers", + "subscription_url": "https://api.github.com/repos/LeonidLapshin/appformer/subscription", + "commits_url": "https://api.github.com/repos/LeonidLapshin/appformer/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/LeonidLapshin/appformer/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/LeonidLapshin/appformer/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/LeonidLapshin/appformer/issues/comments{/number}", + "contents_url": "https://api.github.com/repos/LeonidLapshin/appformer/contents/{+path}", + "compare_url": "https://api.github.com/repos/LeonidLapshin/appformer/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/LeonidLapshin/appformer/merges", + "archive_url": "https://api.github.com/repos/LeonidLapshin/appformer/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/LeonidLapshin/appformer/downloads", + "issues_url": "https://api.github.com/repos/LeonidLapshin/appformer/issues{/number}", + "pulls_url": "https://api.github.com/repos/LeonidLapshin/appformer/pulls{/number}", + "milestones_url": "https://api.github.com/repos/LeonidLapshin/appformer/milestones{/number}", + "notifications_url": "https://api.github.com/repos/LeonidLapshin/appformer/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/LeonidLapshin/appformer/labels{/name}", + "releases_url": "https://api.github.com/repos/LeonidLapshin/appformer/releases{/id}", + "deployments_url": "https://api.github.com/repos/LeonidLapshin/appformer/deployments", + "created_at": "2020-09-03T09:58:31Z", + "updated_at": "2020-09-03T10:56:36Z", + "pushed_at": "2020-09-07T11:51:42Z", + "git_url": "git://github.com/LeonidLapshin/appformer.git", + "ssh_url": "git@github.com:LeonidLapshin/appformer.git", + "clone_url": "https://github.com/LeonidLapshin/appformer.git", + "svn_url": "https://github.com/LeonidLapshin/appformer", + "homepage": null, + "size": 55016, + "stargazers_count": 0, + "watchers_count": 0, + "language": "Java", + "has_issues": false, + "has_projects": true, + "has_downloads": true, + "has_wiki": true, + "has_pages": false, + "forks_count": 0, + "mirror_url": null, + "archived": false, + "disabled": false, + "open_issues_count": 6, + "license": { + "key": "apache-2.0", + "name": "Apache License 2.0", + "spdx_id": "Apache-2.0", + "url": "https://api.github.com/licenses/apache-2.0", + "node_id": "MDc6TGljZW5zZTI=" + }, + "forks": 0, + "open_issues": 6, + "watchers": 0, + "default_branch": "main" + }, + { + "id": 292418276, + "node_id": "MDEwOlJlcG9zaXRvcnkyOTI0MTgyNzY=", + "name": "appformer", + "full_name": "hikemachado/appformer", + "private": false, + "owner": { + "login": "hikemachado", + "id": 22946589, + "node_id": "MDQ6VXNlcjIyOTQ2NTg5", + "avatar_url": "https://avatars0.githubusercontent.com/u/22946589?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/hikemachado", + "html_url": "https://github.com/hikemachado", + "followers_url": "https://api.github.com/users/hikemachado/followers", + "following_url": "https://api.github.com/users/hikemachado/following{/other_user}", + "gists_url": "https://api.github.com/users/hikemachado/gists{/gist_id}", + "starred_url": "https://api.github.com/users/hikemachado/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/hikemachado/subscriptions", + "organizations_url": "https://api.github.com/users/hikemachado/orgs", + "repos_url": "https://api.github.com/users/hikemachado/repos", + "events_url": "https://api.github.com/users/hikemachado/events{/privacy}", + "received_events_url": "https://api.github.com/users/hikemachado/received_events", + "type": "User", + "site_admin": false + }, + "html_url": "https://github.com/hikemachado/appformer", + "description": "A web platform to rapidly build forms for data management and business automation.", + "fork": true, + "url": "https://api.github.com/repos/hikemachado/appformer", + "forks_url": "https://api.github.com/repos/hikemachado/appformer/forks", + "keys_url": "https://api.github.com/repos/hikemachado/appformer/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/hikemachado/appformer/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/hikemachado/appformer/teams", + "hooks_url": "https://api.github.com/repos/hikemachado/appformer/hooks", + "issue_events_url": "https://api.github.com/repos/hikemachado/appformer/issues/events{/number}", + "events_url": "https://api.github.com/repos/hikemachado/appformer/events", + "assignees_url": "https://api.github.com/repos/hikemachado/appformer/assignees{/user}", + "branches_url": "https://api.github.com/repos/hikemachado/appformer/branches{/branch}", + "tags_url": "https://api.github.com/repos/hikemachado/appformer/tags", + "blobs_url": "https://api.github.com/repos/hikemachado/appformer/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/hikemachado/appformer/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/hikemachado/appformer/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/hikemachado/appformer/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/hikemachado/appformer/statuses/{sha}", + "languages_url": "https://api.github.com/repos/hikemachado/appformer/languages", + "stargazers_url": "https://api.github.com/repos/hikemachado/appformer/stargazers", + "contributors_url": "https://api.github.com/repos/hikemachado/appformer/contributors", + "subscribers_url": "https://api.github.com/repos/hikemachado/appformer/subscribers", + "subscription_url": "https://api.github.com/repos/hikemachado/appformer/subscription", + "commits_url": "https://api.github.com/repos/hikemachado/appformer/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/hikemachado/appformer/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/hikemachado/appformer/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/hikemachado/appformer/issues/comments{/number}", + "contents_url": "https://api.github.com/repos/hikemachado/appformer/contents/{+path}", + "compare_url": "https://api.github.com/repos/hikemachado/appformer/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/hikemachado/appformer/merges", + "archive_url": "https://api.github.com/repos/hikemachado/appformer/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/hikemachado/appformer/downloads", + "issues_url": "https://api.github.com/repos/hikemachado/appformer/issues{/number}", + "pulls_url": "https://api.github.com/repos/hikemachado/appformer/pulls{/number}", + "milestones_url": "https://api.github.com/repos/hikemachado/appformer/milestones{/number}", + "notifications_url": "https://api.github.com/repos/hikemachado/appformer/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/hikemachado/appformer/labels{/name}", + "releases_url": "https://api.github.com/repos/hikemachado/appformer/releases{/id}", + "deployments_url": "https://api.github.com/repos/hikemachado/appformer/deployments", + "created_at": "2020-09-02T23:41:43Z", + "updated_at": "2020-09-08T13:56:52Z", + "pushed_at": "2020-09-09T20:13:11Z", + "git_url": "git://github.com/hikemachado/appformer.git", + "ssh_url": "git@github.com:hikemachado/appformer.git", + "clone_url": "https://github.com/hikemachado/appformer.git", + "svn_url": "https://github.com/hikemachado/appformer", + "homepage": null, + "size": 55286, + "stargazers_count": 0, + "watchers_count": 0, + "language": "Java", + "has_issues": false, + "has_projects": true, + "has_downloads": true, + "has_wiki": true, + "has_pages": false, + "forks_count": 0, + "mirror_url": null, + "archived": false, + "disabled": false, + "open_issues_count": 0, + "license": { + "key": "apache-2.0", + "name": "Apache License 2.0", + "spdx_id": "Apache-2.0", + "url": "https://api.github.com/licenses/apache-2.0", + "node_id": "MDc6TGljZW5zZTI=" + }, + "forks": 0, + "open_issues": 0, + "watchers": 0, + "default_branch": "main" + }, + { + "id": 291643615, + "node_id": "MDEwOlJlcG9zaXRvcnkyOTE2NDM2MTU=", + "name": "appformer", + "full_name": "vigneshvarma/appformer", + "private": false, + "owner": { + "login": "vigneshvarma", + "id": 28886514, + "node_id": "MDQ6VXNlcjI4ODg2NTE0", + "avatar_url": "https://avatars3.githubusercontent.com/u/28886514?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/vigneshvarma", + "html_url": "https://github.com/vigneshvarma", + "followers_url": "https://api.github.com/users/vigneshvarma/followers", + "following_url": "https://api.github.com/users/vigneshvarma/following{/other_user}", + "gists_url": "https://api.github.com/users/vigneshvarma/gists{/gist_id}", + "starred_url": "https://api.github.com/users/vigneshvarma/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/vigneshvarma/subscriptions", + "organizations_url": "https://api.github.com/users/vigneshvarma/orgs", + "repos_url": "https://api.github.com/users/vigneshvarma/repos", + "events_url": "https://api.github.com/users/vigneshvarma/events{/privacy}", + "received_events_url": "https://api.github.com/users/vigneshvarma/received_events", + "type": "User", + "site_admin": false + }, + "html_url": "https://github.com/vigneshvarma/appformer", + "description": "A web platform to rapidly build forms for data management and business automation.", + "fork": true, + "url": "https://api.github.com/repos/vigneshvarma/appformer", + "forks_url": "https://api.github.com/repos/vigneshvarma/appformer/forks", + "keys_url": "https://api.github.com/repos/vigneshvarma/appformer/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/vigneshvarma/appformer/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/vigneshvarma/appformer/teams", + "hooks_url": "https://api.github.com/repos/vigneshvarma/appformer/hooks", + "issue_events_url": "https://api.github.com/repos/vigneshvarma/appformer/issues/events{/number}", + "events_url": "https://api.github.com/repos/vigneshvarma/appformer/events", + "assignees_url": "https://api.github.com/repos/vigneshvarma/appformer/assignees{/user}", + "branches_url": "https://api.github.com/repos/vigneshvarma/appformer/branches{/branch}", + "tags_url": "https://api.github.com/repos/vigneshvarma/appformer/tags", + "blobs_url": "https://api.github.com/repos/vigneshvarma/appformer/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/vigneshvarma/appformer/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/vigneshvarma/appformer/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/vigneshvarma/appformer/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/vigneshvarma/appformer/statuses/{sha}", + "languages_url": "https://api.github.com/repos/vigneshvarma/appformer/languages", + "stargazers_url": "https://api.github.com/repos/vigneshvarma/appformer/stargazers", + "contributors_url": "https://api.github.com/repos/vigneshvarma/appformer/contributors", + "subscribers_url": "https://api.github.com/repos/vigneshvarma/appformer/subscribers", + "subscription_url": "https://api.github.com/repos/vigneshvarma/appformer/subscription", + "commits_url": "https://api.github.com/repos/vigneshvarma/appformer/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/vigneshvarma/appformer/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/vigneshvarma/appformer/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/vigneshvarma/appformer/issues/comments{/number}", + "contents_url": "https://api.github.com/repos/vigneshvarma/appformer/contents/{+path}", + "compare_url": "https://api.github.com/repos/vigneshvarma/appformer/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/vigneshvarma/appformer/merges", + "archive_url": "https://api.github.com/repos/vigneshvarma/appformer/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/vigneshvarma/appformer/downloads", + "issues_url": "https://api.github.com/repos/vigneshvarma/appformer/issues{/number}", + "pulls_url": "https://api.github.com/repos/vigneshvarma/appformer/pulls{/number}", + "milestones_url": "https://api.github.com/repos/vigneshvarma/appformer/milestones{/number}", + "notifications_url": "https://api.github.com/repos/vigneshvarma/appformer/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/vigneshvarma/appformer/labels{/name}", + "releases_url": "https://api.github.com/repos/vigneshvarma/appformer/releases{/id}", + "deployments_url": "https://api.github.com/repos/vigneshvarma/appformer/deployments", + "created_at": "2020-08-31T07:18:40Z", + "updated_at": "2020-08-31T07:18:42Z", + "pushed_at": "2020-08-28T14:33:42Z", + "git_url": "git://github.com/vigneshvarma/appformer.git", + "ssh_url": "git@github.com:vigneshvarma/appformer.git", + "clone_url": "https://github.com/vigneshvarma/appformer.git", + "svn_url": "https://github.com/vigneshvarma/appformer", + "homepage": null, + "size": 54994, + "stargazers_count": 0, + "watchers_count": 0, + "language": null, + "has_issues": false, + "has_projects": true, + "has_downloads": true, + "has_wiki": true, + "has_pages": false, + "forks_count": 0, + "mirror_url": null, + "archived": false, + "disabled": false, + "open_issues_count": 0, + "license": { + "key": "apache-2.0", + "name": "Apache License 2.0", + "spdx_id": "Apache-2.0", + "url": "https://api.github.com/licenses/apache-2.0", + "node_id": "MDc6TGljZW5zZTI=" + }, + "forks": 0, + "open_issues": 0, + "watchers": 0, + "default_branch": "main" + }, + { + "id": 290188663, + "node_id": "MDEwOlJlcG9zaXRvcnkyOTAxODg2NjM=", + "name": "appformer", + "full_name": "FireBurn/appformer", + "private": false, + "owner": { + "login": "FireBurn", + "id": 86515, + "node_id": "MDQ6VXNlcjg2NTE1", + "avatar_url": "https://avatars3.githubusercontent.com/u/86515?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/FireBurn", + "html_url": "https://github.com/FireBurn", + "followers_url": "https://api.github.com/users/FireBurn/followers", + "following_url": "https://api.github.com/users/FireBurn/following{/other_user}", + "gists_url": "https://api.github.com/users/FireBurn/gists{/gist_id}", + "starred_url": "https://api.github.com/users/FireBurn/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/FireBurn/subscriptions", + "organizations_url": "https://api.github.com/users/FireBurn/orgs", + "repos_url": "https://api.github.com/users/FireBurn/repos", + "events_url": "https://api.github.com/users/FireBurn/events{/privacy}", + "received_events_url": "https://api.github.com/users/FireBurn/received_events", + "type": "User", + "site_admin": false + }, + "html_url": "https://github.com/FireBurn/appformer", + "description": "A web platform to rapidly build forms for data management and business automation.", + "fork": true, + "url": "https://api.github.com/repos/FireBurn/appformer", + "forks_url": "https://api.github.com/repos/FireBurn/appformer/forks", + "keys_url": "https://api.github.com/repos/FireBurn/appformer/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/FireBurn/appformer/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/FireBurn/appformer/teams", + "hooks_url": "https://api.github.com/repos/FireBurn/appformer/hooks", + "issue_events_url": "https://api.github.com/repos/FireBurn/appformer/issues/events{/number}", + "events_url": "https://api.github.com/repos/FireBurn/appformer/events", + "assignees_url": "https://api.github.com/repos/FireBurn/appformer/assignees{/user}", + "branches_url": "https://api.github.com/repos/FireBurn/appformer/branches{/branch}", + "tags_url": "https://api.github.com/repos/FireBurn/appformer/tags", + "blobs_url": "https://api.github.com/repos/FireBurn/appformer/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/FireBurn/appformer/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/FireBurn/appformer/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/FireBurn/appformer/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/FireBurn/appformer/statuses/{sha}", + "languages_url": "https://api.github.com/repos/FireBurn/appformer/languages", + "stargazers_url": "https://api.github.com/repos/FireBurn/appformer/stargazers", + "contributors_url": "https://api.github.com/repos/FireBurn/appformer/contributors", + "subscribers_url": "https://api.github.com/repos/FireBurn/appformer/subscribers", + "subscription_url": "https://api.github.com/repos/FireBurn/appformer/subscription", + "commits_url": "https://api.github.com/repos/FireBurn/appformer/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/FireBurn/appformer/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/FireBurn/appformer/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/FireBurn/appformer/issues/comments{/number}", + "contents_url": "https://api.github.com/repos/FireBurn/appformer/contents/{+path}", + "compare_url": "https://api.github.com/repos/FireBurn/appformer/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/FireBurn/appformer/merges", + "archive_url": "https://api.github.com/repos/FireBurn/appformer/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/FireBurn/appformer/downloads", + "issues_url": "https://api.github.com/repos/FireBurn/appformer/issues{/number}", + "pulls_url": "https://api.github.com/repos/FireBurn/appformer/pulls{/number}", + "milestones_url": "https://api.github.com/repos/FireBurn/appformer/milestones{/number}", + "notifications_url": "https://api.github.com/repos/FireBurn/appformer/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/FireBurn/appformer/labels{/name}", + "releases_url": "https://api.github.com/repos/FireBurn/appformer/releases{/id}", + "deployments_url": "https://api.github.com/repos/FireBurn/appformer/deployments", + "created_at": "2020-08-25T10:49:17Z", + "updated_at": "2020-08-25T10:49:19Z", + "pushed_at": "2020-09-03T16:19:05Z", + "git_url": "git://github.com/FireBurn/appformer.git", + "ssh_url": "git@github.com:FireBurn/appformer.git", + "clone_url": "https://github.com/FireBurn/appformer.git", + "svn_url": "https://github.com/FireBurn/appformer", + "homepage": null, + "size": 54935, + "stargazers_count": 0, + "watchers_count": 0, + "language": null, + "has_issues": false, + "has_projects": true, + "has_downloads": true, + "has_wiki": true, + "has_pages": false, + "forks_count": 0, + "mirror_url": null, + "archived": false, + "disabled": false, + "open_issues_count": 0, + "license": { + "key": "apache-2.0", + "name": "Apache License 2.0", + "spdx_id": "Apache-2.0", + "url": "https://api.github.com/licenses/apache-2.0", + "node_id": "MDc6TGljZW5zZTI=" + }, + "forks": 0, + "open_issues": 0, + "watchers": 0, + "default_branch": "main" + }, + { + "id": 289189877, + "node_id": "MDEwOlJlcG9zaXRvcnkyODkxODk4Nzc=", + "name": "appformer", + "full_name": "hw1020/appformer", + "private": false, + "owner": { + "login": "hw1020", + "id": 43195286, + "node_id": "MDQ6VXNlcjQzMTk1Mjg2", + "avatar_url": "https://avatars3.githubusercontent.com/u/43195286?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/hw1020", + "html_url": "https://github.com/hw1020", + "followers_url": "https://api.github.com/users/hw1020/followers", + "following_url": "https://api.github.com/users/hw1020/following{/other_user}", + "gists_url": "https://api.github.com/users/hw1020/gists{/gist_id}", + "starred_url": "https://api.github.com/users/hw1020/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/hw1020/subscriptions", + "organizations_url": "https://api.github.com/users/hw1020/orgs", + "repos_url": "https://api.github.com/users/hw1020/repos", + "events_url": "https://api.github.com/users/hw1020/events{/privacy}", + "received_events_url": "https://api.github.com/users/hw1020/received_events", + "type": "User", + "site_admin": false + }, + "html_url": "https://github.com/hw1020/appformer", + "description": "A web platform to rapidly build forms for data management and business automation.", + "fork": true, + "url": "https://api.github.com/repos/hw1020/appformer", + "forks_url": "https://api.github.com/repos/hw1020/appformer/forks", + "keys_url": "https://api.github.com/repos/hw1020/appformer/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/hw1020/appformer/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/hw1020/appformer/teams", + "hooks_url": "https://api.github.com/repos/hw1020/appformer/hooks", + "issue_events_url": "https://api.github.com/repos/hw1020/appformer/issues/events{/number}", + "events_url": "https://api.github.com/repos/hw1020/appformer/events", + "assignees_url": "https://api.github.com/repos/hw1020/appformer/assignees{/user}", + "branches_url": "https://api.github.com/repos/hw1020/appformer/branches{/branch}", + "tags_url": "https://api.github.com/repos/hw1020/appformer/tags", + "blobs_url": "https://api.github.com/repos/hw1020/appformer/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/hw1020/appformer/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/hw1020/appformer/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/hw1020/appformer/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/hw1020/appformer/statuses/{sha}", + "languages_url": "https://api.github.com/repos/hw1020/appformer/languages", + "stargazers_url": "https://api.github.com/repos/hw1020/appformer/stargazers", + "contributors_url": "https://api.github.com/repos/hw1020/appformer/contributors", + "subscribers_url": "https://api.github.com/repos/hw1020/appformer/subscribers", + "subscription_url": "https://api.github.com/repos/hw1020/appformer/subscription", + "commits_url": "https://api.github.com/repos/hw1020/appformer/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/hw1020/appformer/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/hw1020/appformer/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/hw1020/appformer/issues/comments{/number}", + "contents_url": "https://api.github.com/repos/hw1020/appformer/contents/{+path}", + "compare_url": "https://api.github.com/repos/hw1020/appformer/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/hw1020/appformer/merges", + "archive_url": "https://api.github.com/repos/hw1020/appformer/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/hw1020/appformer/downloads", + "issues_url": "https://api.github.com/repos/hw1020/appformer/issues{/number}", + "pulls_url": "https://api.github.com/repos/hw1020/appformer/pulls{/number}", + "milestones_url": "https://api.github.com/repos/hw1020/appformer/milestones{/number}", + "notifications_url": "https://api.github.com/repos/hw1020/appformer/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/hw1020/appformer/labels{/name}", + "releases_url": "https://api.github.com/repos/hw1020/appformer/releases{/id}", + "deployments_url": "https://api.github.com/repos/hw1020/appformer/deployments", + "created_at": "2020-08-21T05:57:58Z", + "updated_at": "2020-08-21T05:58:00Z", + "pushed_at": "2020-08-20T19:56:01Z", + "git_url": "git://github.com/hw1020/appformer.git", + "ssh_url": "git@github.com:hw1020/appformer.git", + "clone_url": "https://github.com/hw1020/appformer.git", + "svn_url": "https://github.com/hw1020/appformer", + "homepage": null, + "size": 54886, + "stargazers_count": 0, + "watchers_count": 0, + "language": null, + "has_issues": false, + "has_projects": true, + "has_downloads": true, + "has_wiki": true, + "has_pages": false, + "forks_count": 0, + "mirror_url": null, + "archived": false, + "disabled": false, + "open_issues_count": 0, + "license": { + "key": "apache-2.0", + "name": "Apache License 2.0", + "spdx_id": "Apache-2.0", + "url": "https://api.github.com/licenses/apache-2.0", + "node_id": "MDc6TGljZW5zZTI=" + }, + "forks": 0, + "open_issues": 0, + "watchers": 0, + "default_branch": "main" + }, + { + "id": 288657587, + "node_id": "MDEwOlJlcG9zaXRvcnkyODg2NTc1ODc=", + "name": "appformer", + "full_name": "vpellegrino/appformer", + "private": false, + "owner": { + "login": "vpellegrino", + "id": 22591802, + "node_id": "MDQ6VXNlcjIyNTkxODAy", + "avatar_url": "https://avatars0.githubusercontent.com/u/22591802?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/vpellegrino", + "html_url": "https://github.com/vpellegrino", + "followers_url": "https://api.github.com/users/vpellegrino/followers", + "following_url": "https://api.github.com/users/vpellegrino/following{/other_user}", + "gists_url": "https://api.github.com/users/vpellegrino/gists{/gist_id}", + "starred_url": "https://api.github.com/users/vpellegrino/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/vpellegrino/subscriptions", + "organizations_url": "https://api.github.com/users/vpellegrino/orgs", + "repos_url": "https://api.github.com/users/vpellegrino/repos", + "events_url": "https://api.github.com/users/vpellegrino/events{/privacy}", + "received_events_url": "https://api.github.com/users/vpellegrino/received_events", + "type": "User", + "site_admin": false + }, + "html_url": "https://github.com/vpellegrino/appformer", + "description": "A web platform to rapidly build forms for data management and business automation.", + "fork": true, + "url": "https://api.github.com/repos/vpellegrino/appformer", + "forks_url": "https://api.github.com/repos/vpellegrino/appformer/forks", + "keys_url": "https://api.github.com/repos/vpellegrino/appformer/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/vpellegrino/appformer/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/vpellegrino/appformer/teams", + "hooks_url": "https://api.github.com/repos/vpellegrino/appformer/hooks", + "issue_events_url": "https://api.github.com/repos/vpellegrino/appformer/issues/events{/number}", + "events_url": "https://api.github.com/repos/vpellegrino/appformer/events", + "assignees_url": "https://api.github.com/repos/vpellegrino/appformer/assignees{/user}", + "branches_url": "https://api.github.com/repos/vpellegrino/appformer/branches{/branch}", + "tags_url": "https://api.github.com/repos/vpellegrino/appformer/tags", + "blobs_url": "https://api.github.com/repos/vpellegrino/appformer/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/vpellegrino/appformer/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/vpellegrino/appformer/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/vpellegrino/appformer/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/vpellegrino/appformer/statuses/{sha}", + "languages_url": "https://api.github.com/repos/vpellegrino/appformer/languages", + "stargazers_url": "https://api.github.com/repos/vpellegrino/appformer/stargazers", + "contributors_url": "https://api.github.com/repos/vpellegrino/appformer/contributors", + "subscribers_url": "https://api.github.com/repos/vpellegrino/appformer/subscribers", + "subscription_url": "https://api.github.com/repos/vpellegrino/appformer/subscription", + "commits_url": "https://api.github.com/repos/vpellegrino/appformer/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/vpellegrino/appformer/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/vpellegrino/appformer/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/vpellegrino/appformer/issues/comments{/number}", + "contents_url": "https://api.github.com/repos/vpellegrino/appformer/contents/{+path}", + "compare_url": "https://api.github.com/repos/vpellegrino/appformer/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/vpellegrino/appformer/merges", + "archive_url": "https://api.github.com/repos/vpellegrino/appformer/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/vpellegrino/appformer/downloads", + "issues_url": "https://api.github.com/repos/vpellegrino/appformer/issues{/number}", + "pulls_url": "https://api.github.com/repos/vpellegrino/appformer/pulls{/number}", + "milestones_url": "https://api.github.com/repos/vpellegrino/appformer/milestones{/number}", + "notifications_url": "https://api.github.com/repos/vpellegrino/appformer/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/vpellegrino/appformer/labels{/name}", + "releases_url": "https://api.github.com/repos/vpellegrino/appformer/releases{/id}", + "deployments_url": "https://api.github.com/repos/vpellegrino/appformer/deployments", + "created_at": "2020-08-19T06:56:44Z", + "updated_at": "2020-08-19T06:56:45Z", + "pushed_at": "2020-08-18T21:46:34Z", + "git_url": "git://github.com/vpellegrino/appformer.git", + "ssh_url": "git@github.com:vpellegrino/appformer.git", + "clone_url": "https://github.com/vpellegrino/appformer.git", + "svn_url": "https://github.com/vpellegrino/appformer", + "homepage": null, + "size": 54886, + "stargazers_count": 0, + "watchers_count": 0, + "language": null, + "has_issues": false, + "has_projects": true, + "has_downloads": true, + "has_wiki": true, + "has_pages": false, + "forks_count": 0, + "mirror_url": null, + "archived": false, + "disabled": false, + "open_issues_count": 0, + "license": { + "key": "apache-2.0", + "name": "Apache License 2.0", + "spdx_id": "Apache-2.0", + "url": "https://api.github.com/licenses/apache-2.0", + "node_id": "MDc6TGljZW5zZTI=" + }, + "forks": 0, + "open_issues": 0, + "watchers": 0, + "default_branch": "main" + }, + { + "id": 284051049, + "node_id": "MDEwOlJlcG9zaXRvcnkyODQwNTEwNDk=", + "name": "appformer", + "full_name": "ljmotta/appformer", + "private": false, + "owner": { + "login": "ljmotta", + "id": 24302289, + "node_id": "MDQ6VXNlcjI0MzAyMjg5", + "avatar_url": "https://avatars2.githubusercontent.com/u/24302289?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/ljmotta", + "html_url": "https://github.com/ljmotta", + "followers_url": "https://api.github.com/users/ljmotta/followers", + "following_url": "https://api.github.com/users/ljmotta/following{/other_user}", + "gists_url": "https://api.github.com/users/ljmotta/gists{/gist_id}", + "starred_url": "https://api.github.com/users/ljmotta/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/ljmotta/subscriptions", + "organizations_url": "https://api.github.com/users/ljmotta/orgs", + "repos_url": "https://api.github.com/users/ljmotta/repos", + "events_url": "https://api.github.com/users/ljmotta/events{/privacy}", + "received_events_url": "https://api.github.com/users/ljmotta/received_events", + "type": "User", + "site_admin": false + }, + "html_url": "https://github.com/ljmotta/appformer", + "description": "A web platform to rapidly build forms for data management and business automation.", + "fork": true, + "url": "https://api.github.com/repos/ljmotta/appformer", + "forks_url": "https://api.github.com/repos/ljmotta/appformer/forks", + "keys_url": "https://api.github.com/repos/ljmotta/appformer/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/ljmotta/appformer/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/ljmotta/appformer/teams", + "hooks_url": "https://api.github.com/repos/ljmotta/appformer/hooks", + "issue_events_url": "https://api.github.com/repos/ljmotta/appformer/issues/events{/number}", + "events_url": "https://api.github.com/repos/ljmotta/appformer/events", + "assignees_url": "https://api.github.com/repos/ljmotta/appformer/assignees{/user}", + "branches_url": "https://api.github.com/repos/ljmotta/appformer/branches{/branch}", + "tags_url": "https://api.github.com/repos/ljmotta/appformer/tags", + "blobs_url": "https://api.github.com/repos/ljmotta/appformer/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/ljmotta/appformer/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/ljmotta/appformer/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/ljmotta/appformer/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/ljmotta/appformer/statuses/{sha}", + "languages_url": "https://api.github.com/repos/ljmotta/appformer/languages", + "stargazers_url": "https://api.github.com/repos/ljmotta/appformer/stargazers", + "contributors_url": "https://api.github.com/repos/ljmotta/appformer/contributors", + "subscribers_url": "https://api.github.com/repos/ljmotta/appformer/subscribers", + "subscription_url": "https://api.github.com/repos/ljmotta/appformer/subscription", + "commits_url": "https://api.github.com/repos/ljmotta/appformer/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/ljmotta/appformer/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/ljmotta/appformer/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/ljmotta/appformer/issues/comments{/number}", + "contents_url": "https://api.github.com/repos/ljmotta/appformer/contents/{+path}", + "compare_url": "https://api.github.com/repos/ljmotta/appformer/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/ljmotta/appformer/merges", + "archive_url": "https://api.github.com/repos/ljmotta/appformer/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/ljmotta/appformer/downloads", + "issues_url": "https://api.github.com/repos/ljmotta/appformer/issues{/number}", + "pulls_url": "https://api.github.com/repos/ljmotta/appformer/pulls{/number}", + "milestones_url": "https://api.github.com/repos/ljmotta/appformer/milestones{/number}", + "notifications_url": "https://api.github.com/repos/ljmotta/appformer/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/ljmotta/appformer/labels{/name}", + "releases_url": "https://api.github.com/repos/ljmotta/appformer/releases{/id}", + "deployments_url": "https://api.github.com/repos/ljmotta/appformer/deployments", + "created_at": "2020-07-31T14:07:35Z", + "updated_at": "2020-07-31T14:07:37Z", + "pushed_at": "2020-07-31T22:30:36Z", + "git_url": "git://github.com/ljmotta/appformer.git", + "ssh_url": "git@github.com:ljmotta/appformer.git", + "clone_url": "https://github.com/ljmotta/appformer.git", + "svn_url": "https://github.com/ljmotta/appformer", + "homepage": null, + "size": 54757, + "stargazers_count": 0, + "watchers_count": 0, + "language": null, + "has_issues": false, + "has_projects": true, + "has_downloads": true, + "has_wiki": true, + "has_pages": false, + "forks_count": 0, + "mirror_url": null, + "archived": false, + "disabled": false, + "open_issues_count": 0, + "license": { + "key": "apache-2.0", + "name": "Apache License 2.0", + "spdx_id": "Apache-2.0", + "url": "https://api.github.com/licenses/apache-2.0", + "node_id": "MDc6TGljZW5zZTI=" + }, + "forks": 0, + "open_issues": 0, + "watchers": 0, + "default_branch": "main" + }, + { + "id": 283213313, + "node_id": "MDEwOlJlcG9zaXRvcnkyODMyMTMzMTM=", + "name": "appformer", + "full_name": "Prodaxis/appformer", + "private": false, + "owner": { + "login": "Prodaxis", + "id": 46787532, + "node_id": "MDEyOk9yZ2FuaXphdGlvbjQ2Nzg3NTMy", + "avatar_url": "https://avatars1.githubusercontent.com/u/46787532?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/Prodaxis", + "html_url": "https://github.com/Prodaxis", + "followers_url": "https://api.github.com/users/Prodaxis/followers", + "following_url": "https://api.github.com/users/Prodaxis/following{/other_user}", + "gists_url": "https://api.github.com/users/Prodaxis/gists{/gist_id}", + "starred_url": "https://api.github.com/users/Prodaxis/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/Prodaxis/subscriptions", + "organizations_url": "https://api.github.com/users/Prodaxis/orgs", + "repos_url": "https://api.github.com/users/Prodaxis/repos", + "events_url": "https://api.github.com/users/Prodaxis/events{/privacy}", + "received_events_url": "https://api.github.com/users/Prodaxis/received_events", + "type": "Organization", + "site_admin": false + }, + "html_url": "https://github.com/Prodaxis/appformer", + "description": "A web platform to rapidly build forms for data management and business automation.", + "fork": true, + "url": "https://api.github.com/repos/Prodaxis/appformer", + "forks_url": "https://api.github.com/repos/Prodaxis/appformer/forks", + "keys_url": "https://api.github.com/repos/Prodaxis/appformer/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/Prodaxis/appformer/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/Prodaxis/appformer/teams", + "hooks_url": "https://api.github.com/repos/Prodaxis/appformer/hooks", + "issue_events_url": "https://api.github.com/repos/Prodaxis/appformer/issues/events{/number}", + "events_url": "https://api.github.com/repos/Prodaxis/appformer/events", + "assignees_url": "https://api.github.com/repos/Prodaxis/appformer/assignees{/user}", + "branches_url": "https://api.github.com/repos/Prodaxis/appformer/branches{/branch}", + "tags_url": "https://api.github.com/repos/Prodaxis/appformer/tags", + "blobs_url": "https://api.github.com/repos/Prodaxis/appformer/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/Prodaxis/appformer/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/Prodaxis/appformer/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/Prodaxis/appformer/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/Prodaxis/appformer/statuses/{sha}", + "languages_url": "https://api.github.com/repos/Prodaxis/appformer/languages", + "stargazers_url": "https://api.github.com/repos/Prodaxis/appformer/stargazers", + "contributors_url": "https://api.github.com/repos/Prodaxis/appformer/contributors", + "subscribers_url": "https://api.github.com/repos/Prodaxis/appformer/subscribers", + "subscription_url": "https://api.github.com/repos/Prodaxis/appformer/subscription", + "commits_url": "https://api.github.com/repos/Prodaxis/appformer/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/Prodaxis/appformer/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/Prodaxis/appformer/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/Prodaxis/appformer/issues/comments{/number}", + "contents_url": "https://api.github.com/repos/Prodaxis/appformer/contents/{+path}", + "compare_url": "https://api.github.com/repos/Prodaxis/appformer/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/Prodaxis/appformer/merges", + "archive_url": "https://api.github.com/repos/Prodaxis/appformer/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/Prodaxis/appformer/downloads", + "issues_url": "https://api.github.com/repos/Prodaxis/appformer/issues{/number}", + "pulls_url": "https://api.github.com/repos/Prodaxis/appformer/pulls{/number}", + "milestones_url": "https://api.github.com/repos/Prodaxis/appformer/milestones{/number}", + "notifications_url": "https://api.github.com/repos/Prodaxis/appformer/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/Prodaxis/appformer/labels{/name}", + "releases_url": "https://api.github.com/repos/Prodaxis/appformer/releases{/id}", + "deployments_url": "https://api.github.com/repos/Prodaxis/appformer/deployments", + "created_at": "2020-07-28T12:59:33Z", + "updated_at": "2020-08-24T13:38:06Z", + "pushed_at": "2020-08-24T13:38:03Z", + "git_url": "git://github.com/Prodaxis/appformer.git", + "ssh_url": "git@github.com:Prodaxis/appformer.git", + "clone_url": "https://github.com/Prodaxis/appformer.git", + "svn_url": "https://github.com/Prodaxis/appformer", + "homepage": null, + "size": 49902, + "stargazers_count": 0, + "watchers_count": 0, + "language": "Java", + "has_issues": false, + "has_projects": true, + "has_downloads": true, + "has_wiki": true, + "has_pages": false, + "forks_count": 0, + "mirror_url": null, + "archived": false, + "disabled": false, + "open_issues_count": 0, + "license": { + "key": "apache-2.0", + "name": "Apache License 2.0", + "spdx_id": "Apache-2.0", + "url": "https://api.github.com/licenses/apache-2.0", + "node_id": "MDc6TGljZW5zZTI=" + }, + "forks": 0, + "open_issues": 0, + "watchers": 0, + "default_branch": "7.38.0.Final" + }, + { + "id": 281405439, + "node_id": "MDEwOlJlcG9zaXRvcnkyODE0MDU0Mzk=", + "name": "appformer", + "full_name": "afalhambra/appformer", + "private": false, + "owner": { + "login": "afalhambra", + "id": 33941520, + "node_id": "MDQ6VXNlcjMzOTQxNTIw", + "avatar_url": "https://avatars1.githubusercontent.com/u/33941520?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/afalhambra", + "html_url": "https://github.com/afalhambra", + "followers_url": "https://api.github.com/users/afalhambra/followers", + "following_url": "https://api.github.com/users/afalhambra/following{/other_user}", + "gists_url": "https://api.github.com/users/afalhambra/gists{/gist_id}", + "starred_url": "https://api.github.com/users/afalhambra/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/afalhambra/subscriptions", + "organizations_url": "https://api.github.com/users/afalhambra/orgs", + "repos_url": "https://api.github.com/users/afalhambra/repos", + "events_url": "https://api.github.com/users/afalhambra/events{/privacy}", + "received_events_url": "https://api.github.com/users/afalhambra/received_events", + "type": "User", + "site_admin": false + }, + "html_url": "https://github.com/afalhambra/appformer", + "description": "A web platform to rapidly build forms for data management and business automation.", + "fork": true, + "url": "https://api.github.com/repos/afalhambra/appformer", + "forks_url": "https://api.github.com/repos/afalhambra/appformer/forks", + "keys_url": "https://api.github.com/repos/afalhambra/appformer/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/afalhambra/appformer/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/afalhambra/appformer/teams", + "hooks_url": "https://api.github.com/repos/afalhambra/appformer/hooks", + "issue_events_url": "https://api.github.com/repos/afalhambra/appformer/issues/events{/number}", + "events_url": "https://api.github.com/repos/afalhambra/appformer/events", + "assignees_url": "https://api.github.com/repos/afalhambra/appformer/assignees{/user}", + "branches_url": "https://api.github.com/repos/afalhambra/appformer/branches{/branch}", + "tags_url": "https://api.github.com/repos/afalhambra/appformer/tags", + "blobs_url": "https://api.github.com/repos/afalhambra/appformer/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/afalhambra/appformer/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/afalhambra/appformer/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/afalhambra/appformer/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/afalhambra/appformer/statuses/{sha}", + "languages_url": "https://api.github.com/repos/afalhambra/appformer/languages", + "stargazers_url": "https://api.github.com/repos/afalhambra/appformer/stargazers", + "contributors_url": "https://api.github.com/repos/afalhambra/appformer/contributors", + "subscribers_url": "https://api.github.com/repos/afalhambra/appformer/subscribers", + "subscription_url": "https://api.github.com/repos/afalhambra/appformer/subscription", + "commits_url": "https://api.github.com/repos/afalhambra/appformer/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/afalhambra/appformer/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/afalhambra/appformer/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/afalhambra/appformer/issues/comments{/number}", + "contents_url": "https://api.github.com/repos/afalhambra/appformer/contents/{+path}", + "compare_url": "https://api.github.com/repos/afalhambra/appformer/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/afalhambra/appformer/merges", + "archive_url": "https://api.github.com/repos/afalhambra/appformer/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/afalhambra/appformer/downloads", + "issues_url": "https://api.github.com/repos/afalhambra/appformer/issues{/number}", + "pulls_url": "https://api.github.com/repos/afalhambra/appformer/pulls{/number}", + "milestones_url": "https://api.github.com/repos/afalhambra/appformer/milestones{/number}", + "notifications_url": "https://api.github.com/repos/afalhambra/appformer/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/afalhambra/appformer/labels{/name}", + "releases_url": "https://api.github.com/repos/afalhambra/appformer/releases{/id}", + "deployments_url": "https://api.github.com/repos/afalhambra/appformer/deployments", + "created_at": "2020-07-21T13:27:42Z", + "updated_at": "2020-07-21T13:27:44Z", + "pushed_at": "2020-07-21T12:25:48Z", + "git_url": "git://github.com/afalhambra/appformer.git", + "ssh_url": "git@github.com:afalhambra/appformer.git", + "clone_url": "https://github.com/afalhambra/appformer.git", + "svn_url": "https://github.com/afalhambra/appformer", + "homepage": null, + "size": 54608, + "stargazers_count": 0, + "watchers_count": 0, + "language": null, + "has_issues": false, + "has_projects": true, + "has_downloads": true, + "has_wiki": true, + "has_pages": false, + "forks_count": 0, + "mirror_url": null, + "archived": false, + "disabled": false, + "open_issues_count": 0, + "license": { + "key": "apache-2.0", + "name": "Apache License 2.0", + "spdx_id": "Apache-2.0", + "url": "https://api.github.com/licenses/apache-2.0", + "node_id": "MDc6TGljZW5zZTI=" + }, + "forks": 0, + "open_issues": 0, + "watchers": 0, + "default_branch": "main" + }, + { + "id": 276890764, + "node_id": "MDEwOlJlcG9zaXRvcnkyNzY4OTA3NjQ=", + "name": "appformer", + "full_name": "HomeIncorporated/appformer", + "private": false, + "owner": { + "login": "HomeIncorporated", + "id": 67231900, + "node_id": "MDEyOk9yZ2FuaXphdGlvbjY3MjMxOTAw", + "avatar_url": "https://avatars2.githubusercontent.com/u/67231900?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/HomeIncorporated", + "html_url": "https://github.com/HomeIncorporated", + "followers_url": "https://api.github.com/users/HomeIncorporated/followers", + "following_url": "https://api.github.com/users/HomeIncorporated/following{/other_user}", + "gists_url": "https://api.github.com/users/HomeIncorporated/gists{/gist_id}", + "starred_url": "https://api.github.com/users/HomeIncorporated/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/HomeIncorporated/subscriptions", + "organizations_url": "https://api.github.com/users/HomeIncorporated/orgs", + "repos_url": "https://api.github.com/users/HomeIncorporated/repos", + "events_url": "https://api.github.com/users/HomeIncorporated/events{/privacy}", + "received_events_url": "https://api.github.com/users/HomeIncorporated/received_events", + "type": "Organization", + "site_admin": false + }, + "html_url": "https://github.com/HomeIncorporated/appformer", + "description": "A web platform to rapidly build forms for data management and business automation.", + "fork": true, + "url": "https://api.github.com/repos/HomeIncorporated/appformer", + "forks_url": "https://api.github.com/repos/HomeIncorporated/appformer/forks", + "keys_url": "https://api.github.com/repos/HomeIncorporated/appformer/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/HomeIncorporated/appformer/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/HomeIncorporated/appformer/teams", + "hooks_url": "https://api.github.com/repos/HomeIncorporated/appformer/hooks", + "issue_events_url": "https://api.github.com/repos/HomeIncorporated/appformer/issues/events{/number}", + "events_url": "https://api.github.com/repos/HomeIncorporated/appformer/events", + "assignees_url": "https://api.github.com/repos/HomeIncorporated/appformer/assignees{/user}", + "branches_url": "https://api.github.com/repos/HomeIncorporated/appformer/branches{/branch}", + "tags_url": "https://api.github.com/repos/HomeIncorporated/appformer/tags", + "blobs_url": "https://api.github.com/repos/HomeIncorporated/appformer/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/HomeIncorporated/appformer/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/HomeIncorporated/appformer/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/HomeIncorporated/appformer/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/HomeIncorporated/appformer/statuses/{sha}", + "languages_url": "https://api.github.com/repos/HomeIncorporated/appformer/languages", + "stargazers_url": "https://api.github.com/repos/HomeIncorporated/appformer/stargazers", + "contributors_url": "https://api.github.com/repos/HomeIncorporated/appformer/contributors", + "subscribers_url": "https://api.github.com/repos/HomeIncorporated/appformer/subscribers", + "subscription_url": "https://api.github.com/repos/HomeIncorporated/appformer/subscription", + "commits_url": "https://api.github.com/repos/HomeIncorporated/appformer/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/HomeIncorporated/appformer/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/HomeIncorporated/appformer/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/HomeIncorporated/appformer/issues/comments{/number}", + "contents_url": "https://api.github.com/repos/HomeIncorporated/appformer/contents/{+path}", + "compare_url": "https://api.github.com/repos/HomeIncorporated/appformer/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/HomeIncorporated/appformer/merges", + "archive_url": "https://api.github.com/repos/HomeIncorporated/appformer/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/HomeIncorporated/appformer/downloads", + "issues_url": "https://api.github.com/repos/HomeIncorporated/appformer/issues{/number}", + "pulls_url": "https://api.github.com/repos/HomeIncorporated/appformer/pulls{/number}", + "milestones_url": "https://api.github.com/repos/HomeIncorporated/appformer/milestones{/number}", + "notifications_url": "https://api.github.com/repos/HomeIncorporated/appformer/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/HomeIncorporated/appformer/labels{/name}", + "releases_url": "https://api.github.com/repos/HomeIncorporated/appformer/releases{/id}", + "deployments_url": "https://api.github.com/repos/HomeIncorporated/appformer/deployments", + "created_at": "2020-07-03T12:07:46Z", + "updated_at": "2020-07-03T12:07:48Z", + "pushed_at": "2020-07-03T11:39:49Z", + "git_url": "git://github.com/HomeIncorporated/appformer.git", + "ssh_url": "git@github.com:HomeIncorporated/appformer.git", + "clone_url": "https://github.com/HomeIncorporated/appformer.git", + "svn_url": "https://github.com/HomeIncorporated/appformer", + "homepage": null, + "size": 54433, + "stargazers_count": 0, + "watchers_count": 0, + "language": null, + "has_issues": false, + "has_projects": true, + "has_downloads": true, + "has_wiki": true, + "has_pages": false, + "forks_count": 0, + "mirror_url": null, + "archived": false, + "disabled": false, + "open_issues_count": 0, + "license": { + "key": "apache-2.0", + "name": "Apache License 2.0", + "spdx_id": "Apache-2.0", + "url": "https://api.github.com/licenses/apache-2.0", + "node_id": "MDc6TGljZW5zZTI=" + }, + "forks": 0, + "open_issues": 0, + "watchers": 0, + "default_branch": "main" + } +] \ No newline at end of file diff --git a/jenkins-pipeline-shared-libraries/test/resources/logback-test.xml b/jenkins-pipeline-shared-libraries/test/resources/logback-test.xml new file mode 100644 index 000000000..f6dd8289b --- /dev/null +++ b/jenkins-pipeline-shared-libraries/test/resources/logback-test.xml @@ -0,0 +1,13 @@ + + + + + %d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n + + + + + + + + \ No newline at end of file diff --git a/jenkins-pipeline-shared-libraries/test/resources/logback.xml b/jenkins-pipeline-shared-libraries/test/resources/logback.xml new file mode 100644 index 000000000..f6dd8289b --- /dev/null +++ b/jenkins-pipeline-shared-libraries/test/resources/logback.xml @@ -0,0 +1,13 @@ + + + + + %d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n + + + + + + + + \ No newline at end of file diff --git a/jenkins-pipeline-shared-libraries/test/resources/project-branches-mapping.properties b/jenkins-pipeline-shared-libraries/test/resources/project-branches-mapping.properties new file mode 100644 index 000000000..1cdbb4925 --- /dev/null +++ b/jenkins-pipeline-shared-libraries/test/resources/project-branches-mapping.properties @@ -0,0 +1,2 @@ +optaplanner.main=7.x +optaplanner.trigger.7.x=main \ No newline at end of file diff --git a/jenkins-pipeline-shared-libraries/test/resources/pull_request_empty.json b/jenkins-pipeline-shared-libraries/test/resources/pull_request_empty.json new file mode 100644 index 000000000..c44dc44f3 --- /dev/null +++ b/jenkins-pipeline-shared-libraries/test/resources/pull_request_empty.json @@ -0,0 +1,3 @@ +[ + +] \ No newline at end of file diff --git a/jenkins-pipeline-shared-libraries/test/resources/pull_request_not_empty.json b/jenkins-pipeline-shared-libraries/test/resources/pull_request_not_empty.json new file mode 100644 index 000000000..c0d6e3973 --- /dev/null +++ b/jenkins-pipeline-shared-libraries/test/resources/pull_request_not_empty.json @@ -0,0 +1,1023 @@ +[ + { + "url": "https://api.github.com/repos/kiegroup/lienzo-tests/pulls/95", + "id": 448263493, + "node_id": "MDExOlB1bGxSZXF1ZXN0NDQ4MjYzNDkz", + "html_url": "https://github.com/kiegroup/lienzo-tests/pull/95", + "diff_url": "https://github.com/kiegroup/lienzo-tests/pull/95.diff", + "patch_url": "https://github.com/kiegroup/lienzo-tests/pull/95.patch", + "issue_url": "https://api.github.com/repos/kiegroup/lienzo-tests/issues/95", + "number": 95, + "state": "open", + "locked": false, + "title": "[TEST] BXMSPROD860 test readme updated. DO NOT MERGE", + "user": { + "login": "whatevergroup", + "id": 25130444, + "node_id": "MDQ6VXNlcjI1MTMwNDQ0", + "avatar_url": "https://avatars3.githubusercontent.com/u/25130444?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/whatevergroup", + "html_url": "https://github.com/whatevergroup", + "followers_url": "https://api.github.com/users/whatevergroup/followers", + "following_url": "https://api.github.com/users/whatevergroup/following{/other_user}", + "gists_url": "https://api.github.com/users/whatevergroup/gists{/gist_id}", + "starred_url": "https://api.github.com/users/whatevergroup/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/whatevergroup/subscriptions", + "organizations_url": "https://api.github.com/users/whatevergroup/orgs", + "repos_url": "https://api.github.com/users/whatevergroup/repos", + "events_url": "https://api.github.com/users/whatevergroup/events{/privacy}", + "received_events_url": "https://api.github.com/users/whatevergroup/received_events", + "type": "User", + "site_admin": false + }, + "body": "", + "created_at": "2020-07-13T13:39:41Z", + "updated_at": "2020-07-13T13:39:41Z", + "closed_at": null, + "merged_at": null, + "merge_commit_sha": "6aa7e42b102d7f8f6afd4978c006a14b8d4887e6", + "assignee": null, + "assignees": [ + + ], + "requested_reviewers": [ + + ], + "requested_teams": [ + + ], + "labels": [ + { + "id": 1739215841, + "node_id": "MDU6TGFiZWwxNzM5MjE1ODQx", + "url": "https://api.github.com/repos/kiegroup/lienzo-tests/labels/DO%20NOT%20MERGE", + "name": "DO NOT MERGE", + "color": "ff0000", + "default": false, + "description": "" + } + ], + "milestone": null, + "draft": true, + "commits_url": "https://api.github.com/repos/kiegroup/lienzo-tests/pulls/95/commits", + "review_comments_url": "https://api.github.com/repos/kiegroup/lienzo-tests/pulls/95/comments", + "review_comment_url": "https://api.github.com/repos/kiegroup/lienzo-tests/pulls/comments{/number}", + "comments_url": "https://api.github.com/repos/kiegroup/lienzo-tests/issues/95/comments", + "statuses_url": "https://api.github.com/repos/kiegroup/lienzo-tests/statuses/d9310dfcc5e946e21a0218baf6ab1777f83c2f6c", + "head": { + "label": "whatevergroup:BXMSPROD860_test", + "ref": "BXMSPROD860_test", + "sha": "d9310dfcc5e946e21a0218baf6ab1777f83c2f6c", + "user": { + "login": "whatevergroup", + "id": 25130444, + "node_id": "MDQ6VXNlcjI1MTMwNDQ0", + "avatar_url": "https://avatars3.githubusercontent.com/u/25130444?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/whatevergroup", + "html_url": "https://github.com/whatevergroup", + "followers_url": "https://api.github.com/users/whatevergroup/followers", + "following_url": "https://api.github.com/users/whatevergroup/following{/other_user}", + "gists_url": "https://api.github.com/users/whatevergroup/gists{/gist_id}", + "starred_url": "https://api.github.com/users/whatevergroup/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/whatevergroup/subscriptions", + "organizations_url": "https://api.github.com/users/whatevergroup/orgs", + "repos_url": "https://api.github.com/users/whatevergroup/repos", + "events_url": "https://api.github.com/users/whatevergroup/events{/privacy}", + "received_events_url": "https://api.github.com/users/whatevergroup/received_events", + "type": "User", + "site_admin": false + }, + "repo": { + "id": 225823425, + "node_id": "MDEwOlJlcG9zaXRvcnkyMjU4MjM0MjU=", + "name": "lienzo-tests", + "full_name": "whatevergroup/lienzo-tests", + "private": false, + "owner": { + "login": "whatevergroup", + "id": 25130444, + "node_id": "MDQ6VXNlcjI1MTMwNDQ0", + "avatar_url": "https://avatars3.githubusercontent.com/u/25130444?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/whatevergroup", + "html_url": "https://github.com/whatevergroup", + "followers_url": "https://api.github.com/users/whatevergroup/followers", + "following_url": "https://api.github.com/users/whatevergroup/following{/other_user}", + "gists_url": "https://api.github.com/users/whatevergroup/gists{/gist_id}", + "starred_url": "https://api.github.com/users/whatevergroup/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/whatevergroup/subscriptions", + "organizations_url": "https://api.github.com/users/whatevergroup/orgs", + "repos_url": "https://api.github.com/users/whatevergroup/repos", + "events_url": "https://api.github.com/users/whatevergroup/events{/privacy}", + "received_events_url": "https://api.github.com/users/whatevergroup/received_events", + "type": "User", + "site_admin": false + }, + "html_url": "https://github.com/whatevergroup/lienzo-tests", + "description": null, + "fork": true, + "url": "https://api.github.com/repos/whatevergroup/lienzo-tests", + "forks_url": "https://api.github.com/repos/whatevergroup/lienzo-tests/forks", + "keys_url": "https://api.github.com/repos/whatevergroup/lienzo-tests/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/whatevergroup/lienzo-tests/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/whatevergroup/lienzo-tests/teams", + "hooks_url": "https://api.github.com/repos/whatevergroup/lienzo-tests/hooks", + "issue_events_url": "https://api.github.com/repos/whatevergroup/lienzo-tests/issues/events{/number}", + "events_url": "https://api.github.com/repos/whatevergroup/lienzo-tests/events", + "assignees_url": "https://api.github.com/repos/whatevergroup/lienzo-tests/assignees{/user}", + "branches_url": "https://api.github.com/repos/whatevergroup/lienzo-tests/branches{/branch}", + "tags_url": "https://api.github.com/repos/whatevergroup/lienzo-tests/tags", + "blobs_url": "https://api.github.com/repos/whatevergroup/lienzo-tests/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/whatevergroup/lienzo-tests/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/whatevergroup/lienzo-tests/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/whatevergroup/lienzo-tests/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/whatevergroup/lienzo-tests/statuses/{sha}", + "languages_url": "https://api.github.com/repos/whatevergroup/lienzo-tests/languages", + "stargazers_url": "https://api.github.com/repos/whatevergroup/lienzo-tests/stargazers", + "contributors_url": "https://api.github.com/repos/whatevergroup/lienzo-tests/contributors", + "subscribers_url": "https://api.github.com/repos/whatevergroup/lienzo-tests/subscribers", + "subscription_url": "https://api.github.com/repos/whatevergroup/lienzo-tests/subscription", + "commits_url": "https://api.github.com/repos/whatevergroup/lienzo-tests/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/whatevergroup/lienzo-tests/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/whatevergroup/lienzo-tests/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/whatevergroup/lienzo-tests/issues/comments{/number}", + "contents_url": "https://api.github.com/repos/whatevergroup/lienzo-tests/contents/{+path}", + "compare_url": "https://api.github.com/repos/whatevergroup/lienzo-tests/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/whatevergroup/lienzo-tests/merges", + "archive_url": "https://api.github.com/repos/whatevergroup/lienzo-tests/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/whatevergroup/lienzo-tests/downloads", + "issues_url": "https://api.github.com/repos/whatevergroup/lienzo-tests/issues{/number}", + "pulls_url": "https://api.github.com/repos/whatevergroup/lienzo-tests/pulls{/number}", + "milestones_url": "https://api.github.com/repos/whatevergroup/lienzo-tests/milestones{/number}", + "notifications_url": "https://api.github.com/repos/whatevergroup/lienzo-tests/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/whatevergroup/lienzo-tests/labels{/name}", + "releases_url": "https://api.github.com/repos/whatevergroup/lienzo-tests/releases{/id}", + "deployments_url": "https://api.github.com/repos/whatevergroup/lienzo-tests/deployments", + "created_at": "2019-12-04T09:02:28Z", + "updated_at": "2020-07-10T07:56:43Z", + "pushed_at": "2020-07-13T13:39:18Z", + "git_url": "git://github.com/whatevergroup/lienzo-tests.git", + "ssh_url": "git@github.com:whatevergroup/lienzo-tests.git", + "clone_url": "https://github.com/whatevergroup/lienzo-tests.git", + "svn_url": "https://github.com/whatevergroup/lienzo-tests", + "homepage": null, + "size": 507, + "stargazers_count": 0, + "watchers_count": 0, + "language": "Java", + "has_issues": false, + "has_projects": true, + "has_downloads": true, + "has_wiki": true, + "has_pages": false, + "forks_count": 0, + "mirror_url": null, + "archived": false, + "disabled": false, + "open_issues_count": 1, + "license": null, + "forks": 0, + "open_issues": 1, + "watchers": 0, + "default_branch": "main" + } + }, + "base": { + "label": "kiegroup:main", + "ref": "main", + "sha": "6c2f837a819056bed0cc565d22ddd9873f188731", + "user": { + "login": "kiegroup", + "id": 517980, + "node_id": "MDEyOk9yZ2FuaXphdGlvbjUxNzk4MA==", + "avatar_url": "https://avatars3.githubusercontent.com/u/517980?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/kiegroup", + "html_url": "https://github.com/kiegroup", + "followers_url": "https://api.github.com/users/kiegroup/followers", + "following_url": "https://api.github.com/users/kiegroup/following{/other_user}", + "gists_url": "https://api.github.com/users/kiegroup/gists{/gist_id}", + "starred_url": "https://api.github.com/users/kiegroup/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/kiegroup/subscriptions", + "organizations_url": "https://api.github.com/users/kiegroup/orgs", + "repos_url": "https://api.github.com/users/kiegroup/repos", + "events_url": "https://api.github.com/users/kiegroup/events{/privacy}", + "received_events_url": "https://api.github.com/users/kiegroup/received_events", + "type": "Organization", + "site_admin": false + }, + "repo": { + "id": 120047378, + "node_id": "MDEwOlJlcG9zaXRvcnkxMjAwNDczNzg=", + "name": "lienzo-tests", + "full_name": "kiegroup/lienzo-tests", + "private": false, + "owner": { + "login": "kiegroup", + "id": 517980, + "node_id": "MDEyOk9yZ2FuaXphdGlvbjUxNzk4MA==", + "avatar_url": "https://avatars3.githubusercontent.com/u/517980?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/kiegroup", + "html_url": "https://github.com/kiegroup", + "followers_url": "https://api.github.com/users/kiegroup/followers", + "following_url": "https://api.github.com/users/kiegroup/following{/other_user}", + "gists_url": "https://api.github.com/users/kiegroup/gists{/gist_id}", + "starred_url": "https://api.github.com/users/kiegroup/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/kiegroup/subscriptions", + "organizations_url": "https://api.github.com/users/kiegroup/orgs", + "repos_url": "https://api.github.com/users/kiegroup/repos", + "events_url": "https://api.github.com/users/kiegroup/events{/privacy}", + "received_events_url": "https://api.github.com/users/kiegroup/received_events", + "type": "Organization", + "site_admin": false + }, + "html_url": "https://github.com/kiegroup/lienzo-tests", + "description": null, + "fork": true, + "url": "https://api.github.com/repos/kiegroup/lienzo-tests", + "forks_url": "https://api.github.com/repos/kiegroup/lienzo-tests/forks", + "keys_url": "https://api.github.com/repos/kiegroup/lienzo-tests/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/kiegroup/lienzo-tests/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/kiegroup/lienzo-tests/teams", + "hooks_url": "https://api.github.com/repos/kiegroup/lienzo-tests/hooks", + "issue_events_url": "https://api.github.com/repos/kiegroup/lienzo-tests/issues/events{/number}", + "events_url": "https://api.github.com/repos/kiegroup/lienzo-tests/events", + "assignees_url": "https://api.github.com/repos/kiegroup/lienzo-tests/assignees{/user}", + "branches_url": "https://api.github.com/repos/kiegroup/lienzo-tests/branches{/branch}", + "tags_url": "https://api.github.com/repos/kiegroup/lienzo-tests/tags", + "blobs_url": "https://api.github.com/repos/kiegroup/lienzo-tests/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/kiegroup/lienzo-tests/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/kiegroup/lienzo-tests/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/kiegroup/lienzo-tests/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/kiegroup/lienzo-tests/statuses/{sha}", + "languages_url": "https://api.github.com/repos/kiegroup/lienzo-tests/languages", + "stargazers_url": "https://api.github.com/repos/kiegroup/lienzo-tests/stargazers", + "contributors_url": "https://api.github.com/repos/kiegroup/lienzo-tests/contributors", + "subscribers_url": "https://api.github.com/repos/kiegroup/lienzo-tests/subscribers", + "subscription_url": "https://api.github.com/repos/kiegroup/lienzo-tests/subscription", + "commits_url": "https://api.github.com/repos/kiegroup/lienzo-tests/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/kiegroup/lienzo-tests/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/kiegroup/lienzo-tests/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/kiegroup/lienzo-tests/issues/comments{/number}", + "contents_url": "https://api.github.com/repos/kiegroup/lienzo-tests/contents/{+path}", + "compare_url": "https://api.github.com/repos/kiegroup/lienzo-tests/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/kiegroup/lienzo-tests/merges", + "archive_url": "https://api.github.com/repos/kiegroup/lienzo-tests/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/kiegroup/lienzo-tests/downloads", + "issues_url": "https://api.github.com/repos/kiegroup/lienzo-tests/issues{/number}", + "pulls_url": "https://api.github.com/repos/kiegroup/lienzo-tests/pulls{/number}", + "milestones_url": "https://api.github.com/repos/kiegroup/lienzo-tests/milestones{/number}", + "notifications_url": "https://api.github.com/repos/kiegroup/lienzo-tests/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/kiegroup/lienzo-tests/labels{/name}", + "releases_url": "https://api.github.com/repos/kiegroup/lienzo-tests/releases{/id}", + "deployments_url": "https://api.github.com/repos/kiegroup/lienzo-tests/deployments", + "created_at": "2018-02-03T00:57:57Z", + "updated_at": "2020-07-06T09:09:41Z", + "pushed_at": "2020-07-13T13:39:42Z", + "git_url": "git://github.com/kiegroup/lienzo-tests.git", + "ssh_url": "git@github.com:kiegroup/lienzo-tests.git", + "clone_url": "https://github.com/kiegroup/lienzo-tests.git", + "svn_url": "https://github.com/kiegroup/lienzo-tests", + "homepage": null, + "size": 485, + "stargazers_count": 0, + "watchers_count": 0, + "language": "Java", + "has_issues": false, + "has_projects": true, + "has_downloads": true, + "has_wiki": true, + "has_pages": false, + "forks_count": 23, + "mirror_url": null, + "archived": false, + "disabled": false, + "open_issues_count": 3, + "license": null, + "forks": 23, + "open_issues": 3, + "watchers": 0, + "default_branch": "main" + } + }, + "_links": { + "self": { + "href": "https://api.github.com/repos/kiegroup/lienzo-tests/pulls/95" + }, + "html": { + "href": "https://github.com/kiegroup/lienzo-tests/pull/95" + }, + "issue": { + "href": "https://api.github.com/repos/kiegroup/lienzo-tests/issues/95" + }, + "comments": { + "href": "https://api.github.com/repos/kiegroup/lienzo-tests/issues/95/comments" + }, + "review_comments": { + "href": "https://api.github.com/repos/kiegroup/lienzo-tests/pulls/95/comments" + }, + "review_comment": { + "href": "https://api.github.com/repos/kiegroup/lienzo-tests/pulls/comments{/number}" + }, + "commits": { + "href": "https://api.github.com/repos/kiegroup/lienzo-tests/pulls/95/commits" + }, + "statuses": { + "href": "https://api.github.com/repos/kiegroup/lienzo-tests/statuses/d9310dfcc5e946e21a0218baf6ab1777f83c2f6c" + } + }, + "author_association": "NONE", + "active_lock_reason": null + }, + { + "url": "https://api.github.com/repos/kiegroup/lienzo-tests/pulls/94", + "id": 426859384, + "node_id": "MDExOlB1bGxSZXF1ZXN0NDI2ODU5Mzg0", + "html_url": "https://github.com/kiegroup/lienzo-tests/pull/94", + "diff_url": "https://github.com/kiegroup/lienzo-tests/pull/94.diff", + "patch_url": "https://github.com/kiegroup/lienzo-tests/pull/94.patch", + "issue_url": "https://api.github.com/repos/kiegroup/lienzo-tests/issues/94", + "number": 94, + "state": "open", + "locked": false, + "title": "JBPM-9175: Stunner - Usability improvements around selection / drag capabilities", + "user": { + "login": "handreyrc", + "id": 47090342, + "node_id": "MDQ6VXNlcjQ3MDkwMzQy", + "avatar_url": "https://avatars0.githubusercontent.com/u/47090342?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/handreyrc", + "html_url": "https://github.com/handreyrc", + "followers_url": "https://api.github.com/users/handreyrc/followers", + "following_url": "https://api.github.com/users/handreyrc/following{/other_user}", + "gists_url": "https://api.github.com/users/handreyrc/gists{/gist_id}", + "starred_url": "https://api.github.com/users/handreyrc/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/handreyrc/subscriptions", + "organizations_url": "https://api.github.com/users/handreyrc/orgs", + "repos_url": "https://api.github.com/users/handreyrc/repos", + "events_url": "https://api.github.com/users/handreyrc/events{/privacy}", + "received_events_url": "https://api.github.com/users/handreyrc/received_events", + "type": "User", + "site_admin": false + }, + "body": "@romartin, @inodeman : Please, could you review this PR?\r\n\r\nSee https://issues.redhat.com/browse/JBPM-9175\r\n\r\nAssembly with:\r\n* https://github.com/kiegroup/kie-wb-common/pull/3319\r\n* https://github.com/kiegroup/lienzo-core/pull/111\r\n* https://github.com/kiegroup/lienzo-tests/pull/94", + "created_at": "2020-06-02T22:21:05Z", + "updated_at": "2020-07-08T22:08:26Z", + "closed_at": null, + "merged_at": null, + "merge_commit_sha": "b3a6bf705db9da02b4f0a47bf895d17479750f82", + "assignee": null, + "assignees": [ + + ], + "requested_reviewers": [ + { + "login": "domhanak", + "id": 5937251, + "node_id": "MDQ6VXNlcjU5MzcyNTE=", + "avatar_url": "https://avatars1.githubusercontent.com/u/5937251?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/domhanak", + "html_url": "https://github.com/domhanak", + "followers_url": "https://api.github.com/users/domhanak/followers", + "following_url": "https://api.github.com/users/domhanak/following{/other_user}", + "gists_url": "https://api.github.com/users/domhanak/gists{/gist_id}", + "starred_url": "https://api.github.com/users/domhanak/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/domhanak/subscriptions", + "organizations_url": "https://api.github.com/users/domhanak/orgs", + "repos_url": "https://api.github.com/users/domhanak/repos", + "events_url": "https://api.github.com/users/domhanak/events{/privacy}", + "received_events_url": "https://api.github.com/users/domhanak/received_events", + "type": "User", + "site_admin": false + } + ], + "requested_teams": [ + + ], + "labels": [ + + ], + "milestone": null, + "draft": false, + "commits_url": "https://api.github.com/repos/kiegroup/lienzo-tests/pulls/94/commits", + "review_comments_url": "https://api.github.com/repos/kiegroup/lienzo-tests/pulls/94/comments", + "review_comment_url": "https://api.github.com/repos/kiegroup/lienzo-tests/pulls/comments{/number}", + "comments_url": "https://api.github.com/repos/kiegroup/lienzo-tests/issues/94/comments", + "statuses_url": "https://api.github.com/repos/kiegroup/lienzo-tests/statuses/a79e2faef90ac539e14030f67707d54d06506671", + "head": { + "label": "handreyrc:JBPM-9175", + "ref": "JBPM-9175", + "sha": "a79e2faef90ac539e14030f67707d54d06506671", + "user": { + "login": "handreyrc", + "id": 47090342, + "node_id": "MDQ6VXNlcjQ3MDkwMzQy", + "avatar_url": "https://avatars0.githubusercontent.com/u/47090342?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/handreyrc", + "html_url": "https://github.com/handreyrc", + "followers_url": "https://api.github.com/users/handreyrc/followers", + "following_url": "https://api.github.com/users/handreyrc/following{/other_user}", + "gists_url": "https://api.github.com/users/handreyrc/gists{/gist_id}", + "starred_url": "https://api.github.com/users/handreyrc/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/handreyrc/subscriptions", + "organizations_url": "https://api.github.com/users/handreyrc/orgs", + "repos_url": "https://api.github.com/users/handreyrc/repos", + "events_url": "https://api.github.com/users/handreyrc/events{/privacy}", + "received_events_url": "https://api.github.com/users/handreyrc/received_events", + "type": "User", + "site_admin": false + }, + "repo": { + "id": 255934119, + "node_id": "MDEwOlJlcG9zaXRvcnkyNTU5MzQxMTk=", + "name": "lienzo-tests", + "full_name": "handreyrc/lienzo-tests", + "private": false, + "owner": { + "login": "handreyrc", + "id": 47090342, + "node_id": "MDQ6VXNlcjQ3MDkwMzQy", + "avatar_url": "https://avatars0.githubusercontent.com/u/47090342?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/handreyrc", + "html_url": "https://github.com/handreyrc", + "followers_url": "https://api.github.com/users/handreyrc/followers", + "following_url": "https://api.github.com/users/handreyrc/following{/other_user}", + "gists_url": "https://api.github.com/users/handreyrc/gists{/gist_id}", + "starred_url": "https://api.github.com/users/handreyrc/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/handreyrc/subscriptions", + "organizations_url": "https://api.github.com/users/handreyrc/orgs", + "repos_url": "https://api.github.com/users/handreyrc/repos", + "events_url": "https://api.github.com/users/handreyrc/events{/privacy}", + "received_events_url": "https://api.github.com/users/handreyrc/received_events", + "type": "User", + "site_admin": false + }, + "html_url": "https://github.com/handreyrc/lienzo-tests", + "description": null, + "fork": true, + "url": "https://api.github.com/repos/handreyrc/lienzo-tests", + "forks_url": "https://api.github.com/repos/handreyrc/lienzo-tests/forks", + "keys_url": "https://api.github.com/repos/handreyrc/lienzo-tests/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/handreyrc/lienzo-tests/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/handreyrc/lienzo-tests/teams", + "hooks_url": "https://api.github.com/repos/handreyrc/lienzo-tests/hooks", + "issue_events_url": "https://api.github.com/repos/handreyrc/lienzo-tests/issues/events{/number}", + "events_url": "https://api.github.com/repos/handreyrc/lienzo-tests/events", + "assignees_url": "https://api.github.com/repos/handreyrc/lienzo-tests/assignees{/user}", + "branches_url": "https://api.github.com/repos/handreyrc/lienzo-tests/branches{/branch}", + "tags_url": "https://api.github.com/repos/handreyrc/lienzo-tests/tags", + "blobs_url": "https://api.github.com/repos/handreyrc/lienzo-tests/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/handreyrc/lienzo-tests/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/handreyrc/lienzo-tests/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/handreyrc/lienzo-tests/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/handreyrc/lienzo-tests/statuses/{sha}", + "languages_url": "https://api.github.com/repos/handreyrc/lienzo-tests/languages", + "stargazers_url": "https://api.github.com/repos/handreyrc/lienzo-tests/stargazers", + "contributors_url": "https://api.github.com/repos/handreyrc/lienzo-tests/contributors", + "subscribers_url": "https://api.github.com/repos/handreyrc/lienzo-tests/subscribers", + "subscription_url": "https://api.github.com/repos/handreyrc/lienzo-tests/subscription", + "commits_url": "https://api.github.com/repos/handreyrc/lienzo-tests/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/handreyrc/lienzo-tests/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/handreyrc/lienzo-tests/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/handreyrc/lienzo-tests/issues/comments{/number}", + "contents_url": "https://api.github.com/repos/handreyrc/lienzo-tests/contents/{+path}", + "compare_url": "https://api.github.com/repos/handreyrc/lienzo-tests/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/handreyrc/lienzo-tests/merges", + "archive_url": "https://api.github.com/repos/handreyrc/lienzo-tests/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/handreyrc/lienzo-tests/downloads", + "issues_url": "https://api.github.com/repos/handreyrc/lienzo-tests/issues{/number}", + "pulls_url": "https://api.github.com/repos/handreyrc/lienzo-tests/pulls{/number}", + "milestones_url": "https://api.github.com/repos/handreyrc/lienzo-tests/milestones{/number}", + "notifications_url": "https://api.github.com/repos/handreyrc/lienzo-tests/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/handreyrc/lienzo-tests/labels{/name}", + "releases_url": "https://api.github.com/repos/handreyrc/lienzo-tests/releases{/id}", + "deployments_url": "https://api.github.com/repos/handreyrc/lienzo-tests/deployments", + "created_at": "2020-04-15T14:00:37Z", + "updated_at": "2020-04-15T14:00:39Z", + "pushed_at": "2020-07-08T20:27:47Z", + "git_url": "git://github.com/handreyrc/lienzo-tests.git", + "ssh_url": "git@github.com:handreyrc/lienzo-tests.git", + "clone_url": "https://github.com/handreyrc/lienzo-tests.git", + "svn_url": "https://github.com/handreyrc/lienzo-tests", + "homepage": null, + "size": 489, + "stargazers_count": 0, + "watchers_count": 0, + "language": null, + "has_issues": false, + "has_projects": true, + "has_downloads": true, + "has_wiki": true, + "has_pages": false, + "forks_count": 0, + "mirror_url": null, + "archived": false, + "disabled": false, + "open_issues_count": 0, + "license": null, + "forks": 0, + "open_issues": 0, + "watchers": 0, + "default_branch": "main" + } + }, + "base": { + "label": "kiegroup:main", + "ref": "main", + "sha": "6c2f837a819056bed0cc565d22ddd9873f188731", + "user": { + "login": "kiegroup", + "id": 517980, + "node_id": "MDEyOk9yZ2FuaXphdGlvbjUxNzk4MA==", + "avatar_url": "https://avatars3.githubusercontent.com/u/517980?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/kiegroup", + "html_url": "https://github.com/kiegroup", + "followers_url": "https://api.github.com/users/kiegroup/followers", + "following_url": "https://api.github.com/users/kiegroup/following{/other_user}", + "gists_url": "https://api.github.com/users/kiegroup/gists{/gist_id}", + "starred_url": "https://api.github.com/users/kiegroup/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/kiegroup/subscriptions", + "organizations_url": "https://api.github.com/users/kiegroup/orgs", + "repos_url": "https://api.github.com/users/kiegroup/repos", + "events_url": "https://api.github.com/users/kiegroup/events{/privacy}", + "received_events_url": "https://api.github.com/users/kiegroup/received_events", + "type": "Organization", + "site_admin": false + }, + "repo": { + "id": 120047378, + "node_id": "MDEwOlJlcG9zaXRvcnkxMjAwNDczNzg=", + "name": "lienzo-tests", + "full_name": "kiegroup/lienzo-tests", + "private": false, + "owner": { + "login": "kiegroup", + "id": 517980, + "node_id": "MDEyOk9yZ2FuaXphdGlvbjUxNzk4MA==", + "avatar_url": "https://avatars3.githubusercontent.com/u/517980?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/kiegroup", + "html_url": "https://github.com/kiegroup", + "followers_url": "https://api.github.com/users/kiegroup/followers", + "following_url": "https://api.github.com/users/kiegroup/following{/other_user}", + "gists_url": "https://api.github.com/users/kiegroup/gists{/gist_id}", + "starred_url": "https://api.github.com/users/kiegroup/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/kiegroup/subscriptions", + "organizations_url": "https://api.github.com/users/kiegroup/orgs", + "repos_url": "https://api.github.com/users/kiegroup/repos", + "events_url": "https://api.github.com/users/kiegroup/events{/privacy}", + "received_events_url": "https://api.github.com/users/kiegroup/received_events", + "type": "Organization", + "site_admin": false + }, + "html_url": "https://github.com/kiegroup/lienzo-tests", + "description": null, + "fork": true, + "url": "https://api.github.com/repos/kiegroup/lienzo-tests", + "forks_url": "https://api.github.com/repos/kiegroup/lienzo-tests/forks", + "keys_url": "https://api.github.com/repos/kiegroup/lienzo-tests/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/kiegroup/lienzo-tests/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/kiegroup/lienzo-tests/teams", + "hooks_url": "https://api.github.com/repos/kiegroup/lienzo-tests/hooks", + "issue_events_url": "https://api.github.com/repos/kiegroup/lienzo-tests/issues/events{/number}", + "events_url": "https://api.github.com/repos/kiegroup/lienzo-tests/events", + "assignees_url": "https://api.github.com/repos/kiegroup/lienzo-tests/assignees{/user}", + "branches_url": "https://api.github.com/repos/kiegroup/lienzo-tests/branches{/branch}", + "tags_url": "https://api.github.com/repos/kiegroup/lienzo-tests/tags", + "blobs_url": "https://api.github.com/repos/kiegroup/lienzo-tests/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/kiegroup/lienzo-tests/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/kiegroup/lienzo-tests/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/kiegroup/lienzo-tests/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/kiegroup/lienzo-tests/statuses/{sha}", + "languages_url": "https://api.github.com/repos/kiegroup/lienzo-tests/languages", + "stargazers_url": "https://api.github.com/repos/kiegroup/lienzo-tests/stargazers", + "contributors_url": "https://api.github.com/repos/kiegroup/lienzo-tests/contributors", + "subscribers_url": "https://api.github.com/repos/kiegroup/lienzo-tests/subscribers", + "subscription_url": "https://api.github.com/repos/kiegroup/lienzo-tests/subscription", + "commits_url": "https://api.github.com/repos/kiegroup/lienzo-tests/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/kiegroup/lienzo-tests/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/kiegroup/lienzo-tests/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/kiegroup/lienzo-tests/issues/comments{/number}", + "contents_url": "https://api.github.com/repos/kiegroup/lienzo-tests/contents/{+path}", + "compare_url": "https://api.github.com/repos/kiegroup/lienzo-tests/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/kiegroup/lienzo-tests/merges", + "archive_url": "https://api.github.com/repos/kiegroup/lienzo-tests/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/kiegroup/lienzo-tests/downloads", + "issues_url": "https://api.github.com/repos/kiegroup/lienzo-tests/issues{/number}", + "pulls_url": "https://api.github.com/repos/kiegroup/lienzo-tests/pulls{/number}", + "milestones_url": "https://api.github.com/repos/kiegroup/lienzo-tests/milestones{/number}", + "notifications_url": "https://api.github.com/repos/kiegroup/lienzo-tests/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/kiegroup/lienzo-tests/labels{/name}", + "releases_url": "https://api.github.com/repos/kiegroup/lienzo-tests/releases{/id}", + "deployments_url": "https://api.github.com/repos/kiegroup/lienzo-tests/deployments", + "created_at": "2018-02-03T00:57:57Z", + "updated_at": "2020-07-06T09:09:41Z", + "pushed_at": "2020-07-13T13:39:42Z", + "git_url": "git://github.com/kiegroup/lienzo-tests.git", + "ssh_url": "git@github.com:kiegroup/lienzo-tests.git", + "clone_url": "https://github.com/kiegroup/lienzo-tests.git", + "svn_url": "https://github.com/kiegroup/lienzo-tests", + "homepage": null, + "size": 485, + "stargazers_count": 0, + "watchers_count": 0, + "language": "Java", + "has_issues": false, + "has_projects": true, + "has_downloads": true, + "has_wiki": true, + "has_pages": false, + "forks_count": 23, + "mirror_url": null, + "archived": false, + "disabled": false, + "open_issues_count": 3, + "license": null, + "forks": 23, + "open_issues": 3, + "watchers": 0, + "default_branch": "main" + } + }, + "_links": { + "self": { + "href": "https://api.github.com/repos/kiegroup/lienzo-tests/pulls/94" + }, + "html": { + "href": "https://github.com/kiegroup/lienzo-tests/pull/94" + }, + "issue": { + "href": "https://api.github.com/repos/kiegroup/lienzo-tests/issues/94" + }, + "comments": { + "href": "https://api.github.com/repos/kiegroup/lienzo-tests/issues/94/comments" + }, + "review_comments": { + "href": "https://api.github.com/repos/kiegroup/lienzo-tests/pulls/94/comments" + }, + "review_comment": { + "href": "https://api.github.com/repos/kiegroup/lienzo-tests/pulls/comments{/number}" + }, + "commits": { + "href": "https://api.github.com/repos/kiegroup/lienzo-tests/pulls/94/commits" + }, + "statuses": { + "href": "https://api.github.com/repos/kiegroup/lienzo-tests/statuses/a79e2faef90ac539e14030f67707d54d06506671" + } + }, + "author_association": "NONE", + "active_lock_reason": null + }, + { + "url": "https://api.github.com/repos/kiegroup/lienzo-tests/pulls/77", + "id": 338761051, + "node_id": "MDExOlB1bGxSZXF1ZXN0MzM4NzYxMDUx", + "html_url": "https://github.com/kiegroup/lienzo-tests/pull/77", + "diff_url": "https://github.com/kiegroup/lienzo-tests/pull/77.diff", + "patch_url": "https://github.com/kiegroup/lienzo-tests/pull/77.patch", + "issue_url": "https://api.github.com/repos/kiegroup/lienzo-tests/issues/77", + "number": 77, + "state": "open", + "locked": false, + "title": "JBPM-8858 [Stunner] Lienzo - Unit Testing for the core migration to n…", + "user": { + "login": "treblereel", + "id": 3542552, + "node_id": "MDQ6VXNlcjM1NDI1NTI=", + "avatar_url": "https://avatars3.githubusercontent.com/u/3542552?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/treblereel", + "html_url": "https://github.com/treblereel", + "followers_url": "https://api.github.com/users/treblereel/followers", + "following_url": "https://api.github.com/users/treblereel/following{/other_user}", + "gists_url": "https://api.github.com/users/treblereel/gists{/gist_id}", + "starred_url": "https://api.github.com/users/treblereel/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/treblereel/subscriptions", + "organizations_url": "https://api.github.com/users/treblereel/orgs", + "repos_url": "https://api.github.com/users/treblereel/repos", + "events_url": "https://api.github.com/users/treblereel/events{/privacy}", + "received_events_url": "https://api.github.com/users/treblereel/received_events", + "type": "User", + "site_admin": false + }, + "body": "…ative APIs\r\n\r\nJIRA: https://issues.jboss.org/browse/JBPM-8858\r\ndepends on https://github.com/romartin/lienzo-core/pull/1\r\n\r\n@romartin ", + "created_at": "2019-11-08T16:43:29Z", + "updated_at": "2020-02-27T12:49:34Z", + "closed_at": null, + "merged_at": null, + "merge_commit_sha": "8e9dd825891635c96f83dff8cd9a08004df693c4", + "assignee": null, + "assignees": [ + + ], + "requested_reviewers": [ + { + "login": "romartin", + "id": 4602417, + "node_id": "MDQ6VXNlcjQ2MDI0MTc=", + "avatar_url": "https://avatars1.githubusercontent.com/u/4602417?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/romartin", + "html_url": "https://github.com/romartin", + "followers_url": "https://api.github.com/users/romartin/followers", + "following_url": "https://api.github.com/users/romartin/following{/other_user}", + "gists_url": "https://api.github.com/users/romartin/gists{/gist_id}", + "starred_url": "https://api.github.com/users/romartin/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/romartin/subscriptions", + "organizations_url": "https://api.github.com/users/romartin/orgs", + "repos_url": "https://api.github.com/users/romartin/repos", + "events_url": "https://api.github.com/users/romartin/events{/privacy}", + "received_events_url": "https://api.github.com/users/romartin/received_events", + "type": "User", + "site_admin": false + } + ], + "requested_teams": [ + + ], + "labels": [ + + ], + "milestone": null, + "draft": false, + "commits_url": "https://api.github.com/repos/kiegroup/lienzo-tests/pulls/77/commits", + "review_comments_url": "https://api.github.com/repos/kiegroup/lienzo-tests/pulls/77/comments", + "review_comment_url": "https://api.github.com/repos/kiegroup/lienzo-tests/pulls/comments{/number}", + "comments_url": "https://api.github.com/repos/kiegroup/lienzo-tests/issues/77/comments", + "statuses_url": "https://api.github.com/repos/kiegroup/lienzo-tests/statuses/7890405ad9ee392619dab669c4a47f093dbbd34f", + "head": { + "label": "treblereel:JBPM-8858", + "ref": "JBPM-8858", + "sha": "7890405ad9ee392619dab669c4a47f093dbbd34f", + "user": { + "login": "treblereel", + "id": 3542552, + "node_id": "MDQ6VXNlcjM1NDI1NTI=", + "avatar_url": "https://avatars3.githubusercontent.com/u/3542552?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/treblereel", + "html_url": "https://github.com/treblereel", + "followers_url": "https://api.github.com/users/treblereel/followers", + "following_url": "https://api.github.com/users/treblereel/following{/other_user}", + "gists_url": "https://api.github.com/users/treblereel/gists{/gist_id}", + "starred_url": "https://api.github.com/users/treblereel/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/treblereel/subscriptions", + "organizations_url": "https://api.github.com/users/treblereel/orgs", + "repos_url": "https://api.github.com/users/treblereel/repos", + "events_url": "https://api.github.com/users/treblereel/events{/privacy}", + "received_events_url": "https://api.github.com/users/treblereel/received_events", + "type": "User", + "site_admin": false + }, + "repo": { + "id": 168139066, + "node_id": "MDEwOlJlcG9zaXRvcnkxNjgxMzkwNjY=", + "name": "lienzo-tests", + "full_name": "treblereel/lienzo-tests", + "private": false, + "owner": { + "login": "treblereel", + "id": 3542552, + "node_id": "MDQ6VXNlcjM1NDI1NTI=", + "avatar_url": "https://avatars3.githubusercontent.com/u/3542552?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/treblereel", + "html_url": "https://github.com/treblereel", + "followers_url": "https://api.github.com/users/treblereel/followers", + "following_url": "https://api.github.com/users/treblereel/following{/other_user}", + "gists_url": "https://api.github.com/users/treblereel/gists{/gist_id}", + "starred_url": "https://api.github.com/users/treblereel/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/treblereel/subscriptions", + "organizations_url": "https://api.github.com/users/treblereel/orgs", + "repos_url": "https://api.github.com/users/treblereel/repos", + "events_url": "https://api.github.com/users/treblereel/events{/privacy}", + "received_events_url": "https://api.github.com/users/treblereel/received_events", + "type": "User", + "site_admin": false + }, + "html_url": "https://github.com/treblereel/lienzo-tests", + "description": null, + "fork": true, + "url": "https://api.github.com/repos/treblereel/lienzo-tests", + "forks_url": "https://api.github.com/repos/treblereel/lienzo-tests/forks", + "keys_url": "https://api.github.com/repos/treblereel/lienzo-tests/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/treblereel/lienzo-tests/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/treblereel/lienzo-tests/teams", + "hooks_url": "https://api.github.com/repos/treblereel/lienzo-tests/hooks", + "issue_events_url": "https://api.github.com/repos/treblereel/lienzo-tests/issues/events{/number}", + "events_url": "https://api.github.com/repos/treblereel/lienzo-tests/events", + "assignees_url": "https://api.github.com/repos/treblereel/lienzo-tests/assignees{/user}", + "branches_url": "https://api.github.com/repos/treblereel/lienzo-tests/branches{/branch}", + "tags_url": "https://api.github.com/repos/treblereel/lienzo-tests/tags", + "blobs_url": "https://api.github.com/repos/treblereel/lienzo-tests/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/treblereel/lienzo-tests/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/treblereel/lienzo-tests/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/treblereel/lienzo-tests/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/treblereel/lienzo-tests/statuses/{sha}", + "languages_url": "https://api.github.com/repos/treblereel/lienzo-tests/languages", + "stargazers_url": "https://api.github.com/repos/treblereel/lienzo-tests/stargazers", + "contributors_url": "https://api.github.com/repos/treblereel/lienzo-tests/contributors", + "subscribers_url": "https://api.github.com/repos/treblereel/lienzo-tests/subscribers", + "subscription_url": "https://api.github.com/repos/treblereel/lienzo-tests/subscription", + "commits_url": "https://api.github.com/repos/treblereel/lienzo-tests/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/treblereel/lienzo-tests/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/treblereel/lienzo-tests/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/treblereel/lienzo-tests/issues/comments{/number}", + "contents_url": "https://api.github.com/repos/treblereel/lienzo-tests/contents/{+path}", + "compare_url": "https://api.github.com/repos/treblereel/lienzo-tests/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/treblereel/lienzo-tests/merges", + "archive_url": "https://api.github.com/repos/treblereel/lienzo-tests/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/treblereel/lienzo-tests/downloads", + "issues_url": "https://api.github.com/repos/treblereel/lienzo-tests/issues{/number}", + "pulls_url": "https://api.github.com/repos/treblereel/lienzo-tests/pulls{/number}", + "milestones_url": "https://api.github.com/repos/treblereel/lienzo-tests/milestones{/number}", + "notifications_url": "https://api.github.com/repos/treblereel/lienzo-tests/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/treblereel/lienzo-tests/labels{/name}", + "releases_url": "https://api.github.com/repos/treblereel/lienzo-tests/releases{/id}", + "deployments_url": "https://api.github.com/repos/treblereel/lienzo-tests/deployments", + "created_at": "2019-01-29T10:51:10Z", + "updated_at": "2019-01-29T10:51:13Z", + "pushed_at": "2019-11-08T16:41:51Z", + "git_url": "git://github.com/treblereel/lienzo-tests.git", + "ssh_url": "git@github.com:treblereel/lienzo-tests.git", + "clone_url": "https://github.com/treblereel/lienzo-tests.git", + "svn_url": "https://github.com/treblereel/lienzo-tests", + "homepage": null, + "size": 542, + "stargazers_count": 0, + "watchers_count": 0, + "language": "Java", + "has_issues": false, + "has_projects": true, + "has_downloads": true, + "has_wiki": true, + "has_pages": false, + "forks_count": 0, + "mirror_url": null, + "archived": false, + "disabled": false, + "open_issues_count": 0, + "license": null, + "forks": 0, + "open_issues": 0, + "watchers": 0, + "default_branch": "main" + } + }, + "base": { + "label": "kiegroup:main", + "ref": "main", + "sha": "8480be617ea586c7b5d1509a3b19ee207e98dabf", + "user": { + "login": "kiegroup", + "id": 517980, + "node_id": "MDEyOk9yZ2FuaXphdGlvbjUxNzk4MA==", + "avatar_url": "https://avatars3.githubusercontent.com/u/517980?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/kiegroup", + "html_url": "https://github.com/kiegroup", + "followers_url": "https://api.github.com/users/kiegroup/followers", + "following_url": "https://api.github.com/users/kiegroup/following{/other_user}", + "gists_url": "https://api.github.com/users/kiegroup/gists{/gist_id}", + "starred_url": "https://api.github.com/users/kiegroup/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/kiegroup/subscriptions", + "organizations_url": "https://api.github.com/users/kiegroup/orgs", + "repos_url": "https://api.github.com/users/kiegroup/repos", + "events_url": "https://api.github.com/users/kiegroup/events{/privacy}", + "received_events_url": "https://api.github.com/users/kiegroup/received_events", + "type": "Organization", + "site_admin": false + }, + "repo": { + "id": 120047378, + "node_id": "MDEwOlJlcG9zaXRvcnkxMjAwNDczNzg=", + "name": "lienzo-tests", + "full_name": "kiegroup/lienzo-tests", + "private": false, + "owner": { + "login": "kiegroup", + "id": 517980, + "node_id": "MDEyOk9yZ2FuaXphdGlvbjUxNzk4MA==", + "avatar_url": "https://avatars3.githubusercontent.com/u/517980?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/kiegroup", + "html_url": "https://github.com/kiegroup", + "followers_url": "https://api.github.com/users/kiegroup/followers", + "following_url": "https://api.github.com/users/kiegroup/following{/other_user}", + "gists_url": "https://api.github.com/users/kiegroup/gists{/gist_id}", + "starred_url": "https://api.github.com/users/kiegroup/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/kiegroup/subscriptions", + "organizations_url": "https://api.github.com/users/kiegroup/orgs", + "repos_url": "https://api.github.com/users/kiegroup/repos", + "events_url": "https://api.github.com/users/kiegroup/events{/privacy}", + "received_events_url": "https://api.github.com/users/kiegroup/received_events", + "type": "Organization", + "site_admin": false + }, + "html_url": "https://github.com/kiegroup/lienzo-tests", + "description": null, + "fork": true, + "url": "https://api.github.com/repos/kiegroup/lienzo-tests", + "forks_url": "https://api.github.com/repos/kiegroup/lienzo-tests/forks", + "keys_url": "https://api.github.com/repos/kiegroup/lienzo-tests/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/kiegroup/lienzo-tests/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/kiegroup/lienzo-tests/teams", + "hooks_url": "https://api.github.com/repos/kiegroup/lienzo-tests/hooks", + "issue_events_url": "https://api.github.com/repos/kiegroup/lienzo-tests/issues/events{/number}", + "events_url": "https://api.github.com/repos/kiegroup/lienzo-tests/events", + "assignees_url": "https://api.github.com/repos/kiegroup/lienzo-tests/assignees{/user}", + "branches_url": "https://api.github.com/repos/kiegroup/lienzo-tests/branches{/branch}", + "tags_url": "https://api.github.com/repos/kiegroup/lienzo-tests/tags", + "blobs_url": "https://api.github.com/repos/kiegroup/lienzo-tests/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/kiegroup/lienzo-tests/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/kiegroup/lienzo-tests/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/kiegroup/lienzo-tests/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/kiegroup/lienzo-tests/statuses/{sha}", + "languages_url": "https://api.github.com/repos/kiegroup/lienzo-tests/languages", + "stargazers_url": "https://api.github.com/repos/kiegroup/lienzo-tests/stargazers", + "contributors_url": "https://api.github.com/repos/kiegroup/lienzo-tests/contributors", + "subscribers_url": "https://api.github.com/repos/kiegroup/lienzo-tests/subscribers", + "subscription_url": "https://api.github.com/repos/kiegroup/lienzo-tests/subscription", + "commits_url": "https://api.github.com/repos/kiegroup/lienzo-tests/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/kiegroup/lienzo-tests/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/kiegroup/lienzo-tests/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/kiegroup/lienzo-tests/issues/comments{/number}", + "contents_url": "https://api.github.com/repos/kiegroup/lienzo-tests/contents/{+path}", + "compare_url": "https://api.github.com/repos/kiegroup/lienzo-tests/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/kiegroup/lienzo-tests/merges", + "archive_url": "https://api.github.com/repos/kiegroup/lienzo-tests/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/kiegroup/lienzo-tests/downloads", + "issues_url": "https://api.github.com/repos/kiegroup/lienzo-tests/issues{/number}", + "pulls_url": "https://api.github.com/repos/kiegroup/lienzo-tests/pulls{/number}", + "milestones_url": "https://api.github.com/repos/kiegroup/lienzo-tests/milestones{/number}", + "notifications_url": "https://api.github.com/repos/kiegroup/lienzo-tests/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/kiegroup/lienzo-tests/labels{/name}", + "releases_url": "https://api.github.com/repos/kiegroup/lienzo-tests/releases{/id}", + "deployments_url": "https://api.github.com/repos/kiegroup/lienzo-tests/deployments", + "created_at": "2018-02-03T00:57:57Z", + "updated_at": "2020-07-06T09:09:41Z", + "pushed_at": "2020-07-13T13:39:42Z", + "git_url": "git://github.com/kiegroup/lienzo-tests.git", + "ssh_url": "git@github.com:kiegroup/lienzo-tests.git", + "clone_url": "https://github.com/kiegroup/lienzo-tests.git", + "svn_url": "https://github.com/kiegroup/lienzo-tests", + "homepage": null, + "size": 485, + "stargazers_count": 0, + "watchers_count": 0, + "language": "Java", + "has_issues": false, + "has_projects": true, + "has_downloads": true, + "has_wiki": true, + "has_pages": false, + "forks_count": 23, + "mirror_url": null, + "archived": false, + "disabled": false, + "open_issues_count": 3, + "license": null, + "forks": 23, + "open_issues": 3, + "watchers": 0, + "default_branch": "main" + } + }, + "_links": { + "self": { + "href": "https://api.github.com/repos/kiegroup/lienzo-tests/pulls/77" + }, + "html": { + "href": "https://github.com/kiegroup/lienzo-tests/pull/77" + }, + "issue": { + "href": "https://api.github.com/repos/kiegroup/lienzo-tests/issues/77" + }, + "comments": { + "href": "https://api.github.com/repos/kiegroup/lienzo-tests/issues/77/comments" + }, + "review_comments": { + "href": "https://api.github.com/repos/kiegroup/lienzo-tests/pulls/77/comments" + }, + "review_comment": { + "href": "https://api.github.com/repos/kiegroup/lienzo-tests/pulls/comments{/number}" + }, + "commits": { + "href": "https://api.github.com/repos/kiegroup/lienzo-tests/pulls/77/commits" + }, + "statuses": { + "href": "https://api.github.com/repos/kiegroup/lienzo-tests/statuses/7890405ad9ee392619dab669c4a47f093dbbd34f" + } + }, + "author_association": "NONE", + "active_lock_reason": null + } +] \ No newline at end of file diff --git a/jenkins-pipeline-shared-libraries/test/vars/AbstractShellSpec.groovy b/jenkins-pipeline-shared-libraries/test/vars/AbstractShellSpec.groovy new file mode 100644 index 000000000..20170ba54 --- /dev/null +++ b/jenkins-pipeline-shared-libraries/test/vars/AbstractShellSpec.groovy @@ -0,0 +1,172 @@ +import com.homeaway.devtools.jenkins.testing.JenkinsPipelineSpecification +import org.jenkinsci.plugins.workflow.steps.Step +import org.jenkinsci.plugins.workflow.steps.StepContext +import org.jenkinsci.plugins.workflow.steps.StepExecution +import org.kie.jenkins.shell.AbstractShell +import org.kie.jenkins.shell.installation.Installation + +class AbstractShellSpec extends JenkinsPipelineSpecification { + + def steps + def env = [:] + + def setup() { + steps = new Step() { + + @Override + StepExecution start(StepContext stepContext) throws Exception { + return null + } + + } + } + + class DummyShell extends AbstractShell { + DummyShell(def script, String installationDir = '', String cpuArchitecture = '') { + super(script, installationDir, cpuArchitecture) + } + + @Override + String getFullCommand(String command, String directory) { + return "${directory}${command}" + } + } + + def "[AbstractShell.groovy] execute"() { + setup: + def shell = new DummyShell(steps) + when: + shell.execute('whatever') + then: + 1 * getPipelineMock('sh')("whatever") + } + + def "[AbstractShell.groovy] execute with directory"() { + setup: + def shell = new DummyShell(steps) + when: + shell.execute('whatever', 'DIR') + then: + 1 * getPipelineMock('sh')("DIRwhatever") + } + + def "[AbstractShell.groovy] executeWithOutput"() { + setup: + def shell = new DummyShell(steps) + when: + def result = shell.executeWithOutput('whatever') + then: + 1 * getPipelineMock('sh')([returnStdout: true, script: "whatever"]) >> 'output ' + result == 'output' + } + + def "[AbstractShell.groovy] executeWithOutput with directory"() { + setup: + def shell = new DummyShell(steps) + when: + def result = shell.executeWithOutput('whatever', 'DIR') + then: + 1 * getPipelineMock('sh')([returnStdout: true, script: "DIRwhatever"]) >> 'output ' + result == 'output' + } + + def "[AbstractShell.groovy] executeWithStatus"() { + setup: + def shell = new DummyShell(steps) + when: + def result = shell.executeWithStatus('whatever') + then: + 1 * getPipelineMock('sh')([returnStatus: true, script: "whatever"]) >> 0 + result == 0 + } + + def "[AbstractShell.groovy] executeWithStatus with directory"() { + setup: + def shell = new DummyShell(steps) + when: + def result = shell.executeWithStatus('whatever', 'DIR') + then: + 1 * getPipelineMock('sh')([returnStatus: true, script: "DIRwhatever"]) >> 0 + result == 0 + } + + def "[AbstractShell.groovy] environment variables handling"() { + setup: + def install = Mock(Installation) + 1 * getPipelineMock('sh')([returnStdout: true, script: 'mktemp -d']) >> 'TMP_FOLDER' + def shell = new DummyShell(steps) + shell.install(install) + 1 * install.getExtraEnvVars() >> [ installkey: 'installvalue' ] + when: + shell.addEnvironmentVariable('KEY1', 'VALUE1') + shell.addEnvironmentVariable('key2', 'value2') + then: + shell.getEnvironmentVariables() == [ installkey: 'installvalue', KEY1: 'VALUE1', key2 : 'value2'] + } + + def "[AbstractShell.groovy] install"() { + setup: + def install = Mock(Installation) + 1 * getPipelineMock('sh')([returnStdout: true, script: 'mktemp -d']) >> 'TMP_FOLDER' + def shell = new DummyShell(steps) + when: + shell.install(install) + then: + shell.getInstallationDir() == 'TMP_FOLDER' + shell.cpuArchitecture == 'amd64' + 1 * install.setCpuArchitecture('amd64') + 1 * install.install('TMP_FOLDER') + shell.installations == [install] + } + + def "[AbstractShell.groovy] install with installationDir and cpuArchitecture"() { + setup: + def install = Mock(Installation) + 0 * getPipelineMock('sh')([returnStdout: true, script: 'mktemp -d']) >> 'TMP_FOLDER' + when: + def shell = new DummyShell(steps, 'DUMMY_FOLDER', 'CPUARCH') + shell.install(install) + then: + shell.getInstallationDir() == 'DUMMY_FOLDER' + shell.cpuArchitecture == 'CPUARCH' + 1 * install.setCpuArchitecture('CPUARCH') + 1 * install.install('DUMMY_FOLDER') + shell.installations == [install] + } + + def "[AbstractShell.groovy] install with debug before"() { + setup: + def install = Mock(Installation) + 1 * getPipelineMock('sh')([returnStdout: true, script: 'mktemp -d']) >> 'TMP_FOLDER' + when: + def shell = new DummyShell(steps) + shell.enableDebug() + shell.install(install) + then: + shell.getInstallationDir() == 'TMP_FOLDER' + shell.cpuArchitecture == 'amd64' + shell.debug == true + 1 * install.setCpuArchitecture('amd64') + 1 * install.install('TMP_FOLDER') + 1 * install.enableDebug() + shell.installations == [install] + } + + def "[AbstractShell.groovy] install with debug after"() { + setup: + def install = Mock(Installation) + 1 * getPipelineMock('sh')([returnStdout: true, script: 'mktemp -d']) >> 'TMP_FOLDER' + when: + def shell = new DummyShell(steps) + shell.install(install) + shell.enableDebug() + then: + shell.getInstallationDir() == 'TMP_FOLDER' + shell.cpuArchitecture == 'amd64' + shell.debug == true + 1 * install.setCpuArchitecture('amd64') + 1 * install.install('TMP_FOLDER') + 1 * install.enableDebug() + shell.installations == [install] + } +} diff --git a/jenkins-pipeline-shared-libraries/test/vars/BuildChainSpec.groovy b/jenkins-pipeline-shared-libraries/test/vars/BuildChainSpec.groovy new file mode 100644 index 000000000..06347a638 --- /dev/null +++ b/jenkins-pipeline-shared-libraries/test/vars/BuildChainSpec.groovy @@ -0,0 +1,37 @@ +import com.homeaway.devtools.jenkins.testing.JenkinsPipelineSpecification +import org.yaml.snakeyaml.Yaml + +class BuildChainSpec extends JenkinsPipelineSpecification { + def groovyScript = null + + def setup() { + groovyScript = loadPipelineScriptForTest("vars/buildChain.groovy") + explicitlyMockPipelineVariable("out") + } + + def "[build-chain.groovy] get BuildChain Version From CompositeAction File"() { + setup: + def actionYaml = new Yaml().load(new File(getClass().getResource('/build-chain-action.yml').toURI()).text) + GroovySpy(URL, global: true, useObjenesis: true) + def mockURL = GroovyMock(URL) + + when: + def result = groovyScript.getBuildChainVersionFromCompositeActionFile('buildChain-action.yml') + then: + 1 * getPipelineMock('readYaml')([file: 'buildChain-action.yml']) >> { return actionYaml } + result == '^smcVersionString' + } + + def "[build-chain.groovy] get BuildChain Version From CompositeAction File from single action file"() { + setup: + def actionYaml = new Yaml().load(new File(getClass().getResource('/build-chain-action-single.yml').toURI()).text) + GroovySpy(URL, global: true, useObjenesis: true) + def mockURL = GroovyMock(URL) + + when: + def result = groovyScript.getBuildChainVersionFromCompositeActionFile('build-chain-action-single.yml') + then: + 1 * getPipelineMock('readYaml')([file: 'build-chain-action-single.yml']) >> { return actionYaml } + result == '^v2.6.17' + } +} diff --git a/jenkins-pipeline-shared-libraries/test/vars/CloudSpec.groovy b/jenkins-pipeline-shared-libraries/test/vars/CloudSpec.groovy new file mode 100644 index 000000000..c23df847b --- /dev/null +++ b/jenkins-pipeline-shared-libraries/test/vars/CloudSpec.groovy @@ -0,0 +1,954 @@ +import com.homeaway.devtools.jenkins.testing.JenkinsPipelineSpecification + +class CloudSpec extends JenkinsPipelineSpecification { + + def groovyScript = null + def projectBranchMappingProperties = null + + def setup() { + groovyScript = loadPipelineScriptForTest('vars/cloud.groovy') + explicitlyMockPipelineVariable("out") + } + + ///////////////////////////////////////////////////////////////////// + // isQuayImagePublic + + def "[cloud.groovy] isQuayImagePublic token returns true"() { + setup: + groovyScript.getBinding().setVariable('QUAY_TOKEN', 'quaytoken') + when: + def result = groovyScript.isQuayImagePublic('namespace', 'repository', [ token: 'TOKEN' ]) + then: + 1 * getPipelineMock('util.executeWithCredentialsMap')([token: 'TOKEN'], _) + 1 * getPipelineMock('sh')([returnStdout: true, script: "curl -H 'Authorization: Bearer quaytoken' -X GET https://quay.io/api/v1/repository/namespace/repository | jq '.is_public'"]) >> 'true' + result + } + + def "[cloud.groovy] isQuayImagePublic token returns false"() { + setup: + groovyScript.getBinding().setVariable('QUAY_TOKEN', 'quaytoken') + when: + def result = groovyScript.isQuayImagePublic('namespace', 'repository', [ token: 'TOKEN' ]) + then: + 1 * getPipelineMock('util.executeWithCredentialsMap')([token: 'TOKEN'], _) + 1 * getPipelineMock('sh')([returnStdout: true, script: "curl -H 'Authorization: Bearer quaytoken' -X GET https://quay.io/api/v1/repository/namespace/repository | jq '.is_public'"]) >> 'false' + !result + } + + def "[cloud.groovy] isQuayImagePublic token returns anything"() { + setup: + groovyScript.getBinding().setVariable('QUAY_TOKEN', 'quaytoken') + when: + def result = groovyScript.isQuayImagePublic('namespace', 'repository', [ token: 'TOKEN' ]) + then: + 1 * getPipelineMock('util.executeWithCredentialsMap')([token: 'TOKEN'], _) + 1 * getPipelineMock('sh')([returnStdout: true, script: "curl -H 'Authorization: Bearer quaytoken' -X GET https://quay.io/api/v1/repository/namespace/repository | jq '.is_public'"]) >> 'anything' + !result + } + + def "[cloud.groovy] isQuayImagePublic usernamePassword returns true"() { + setup: + groovyScript.getBinding().setVariable('QUAY_TOKEN', 'quaytoken') + when: + def result = groovyScript.isQuayImagePublic('namespace', 'repository', [ usernamePassword: 'USERNAME_PASSWORD' ]) + then: + 1 * getPipelineMock('util.executeWithCredentialsMap')([usernamePassword: 'USERNAME_PASSWORD'], _) + 1 * getPipelineMock('sh')([returnStdout: true, script: "curl -H 'Authorization: Bearer quaytoken' -X GET https://quay.io/api/v1/repository/namespace/repository | jq '.is_public'"]) >> 'true' + result + } + + def "[cloud.groovy] isQuayImagePublic usernamePassword returns false"() { + setup: + groovyScript.getBinding().setVariable('QUAY_TOKEN', 'quaytoken') + when: + def result = groovyScript.isQuayImagePublic('namespace', 'repository', [ usernamePassword: 'USERNAME_PASSWORD' ]) + then: + 1 * getPipelineMock('util.executeWithCredentialsMap')([usernamePassword: 'USERNAME_PASSWORD'], _) + 1 * getPipelineMock('sh')([returnStdout: true, script: "curl -H 'Authorization: Bearer quaytoken' -X GET https://quay.io/api/v1/repository/namespace/repository | jq '.is_public'"]) >> 'false' + !result + } + + def "[cloud.groovy] isQuayImagePublic usernamePassword returns anything"() { + setup: + groovyScript.getBinding().setVariable('QUAY_TOKEN', 'quaytoken') + when: + def result = groovyScript.isQuayImagePublic('namespace', 'repository', [ usernamePassword: 'USERNAME_PASSWORD' ]) + then: + 1 * getPipelineMock('util.executeWithCredentialsMap')([usernamePassword: 'USERNAME_PASSWORD'], _) + 1 * getPipelineMock('sh')([returnStdout: true, script: "curl -H 'Authorization: Bearer quaytoken' -X GET https://quay.io/api/v1/repository/namespace/repository | jq '.is_public'"]) >> 'anything' + !result + } + + def "[cloud.groovy] isQuayImagePublic no creds"() { + setup: + groovyScript.getBinding().setVariable('QUAY_TOKEN', 'quaytoken') + when: + def result = groovyScript.isQuayImagePublic('namespace', 'repository') + then: + 1 * getPipelineMock('util.executeWithCredentialsMap')([ token: '', usernamePassword: '' ], _) + 1 * getPipelineMock('sh')([returnStdout: true, script: "curl -H 'Authorization: Bearer quaytoken' -X GET https://quay.io/api/v1/repository/namespace/repository | jq '.is_public'"]) >> 'anything' + !result + } + + ///////////////////////////////////////////////////////////////////// + // setQuayImagePublic + + def "[cloud.groovy] setQuayImagePublic token returns true"() { + setup: + groovyScript.getBinding().setVariable('QUAY_TOKEN', 'quaytoken') + when: + def result = groovyScript.setQuayImagePublic('namespace', 'repository', [ token: 'TOKEN' ]) + then: + 1 * getPipelineMock('util.executeWithCredentialsMap')([token: 'TOKEN'], _) + 1 * getPipelineMock('sh')([returnStdout: true, script: "curl -H 'Content-Type: application/json' -H 'Authorization: Bearer quaytoken' -X POST --data '{\"visibility\": \"public\"}' https://quay.io/api/v1/repository/namespace/repository/changevisibility | jq '.success'"]) >> 'true' + result + } + + def "[cloud.groovy] setQuayImagePublic token returns false"() { + setup: + groovyScript.getBinding().setVariable('QUAY_TOKEN', 'quaytoken') + when: + def result = groovyScript.setQuayImagePublic('namespace', 'repository', [ token: 'TOKEN' ]) + then: + 1 * getPipelineMock('util.executeWithCredentialsMap')([token: 'TOKEN'], _) + 1 * getPipelineMock('sh')([returnStdout: true, script: "curl -H 'Content-Type: application/json' -H 'Authorization: Bearer quaytoken' -X POST --data '{\"visibility\": \"public\"}' https://quay.io/api/v1/repository/namespace/repository/changevisibility | jq '.success'"]) >> 'false' + !result + } + + def "[cloud.groovy] setQuayImagePublic token curl returns anything"() { + setup: + groovyScript.getBinding().setVariable('QUAY_TOKEN', 'quaytoken') + when: + def result = groovyScript.setQuayImagePublic('namespace', 'repository', [ token: 'TOKEN' ]) + then: + 1 * getPipelineMock('util.executeWithCredentialsMap')([token: 'TOKEN'], _) + 1 * getPipelineMock('sh')([returnStdout: true, script: "curl -H 'Content-Type: application/json' -H 'Authorization: Bearer quaytoken' -X POST --data '{\"visibility\": \"public\"}' https://quay.io/api/v1/repository/namespace/repository/changevisibility | jq '.success'"]) >> 'anything' + !result + } + + def "[cloud.groovy] setQuayImagePublic usernamePassword returns true"() { + setup: + groovyScript.getBinding().setVariable('QUAY_TOKEN', 'quaytoken') + when: + def result = groovyScript.setQuayImagePublic('namespace', 'repository', [ usernamePassword: 'USERNAME_PASSWORD' ]) + then: + 1 * getPipelineMock('util.executeWithCredentialsMap')([usernamePassword: 'USERNAME_PASSWORD'], _) + 1 * getPipelineMock('sh')([returnStdout: true, script: "curl -H 'Content-Type: application/json' -H 'Authorization: Bearer quaytoken' -X POST --data '{\"visibility\": \"public\"}' https://quay.io/api/v1/repository/namespace/repository/changevisibility | jq '.success'"]) >> 'true' + result + } + + def "[cloud.groovy] setQuayImagePublic usernamePassword returns false"() { + setup: + groovyScript.getBinding().setVariable('QUAY_TOKEN', 'quaytoken') + when: + def result = groovyScript.setQuayImagePublic('namespace', 'repository', [ usernamePassword: 'USERNAME_PASSWORD' ]) + then: + 1 * getPipelineMock('util.executeWithCredentialsMap')([usernamePassword: 'USERNAME_PASSWORD'], _) + 1 * getPipelineMock('sh')([returnStdout: true, script: "curl -H 'Content-Type: application/json' -H 'Authorization: Bearer quaytoken' -X POST --data '{\"visibility\": \"public\"}' https://quay.io/api/v1/repository/namespace/repository/changevisibility | jq '.success'"]) >> 'false' + !result + } + + def "[cloud.groovy] setQuayImagePublic usernamePassword curl returns anything"() { + setup: + groovyScript.getBinding().setVariable('QUAY_TOKEN', 'quaytoken') + when: + def result = groovyScript.setQuayImagePublic('namespace', 'repository', [ usernamePassword: 'USERNAME_PASSWORD' ]) + then: + 1 * getPipelineMock('util.executeWithCredentialsMap')([usernamePassword: 'USERNAME_PASSWORD'], _) + 1 * getPipelineMock('sh')([returnStdout: true, script: "curl -H 'Content-Type: application/json' -H 'Authorization: Bearer quaytoken' -X POST --data '{\"visibility\": \"public\"}' https://quay.io/api/v1/repository/namespace/repository/changevisibility | jq '.success'"]) >> 'anything' + !result + } + + def "[cloud.groovy] setQuayImagePublic no creds"() { + setup: + groovyScript.getBinding().setVariable('QUAY_TOKEN', 'quaytoken') + when: + def result = groovyScript.setQuayImagePublic('namespace', 'repository') + then: + 1 * getPipelineMock('util.executeWithCredentialsMap')([ token: '', usernamePassword: '' ], _) + 1 * getPipelineMock('sh')([returnStdout: true, script: "curl -H 'Content-Type: application/json' -H 'Authorization: Bearer quaytoken' -X POST --data '{\"visibility\": \"public\"}' https://quay.io/api/v1/repository/namespace/repository/changevisibility | jq '.success'"]) >> 'anything' + !result + } + + ///////////////////////////////////////////////////////////////////// + // makeQuayImagePublic + + def "[cloud.groovy] makeQuayImagePublic token already public"() { + setup: + groovyScript.getBinding().setVariable('QUAY_TOKEN', 'quaytoken') + when: + groovyScript.makeQuayImagePublic('namespace', 'repository', [ token: 'TOKEN' ]) + then: + 1 * getPipelineMock('util.executeWithCredentialsMap')([token: 'TOKEN'], _) + 1 * getPipelineMock('sh')([returnStdout: true, script: "curl -H 'Authorization: Bearer quaytoken' -X GET https://quay.io/api/v1/repository/namespace/repository | jq '.is_public'"]) >> 'true' + 0 * getPipelineMock('sh')([returnStdout: true, script: "curl -H 'Content-Type: application/json' -H 'Authorization: Bearer quaytoken' -X POST --data '{\"visibility\": \"public\"}' https://quay.io/api/v1/repository/namespace/repository/changevisibility | jq '.success'"]) >> 'anything' + } + + def "[cloud.groovy] makeQuayImagePublic token not yet public"() { + setup: + groovyScript.getBinding().setVariable('QUAY_TOKEN', 'quaytoken') + when: + groovyScript.makeQuayImagePublic('namespace', 'repository', [ token: 'TOKEN' ]) + then: + 2 * getPipelineMock('util.executeWithCredentialsMap')([token: 'TOKEN'], _) + 1 * getPipelineMock('sh')([returnStdout: true, script: "curl -H 'Authorization: Bearer quaytoken' -X GET https://quay.io/api/v1/repository/namespace/repository | jq '.is_public'"]) >> 'false' + 1 * getPipelineMock('sh')([returnStdout: true, script: "curl -H 'Content-Type: application/json' -H 'Authorization: Bearer quaytoken' -X POST --data '{\"visibility\": \"public\"}' https://quay.io/api/v1/repository/namespace/repository/changevisibility | jq '.success'"]) >> 'true' + } + + def "[cloud.groovy] makeQuayImagePublic token raise error"() { + setup: + groovyScript.getBinding().setVariable('QUAY_TOKEN', 'quaytoken') + when: + groovyScript.makeQuayImagePublic('namespace', 'repository', [ token: 'TOKEN' ]) + then: + 2 * getPipelineMock('util.executeWithCredentialsMap')([token: 'TOKEN'], _) + 1 * getPipelineMock('sh')([returnStdout: true, script: "curl -H 'Authorization: Bearer quaytoken' -X GET https://quay.io/api/v1/repository/namespace/repository | jq '.is_public'"]) >> 'false' + 1 * getPipelineMock('sh')([returnStdout: true, script: "curl -H 'Content-Type: application/json' -H 'Authorization: Bearer quaytoken' -X POST --data '{\"visibility\": \"public\"}' https://quay.io/api/v1/repository/namespace/repository/changevisibility | jq '.success'"]) >> 'false' + 1 * getPipelineMock('error').call('Cannot set image quay.io/namespace/repository as visible') + } + + def "[cloud.groovy] makeQuayImagePublic usernamePassword already public"() { + setup: + groovyScript.getBinding().setVariable('QUAY_TOKEN', 'quaytoken') + when: + groovyScript.makeQuayImagePublic('namespace', 'repository', [ usernamePassword: 'USERNAME_PASSWORD' ]) + then: + 1 * getPipelineMock('util.executeWithCredentialsMap')([usernamePassword: 'USERNAME_PASSWORD'], _) + 1 * getPipelineMock('sh')([returnStdout: true, script: "curl -H 'Authorization: Bearer quaytoken' -X GET https://quay.io/api/v1/repository/namespace/repository | jq '.is_public'"]) >> 'true' + 0 * getPipelineMock('sh')([returnStdout: true, script: "curl -H 'Content-Type: application/json' -H 'Authorization: Bearer quaytoken' -X POST --data '{\"visibility\": \"public\"}' https://quay.io/api/v1/repository/namespace/repository/changevisibility | jq '.success'"]) >> 'anything' + } + + def "[cloud.groovy] makeQuayImagePublic usernamePassword not yet public"() { + setup: + groovyScript.getBinding().setVariable('QUAY_TOKEN', 'quaytoken') + when: + groovyScript.makeQuayImagePublic('namespace', 'repository', [ usernamePassword: 'USERNAME_PASSWORD' ]) + then: + 2 * getPipelineMock('util.executeWithCredentialsMap')([usernamePassword: 'USERNAME_PASSWORD'], _) + 1 * getPipelineMock('sh')([returnStdout: true, script: "curl -H 'Authorization: Bearer quaytoken' -X GET https://quay.io/api/v1/repository/namespace/repository | jq '.is_public'"]) >> 'false' + 1 * getPipelineMock('sh')([returnStdout: true, script: "curl -H 'Content-Type: application/json' -H 'Authorization: Bearer quaytoken' -X POST --data '{\"visibility\": \"public\"}' https://quay.io/api/v1/repository/namespace/repository/changevisibility | jq '.success'"]) >> 'true' + } + + def "[cloud.groovy] makeQuayImagePublic usernamePassword raise error"() { + setup: + groovyScript.getBinding().setVariable('QUAY_TOKEN', 'quaytoken') + when: + groovyScript.makeQuayImagePublic('namespace', 'repository', [ usernamePassword: 'USERNAME_PASSWORD' ]) + then: + 2 * getPipelineMock('util.executeWithCredentialsMap')([usernamePassword: 'USERNAME_PASSWORD'], _) + 1 * getPipelineMock('sh')([returnStdout: true, script: "curl -H 'Authorization: Bearer quaytoken' -X GET https://quay.io/api/v1/repository/namespace/repository | jq '.is_public'"]) >> 'false' + 1 * getPipelineMock('sh')([returnStdout: true, script: "curl -H 'Content-Type: application/json' -H 'Authorization: Bearer quaytoken' -X POST --data '{\"visibility\": \"public\"}' https://quay.io/api/v1/repository/namespace/repository/changevisibility | jq '.success'"]) >> 'false' + 1 * getPipelineMock('error').call('Cannot set image quay.io/namespace/repository as visible') + } + + def "[cloud.groovy] makeQuayImagePublic no creds"() { + setup: + groovyScript.getBinding().setVariable('QUAY_TOKEN', 'quaytoken') + when: + groovyScript.makeQuayImagePublic('namespace', 'repository') + then: + 1 * getPipelineMock('util.executeWithCredentialsMap')([ token: '', usernamePassword: ''], _) + 1 * getPipelineMock('sh')([returnStdout: true, script: "curl -H 'Authorization: Bearer quaytoken' -X GET https://quay.io/api/v1/repository/namespace/repository | jq '.is_public'"]) >> 'true' + 0 * getPipelineMock('sh')([returnStdout: true, script: "curl -H 'Content-Type: application/json' -H 'Authorization: Bearer quaytoken' -X POST --data '{\"visibility\": \"public\"}' https://quay.io/api/v1/repository/namespace/repository/changevisibility | jq '.success'"]) + } + + ///////////////////////////////////////////////////////////////////// + // cleanContainersAndImages + + def "[cloud.groovy] cleanContainersAndImages no container engine"() { + when: + groovyScript.cleanContainersAndImages() + then: + 1 * getPipelineMock("sh")([script: "podman ps -a -q | tr '\\n' ','", returnStdout: true]) >> "one,two" + 1 * getPipelineMock("sh")("podman rm -f one || date") + 1 * getPipelineMock("sh")("podman rm -f two || date") + 1 * getPipelineMock("sh")([script: "podman images -q | tr '\\n' ','", returnStdout: true]) >> "hello,bonjour,hallo,ola" + 1 * getPipelineMock("sh")("podman rmi -f hello || date") + 1 * getPipelineMock("sh")("podman rmi -f bonjour || date") + 1 * getPipelineMock("sh")("podman rmi -f hallo || date") + 1 * getPipelineMock("sh")("podman rmi -f ola || date") + } + + def "[cloud.groovy] cleanContainersAndImages with docker"() { + when: + groovyScript.cleanContainersAndImages('docker') + then: + 1 * getPipelineMock("sh")([script: "docker ps -a -q | tr '\\n' ','", returnStdout: true]) >> "one,two" + 1 * getPipelineMock("sh")("docker rm -f one || date") + 1 * getPipelineMock("sh")("docker rm -f two || date") + 1 * getPipelineMock("sh")([script: "docker images -q | tr '\\n' ','", returnStdout: true]) >> "hello,bonjour,hallo,ola" + 1 * getPipelineMock("sh")("docker rmi -f hello || date") + 1 * getPipelineMock("sh")("docker rmi -f bonjour || date") + 1 * getPipelineMock("sh")("docker rmi -f hallo || date") + 1 * getPipelineMock("sh")("docker rmi -f ola || date") + } + + def "[cloud.groovy] cleanContainersAndImages no containers/images"() { + when: + groovyScript.cleanContainersAndImages() + then: + 1 * getPipelineMock("sh")([script: "podman ps -a -q | tr '\\n' ','", returnStdout: true]) >> "" + 0 * getPipelineMock("sh")("podman rm -f one || date") + 0 * getPipelineMock("sh")("podman rm -f || date") + 1 * getPipelineMock("sh")([script: "podman images -q | tr '\\n' ','", returnStdout: true]) >> "" + 0 * getPipelineMock("sh")("podman rmi -f hello || date") + 0 * getPipelineMock("sh")("podman rmi -f || date") + } + + ///////////////////////////////////////////////////////////////////// + // startLocalRegistry & cleanLocalRegistry + + def "[cloud.groovy] cleanLocalRegistry default"() { + when: + groovyScript.cleanLocalRegistry() + then: + 1 * getPipelineMock("sh")("docker rm -f registry-5000 || true") + } + + def "[cloud.groovy] cleanLocalRegistry with port"() { + when: + groovyScript.cleanLocalRegistry(6986) + then: + 1 * getPipelineMock("sh")("docker rm -f registry-6986 || true") + } + + def "[cloud.groovy] startLocalRegistry default"() { + when: + def result = groovyScript.startLocalRegistry() + then: + 1 * getPipelineMock("sh")("docker rm -f registry-5000 || true") + 1 * getPipelineMock("sh")("docker run -d -p 5000:5000 --restart=always --name registry-5000 registry:2") + result == "localhost:5000" + } + + def "[cloud.groovy] startLocalRegistry with port"() { + when: + def result = groovyScript.startLocalRegistry(63213) + then: + 1 * getPipelineMock("sh")("docker rm -f registry-63213 || true") + 1 * getPipelineMock("sh")("docker run -d -p 63213:5000 --restart=always --name registry-63213 registry:2") + result == "localhost:63213" + } + + ///////////////////////////////////////////////////////////////////// + // skopeoCopyRegistryImages + + def "[cloud.groovy] skopeoCopyRegistryImages default"() { + when: + groovyScript.skopeoCopyRegistryImages('OLD_IMAGE', 'NEW_IMAGE') + then: + 1 * getPipelineMock("sh")("skopeo copy --retry-times 3 --tls-verify=false --all docker://OLD_IMAGE docker://NEW_IMAGE") + } + + def "[cloud.groovy] skopeoCopyRegistryImages default with 5 retries"() { + when: + groovyScript.skopeoCopyRegistryImages('OLD_IMAGE', 'NEW_IMAGE', 5) + then: + 1 * getPipelineMock("sh")("skopeo copy --retry-times 5 --tls-verify=false --all docker://OLD_IMAGE docker://NEW_IMAGE") + } + + ///////////////////////////////////////////////////////////////////// + // dockerSquashImage + + def "[cloud.groovy] dockerSquashImage default"() { + when: + def result = groovyScript.dockerSquashImage('BASE_IMAGE') + then: + 1 * getPipelineMock("sh")([returnStdout: true, script: "docker history BASE_IMAGE | grep buildkit.dockerfile | wc -l"]) >> "6" + 1 * getPipelineMock("echo")("Got 7 layers to squash") + 1 * getPipelineMock("sh")("docker-squash -v -m 'BASE_IMAGE squashed' -f 7 -t BASE_IMAGE BASE_IMAGE") + 1 * getPipelineMock("sh")("docker push BASE_IMAGE") + result == 'BASE_IMAGE' + } + + def "[cloud.groovy] dockerSquashImage with squash message"() { + when: + def result = groovyScript.dockerSquashImage('BASE_IMAGE', 'MESSAGE') + then: + 1 * getPipelineMock("sh")([returnStdout: true, script: "docker history BASE_IMAGE | grep buildkit.dockerfile | wc -l"]) >> "6" + 1 * getPipelineMock("echo")("Got 7 layers to squash") + 1 * getPipelineMock("sh")("docker-squash -v -m 'MESSAGE' -f 7 -t BASE_IMAGE BASE_IMAGE") + 1 * getPipelineMock("sh")("docker push BASE_IMAGE") + result == 'BASE_IMAGE' + } + + def "[cloud.groovy] dockerSquashImage with message and no replaceCurrentImage"() { + when: + def result = groovyScript.dockerSquashImage('BASE_IMAGE', 'MESSAGE', false) + then: + 1 * getPipelineMock("sh")([returnStdout: true, script: "docker history BASE_IMAGE | grep buildkit.dockerfile | wc -l"]) >> "6" + 1 * getPipelineMock("echo")("Got 7 layers to squash") + 1 * getPipelineMock("sh")("docker-squash -v -m 'MESSAGE' -f 7 -t BASE_IMAGE-squashed BASE_IMAGE") + 1 * getPipelineMock("sh")("docker push BASE_IMAGE-squashed") + result == 'BASE_IMAGE-squashed' + } + + ///////////////////////////////////////////////////////////////////// + // dockerDebugImage + + def "[cloud.groovy] dockerDebugImage default"() { + when: + groovyScript.dockerDebugImage('IMAGE') + then: + 1 * getPipelineMock("sh")("docker images") + 1 * getPipelineMock("sh")("docker history IMAGE") + 1 * getPipelineMock("sh")("docker inspect IMAGE") + } + + ///////////////////////////////////////////////////////////////////// + // dockerBuildPlatformImage + + def "[cloud.groovy] dockerBuildPlatformImage default"() { + when: + groovyScript.dockerBuildPlatformImage('IMAGE', 'PLATFORM') + then: + 1 * getPipelineMock("sh")("docker buildx build --push --sbom=false --provenance=false --platform PLATFORM -t IMAGE .") + 1 * getPipelineMock("sh")("docker buildx imagetools inspect IMAGE") + 1 * getPipelineMock("sh")("docker pull --platform PLATFORM IMAGE") + } + + def "[cloud.groovy] dockerBuildPlatformImage outputToFile"() { + setup: + groovyScript.getBinding().setVariable("WORKSPACE", "WORKSPACE") + when: + groovyScript.dockerBuildPlatformImage('IMAGE', 'PLATFORM', true) + then: + 1 * getPipelineMock("sh")("docker buildx build --push --sbom=false --provenance=false --platform PLATFORM -t IMAGE . 2> WORKSPACE/IMAGE-PLATFORM-build.log") + 1 * getPipelineMock("sh")("docker buildx imagetools inspect IMAGE") + 1 * getPipelineMock("sh")("docker pull --platform PLATFORM IMAGE") + } + + ///////////////////////////////////////////////////////////////////// + // dockerCreateManifest + + def "[cloud.groovy] dockerCreateManifest default"() { + when: + groovyScript.dockerCreateManifest('IMAGE', [ 'IMAGE1', 'IMAGE2' ]) + then: + 1 * getPipelineMock("sh")("docker manifest rm IMAGE || true") + 1 * getPipelineMock("sh")("docker manifest create IMAGE --insecure IMAGE1 IMAGE2") + 1 * getPipelineMock("sh")("docker manifest push IMAGE") + } + + ///////////////////////////////////////////////////////////////////// + // prepareForDockerMultiplatformBuild, debugDockerMultiplatformBuild & cleanDockerMultiplatformBuild + + def "[cloud.groovy] debugDockerMultiplatformBuild default"() { + when: + groovyScript.debugDockerMultiplatformBuild() + then: + 1 * getPipelineMock("sh")("docker context ls") + 1 * getPipelineMock("sh")("docker buildx inspect") + 1 * getPipelineMock("sh")("docker buildx ls") + } + + def "[cloud.groovy] cleanDockerMultiplatformBuild default"() { + when: + groovyScript.cleanDockerMultiplatformBuild() + then: + 1 * getPipelineMock("sh")("docker buildx rm mybuilder || true") + 1 * getPipelineMock("sh")("docker rm -f binfmt || true") + } + + def "[cloud.groovy] prepareForDockerMultiplatformBuild default"() { + setup: + groovyScript.getBinding().setVariable("WORKSPACE", "WORKSPACE") + when: + groovyScript.prepareForDockerMultiplatformBuild() + then: + 1 * getPipelineMock("sh")('docker run --rm --privileged --name binfmt docker.io/tonistiigi/binfmt --install all') + 1 * getPipelineMock("writeFile")([file: 'buildkitd.toml', text: "debug = false\n"]) + 1 * getPipelineMock("sh")('docker buildx create --name mybuilder --driver docker-container --driver-opt network=host --bootstrap --config ${WORKSPACE}/buildkitd.toml') + 1 * getPipelineMock("sh")("docker buildx use mybuilder") + 0 * getPipelineMock("sh")("cat buildkitd.toml") + 1 * getPipelineMock("sh")("docker buildx rm mybuilder || true") + 1 * getPipelineMock("sh")("docker rm -f binfmt || true") + } + + def "[cloud.groovy] prepareForDockerMultiplatformBuild with insecure registries"() { + setup: + groovyScript.getBinding().setVariable("WORKSPACE", "WORKSPACE") + when: + groovyScript.prepareForDockerMultiplatformBuild(["localhost:5000","localhost:5001"]) + then: + 1 * getPipelineMock("sh")('docker run --rm --privileged --name binfmt docker.io/tonistiigi/binfmt --install all') + 1 * getPipelineMock("writeFile")([file: 'buildkitd.toml', text: """debug = false +[registry."localhost:5000"] +http = true +[registry."localhost:5001"] +http = true +"""]) + 1 * getPipelineMock("sh")('docker buildx create --name mybuilder --driver docker-container --driver-opt network=host --bootstrap --config ${WORKSPACE}/buildkitd.toml') + 1 * getPipelineMock("sh")("docker buildx use mybuilder") + 0 * getPipelineMock("sh")("cat buildkitd.toml") + 1 * getPipelineMock("sh")("docker buildx rm mybuilder || true") + 1 * getPipelineMock("sh")("docker rm -f binfmt || true") + } + + def "[cloud.groovy] prepareForDockerMultiplatformBuild with mirror registry"() { + setup: + groovyScript.getBinding().setVariable("WORKSPACE", "WORKSPACE") + when: + groovyScript.prepareForDockerMultiplatformBuild([], [ + [ + name: 'REGISTRY1', + mirrors: [ + [ + url: 'MIRROR1', + insecure: true + ], + ] + ], + [ + name: 'REGISTRY2', + mirrors: [ + [ + url: 'MIRROR2', + insecure: true + ], + [ + url: 'MIRROR3', + insecure: false + ], + ] + ], + ]) + then: + 1 * getPipelineMock("sh")('docker run --rm --privileged --name binfmt docker.io/tonistiigi/binfmt --install all') + 1 * getPipelineMock("writeFile")([file: 'buildkitd.toml', text: """debug = false +[registry."REGISTRY1"] +mirrors = ["MIRROR1"] +[registry."MIRROR1"] +http = true +[registry."REGISTRY2"] +mirrors = ["MIRROR2","MIRROR3"] +[registry."MIRROR2"] +http = true +[registry."MIRROR3"] +http = false +"""]) + 1 * getPipelineMock("sh")('docker buildx create --name mybuilder --driver docker-container --driver-opt network=host --bootstrap --config ${WORKSPACE}/buildkitd.toml') + 1 * getPipelineMock("sh")("docker buildx use mybuilder") + 0 * getPipelineMock("sh")("cat buildkitd.toml") + 1 * getPipelineMock("sh")("docker buildx rm mybuilder || true") + 1 * getPipelineMock("sh")("docker rm -f binfmt || true") + } + + def "[cloud.groovy] prepareForDockerMultiplatformBuild with debug"() { + setup: + groovyScript.getBinding().setVariable("WORKSPACE", "WORKSPACE") + when: + groovyScript.prepareForDockerMultiplatformBuild([], [], true) + then: + 1 * getPipelineMock("sh")('docker run --rm --privileged --name binfmt docker.io/tonistiigi/binfmt --install all') + 1 * getPipelineMock("writeFile")([file: 'buildkitd.toml', text: "debug = true\n"]) + 1 * getPipelineMock("sh")('docker buildx create --name mybuilder --driver docker-container --driver-opt network=host --bootstrap --config ${WORKSPACE}/buildkitd.toml') + 1 * getPipelineMock("sh")("docker buildx use mybuilder") + 1 * getPipelineMock("sh")("cat buildkitd.toml") + 1 * getPipelineMock("sh")("docker buildx rm mybuilder || true") + 1 * getPipelineMock("sh")("docker rm -f binfmt || true") + 3 * getPipelineMock("sh")("docker context ls") + 3 * getPipelineMock("sh")("docker buildx inspect") + 3 * getPipelineMock("sh")("docker buildx ls") + } + + def "[cloud.groovy] prepareForDockerMultiplatformBuild with all"() { + setup: + groovyScript.getBinding().setVariable("WORKSPACE", "WORKSPACE") + when: + groovyScript.prepareForDockerMultiplatformBuild( + ["localhost:5000","localhost:5001"], + [ + [ + name: 'REGISTRY1', + mirrors: [ + [ + url: 'MIRROR1', + insecure: true + ], + ] + ], + [ + name: 'REGISTRY2', + mirrors: [ + [ + url: 'MIRROR2', + insecure: true + ], + [ + url: 'MIRROR3', + insecure: false + ], + ] + ], + ], + true) + then: + 1 * getPipelineMock("sh")('docker run --rm --privileged --name binfmt docker.io/tonistiigi/binfmt --install all') + 1 * getPipelineMock("writeFile")([file: 'buildkitd.toml', text: """debug = true +[registry."localhost:5000"] +http = true +[registry."localhost:5001"] +http = true +[registry."REGISTRY1"] +mirrors = ["MIRROR1"] +[registry."MIRROR1"] +http = true +[registry."REGISTRY2"] +mirrors = ["MIRROR2","MIRROR3"] +[registry."MIRROR2"] +http = true +[registry."MIRROR3"] +http = false +"""]) + 1 * getPipelineMock("sh")('docker buildx create --name mybuilder --driver docker-container --driver-opt network=host --bootstrap --config ${WORKSPACE}/buildkitd.toml') + 1 * getPipelineMock("sh")("docker buildx use mybuilder") + 1 * getPipelineMock("sh")("cat buildkitd.toml") + 1 * getPipelineMock("sh")("docker buildx rm mybuilder || true") + 1 * getPipelineMock("sh")("docker rm -f binfmt || true") + 3 * getPipelineMock("sh")("docker context ls") + 3 * getPipelineMock("sh")("docker buildx inspect") + 3 * getPipelineMock("sh")("docker buildx ls") + } + + def "[cloud.groovy] getDockerIOMirrorRegistryConfig without env"() { + when: + def result = groovyScript.getDockerIOMirrorRegistryConfig() + then: + result == [ + name: 'docker.io', + mirrors: [ + [ + url : 'mirror.gcr.io', + insecure: false, + ] + ], + ] + } + + def "[cloud.groovy] getDockerIOMirrorRegistryConfig with env defined"() { + setup: + groovyScript.getBinding().setVariable("env", [DOCKER_REGISTRY_MIRROR: 'REGISTRY_MIRROR']) + when: + def result = groovyScript.getDockerIOMirrorRegistryConfig() + then: + result == [ + name: 'docker.io', + mirrors: [ + [ + url : 'REGISTRY_MIRROR', + insecure: true, + ] + ], + ] + } + + ///////////////////////////////////////////////////////////////////// + // dockerBuildMultiPlatformImages + + def "[cloud.groovy] dockerBuildMultiPlatformImages default"() { + when: + groovyScript.dockerBuildMultiPlatformImages('IMAGE', ['linux/p1', 'windows/p2']) + then: + // First platform build + 1 * getPipelineMock("sh")("docker buildx build --push --sbom=false --provenance=false --platform linux/p1 -t IMAGE-linux-p1 .") + 1 * getPipelineMock("sh")("docker buildx imagetools inspect IMAGE-linux-p1") + 1 * getPipelineMock("sh")("docker pull --platform linux/p1 IMAGE-linux-p1") + 1 * getPipelineMock("sh")([returnStdout: true, script: "docker history IMAGE-linux-p1 | grep buildkit.dockerfile | wc -l"]) >> "6" + 1 * getPipelineMock("echo")("Got 7 layers to squash") + 1 * getPipelineMock("sh")("docker-squash -v -m 'Squashed IMAGE' -f 7 -t IMAGE-linux-p1 IMAGE-linux-p1") + 1 * getPipelineMock("sh")("docker push IMAGE-linux-p1") + + // Second platform build + 1 * getPipelineMock("sh")("docker buildx build --push --sbom=false --provenance=false --platform windows/p2 -t IMAGE-windows-p2 .") + 1 * getPipelineMock("sh")("docker buildx imagetools inspect IMAGE-windows-p2") + 1 * getPipelineMock("sh")("docker pull --platform windows/p2 IMAGE-windows-p2") + 1 * getPipelineMock("sh")([returnStdout: true, script: "docker history IMAGE-windows-p2 | grep buildkit.dockerfile | wc -l"]) >> "6" + 1 * getPipelineMock("echo")("Got 7 layers to squash") + 1 * getPipelineMock("sh")("docker-squash -v -m 'Squashed IMAGE' -f 7 -t IMAGE-windows-p2 IMAGE-windows-p2") + 1 * getPipelineMock("sh")("docker push IMAGE-windows-p2") + + // Manifest creation + 1 * getPipelineMock("sh")("docker manifest rm IMAGE || true") + 1 * getPipelineMock("sh")("docker manifest create IMAGE --insecure IMAGE-linux-p1 IMAGE-windows-p2") + 1 * getPipelineMock("sh")("docker manifest push IMAGE") + } + + def "[cloud.groovy] dockerBuildMultiPlatformImages no squash"() { + when: + groovyScript.dockerBuildMultiPlatformImages('IMAGE', ['linux/p1', 'windows/p2'], false) + then: + // First platform build + 1 * getPipelineMock("sh")("docker buildx build --push --sbom=false --provenance=false --platform linux/p1 -t IMAGE-linux-p1 .") + 1 * getPipelineMock("sh")("docker buildx imagetools inspect IMAGE-linux-p1") + 1 * getPipelineMock("sh")("docker pull --platform linux/p1 IMAGE-linux-p1") + 0 * getPipelineMock("sh")([returnStdout: true, script: "docker history IMAGE-linux-p1 | grep buildkit.dockerfile | wc -l"]) >> "6" + 0 * getPipelineMock("echo")("Got 7 layers to squash") + 0 * getPipelineMock("sh")("docker-squash -v -m 'Squashed IMAGE' -f 7 -t IMAGE-linux-p1 IMAGE-linux-p1") + 0 * getPipelineMock("sh")("docker push IMAGE-linux-p1") + + // Second platform build + 1 * getPipelineMock("sh")("docker buildx build --push --sbom=false --provenance=false --platform windows/p2 -t IMAGE-windows-p2 .") + 1 * getPipelineMock("sh")("docker buildx imagetools inspect IMAGE-windows-p2") + 1 * getPipelineMock("sh")("docker pull --platform windows/p2 IMAGE-windows-p2") + 0 * getPipelineMock("sh")([returnStdout: true, script: "docker history IMAGE-windows-p2 | grep buildkit.dockerfile | wc -l"]) >> "6" + 0 * getPipelineMock("echo")("Got 7 layers to squash") + 0 * getPipelineMock("sh")("docker-squash -v -m 'Squashed IMAGE' -f 7 -t IMAGE-windows-p2 IMAGE-windows-p2") + 0 * getPipelineMock("sh")("docker push IMAGE-windows-p2") + + // Manifest creation + 1 * getPipelineMock("sh")("docker manifest rm IMAGE || true") + 1 * getPipelineMock("sh")("docker manifest create IMAGE --insecure IMAGE-linux-p1 IMAGE-windows-p2") + 1 * getPipelineMock("sh")("docker manifest push IMAGE") + } + + def "[cloud.groovy] dockerBuildMultiPlatformImages with squash and message"() { + when: + groovyScript.dockerBuildMultiPlatformImages('IMAGE', ['linux/p1', 'windows/p2'], true, 'MESSAGE') + then: + // First platform build + 1 * getPipelineMock("sh")("docker buildx build --push --sbom=false --provenance=false --platform linux/p1 -t IMAGE-linux-p1 .") + 1 * getPipelineMock("sh")("docker buildx imagetools inspect IMAGE-linux-p1") + 1 * getPipelineMock("sh")("docker pull --platform linux/p1 IMAGE-linux-p1") + 1 * getPipelineMock("sh")([returnStdout: true, script: "docker history IMAGE-linux-p1 | grep buildkit.dockerfile | wc -l"]) >> "6" + 1 * getPipelineMock("echo")("Got 7 layers to squash") + 1 * getPipelineMock("sh")("docker-squash -v -m 'MESSAGE' -f 7 -t IMAGE-linux-p1 IMAGE-linux-p1") + 1 * getPipelineMock("sh")("docker push IMAGE-linux-p1") + + // Second platform build + 1 * getPipelineMock("sh")("docker buildx build --push --sbom=false --provenance=false --platform windows/p2 -t IMAGE-windows-p2 .") + 1 * getPipelineMock("sh")("docker buildx imagetools inspect IMAGE-windows-p2") + 1 * getPipelineMock("sh")("docker pull --platform windows/p2 IMAGE-windows-p2") + 1 * getPipelineMock("sh")([returnStdout: true, script: "docker history IMAGE-windows-p2 | grep buildkit.dockerfile | wc -l"]) >> "6" + 1 * getPipelineMock("echo")("Got 7 layers to squash") + 1 * getPipelineMock("sh")("docker-squash -v -m 'MESSAGE' -f 7 -t IMAGE-windows-p2 IMAGE-windows-p2") + 1 * getPipelineMock("sh")("docker push IMAGE-windows-p2") + + // Manifest creation + 1 * getPipelineMock("sh")("docker manifest rm IMAGE || true") + 1 * getPipelineMock("sh")("docker manifest create IMAGE --insecure IMAGE-linux-p1 IMAGE-windows-p2") + 1 * getPipelineMock("sh")("docker manifest push IMAGE") + } + + def "[cloud.groovy] dockerBuildMultiPlatformImages with squash and message and debug"() { + when: + groovyScript.dockerBuildMultiPlatformImages('IMAGE', ['linux/p1', 'windows/p2'], true, 'MESSAGE', true) + then: + // First platform build + 1 * getPipelineMock("sh")("docker buildx build --push --sbom=false --provenance=false --platform linux/p1 -t IMAGE-linux-p1 .") + 1 * getPipelineMock("sh")("docker buildx imagetools inspect IMAGE-linux-p1") + 1 * getPipelineMock("sh")("docker pull --platform linux/p1 IMAGE-linux-p1") + 1 * getPipelineMock("sh")([returnStdout: true, script: "docker history IMAGE-linux-p1 | grep buildkit.dockerfile | wc -l"]) >> "6" + 1 * getPipelineMock("echo")("Got 7 layers to squash") + 1 * getPipelineMock("sh")("docker-squash -v -m 'MESSAGE' -f 7 -t IMAGE-linux-p1 IMAGE-linux-p1") + 1 * getPipelineMock("sh")("docker push IMAGE-linux-p1") + + // Second platform build + 1 * getPipelineMock("sh")("docker buildx build --push --sbom=false --provenance=false --platform windows/p2 -t IMAGE-windows-p2 .") + 1 * getPipelineMock("sh")("docker buildx imagetools inspect IMAGE-windows-p2") + 1 * getPipelineMock("sh")("docker pull --platform windows/p2 IMAGE-windows-p2") + 1 * getPipelineMock("sh")([returnStdout: true, script: "docker history IMAGE-windows-p2 | grep buildkit.dockerfile | wc -l"]) >> "6" + 1 * getPipelineMock("echo")("Got 7 layers to squash") + 1 * getPipelineMock("sh")("docker-squash -v -m 'MESSAGE' -f 7 -t IMAGE-windows-p2 IMAGE-windows-p2") + 1 * getPipelineMock("sh")("docker push IMAGE-windows-p2") + + // Manifest creation + 1 * getPipelineMock("sh")("docker manifest rm IMAGE || true") + 1 * getPipelineMock("sh")("docker manifest create IMAGE --insecure IMAGE-linux-p1 IMAGE-windows-p2") + 1 * getPipelineMock("sh")("docker manifest push IMAGE") + + // Debug commands + 5 * getPipelineMock("sh")("docker images") + 1 * getPipelineMock("sh")("docker history IMAGE") + 2 * getPipelineMock("sh")("docker history IMAGE-linux-p1") + 2 * getPipelineMock("sh")("docker history IMAGE-windows-p2") + 1 * getPipelineMock("sh")("docker inspect IMAGE") + 2 * getPipelineMock("sh")("docker inspect IMAGE-linux-p1") + 2 * getPipelineMock("sh")("docker inspect IMAGE-windows-p2") + } + + ///////////////////////////////////////////////////////////////////// + // loginOpenShift + + def "[cloud.groovy] loginOpenShift default"() { + setup: + groovyScript.getBinding().setVariable("OC_USER", 'user') + groovyScript.getBinding().setVariable("OC_PWD", 'password') + when: + groovyScript.loginOpenShift('OPENSHIFT_API', 'OPENSHIFT_CREDS_ID') + then: + 1 * getPipelineMock('usernamePassword.call')([credentialsId: 'OPENSHIFT_CREDS_ID', usernameVariable: 'OC_USER', passwordVariable: 'OC_PWD']) >> 'userNamePassword' + 1 * getPipelineMock("withCredentials")(['userNamePassword'], _ as Closure) + 1 * getPipelineMock("sh")("oc login --username=user --password=password --server=OPENSHIFT_API --insecure-skip-tls-verify") + } + + ///////////////////////////////////////////////////////////////////// + // loginOpenShiftRegistry + + def "[cloud.groovy] getOpenShiftRegistryURL default"() { + when: + def result = groovyScript.getOpenShiftRegistryURL() + then: + 1 * getPipelineMock("sh")([returnStdout: true, script: "oc get routes -n openshift-image-registry | tail -1 | awk '{print \$2}'"]) >> 'OPENSHIFT_URL' + result == 'OPENSHIFT_URL' + } + + ///////////////////////////////////////////////////////////////////// + // loginOpenShiftRegistry + + def "[cloud.groovy] loginOpenShiftRegistry default"() { + when: + groovyScript.loginOpenShiftRegistry() + then: + 1 * getPipelineMock("sh")([returnStdout: true, script: "oc get routes -n openshift-image-registry | tail -1 | awk '{print \$2}'"]) >> 'OPENSHIFT_URL' + 1 * getPipelineMock("sh")("set +x && docker login -u anything -p \$(oc whoami -t) OPENSHIFT_URL") + } + + def "[cloud.groovy] loginOpenShiftRegistry with container engine and options"() { + when: + groovyScript.loginOpenShiftRegistry('podman', '--tls-verify=false') + then: + 1 * getPipelineMock("sh")([returnStdout: true, script: "oc get routes -n openshift-image-registry | tail -1 | awk '{print \$2}'"]) >> 'OPENSHIFT_URL' + 1 * getPipelineMock("sh")("set +x && podman login -u anything -p \$(oc whoami -t) --tls-verify=false OPENSHIFT_URL") + } + + ///////////////////////////////////////////////////////////////////// + // loginContainerRegistry + + def "[cloud.groovy] loginContainerRegistry default"() { + setup: + groovyScript.getBinding().setVariable("REGISTRY_USER", 'user') + groovyScript.getBinding().setVariable("REGISTRY_PWD", 'password') + when: + groovyScript.loginContainerRegistry('REGISTRY', 'REGISTRY_CREDS_ID') + then: + 1 * getPipelineMock('usernamePassword.call')([credentialsId: 'REGISTRY_CREDS_ID', usernameVariable: 'REGISTRY_USER', passwordVariable: 'REGISTRY_PWD']) >> 'userNamePassword' + 1 * getPipelineMock("withCredentials")(['userNamePassword'], _ as Closure) + 1 * getPipelineMock("sh")("set +x && docker login -u user -p password REGISTRY") + } + + def "[cloud.groovy] loginContainerRegistry with container engine and options"() { + setup: + groovyScript.getBinding().setVariable("REGISTRY_USER", 'user') + groovyScript.getBinding().setVariable("REGISTRY_PWD", 'password') + when: + groovyScript.loginContainerRegistry('REGISTRY', 'REGISTRY_CREDS_ID', 'podman', '--tls-verify=false') + then: + 1 * getPipelineMock('usernamePassword.call')([credentialsId: 'REGISTRY_CREDS_ID', usernameVariable: 'REGISTRY_USER', passwordVariable: 'REGISTRY_PWD']) >> 'userNamePassword' + 1 * getPipelineMock("withCredentials")(['userNamePassword'], _ as Closure) + 1 * getPipelineMock("sh")("set +x && podman login -u user -p password --tls-verify=false REGISTRY") + } + + ///////////////////////////////////////////////////////////////////// + // pull, tag and push images + + def "[cloud.groovy] pullImage default"() { + when: + groovyScript.pullImage('IMAGE') + then: + 1 * getPipelineMock('retry')(3, _) + 1 * getPipelineMock("sh")("docker pull IMAGE") + } + + def "[cloud.groovy] pullImage with retries and container engine"() { + when: + groovyScript.pullImage('IMAGE', 1, 'podman', '--tls-verify=false') + then: + 1 * getPipelineMock('retry')(1, _) + 1 * getPipelineMock("sh")("podman pull --tls-verify=false IMAGE") + } + + def "[cloud.groovy] pushImage default"() { + when: + groovyScript.pushImage('IMAGE') + then: + 1 * getPipelineMock('retry')(3, _) + 1 * getPipelineMock("sh")("docker push IMAGE") + } + + def "[cloud.groovy] pushImage with retries and container engine"() { + when: + groovyScript.pushImage('IMAGE', 1, 'podman', '--tls-verify=false') + then: + 1 * getPipelineMock('retry')(1, _) + 1 * getPipelineMock("sh")("podman push --tls-verify=false IMAGE") + } + + def "[cloud.groovy] tagImage default"() { + when: + groovyScript.tagImage('OLD', 'NEW') + then: + 1 * getPipelineMock("sh")("docker tag OLD NEW") + } + + def "[cloud.groovy] tagImage with container engine"() { + when: + groovyScript.tagImage('OLD', 'NEW', 'podman') + then: + 1 * getPipelineMock("sh")("podman tag OLD NEW") + } + + ///////////////////////////////////////////////////////////////////// + // getReducedTag + + def "[cloud.groovy] getReducedTag with correct tag"() { + when: + def result = groovyScript.getReducedTag('1.36.0') + then: + result == "1.36" + } + + def "[cloud.groovy] getReducedTag with incorrect tag raises error"() { + when: + def result = groovyScript.getReducedTag('ANY_TAG') + then: + thrown(Exception) + } + + // updateQuayImageDescription + + def "[cloud.groovy] updateQuayImageDescription simple run with token"() { + setup: + groovyScript.getBinding().setVariable('QUAY_TOKEN', 'quaytoken') + when: + groovyScript.updateQuayImageDescription('DESCRIPTION', 'namespace', 'repository', [token: 'TOKEN']) + then: + 1 * getPipelineMock('util.executeWithCredentialsMap')([token: 'TOKEN'], _) + 1 * getPipelineMock('writeJSON')([file: 'description.json', json: [description: "DESCRIPTION"]]) + 1 * getPipelineMock('sh')([script: "curl -H 'Content-type: application/json' -H 'Authorization: Bearer quaytoken' -X PUT --data-binary '@description.json' https://quay.io/api/v1/repository/namespace/repository"]) + } + + def "[cloud.groovy] updateQuayImageDescription simple run with token and usernamePassword"() { + setup: + groovyScript.getBinding().setVariable('QUAY_TOKEN', 'quaytoken') + when: + groovyScript.updateQuayImageDescription('DESCRIPTION', 'namespace', 'repository', [token: 'Token', usernamePassword: 'USERNAME_PWD']) + then: + 1 * getPipelineMock('util.executeWithCredentialsMap')([token: 'Token', usernamePassword: 'USERNAME_PWD'], _) + 1 * getPipelineMock('writeJSON')([file: 'description.json', json: [description: "DESCRIPTION"]]) + 1 * getPipelineMock('sh')([script: "curl -H 'Content-type: application/json' -H 'Authorization: Bearer quaytoken' -X PUT --data-binary '@description.json' https://quay.io/api/v1/repository/namespace/repository"]) + } + + def "[cloud.groovy] updateQuayImageDescription simple run with usernamePassword"() { + setup: + groovyScript.getBinding().setVariable('QUAY_TOKEN', 'quaytoken') + when: + groovyScript.updateQuayImageDescription('DESCRIPTION', 'namespace', 'repository', [usernamePassword: 'USERNAME_PWD']) + then: + 1 * getPipelineMock('util.executeWithCredentialsMap')([usernamePassword: 'USERNAME_PWD'], _) + 1 * getPipelineMock('writeJSON')([file: 'description.json', json: [description: "DESCRIPTION"]]) + 1 * getPipelineMock('sh')([script: "curl -H 'Content-type: application/json' -H 'Authorization: Bearer quaytoken' -X PUT --data-binary '@description.json' https://quay.io/api/v1/repository/namespace/repository"]) + } + + def "[cloud.groovy] updateQuayImageDescription simple run without token and usernamePassword"() { + setup: + groovyScript.getBinding().setVariable('QUAY_TOKEN', 'quaytoken') + when: + groovyScript.updateQuayImageDescription('DESCRIPTION', 'namespace', 'repository') + then: + 1 * getPipelineMock('util.executeWithCredentialsMap')( _) + 1 * getPipelineMock('writeJSON')([file: 'description.json', json: [description: "DESCRIPTION"]]) + 1 * getPipelineMock('sh')([script: "curl -H 'Content-type: application/json' -H 'Authorization: Bearer quaytoken' -X PUT --data-binary '@description.json' https://quay.io/api/v1/repository/namespace/repository"]) + } +} + diff --git a/jenkins-pipeline-shared-libraries/test/vars/CloudSpec.groovy.orig b/jenkins-pipeline-shared-libraries/test/vars/CloudSpec.groovy.orig new file mode 100644 index 000000000..175660a18 --- /dev/null +++ b/jenkins-pipeline-shared-libraries/test/vars/CloudSpec.groovy.orig @@ -0,0 +1,980 @@ +import com.homeaway.devtools.jenkins.testing.JenkinsPipelineSpecification + +class CloudSpec extends JenkinsPipelineSpecification { + + def groovyScript = null + def projectBranchMappingProperties = null + + def setup() { + groovyScript = loadPipelineScriptForTest('vars/cloud.groovy') + explicitlyMockPipelineVariable("out") + } + + ///////////////////////////////////////////////////////////////////// + // isQuayImagePublic + + def "[cloud.groovy] isQuayImagePublic token returns true"() { + setup: + groovyScript.getBinding().setVariable('QUAY_TOKEN', 'quaytoken') + when: + def result = groovyScript.isQuayImagePublic('namespace', 'repository', [ token: 'TOKEN' ]) + then: + 1 * getPipelineMock('util.executeWithCredentialsMap')([token: 'TOKEN'], _) + 1 * getPipelineMock('sh')([returnStdout: true, script: "curl -H 'Authorization: Bearer quaytoken' -X GET https://quay.io/api/v1/repository/namespace/repository | jq '.is_public'"]) >> 'true' + result + } + + def "[cloud.groovy] isQuayImagePublic token returns false"() { + setup: + groovyScript.getBinding().setVariable('QUAY_TOKEN', 'quaytoken') + when: + def result = groovyScript.isQuayImagePublic('namespace', 'repository', [ token: 'TOKEN' ]) + then: + 1 * getPipelineMock('util.executeWithCredentialsMap')([token: 'TOKEN'], _) + 1 * getPipelineMock('sh')([returnStdout: true, script: "curl -H 'Authorization: Bearer quaytoken' -X GET https://quay.io/api/v1/repository/namespace/repository | jq '.is_public'"]) >> 'false' + !result + } + + def "[cloud.groovy] isQuayImagePublic token returns anything"() { + setup: + groovyScript.getBinding().setVariable('QUAY_TOKEN', 'quaytoken') + when: + def result = groovyScript.isQuayImagePublic('namespace', 'repository', [ token: 'TOKEN' ]) + then: + 1 * getPipelineMock('util.executeWithCredentialsMap')([token: 'TOKEN'], _) + 1 * getPipelineMock('sh')([returnStdout: true, script: "curl -H 'Authorization: Bearer quaytoken' -X GET https://quay.io/api/v1/repository/namespace/repository | jq '.is_public'"]) >> 'anything' + !result + } + + def "[cloud.groovy] isQuayImagePublic usernamePassword returns true"() { + setup: + groovyScript.getBinding().setVariable('QUAY_TOKEN', 'quaytoken') + when: + def result = groovyScript.isQuayImagePublic('namespace', 'repository', [ usernamePassword: 'USERNAME_PASSWORD' ]) + then: + 1 * getPipelineMock('util.executeWithCredentialsMap')([usernamePassword: 'USERNAME_PASSWORD'], _) + 1 * getPipelineMock('sh')([returnStdout: true, script: "curl -H 'Authorization: Bearer quaytoken' -X GET https://quay.io/api/v1/repository/namespace/repository | jq '.is_public'"]) >> 'true' + result + } + + def "[cloud.groovy] isQuayImagePublic usernamePassword returns false"() { + setup: + groovyScript.getBinding().setVariable('QUAY_TOKEN', 'quaytoken') + when: + def result = groovyScript.isQuayImagePublic('namespace', 'repository', [ usernamePassword: 'USERNAME_PASSWORD' ]) + then: + 1 * getPipelineMock('util.executeWithCredentialsMap')([usernamePassword: 'USERNAME_PASSWORD'], _) + 1 * getPipelineMock('sh')([returnStdout: true, script: "curl -H 'Authorization: Bearer quaytoken' -X GET https://quay.io/api/v1/repository/namespace/repository | jq '.is_public'"]) >> 'false' + !result + } + + def "[cloud.groovy] isQuayImagePublic usernamePassword returns anything"() { + setup: + groovyScript.getBinding().setVariable('QUAY_TOKEN', 'quaytoken') + when: + def result = groovyScript.isQuayImagePublic('namespace', 'repository', [ usernamePassword: 'USERNAME_PASSWORD' ]) + then: + 1 * getPipelineMock('util.executeWithCredentialsMap')([usernamePassword: 'USERNAME_PASSWORD'], _) + 1 * getPipelineMock('sh')([returnStdout: true, script: "curl -H 'Authorization: Bearer quaytoken' -X GET https://quay.io/api/v1/repository/namespace/repository | jq '.is_public'"]) >> 'anything' + !result + } + + def "[cloud.groovy] isQuayImagePublic no creds"() { + setup: + groovyScript.getBinding().setVariable('QUAY_TOKEN', 'quaytoken') + when: + def result = groovyScript.isQuayImagePublic('namespace', 'repository') + then: + 1 * getPipelineMock('util.executeWithCredentialsMap')([ token: '', usernamePassword: '' ], _) + 1 * getPipelineMock('sh')([returnStdout: true, script: "curl -H 'Authorization: Bearer quaytoken' -X GET https://quay.io/api/v1/repository/namespace/repository | jq '.is_public'"]) >> 'anything' + !result + } + + ///////////////////////////////////////////////////////////////////// + // setQuayImagePublic + + def "[cloud.groovy] setQuayImagePublic token returns true"() { + setup: + groovyScript.getBinding().setVariable('QUAY_TOKEN', 'quaytoken') + when: + def result = groovyScript.setQuayImagePublic('namespace', 'repository', [ token: 'TOKEN' ]) + then: + 1 * getPipelineMock('util.executeWithCredentialsMap')([token: 'TOKEN'], _) + 1 * getPipelineMock('sh')([returnStdout: true, script: "curl -H 'Content-Type: application/json' -H 'Authorization: Bearer quaytoken' -X POST --data '{\"visibility\": \"public\"}' https://quay.io/api/v1/repository/namespace/repository/changevisibility | jq '.success'"]) >> 'true' + result + } + + def "[cloud.groovy] setQuayImagePublic token returns false"() { + setup: + groovyScript.getBinding().setVariable('QUAY_TOKEN', 'quaytoken') + when: + def result = groovyScript.setQuayImagePublic('namespace', 'repository', [ token: 'TOKEN' ]) + then: + 1 * getPipelineMock('util.executeWithCredentialsMap')([token: 'TOKEN'], _) + 1 * getPipelineMock('sh')([returnStdout: true, script: "curl -H 'Content-Type: application/json' -H 'Authorization: Bearer quaytoken' -X POST --data '{\"visibility\": \"public\"}' https://quay.io/api/v1/repository/namespace/repository/changevisibility | jq '.success'"]) >> 'false' + !result + } + + def "[cloud.groovy] setQuayImagePublic token curl returns anything"() { + setup: + groovyScript.getBinding().setVariable('QUAY_TOKEN', 'quaytoken') + when: + def result = groovyScript.setQuayImagePublic('namespace', 'repository', [ token: 'TOKEN' ]) + then: + 1 * getPipelineMock('util.executeWithCredentialsMap')([token: 'TOKEN'], _) + 1 * getPipelineMock('sh')([returnStdout: true, script: "curl -H 'Content-Type: application/json' -H 'Authorization: Bearer quaytoken' -X POST --data '{\"visibility\": \"public\"}' https://quay.io/api/v1/repository/namespace/repository/changevisibility | jq '.success'"]) >> 'anything' + !result + } + + def "[cloud.groovy] setQuayImagePublic usernamePassword returns true"() { + setup: + groovyScript.getBinding().setVariable('QUAY_TOKEN', 'quaytoken') + when: + def result = groovyScript.setQuayImagePublic('namespace', 'repository', [ usernamePassword: 'USERNAME_PASSWORD' ]) + then: + 1 * getPipelineMock('util.executeWithCredentialsMap')([usernamePassword: 'USERNAME_PASSWORD'], _) + 1 * getPipelineMock('sh')([returnStdout: true, script: "curl -H 'Content-Type: application/json' -H 'Authorization: Bearer quaytoken' -X POST --data '{\"visibility\": \"public\"}' https://quay.io/api/v1/repository/namespace/repository/changevisibility | jq '.success'"]) >> 'true' + result + } + + def "[cloud.groovy] setQuayImagePublic usernamePassword returns false"() { + setup: + groovyScript.getBinding().setVariable('QUAY_TOKEN', 'quaytoken') + when: + def result = groovyScript.setQuayImagePublic('namespace', 'repository', [ usernamePassword: 'USERNAME_PASSWORD' ]) + then: + 1 * getPipelineMock('util.executeWithCredentialsMap')([usernamePassword: 'USERNAME_PASSWORD'], _) + 1 * getPipelineMock('sh')([returnStdout: true, script: "curl -H 'Content-Type: application/json' -H 'Authorization: Bearer quaytoken' -X POST --data '{\"visibility\": \"public\"}' https://quay.io/api/v1/repository/namespace/repository/changevisibility | jq '.success'"]) >> 'false' + !result + } + + def "[cloud.groovy] setQuayImagePublic usernamePassword curl returns anything"() { + setup: + groovyScript.getBinding().setVariable('QUAY_TOKEN', 'quaytoken') + when: + def result = groovyScript.setQuayImagePublic('namespace', 'repository', [ usernamePassword: 'USERNAME_PASSWORD' ]) + then: + 1 * getPipelineMock('util.executeWithCredentialsMap')([usernamePassword: 'USERNAME_PASSWORD'], _) + 1 * getPipelineMock('sh')([returnStdout: true, script: "curl -H 'Content-Type: application/json' -H 'Authorization: Bearer quaytoken' -X POST --data '{\"visibility\": \"public\"}' https://quay.io/api/v1/repository/namespace/repository/changevisibility | jq '.success'"]) >> 'anything' + !result + } + + def "[cloud.groovy] setQuayImagePublic no creds"() { + setup: + groovyScript.getBinding().setVariable('QUAY_TOKEN', 'quaytoken') + when: + def result = groovyScript.setQuayImagePublic('namespace', 'repository') + then: + 1 * getPipelineMock('util.executeWithCredentialsMap')([ token: '', usernamePassword: '' ], _) + 1 * getPipelineMock('sh')([returnStdout: true, script: "curl -H 'Content-Type: application/json' -H 'Authorization: Bearer quaytoken' -X POST --data '{\"visibility\": \"public\"}' https://quay.io/api/v1/repository/namespace/repository/changevisibility | jq '.success'"]) >> 'anything' + !result + } + + ///////////////////////////////////////////////////////////////////// + // makeQuayImagePublic + + def "[cloud.groovy] makeQuayImagePublic token already public"() { + setup: + groovyScript.getBinding().setVariable('QUAY_TOKEN', 'quaytoken') + when: + groovyScript.makeQuayImagePublic('namespace', 'repository', [ token: 'TOKEN' ]) + then: + 1 * getPipelineMock('util.executeWithCredentialsMap')([token: 'TOKEN'], _) + 1 * getPipelineMock('sh')([returnStdout: true, script: "curl -H 'Authorization: Bearer quaytoken' -X GET https://quay.io/api/v1/repository/namespace/repository | jq '.is_public'"]) >> 'true' + 0 * getPipelineMock('sh')([returnStdout: true, script: "curl -H 'Content-Type: application/json' -H 'Authorization: Bearer quaytoken' -X POST --data '{\"visibility\": \"public\"}' https://quay.io/api/v1/repository/namespace/repository/changevisibility | jq '.success'"]) >> 'anything' + } + + def "[cloud.groovy] makeQuayImagePublic token not yet public"() { + setup: + groovyScript.getBinding().setVariable('QUAY_TOKEN', 'quaytoken') + when: + groovyScript.makeQuayImagePublic('namespace', 'repository', [ token: 'TOKEN' ]) + then: + 2 * getPipelineMock('util.executeWithCredentialsMap')([token: 'TOKEN'], _) + 1 * getPipelineMock('sh')([returnStdout: true, script: "curl -H 'Authorization: Bearer quaytoken' -X GET https://quay.io/api/v1/repository/namespace/repository | jq '.is_public'"]) >> 'false' + 1 * getPipelineMock('sh')([returnStdout: true, script: "curl -H 'Content-Type: application/json' -H 'Authorization: Bearer quaytoken' -X POST --data '{\"visibility\": \"public\"}' https://quay.io/api/v1/repository/namespace/repository/changevisibility | jq '.success'"]) >> 'true' + } + + def "[cloud.groovy] makeQuayImagePublic token raise error"() { + setup: + groovyScript.getBinding().setVariable('QUAY_TOKEN', 'quaytoken') + when: + groovyScript.makeQuayImagePublic('namespace', 'repository', [ token: 'TOKEN' ]) + then: + 2 * getPipelineMock('util.executeWithCredentialsMap')([token: 'TOKEN'], _) + 1 * getPipelineMock('sh')([returnStdout: true, script: "curl -H 'Authorization: Bearer quaytoken' -X GET https://quay.io/api/v1/repository/namespace/repository | jq '.is_public'"]) >> 'false' + 1 * getPipelineMock('sh')([returnStdout: true, script: "curl -H 'Content-Type: application/json' -H 'Authorization: Bearer quaytoken' -X POST --data '{\"visibility\": \"public\"}' https://quay.io/api/v1/repository/namespace/repository/changevisibility | jq '.success'"]) >> 'false' + 1 * getPipelineMock('error').call('Cannot set image quay.io/namespace/repository as visible') + } + + def "[cloud.groovy] makeQuayImagePublic usernamePassword already public"() { + setup: + groovyScript.getBinding().setVariable('QUAY_TOKEN', 'quaytoken') + when: + groovyScript.makeQuayImagePublic('namespace', 'repository', [ usernamePassword: 'USERNAME_PASSWORD' ]) + then: + 1 * getPipelineMock('util.executeWithCredentialsMap')([usernamePassword: 'USERNAME_PASSWORD'], _) + 1 * getPipelineMock('sh')([returnStdout: true, script: "curl -H 'Authorization: Bearer quaytoken' -X GET https://quay.io/api/v1/repository/namespace/repository | jq '.is_public'"]) >> 'true' + 0 * getPipelineMock('sh')([returnStdout: true, script: "curl -H 'Content-Type: application/json' -H 'Authorization: Bearer quaytoken' -X POST --data '{\"visibility\": \"public\"}' https://quay.io/api/v1/repository/namespace/repository/changevisibility | jq '.success'"]) >> 'anything' + } + + def "[cloud.groovy] makeQuayImagePublic usernamePassword not yet public"() { + setup: + groovyScript.getBinding().setVariable('QUAY_TOKEN', 'quaytoken') + when: + groovyScript.makeQuayImagePublic('namespace', 'repository', [ usernamePassword: 'USERNAME_PASSWORD' ]) + then: + 2 * getPipelineMock('util.executeWithCredentialsMap')([usernamePassword: 'USERNAME_PASSWORD'], _) + 1 * getPipelineMock('sh')([returnStdout: true, script: "curl -H 'Authorization: Bearer quaytoken' -X GET https://quay.io/api/v1/repository/namespace/repository | jq '.is_public'"]) >> 'false' + 1 * getPipelineMock('sh')([returnStdout: true, script: "curl -H 'Content-Type: application/json' -H 'Authorization: Bearer quaytoken' -X POST --data '{\"visibility\": \"public\"}' https://quay.io/api/v1/repository/namespace/repository/changevisibility | jq '.success'"]) >> 'true' + } + + def "[cloud.groovy] makeQuayImagePublic usernamePassword raise error"() { + setup: + groovyScript.getBinding().setVariable('QUAY_TOKEN', 'quaytoken') + when: + groovyScript.makeQuayImagePublic('namespace', 'repository', [ usernamePassword: 'USERNAME_PASSWORD' ]) + then: + 2 * getPipelineMock('util.executeWithCredentialsMap')([usernamePassword: 'USERNAME_PASSWORD'], _) + 1 * getPipelineMock('sh')([returnStdout: true, script: "curl -H 'Authorization: Bearer quaytoken' -X GET https://quay.io/api/v1/repository/namespace/repository | jq '.is_public'"]) >> 'false' + 1 * getPipelineMock('sh')([returnStdout: true, script: "curl -H 'Content-Type: application/json' -H 'Authorization: Bearer quaytoken' -X POST --data '{\"visibility\": \"public\"}' https://quay.io/api/v1/repository/namespace/repository/changevisibility | jq '.success'"]) >> 'false' + 1 * getPipelineMock('error').call('Cannot set image quay.io/namespace/repository as visible') + } + + def "[cloud.groovy] makeQuayImagePublic no creds"() { + setup: + groovyScript.getBinding().setVariable('QUAY_TOKEN', 'quaytoken') + when: + groovyScript.makeQuayImagePublic('namespace', 'repository') + then: + 1 * getPipelineMock('util.executeWithCredentialsMap')([ token: '', usernamePassword: ''], _) + 1 * getPipelineMock('sh')([returnStdout: true, script: "curl -H 'Authorization: Bearer quaytoken' -X GET https://quay.io/api/v1/repository/namespace/repository | jq '.is_public'"]) >> 'true' + 0 * getPipelineMock('sh')([returnStdout: true, script: "curl -H 'Content-Type: application/json' -H 'Authorization: Bearer quaytoken' -X POST --data '{\"visibility\": \"public\"}' https://quay.io/api/v1/repository/namespace/repository/changevisibility | jq '.success'"]) + } + + ///////////////////////////////////////////////////////////////////// + // cleanContainersAndImages + + def "[cloud.groovy] cleanContainersAndImages no container engine"() { + when: + groovyScript.cleanContainersAndImages() + then: + 1 * getPipelineMock("sh")([script: "podman ps -a -q | tr '\\n' ','", returnStdout: true]) >> "one,two" + 1 * getPipelineMock("sh")("podman rm -f one || date") + 1 * getPipelineMock("sh")("podman rm -f two || date") + 1 * getPipelineMock("sh")([script: "podman images -q | tr '\\n' ','", returnStdout: true]) >> "hello,bonjour,hallo,ola" + 1 * getPipelineMock("sh")("podman rmi -f hello || date") + 1 * getPipelineMock("sh")("podman rmi -f bonjour || date") + 1 * getPipelineMock("sh")("podman rmi -f hallo || date") + 1 * getPipelineMock("sh")("podman rmi -f ola || date") + } + + def "[cloud.groovy] cleanContainersAndImages with docker"() { + when: + groovyScript.cleanContainersAndImages('docker') + then: + 1 * getPipelineMock("sh")([script: "docker ps -a -q | tr '\\n' ','", returnStdout: true]) >> "one,two" + 1 * getPipelineMock("sh")("docker rm -f one || date") + 1 * getPipelineMock("sh")("docker rm -f two || date") + 1 * getPipelineMock("sh")([script: "docker images -q | tr '\\n' ','", returnStdout: true]) >> "hello,bonjour,hallo,ola" + 1 * getPipelineMock("sh")("docker rmi -f hello || date") + 1 * getPipelineMock("sh")("docker rmi -f bonjour || date") + 1 * getPipelineMock("sh")("docker rmi -f hallo || date") + 1 * getPipelineMock("sh")("docker rmi -f ola || date") + } + + def "[cloud.groovy] cleanContainersAndImages no containers/images"() { + when: + groovyScript.cleanContainersAndImages() + then: + 1 * getPipelineMock("sh")([script: "podman ps -a -q | tr '\\n' ','", returnStdout: true]) >> "" + 0 * getPipelineMock("sh")("podman rm -f one || date") + 0 * getPipelineMock("sh")("podman rm -f || date") + 1 * getPipelineMock("sh")([script: "podman images -q | tr '\\n' ','", returnStdout: true]) >> "" + 0 * getPipelineMock("sh")("podman rmi -f hello || date") + 0 * getPipelineMock("sh")("podman rmi -f || date") + } + + ///////////////////////////////////////////////////////////////////// + // startLocalRegistry & cleanLocalRegistry + + def "[cloud.groovy] cleanLocalRegistry default"() { + when: + groovyScript.cleanLocalRegistry() + then: + 1 * getPipelineMock("sh")("docker rm -f registry-5000 || true") + } + + def "[cloud.groovy] cleanLocalRegistry with port"() { + when: + groovyScript.cleanLocalRegistry(6986) + then: + 1 * getPipelineMock("sh")("docker rm -f registry-6986 || true") + } + + def "[cloud.groovy] startLocalRegistry default"() { + when: + def result = groovyScript.startLocalRegistry() + then: + 1 * getPipelineMock("sh")("docker rm -f registry-5000 || true") + 1 * getPipelineMock("sh")("docker run -d -p 5000:5000 --restart=always --name registry-5000 registry:2") + result == "localhost:5000" + } + + def "[cloud.groovy] startLocalRegistry with port"() { + when: + def result = groovyScript.startLocalRegistry(63213) + then: + 1 * getPipelineMock("sh")("docker rm -f registry-63213 || true") + 1 * getPipelineMock("sh")("docker run -d -p 63213:5000 --restart=always --name registry-63213 registry:2") + result == "localhost:63213" + } + + ///////////////////////////////////////////////////////////////////// + // installSkopeo & cleanSkopeo + + def "[cloud.groovy] installSkopeo default"() { + when: + groovyScript.installSkopeo() + then: + 1 * getPipelineMock("sh")('sudo yum -y remove skopeo || true') + 1 * getPipelineMock("sh")('sudo yum -y install --nobest skopeo') + 1 * getPipelineMock("sh")('skopeo --version') + } + + def "[cloud.groovy] cleanSkopeo default"() { + when: + groovyScript.cleanSkopeo() + then: + 1 * getPipelineMock("sh")('sudo yum -y remove skopeo || true') + } + + ///////////////////////////////////////////////////////////////////// + // skopeoCopyRegistryImages + + def "[cloud.groovy] skopeoCopyRegistryImages default"() { + when: + groovyScript.skopeoCopyRegistryImages('OLD_IMAGE', 'NEW_IMAGE') + then: + 1 * getPipelineMock("sh")("skopeo copy --retry-times 3 --tls-verify=false --all docker://OLD_IMAGE docker://NEW_IMAGE") + } + + def "[cloud.groovy] skopeoCopyRegistryImages default with 5 retries"() { + when: + groovyScript.skopeoCopyRegistryImages('OLD_IMAGE', 'NEW_IMAGE', 5) + then: + 1 * getPipelineMock("sh")("skopeo copy --retry-times 5 --tls-verify=false --all docker://OLD_IMAGE docker://NEW_IMAGE") + } + + ///////////////////////////////////////////////////////////////////// + // dockerSquashImage + + def "[cloud.groovy] dockerSquashImage default"() { + when: + def result = groovyScript.dockerSquashImage('BASE_IMAGE') + then: + 1 * getPipelineMock("sh")([returnStdout: true, script: "docker history BASE_IMAGE | grep buildkit.dockerfile | wc -l"]) >> "6" + 1 * getPipelineMock("echo")("Got 7 layers to squash") + 1 * getPipelineMock("util.runWithPythonVirtualEnv")("docker-squash -v -m 'BASE_IMAGE squashed' -f 7 -t BASE_IMAGE BASE_IMAGE", 'cekit') + 1 * getPipelineMock("sh")("docker push BASE_IMAGE") + result == 'BASE_IMAGE' + } + + def "[cloud.groovy] dockerSquashImage with squash message"() { + when: + def result = groovyScript.dockerSquashImage('BASE_IMAGE', 'MESSAGE') + then: + 1 * getPipelineMock("sh")([returnStdout: true, script: "docker history BASE_IMAGE | grep buildkit.dockerfile | wc -l"]) >> "6" + 1 * getPipelineMock("echo")("Got 7 layers to squash") + 1 * getPipelineMock("util.runWithPythonVirtualEnv")("docker-squash -v -m 'MESSAGE' -f 7 -t BASE_IMAGE BASE_IMAGE", 'cekit') + 1 * getPipelineMock("sh")("docker push BASE_IMAGE") + result == 'BASE_IMAGE' + } + + def "[cloud.groovy] dockerSquashImage with message and no replaceCurrentImage"() { + when: + def result = groovyScript.dockerSquashImage('BASE_IMAGE', 'MESSAGE', false) + then: + 1 * getPipelineMock("sh")([returnStdout: true, script: "docker history BASE_IMAGE | grep buildkit.dockerfile | wc -l"]) >> "6" + 1 * getPipelineMock("echo")("Got 7 layers to squash") + 1 * getPipelineMock("util.runWithPythonVirtualEnv")("docker-squash -v -m 'MESSAGE' -f 7 -t BASE_IMAGE-squashed BASE_IMAGE", 'cekit') + 1 * getPipelineMock("sh")("docker push BASE_IMAGE-squashed") + result == 'BASE_IMAGE-squashed' + } + + ///////////////////////////////////////////////////////////////////// + // dockerDebugImage + + def "[cloud.groovy] dockerDebugImage default"() { + when: + groovyScript.dockerDebugImage('IMAGE') + then: + 1 * getPipelineMock("sh")("docker images") + 1 * getPipelineMock("sh")("docker history IMAGE") + 1 * getPipelineMock("sh")("docker inspect IMAGE") + } + + ///////////////////////////////////////////////////////////////////// + // dockerBuildPlatformImage + + def "[cloud.groovy] dockerBuildPlatformImage default"() { + when: + groovyScript.dockerBuildPlatformImage('IMAGE', 'PLATFORM') + then: + 1 * getPipelineMock("sh")("docker buildx build --push --sbom=false --provenance=false --platform PLATFORM -t IMAGE .") + 1 * getPipelineMock("sh")("docker buildx imagetools inspect IMAGE") + 1 * getPipelineMock("sh")("docker pull --platform PLATFORM IMAGE") + } + + def "[cloud.groovy] dockerBuildPlatformImage outputToFile"() { +<<<<<<< HEAD + when: + groovyScript.dockerBuildPlatformImage('IMAGE', 'PLATFORM', true) + then: + 1 * getPipelineMock("sh")("docker buildx build --push --sbom=false --provenance=false --platform PLATFORM -t IMAGE . > IMAGE-PLATFORM-build.log") +======= + setup: + groovyScript.getBinding().setVariable("WORKSPACE", "WORKSPACE") + when: + groovyScript.dockerBuildPlatformImage('IMAGE', 'PLATFORM', true) + then: + 1 * getPipelineMock("sh")("docker buildx build --push --sbom=false --provenance=false --platform PLATFORM -t IMAGE . 2> WORKSPACE/IMAGE-PLATFORM-build.log") +>>>>>>> 87603ffb01daa1655a1cd4102613ef6e0c11c0bd + 1 * getPipelineMock("sh")("docker buildx imagetools inspect IMAGE") + 1 * getPipelineMock("sh")("docker pull --platform PLATFORM IMAGE") + } + + ///////////////////////////////////////////////////////////////////// + // dockerCreateManifest + + def "[cloud.groovy] dockerCreateManifest default"() { + when: + groovyScript.dockerCreateManifest('IMAGE', [ 'IMAGE1', 'IMAGE2' ]) + then: + 1 * getPipelineMock("sh")("docker manifest rm IMAGE || true") + 1 * getPipelineMock("sh")("docker manifest create IMAGE --insecure IMAGE1 IMAGE2") + 1 * getPipelineMock("sh")("docker manifest push IMAGE") + } + + ///////////////////////////////////////////////////////////////////// + // prepareForDockerMultiplatformBuild, debugDockerMultiplatformBuild & cleanDockerMultiplatformBuild + + def "[cloud.groovy] debugDockerMultiplatformBuild default"() { + when: + groovyScript.debugDockerMultiplatformBuild() + then: + 1 * getPipelineMock("sh")("docker context ls") + 1 * getPipelineMock("sh")("docker buildx inspect") + 1 * getPipelineMock("sh")("docker buildx ls") + } + + def "[cloud.groovy] cleanDockerMultiplatformBuild default"() { + when: + groovyScript.cleanDockerMultiplatformBuild() + then: + 1 * getPipelineMock("sh")("docker buildx rm mybuilder || true") + 1 * getPipelineMock("sh")("docker rm -f binfmt || true") + } + + def "[cloud.groovy] prepareForDockerMultiplatformBuild default"() { + setup: + groovyScript.getBinding().setVariable("WORKSPACE", "WORKSPACE") + when: + groovyScript.prepareForDockerMultiplatformBuild() + then: + 1 * getPipelineMock("sh")('docker run --rm --privileged --name binfmt docker.io/tonistiigi/binfmt --install all') + 1 * getPipelineMock("writeFile")([file: 'buildkitd.toml', text: "debug = false\n"]) + 1 * getPipelineMock("sh")('docker buildx create --name mybuilder --driver docker-container --driver-opt network=host --bootstrap --config ${WORKSPACE}/buildkitd.toml') + 1 * getPipelineMock("sh")("docker buildx use mybuilder") + 0 * getPipelineMock("sh")("cat buildkitd.toml") + 1 * getPipelineMock("sh")("docker buildx rm mybuilder || true") + 1 * getPipelineMock("sh")("docker rm -f binfmt || true") + } + + def "[cloud.groovy] prepareForDockerMultiplatformBuild with insecure registries"() { + setup: + groovyScript.getBinding().setVariable("WORKSPACE", "WORKSPACE") + when: + groovyScript.prepareForDockerMultiplatformBuild(["localhost:5000","localhost:5001"]) + then: + 1 * getPipelineMock("sh")('docker run --rm --privileged --name binfmt docker.io/tonistiigi/binfmt --install all') + 1 * getPipelineMock("writeFile")([file: 'buildkitd.toml', text: """debug = false +[registry."localhost:5000"] +http = true +[registry."localhost:5001"] +http = true +"""]) + 1 * getPipelineMock("sh")('docker buildx create --name mybuilder --driver docker-container --driver-opt network=host --bootstrap --config ${WORKSPACE}/buildkitd.toml') + 1 * getPipelineMock("sh")("docker buildx use mybuilder") + 0 * getPipelineMock("sh")("cat buildkitd.toml") + 1 * getPipelineMock("sh")("docker buildx rm mybuilder || true") + 1 * getPipelineMock("sh")("docker rm -f binfmt || true") + } + + def "[cloud.groovy] prepareForDockerMultiplatformBuild with mirror registry"() { + setup: + groovyScript.getBinding().setVariable("WORKSPACE", "WORKSPACE") + when: + groovyScript.prepareForDockerMultiplatformBuild([], [ + [ + name: 'REGISTRY1', + mirrors: [ + [ + url: 'MIRROR1', + insecure: true + ], + ] + ], + [ + name: 'REGISTRY2', + mirrors: [ + [ + url: 'MIRROR2', + insecure: true + ], + [ + url: 'MIRROR3', + insecure: false + ], + ] + ], + ]) + then: + 1 * getPipelineMock("sh")('docker run --rm --privileged --name binfmt docker.io/tonistiigi/binfmt --install all') + 1 * getPipelineMock("writeFile")([file: 'buildkitd.toml', text: """debug = false +[registry."REGISTRY1"] +mirrors = ["MIRROR1"] +[registry."MIRROR1"] +http = true +[registry."REGISTRY2"] +mirrors = ["MIRROR2","MIRROR3"] +[registry."MIRROR2"] +http = true +[registry."MIRROR3"] +http = false +"""]) + 1 * getPipelineMock("sh")('docker buildx create --name mybuilder --driver docker-container --driver-opt network=host --bootstrap --config ${WORKSPACE}/buildkitd.toml') + 1 * getPipelineMock("sh")("docker buildx use mybuilder") + 0 * getPipelineMock("sh")("cat buildkitd.toml") + 1 * getPipelineMock("sh")("docker buildx rm mybuilder || true") + 1 * getPipelineMock("sh")("docker rm -f binfmt || true") + } + + def "[cloud.groovy] prepareForDockerMultiplatformBuild with debug"() { + setup: + groovyScript.getBinding().setVariable("WORKSPACE", "WORKSPACE") + when: + groovyScript.prepareForDockerMultiplatformBuild([], [], true) + then: + 1 * getPipelineMock("sh")('docker run --rm --privileged --name binfmt docker.io/tonistiigi/binfmt --install all') + 1 * getPipelineMock("writeFile")([file: 'buildkitd.toml', text: "debug = true\n"]) + 1 * getPipelineMock("sh")('docker buildx create --name mybuilder --driver docker-container --driver-opt network=host --bootstrap --config ${WORKSPACE}/buildkitd.toml') + 1 * getPipelineMock("sh")("docker buildx use mybuilder") + 1 * getPipelineMock("sh")("cat buildkitd.toml") + 1 * getPipelineMock("sh")("docker buildx rm mybuilder || true") + 1 * getPipelineMock("sh")("docker rm -f binfmt || true") + 3 * getPipelineMock("sh")("docker context ls") + 3 * getPipelineMock("sh")("docker buildx inspect") + 3 * getPipelineMock("sh")("docker buildx ls") + } + + def "[cloud.groovy] prepareForDockerMultiplatformBuild with all"() { + setup: + groovyScript.getBinding().setVariable("WORKSPACE", "WORKSPACE") + when: + groovyScript.prepareForDockerMultiplatformBuild( + ["localhost:5000","localhost:5001"], + [ + [ + name: 'REGISTRY1', + mirrors: [ + [ + url: 'MIRROR1', + insecure: true + ], + ] + ], + [ + name: 'REGISTRY2', + mirrors: [ + [ + url: 'MIRROR2', + insecure: true + ], + [ + url: 'MIRROR3', + insecure: false + ], + ] + ], + ], + true) + then: + 1 * getPipelineMock("sh")('docker run --rm --privileged --name binfmt docker.io/tonistiigi/binfmt --install all') + 1 * getPipelineMock("writeFile")([file: 'buildkitd.toml', text: """debug = true +[registry."localhost:5000"] +http = true +[registry."localhost:5001"] +http = true +[registry."REGISTRY1"] +mirrors = ["MIRROR1"] +[registry."MIRROR1"] +http = true +[registry."REGISTRY2"] +mirrors = ["MIRROR2","MIRROR3"] +[registry."MIRROR2"] +http = true +[registry."MIRROR3"] +http = false +"""]) + 1 * getPipelineMock("sh")('docker buildx create --name mybuilder --driver docker-container --driver-opt network=host --bootstrap --config ${WORKSPACE}/buildkitd.toml') + 1 * getPipelineMock("sh")("docker buildx use mybuilder") + 1 * getPipelineMock("sh")("cat buildkitd.toml") + 1 * getPipelineMock("sh")("docker buildx rm mybuilder || true") + 1 * getPipelineMock("sh")("docker rm -f binfmt || true") + 3 * getPipelineMock("sh")("docker context ls") + 3 * getPipelineMock("sh")("docker buildx inspect") + 3 * getPipelineMock("sh")("docker buildx ls") + } + + def "[cloud.groovy] getDockerIOMirrorRegistryConfig without env"() { + when: + def result = groovyScript.getDockerIOMirrorRegistryConfig() + then: + result == [ + name: 'docker.io', + mirrors: [ + [ + url : 'mirror.gcr.io', + insecure: false, + ] + ], + ] + } + + def "[cloud.groovy] getDockerIOMirrorRegistryConfig with env defined"() { + setup: + groovyScript.getBinding().setVariable("env", [DOCKER_REGISTRY_MIRROR: 'REGISTRY_MIRROR']) + when: + def result = groovyScript.getDockerIOMirrorRegistryConfig() + then: + result == [ + name: 'docker.io', + mirrors: [ + [ + url : 'REGISTRY_MIRROR', + insecure: true, + ] + ], + ] + } + + ///////////////////////////////////////////////////////////////////// + // dockerBuildMultiPlatformImages + + def "[cloud.groovy] dockerBuildMultiPlatformImages default"() { + when: + groovyScript.dockerBuildMultiPlatformImages('IMAGE', ['linux/p1', 'windows/p2']) + then: + // First platform build + 1 * getPipelineMock("sh")("docker buildx build --push --sbom=false --provenance=false --platform linux/p1 -t IMAGE-linux-p1 .") + 1 * getPipelineMock("sh")("docker buildx imagetools inspect IMAGE-linux-p1") + 1 * getPipelineMock("sh")("docker pull --platform linux/p1 IMAGE-linux-p1") + 1 * getPipelineMock("sh")([returnStdout: true, script: "docker history IMAGE-linux-p1 | grep buildkit.dockerfile | wc -l"]) >> "6" + 1 * getPipelineMock("echo")("Got 7 layers to squash") + 1 * getPipelineMock("util.runWithPythonVirtualEnv")("docker-squash -v -m 'Squashed IMAGE' -f 7 -t IMAGE-linux-p1 IMAGE-linux-p1", 'cekit') + 1 * getPipelineMock("sh")("docker push IMAGE-linux-p1") + + // Second platform build + 1 * getPipelineMock("sh")("docker buildx build --push --sbom=false --provenance=false --platform windows/p2 -t IMAGE-windows-p2 .") + 1 * getPipelineMock("sh")("docker buildx imagetools inspect IMAGE-windows-p2") + 1 * getPipelineMock("sh")("docker pull --platform windows/p2 IMAGE-windows-p2") + 1 * getPipelineMock("sh")([returnStdout: true, script: "docker history IMAGE-windows-p2 | grep buildkit.dockerfile | wc -l"]) >> "6" + 1 * getPipelineMock("echo")("Got 7 layers to squash") + 1 * getPipelineMock("util.runWithPythonVirtualEnv")("docker-squash -v -m 'Squashed IMAGE' -f 7 -t IMAGE-windows-p2 IMAGE-windows-p2", 'cekit') + 1 * getPipelineMock("sh")("docker push IMAGE-windows-p2") + + // Manifest creation + 1 * getPipelineMock("sh")("docker manifest rm IMAGE || true") + 1 * getPipelineMock("sh")("docker manifest create IMAGE --insecure IMAGE-linux-p1 IMAGE-windows-p2") + 1 * getPipelineMock("sh")("docker manifest push IMAGE") + } + + def "[cloud.groovy] dockerBuildMultiPlatformImages no squash"() { + when: + groovyScript.dockerBuildMultiPlatformImages('IMAGE', ['linux/p1', 'windows/p2'], false) + then: + // First platform build + 1 * getPipelineMock("sh")("docker buildx build --push --sbom=false --provenance=false --platform linux/p1 -t IMAGE-linux-p1 .") + 1 * getPipelineMock("sh")("docker buildx imagetools inspect IMAGE-linux-p1") + 1 * getPipelineMock("sh")("docker pull --platform linux/p1 IMAGE-linux-p1") + 0 * getPipelineMock("sh")([returnStdout: true, script: "docker history IMAGE-linux-p1 | grep buildkit.dockerfile | wc -l"]) >> "6" + 0 * getPipelineMock("echo")("Got 7 layers to squash") + 0 * getPipelineMock("util.runWithPythonVirtualEnv")("docker-squash -v -m 'Squashed IMAGE' -f 7 -t IMAGE-linux-p1 IMAGE-linux-p1", 'cekit') + 0 * getPipelineMock("sh")("docker push IMAGE-linux-p1") + + // Second platform build + 1 * getPipelineMock("sh")("docker buildx build --push --sbom=false --provenance=false --platform windows/p2 -t IMAGE-windows-p2 .") + 1 * getPipelineMock("sh")("docker buildx imagetools inspect IMAGE-windows-p2") + 1 * getPipelineMock("sh")("docker pull --platform windows/p2 IMAGE-windows-p2") + 0 * getPipelineMock("sh")([returnStdout: true, script: "docker history IMAGE-windows-p2 | grep buildkit.dockerfile | wc -l"]) >> "6" + 0 * getPipelineMock("echo")("Got 7 layers to squash") + 0 * getPipelineMock("util.runWithPythonVirtualEnv")("docker-squash -v -m 'Squashed IMAGE' -f 7 -t IMAGE-windows-p2 IMAGE-windows-p2", 'cekit') + 0 * getPipelineMock("sh")("docker push IMAGE-windows-p2") + + // Manifest creation + 1 * getPipelineMock("sh")("docker manifest rm IMAGE || true") + 1 * getPipelineMock("sh")("docker manifest create IMAGE --insecure IMAGE-linux-p1 IMAGE-windows-p2") + 1 * getPipelineMock("sh")("docker manifest push IMAGE") + } + + def "[cloud.groovy] dockerBuildMultiPlatformImages with squash and message"() { + when: + groovyScript.dockerBuildMultiPlatformImages('IMAGE', ['linux/p1', 'windows/p2'], true, 'MESSAGE') + then: + // First platform build + 1 * getPipelineMock("sh")("docker buildx build --push --sbom=false --provenance=false --platform linux/p1 -t IMAGE-linux-p1 .") + 1 * getPipelineMock("sh")("docker buildx imagetools inspect IMAGE-linux-p1") + 1 * getPipelineMock("sh")("docker pull --platform linux/p1 IMAGE-linux-p1") + 1 * getPipelineMock("sh")([returnStdout: true, script: "docker history IMAGE-linux-p1 | grep buildkit.dockerfile | wc -l"]) >> "6" + 1 * getPipelineMock("echo")("Got 7 layers to squash") + 1 * getPipelineMock("util.runWithPythonVirtualEnv")("docker-squash -v -m 'MESSAGE' -f 7 -t IMAGE-linux-p1 IMAGE-linux-p1", 'cekit') + 1 * getPipelineMock("sh")("docker push IMAGE-linux-p1") + + // Second platform build + 1 * getPipelineMock("sh")("docker buildx build --push --sbom=false --provenance=false --platform windows/p2 -t IMAGE-windows-p2 .") + 1 * getPipelineMock("sh")("docker buildx imagetools inspect IMAGE-windows-p2") + 1 * getPipelineMock("sh")("docker pull --platform windows/p2 IMAGE-windows-p2") + 1 * getPipelineMock("sh")([returnStdout: true, script: "docker history IMAGE-windows-p2 | grep buildkit.dockerfile | wc -l"]) >> "6" + 1 * getPipelineMock("echo")("Got 7 layers to squash") + 1 * getPipelineMock("util.runWithPythonVirtualEnv")("docker-squash -v -m 'MESSAGE' -f 7 -t IMAGE-windows-p2 IMAGE-windows-p2", 'cekit') + 1 * getPipelineMock("sh")("docker push IMAGE-windows-p2") + + // Manifest creation + 1 * getPipelineMock("sh")("docker manifest rm IMAGE || true") + 1 * getPipelineMock("sh")("docker manifest create IMAGE --insecure IMAGE-linux-p1 IMAGE-windows-p2") + 1 * getPipelineMock("sh")("docker manifest push IMAGE") + } + + def "[cloud.groovy] dockerBuildMultiPlatformImages with squash and message and debug"() { + when: + groovyScript.dockerBuildMultiPlatformImages('IMAGE', ['linux/p1', 'windows/p2'], true, 'MESSAGE', true) + then: + // First platform build + 1 * getPipelineMock("sh")("docker buildx build --push --sbom=false --provenance=false --platform linux/p1 -t IMAGE-linux-p1 .") + 1 * getPipelineMock("sh")("docker buildx imagetools inspect IMAGE-linux-p1") + 1 * getPipelineMock("sh")("docker pull --platform linux/p1 IMAGE-linux-p1") + 1 * getPipelineMock("sh")([returnStdout: true, script: "docker history IMAGE-linux-p1 | grep buildkit.dockerfile | wc -l"]) >> "6" + 1 * getPipelineMock("echo")("Got 7 layers to squash") + 1 * getPipelineMock("util.runWithPythonVirtualEnv")("docker-squash -v -m 'MESSAGE' -f 7 -t IMAGE-linux-p1 IMAGE-linux-p1", 'cekit') + 1 * getPipelineMock("sh")("docker push IMAGE-linux-p1") + + // Second platform build + 1 * getPipelineMock("sh")("docker buildx build --push --sbom=false --provenance=false --platform windows/p2 -t IMAGE-windows-p2 .") + 1 * getPipelineMock("sh")("docker buildx imagetools inspect IMAGE-windows-p2") + 1 * getPipelineMock("sh")("docker pull --platform windows/p2 IMAGE-windows-p2") + 1 * getPipelineMock("sh")([returnStdout: true, script: "docker history IMAGE-windows-p2 | grep buildkit.dockerfile | wc -l"]) >> "6" + 1 * getPipelineMock("echo")("Got 7 layers to squash") + 1 * getPipelineMock("util.runWithPythonVirtualEnv")("docker-squash -v -m 'MESSAGE' -f 7 -t IMAGE-windows-p2 IMAGE-windows-p2", 'cekit') + 1 * getPipelineMock("sh")("docker push IMAGE-windows-p2") + + // Manifest creation + 1 * getPipelineMock("sh")("docker manifest rm IMAGE || true") + 1 * getPipelineMock("sh")("docker manifest create IMAGE --insecure IMAGE-linux-p1 IMAGE-windows-p2") + 1 * getPipelineMock("sh")("docker manifest push IMAGE") + + // Debug commands + 5 * getPipelineMock("sh")("docker images") + 1 * getPipelineMock("sh")("docker history IMAGE") + 2 * getPipelineMock("sh")("docker history IMAGE-linux-p1") + 2 * getPipelineMock("sh")("docker history IMAGE-windows-p2") + 1 * getPipelineMock("sh")("docker inspect IMAGE") + 2 * getPipelineMock("sh")("docker inspect IMAGE-linux-p1") + 2 * getPipelineMock("sh")("docker inspect IMAGE-windows-p2") + } + + ///////////////////////////////////////////////////////////////////// + // loginOpenShift + + def "[cloud.groovy] loginOpenShift default"() { + setup: + groovyScript.getBinding().setVariable("OC_USER", 'user') + groovyScript.getBinding().setVariable("OC_PWD", 'password') + when: + groovyScript.loginOpenShift('OPENSHIFT_API', 'OPENSHIFT_CREDS_ID') + then: + 1 * getPipelineMock('usernamePassword.call')([credentialsId: 'OPENSHIFT_CREDS_ID', usernameVariable: 'OC_USER', passwordVariable: 'OC_PWD']) >> 'userNamePassword' + 1 * getPipelineMock("withCredentials")(['userNamePassword'], _ as Closure) + 1 * getPipelineMock("sh")("oc login --username=user --password=password --server=OPENSHIFT_API --insecure-skip-tls-verify") + } + + ///////////////////////////////////////////////////////////////////// + // loginOpenShiftRegistry + + def "[cloud.groovy] getOpenShiftRegistryURL default"() { + when: + def result = groovyScript.getOpenShiftRegistryURL() + then: + 1 * getPipelineMock("sh")([returnStdout: true, script: "oc get routes -n openshift-image-registry | tail -1 | awk '{print \$2}'"]) >> 'OPENSHIFT_URL' + result == 'OPENSHIFT_URL' + } + + ///////////////////////////////////////////////////////////////////// + // loginOpenShiftRegistry + + def "[cloud.groovy] loginOpenShiftRegistry default"() { + when: + groovyScript.loginOpenShiftRegistry() + then: + 1 * getPipelineMock("sh")([returnStdout: true, script: "oc get routes -n openshift-image-registry | tail -1 | awk '{print \$2}'"]) >> 'OPENSHIFT_URL' + 1 * getPipelineMock("sh")("set +x && docker login -u anything -p \$(oc whoami -t) OPENSHIFT_URL") + } + + def "[cloud.groovy] loginOpenShiftRegistry with container engine and options"() { + when: + groovyScript.loginOpenShiftRegistry('podman', '--tls-verify=false') + then: + 1 * getPipelineMock("sh")([returnStdout: true, script: "oc get routes -n openshift-image-registry | tail -1 | awk '{print \$2}'"]) >> 'OPENSHIFT_URL' + 1 * getPipelineMock("sh")("set +x && podman login -u anything -p \$(oc whoami -t) --tls-verify=false OPENSHIFT_URL") + } + + ///////////////////////////////////////////////////////////////////// + // loginContainerRegistry + + def "[cloud.groovy] loginContainerRegistry default"() { + setup: + groovyScript.getBinding().setVariable("REGISTRY_USER", 'user') + groovyScript.getBinding().setVariable("REGISTRY_PWD", 'password') + when: + groovyScript.loginContainerRegistry('REGISTRY', 'REGISTRY_CREDS_ID') + then: + 1 * getPipelineMock('usernamePassword.call')([credentialsId: 'REGISTRY_CREDS_ID', usernameVariable: 'REGISTRY_USER', passwordVariable: 'REGISTRY_PWD']) >> 'userNamePassword' + 1 * getPipelineMock("withCredentials")(['userNamePassword'], _ as Closure) + 1 * getPipelineMock("sh")("set +x && docker login -u user -p password REGISTRY") + } + + def "[cloud.groovy] loginContainerRegistry with container engine and options"() { + setup: + groovyScript.getBinding().setVariable("REGISTRY_USER", 'user') + groovyScript.getBinding().setVariable("REGISTRY_PWD", 'password') + when: + groovyScript.loginContainerRegistry('REGISTRY', 'REGISTRY_CREDS_ID', 'podman', '--tls-verify=false') + then: + 1 * getPipelineMock('usernamePassword.call')([credentialsId: 'REGISTRY_CREDS_ID', usernameVariable: 'REGISTRY_USER', passwordVariable: 'REGISTRY_PWD']) >> 'userNamePassword' + 1 * getPipelineMock("withCredentials")(['userNamePassword'], _ as Closure) + 1 * getPipelineMock("sh")("set +x && podman login -u user -p password --tls-verify=false REGISTRY") + } + + ///////////////////////////////////////////////////////////////////// + // pull, tag and push images + + def "[cloud.groovy] pullImage default"() { + when: + groovyScript.pullImage('IMAGE') + then: + 1 * getPipelineMock('retry')(3, _) + 1 * getPipelineMock("sh")("docker pull IMAGE") + } + + def "[cloud.groovy] pullImage with retries and container engine"() { + when: + groovyScript.pullImage('IMAGE', 1, 'podman', '--tls-verify=false') + then: + 1 * getPipelineMock('retry')(1, _) + 1 * getPipelineMock("sh")("podman pull --tls-verify=false IMAGE") + } + + def "[cloud.groovy] pushImage default"() { + when: + groovyScript.pushImage('IMAGE') + then: + 1 * getPipelineMock('retry')(3, _) + 1 * getPipelineMock("sh")("docker push IMAGE") + } + + def "[cloud.groovy] pushImage with retries and container engine"() { + when: + groovyScript.pushImage('IMAGE', 1, 'podman', '--tls-verify=false') + then: + 1 * getPipelineMock('retry')(1, _) + 1 * getPipelineMock("sh")("podman push --tls-verify=false IMAGE") + } + + def "[cloud.groovy] tagImage default"() { + when: + groovyScript.tagImage('OLD', 'NEW') + then: + 1 * getPipelineMock("sh")("docker tag OLD NEW") + } + + def "[cloud.groovy] tagImage with container engine"() { + when: + groovyScript.tagImage('OLD', 'NEW', 'podman') + then: + 1 * getPipelineMock("sh")("podman tag OLD NEW") + } + + ///////////////////////////////////////////////////////////////////// + // getReducedTag + + def "[cloud.groovy] getReducedTag with correct tag"() { + when: + def result = groovyScript.getReducedTag('1.36.0') + then: + result == "1.36" + } + + def "[cloud.groovy] getReducedTag with incorrect tag raises error"() { + when: + def result = groovyScript.getReducedTag('ANY_TAG') + then: + thrown(Exception) + } + + // updateQuayImageDescription + + def "[cloud.groovy] updateQuayImageDescription simple run with token"() { + setup: + groovyScript.getBinding().setVariable('QUAY_TOKEN', 'quaytoken') + when: + groovyScript.updateQuayImageDescription('DESCRIPTION', 'namespace', 'repository', [token: 'TOKEN']) + then: + 1 * getPipelineMock('util.executeWithCredentialsMap')([token: 'TOKEN'], _) + 1 * getPipelineMock('writeJSON')([file: 'description.json', json: [description: "DESCRIPTION"]]) + 1 * getPipelineMock('sh')([script: "curl -H 'Content-type: application/json' -H 'Authorization: Bearer quaytoken' -X PUT --data-binary '@description.json' https://quay.io/api/v1/repository/namespace/repository"]) + } + + def "[cloud.groovy] updateQuayImageDescription simple run with token and usernamePassword"() { + setup: + groovyScript.getBinding().setVariable('QUAY_TOKEN', 'quaytoken') + when: + groovyScript.updateQuayImageDescription('DESCRIPTION', 'namespace', 'repository', [token: 'Token', usernamePassword: 'USERNAME_PWD']) + then: + 1 * getPipelineMock('util.executeWithCredentialsMap')([token: 'Token', usernamePassword: 'USERNAME_PWD'], _) + 1 * getPipelineMock('writeJSON')([file: 'description.json', json: [description: "DESCRIPTION"]]) + 1 * getPipelineMock('sh')([script: "curl -H 'Content-type: application/json' -H 'Authorization: Bearer quaytoken' -X PUT --data-binary '@description.json' https://quay.io/api/v1/repository/namespace/repository"]) + } + + def "[cloud.groovy] updateQuayImageDescription simple run with usernamePassword"() { + setup: + groovyScript.getBinding().setVariable('QUAY_TOKEN', 'quaytoken') + when: + groovyScript.updateQuayImageDescription('DESCRIPTION', 'namespace', 'repository', [usernamePassword: 'USERNAME_PWD']) + then: + 1 * getPipelineMock('util.executeWithCredentialsMap')([usernamePassword: 'USERNAME_PWD'], _) + 1 * getPipelineMock('writeJSON')([file: 'description.json', json: [description: "DESCRIPTION"]]) + 1 * getPipelineMock('sh')([script: "curl -H 'Content-type: application/json' -H 'Authorization: Bearer quaytoken' -X PUT --data-binary '@description.json' https://quay.io/api/v1/repository/namespace/repository"]) + } + + def "[cloud.groovy] updateQuayImageDescription simple run without token and usernamePassword"() { + setup: + groovyScript.getBinding().setVariable('QUAY_TOKEN', 'quaytoken') + when: + groovyScript.updateQuayImageDescription('DESCRIPTION', 'namespace', 'repository') + then: + 1 * getPipelineMock('util.executeWithCredentialsMap')( _) + 1 * getPipelineMock('writeJSON')([file: 'description.json', json: [description: "DESCRIPTION"]]) + 1 * getPipelineMock('sh')([script: "curl -H 'Content-type: application/json' -H 'Authorization: Bearer quaytoken' -X PUT --data-binary '@description.json' https://quay.io/api/v1/repository/namespace/repository"]) + } +} + diff --git a/jenkins-pipeline-shared-libraries/test/vars/GithubScmSpec.groovy b/jenkins-pipeline-shared-libraries/test/vars/GithubScmSpec.groovy new file mode 100644 index 000000000..c42207ba2 --- /dev/null +++ b/jenkins-pipeline-shared-libraries/test/vars/GithubScmSpec.groovy @@ -0,0 +1,1763 @@ +import com.homeaway.devtools.jenkins.testing.JenkinsPipelineSpecification +import groovy.json.JsonSlurper +import hudson.plugins.git.GitSCM + +class GithubScmSpec extends JenkinsPipelineSpecification { + def groovyScript = null + def pullRequestInfo = null + def pullRequestInfoEmpty = null + def forkListInfo = null + def forkListInfoPage3 = null + def forkListInfoEmpty = null + def forkListInfoMissingOwner = null + def jsonSlurper = new JsonSlurper() + + def setup() { + groovyScript = loadPipelineScriptForTest("vars/githubscm.groovy") + + // shared setup for tagRepository + explicitlyMockPipelineVariable("out") + getPipelineMock("sh")([returnStdout: true, script: 'git log --oneline -1']) >> { + return 'commitIdMock' + } + + // shared setup for pushObject + explicitlyMockPipelineVariable("GITHUB_USER") + explicitlyMockPipelineVariable("GITHUB_TOKEN") + + groovyScript.getBinding().setVariable("OAUTHTOKEN", 'oauth_token') + groovyScript.getBinding().setVariable("MAVEN_SETTINGS_XML", 'path/to/settings.xml') + pullRequestInfo = mockJson('/pull_request_not_empty.json') + pullRequestInfoEmpty = mockJson('/pull_request_empty.json') + forkListInfo = mockJson('/forked_projects.json') + forkListInfoPage3 = mockJson('/forked_projects_page3.json') + forkListInfoEmpty = mockJson('/forked_projects_empty.json') + forkListInfoMissingOwner = mockJson('/forked_projects_missing_owner.json') + + getPipelineMock("sh")([returnStdout: true, script: 'mktemp -d']) >> { + return 'tempDir' + } + } + + def mockJson(def fileName) { + def url = getClass().getResource(fileName) + def data = new File(url.toURI()).text + getPipelineMock("readJSON")(['text': data]) >> jsonSlurper.parseText(data) + return data + } + + def "[githubscm.groovy] resolveRepository"() { + when: + def result = groovyScript.resolveRepository('repository', 'author', 'branches', true) + then: + result == [$class: "GitSCM", branches: [[name: "branches"]], doGenerateSubmoduleConfigurations: false, extensions: [[$class: "CleanBeforeCheckout"], [$class: "SubmoduleOption", disableSubmodules: false, parentCredentials: true, recursiveSubmodules: true, reference: "", trackingSubmodules: false], [$class: "RelativeTargetDirectory", relativeTargetDir: "./"]], "submoduleCfg": [], "userRemoteConfigs": [["credentialsId": "kie-ci", "url": "https://github.com/author/repository.git"]]] + } + + def "[githubscm.groovy] resolveRepository with different credentials"() { + when: + def result = groovyScript.resolveRepository('repository', 'author', 'branches', true, 'ci-usernamePassword') + then: + result == [$class: "GitSCM", branches: [[name: "branches"]], doGenerateSubmoduleConfigurations: false, extensions: [[$class: "CleanBeforeCheckout"], [$class: "SubmoduleOption", disableSubmodules: false, parentCredentials: true, recursiveSubmodules: true, reference: "", trackingSubmodules: false], [$class: "RelativeTargetDirectory", relativeTargetDir: "./"]], "submoduleCfg": [], "userRemoteConfigs": [["credentialsId": "ci-usernamePassword", "url": "https://github.com/author/repository.git"]]] + } + + def "[githubscm.groovy] checkoutIfExists without merge"() { + setup: + def gitSCM = [$class: "GitSCM", branches: [[name: "branches"]], doGenerateSubmoduleConfigurations: false, extensions: [[$class: "CleanBeforeCheckout"], [$class: "SubmoduleOption", disableSubmodules: false, parentCredentials: true, recursiveSubmodules: true, reference: "", trackingSubmodules: false], [$class: "RelativeTargetDirectory", relativeTargetDir: "./"]], "submoduleCfg": [], "userRemoteConfigs": [["credentialsId": "kie-ci", "url": "https://github.com/author/repository.git"]]] + when: + groovyScript.checkoutIfExists('repository', 'author', 'branches', 'defaultAuthor', 'main') + then: + 2 * getPipelineMock("checkout")(gitSCM) + 0 * getPipelineMock("sh")(['returnStdout': true, 'script': _]) + } + + def "[githubscm.groovy] checkoutIfExists first repo does not exist"() { + setup: + def gitSCM = [$class: "GitSCM", branches: [[name: "branches"]], doGenerateSubmoduleConfigurations: false, extensions: [[$class: "CleanBeforeCheckout"], [$class: "SubmoduleOption", disableSubmodules: false, parentCredentials: true, recursiveSubmodules: true, reference: "", trackingSubmodules: false], [$class: "RelativeTargetDirectory", relativeTargetDir: "./"]], "submoduleCfg": [], "userRemoteConfigs": [["credentialsId": "kie-ci", "url": "https://github.com/author/repository.git"]]] + when: + groovyScript.checkoutIfExists('repository', 'author', 'branches', 'defaultAuthor', 'main') + then: + 0 * getPipelineMock("checkout")(null) + + 2 * getPipelineMock("checkout")(gitSCM) + 0 * getPipelineMock("sh")(['returnStdout': true, 'script': _]) + } + + def "[githubscm.groovy] checkoutIfExists with merge true"() { + setup: + groovyScript.getBinding().setVariable("kieCiUserPassword", 'user:password') + def repositoryScmInformation = [$class: "GitSCM", branches: [[name: "branches"]], doGenerateSubmoduleConfigurations: false, extensions: [[$class: "CleanBeforeCheckout"], [$class: "SubmoduleOption", disableSubmodules: false, parentCredentials: true, recursiveSubmodules: true, reference: "", trackingSubmodules: false], [$class: "RelativeTargetDirectory", relativeTargetDir: "./"]], "submoduleCfg": [], "userRemoteConfigs": [["credentialsId": "kie-ci", "url": "https://github.com/author/repository.git"]]] + def repositoryScmInformationMain = [$class: "GitSCM", branches: [[name: "main"]], doGenerateSubmoduleConfigurations: false, extensions: [[$class: "CleanBeforeCheckout"], [$class: "SubmoduleOption", disableSubmodules: false, parentCredentials: true, recursiveSubmodules: true, reference: "", trackingSubmodules: false], [$class: "RelativeTargetDirectory", relativeTargetDir: "./"]], "submoduleCfg": [], "userRemoteConfigs": [["credentialsId": "kie-ci", "url": "https://github.com/defaultAuthor/repository.git"]]] + when: + groovyScript.checkoutIfExists('repository', 'author', 'branches', 'defaultAuthor', 'main', true) + then: + 1 * getPipelineMock('checkout')(repositoryScmInformation) + 1 * getPipelineMock('usernameColonPassword.call')([credentialsId: 'kie-ci', variable: 'kieCiUserPassword']) >> 'userNamePassword' + 1 * getPipelineMock("withCredentials")(['userNamePassword'], _ as Closure) + 1 * getPipelineMock('checkout')(repositoryScmInformationMain) + 1 * getPipelineMock('sh')('git pull https://user:password@github.com/author/repository branches') + 2 * getPipelineMock("sh")(['returnStdout': true, 'script': 'git log --oneline -1']) >> 'git commit information' + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': "curl --globoff -H \"Authorization: token oauth_token\" 'https://api.github.com/repos/defaultAuthor/repository/pulls?head=author:branches&state=open'"]) >> pullRequestInfo + } + + def "[githubscm.groovy] checkoutIfExists with merge true and different forked project name"() { + setup: + groovyScript.getBinding().setVariable("kieCiUserPassword", 'user:password') + def repositoryScmInformation = [$class: "GitSCM", branches: [[name: "branches"]], doGenerateSubmoduleConfigurations: false, extensions: [[$class: "CleanBeforeCheckout"], [$class: "SubmoduleOption", disableSubmodules: false, parentCredentials: true, recursiveSubmodules: true, reference: "", trackingSubmodules: false], [$class: "RelativeTargetDirectory", relativeTargetDir: "./"]], "submoduleCfg": [], "userRemoteConfigs": [["credentialsId": "kie-ci", "url": "https://github.com/author/irtyamine.git"]]] + def repositoryScmInformationMain = [$class: "GitSCM", branches: [[name: "main"]], doGenerateSubmoduleConfigurations: false, extensions: [[$class: "CleanBeforeCheckout"], [$class: "SubmoduleOption", disableSubmodules: false, parentCredentials: true, recursiveSubmodules: true, reference: "", trackingSubmodules: false], [$class: "RelativeTargetDirectory", relativeTargetDir: "./"]], "submoduleCfg": [], "userRemoteConfigs": [["credentialsId": "kie-ci", "url": "https://github.com/defaultAuthor/repository.git"]]] + when: + groovyScript.checkoutIfExists('repository', 'irtyamine', 'branches', 'defaultAuthor', 'main', true) + then: + 0 * getPipelineMock('checkout')(repositoryScmInformation) + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': "curl -H \"Authorization: token oauth_token\" 'https://api.github.com/repos/defaultAuthor/repository/forks?per_page=100&page=1'"]) >> forkListInfo + 1 * getPipelineMock('usernameColonPassword.call')([credentialsId: 'kie-ci', variable: 'kieCiUserPassword']) >> 'userNamePassword' + 1 * getPipelineMock("withCredentials")(['userNamePassword'], _ as Closure) + 1 * getPipelineMock('checkout')(repositoryScmInformationMain) + 1 * getPipelineMock('sh')('git pull https://user:password@github.com/irtyamine/github-action-build-chain branches') + 2 * getPipelineMock("sh")(['returnStdout': true, 'script': 'git log --oneline -1']) >> 'git commit information' + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': "curl --globoff -H \"Authorization: token oauth_token\" 'https://api.github.com/repos/defaultAuthor/repository/pulls?head=irtyamine:branches&state=open'"]) >> pullRequestInfo + } + + def "[githubscm.groovy] checkoutIfExists with merge true and different credentials"() { + setup: + groovyScript.getBinding().setVariable("kieCiUserPassword", 'user:password') + def repositoryScmInformation = [$class: "GitSCM", branches: [[name: "branches"]], doGenerateSubmoduleConfigurations: false, extensions: [[$class: "CleanBeforeCheckout"], [$class: "SubmoduleOption", disableSubmodules: false, parentCredentials: true, recursiveSubmodules: true, reference: "", trackingSubmodules: false], [$class: "RelativeTargetDirectory", relativeTargetDir: "./"]], "submoduleCfg": [], "userRemoteConfigs": [["credentialsId": "ci-usernamePassword", "url": "https://github.com/author/irtyamine.git"]]] + def repositoryScmInformationMain = [$class: "GitSCM", branches: [[name: "main"]], doGenerateSubmoduleConfigurations: false, extensions: [[$class: "CleanBeforeCheckout"], [$class: "SubmoduleOption", disableSubmodules: false, parentCredentials: true, recursiveSubmodules: true, reference: "", trackingSubmodules: false], [$class: "RelativeTargetDirectory", relativeTargetDir: "./"]], "submoduleCfg": [], "userRemoteConfigs": [["credentialsId": "ci-usernamePassword", "url": "https://github.com/defaultAuthor/repository.git"]]] + when: + groovyScript.checkoutIfExists('repository', 'author', 'branches', 'defaultAuthor', 'main', true, ['token': 'ci-token', 'usernamePassword': 'ci-usernamePassword']) + then: + 0 * getPipelineMock('checkout')(repositoryScmInformation) + 1 * getPipelineMock('usernameColonPassword.call')([credentialsId: 'ci-usernamePassword', variable: 'kieCiUserPassword']) >> 'userNamePassword' + 1 * getPipelineMock("withCredentials")(['userNamePassword'], _ as Closure) + 1 * getPipelineMock('checkout')(repositoryScmInformationMain) + 1 * getPipelineMock('sh')('git pull https://user:password@github.com/author/repository branches') + 2 * getPipelineMock("sh")(['returnStdout': true, 'script': 'git log --oneline -1']) >> 'git commit information' + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': "curl --globoff -H \"Authorization: token oauth_token\" 'https://api.github.com/repos/defaultAuthor/repository/pulls?head=author:branches&state=open'"]) >> pullRequestInfo + 2 * getPipelineMock("string.call")(['credentialsId': 'ci-token', 'variable': 'OAUTHTOKEN']) + } + + def "[githubscm.groovy] checkoutIfExists has not PR"() { + setup: + groovyScript.getBinding().setVariable("kieCiUserPassword", 'user:password') + def repositoryScmInformation = [$class: "GitSCM", branches: [[name: "branches"]], doGenerateSubmoduleConfigurations: false, extensions: [[$class: "CleanBeforeCheckout"], [$class: "SubmoduleOption", disableSubmodules: false, parentCredentials: true, recursiveSubmodules: true, reference: "", trackingSubmodules: false], [$class: "RelativeTargetDirectory", relativeTargetDir: "./"]], "submoduleCfg": [], "userRemoteConfigs": [["credentialsId": "kie-ci", "url": "https://github.com/author/irtyamine.git"]]] + def repositoryScmInformationMain = [$class: "GitSCM", branches: [[name: "main"]], doGenerateSubmoduleConfigurations: false, extensions: [[$class: "CleanBeforeCheckout"], [$class: "SubmoduleOption", disableSubmodules: false, parentCredentials: true, recursiveSubmodules: true, reference: "", trackingSubmodules: false], [$class: "RelativeTargetDirectory", relativeTargetDir: "./"]], "submoduleCfg": [], "userRemoteConfigs": [["credentialsId": "kie-ci", "url": "https://github.com/defaultAuthor/repository.git"]]] + when: + groovyScript.checkoutIfExists('repository', 'author', 'branches', 'defaultAuthor', 'main', true) + then: + 0 * getPipelineMock('checkout')(repositoryScmInformation) + 0 * getPipelineMock('usernameColonPassword.call')([credentialsId: 'kie-ci', variable: 'kieCiUserPassword']) >> 'userNamePassword' + + 1 * getPipelineMock('checkout')(repositoryScmInformationMain) + 0 * getPipelineMock('sh')('git pull https://user:password@github.com/author/repository branches') + + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': "curl --globoff -H \"Authorization: token oauth_token\" 'https://api.github.com/repos/defaultAuthor/repository/pulls?head=author:branches&state=open'"]) >> pullRequestInfoEmpty + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': "curl --globoff -H \"Authorization: token oauth_token\" 'https://api.github.com/repos/defaultAuthor/repository/pulls?head=defaultAuthor:branches&state=open'"]) >> pullRequestInfoEmpty + } + + def "[githubscm.groovy] checkoutIfExists Multibranch pipeline job"() { + setup: + def gitSCM = [$class: "GitSCM", branches: [[name: "main"]], doGenerateSubmoduleConfigurations: false, extensions: [[$class: "CleanBeforeCheckout"], [$class: "SubmoduleOption", disableSubmodules: false, parentCredentials: true, recursiveSubmodules: true, reference: "", trackingSubmodules: false], [$class: "RelativeTargetDirectory", relativeTargetDir: "./"]], "submoduleCfg": [], "userRemoteConfigs": [["credentialsId": "kie-ci", "url": "https://github.com/kiegroup/repository.git"]]] + when: + groovyScript.checkoutIfExists('repository', 'kiegroup', 'main', 'kiegroup', 'main') + then: + 0 * getPipelineMock("sh")(['returnStdout': true, 'script': _]) + 2 * getPipelineMock("checkout")(gitSCM) + 0 * getPipelineMock("sh")(['returnStdout': true, 'script': 'git log --oneline -1']) + } + + def "[githubscm.groovy] getRepositoryScm"() { + setup: + def gitSCM = [$class: "GitSCM", branches: [[name: "branches"]], doGenerateSubmoduleConfigurations: false, extensions: [[$class: "CleanBeforeCheckout"], [$class: "SubmoduleOption", disableSubmodules: false, parentCredentials: true, recursiveSubmodules: true, reference: "", trackingSubmodules: false], [$class: "RelativeTargetDirectory", relativeTargetDir: "./"]], "submoduleCfg": [], "userRemoteConfigs": [["credentialsId": "kie-ci", "url": "https://github.com/author/repository.git"]]] + when: + def result = groovyScript.getRepositoryScm('repository', 'author', 'branches') + then: + result == gitSCM + } + + def "[githubscm.groovy] getRepositoryScm with different credentials"() { + setup: + def gitSCM = [$class: "GitSCM", branches: [[name: "branches"]], doGenerateSubmoduleConfigurations: false, extensions: [[$class: "CleanBeforeCheckout"], [$class: "SubmoduleOption", disableSubmodules: false, parentCredentials: true, recursiveSubmodules: true, reference: "", trackingSubmodules: false], [$class: "RelativeTargetDirectory", relativeTargetDir: "./"]], "submoduleCfg": [], "userRemoteConfigs": [["credentialsId": "ci-usernamePassword", "url": "https://github.com/author/repository.git"]]] + when: + def result = groovyScript.getRepositoryScm('repository', 'author', 'branches', 'ci-usernamePassword') + then: + result == gitSCM + } + + def "[githubscm.groovy] getRepositoryScm with non-existent branch"() { + setup: + def gitSCM = [$class: "GitSCM", branches: [[name: "branches"]], doGenerateSubmoduleConfigurations: false, extensions: [[$class: "CleanBeforeCheckout"], [$class: "SubmoduleOption", disableSubmodules: false, parentCredentials: true, recursiveSubmodules: true, reference: "", trackingSubmodules: false], [$class: "RelativeTargetDirectory", relativeTargetDir: "./"]], "submoduleCfg": [], "userRemoteConfigs": [["credentialsId": "kie-ci", "url": "https://github.com/author/repository.git"]]] + getPipelineMock('checkout')(gitSCM) >> { throw new Exception() } + when: + def result = groovyScript.getRepositoryScm('repository', 'author', 'branches') + then: + result == null + } + + def "[githubscm.groovy] mergeSourceIntoTarget"() { + setup: + groovyScript.getBinding().setVariable("kieCiUserPassword", 'user:password') + def repositoryScmInformation = [$class: "GitSCM", branches: [[name: "targetBranches"]], doGenerateSubmoduleConfigurations: false, extensions: [[$class: "CleanBeforeCheckout"], [$class: "SubmoduleOption", disableSubmodules: false, parentCredentials: true, recursiveSubmodules: true, reference: "", trackingSubmodules: false], [$class: "RelativeTargetDirectory", relativeTargetDir: "./"]], "submoduleCfg": [], "userRemoteConfigs": [["credentialsId": "kie-ci", "url": "https://github.com/targetAuthor/targetRepository.git"]]] + when: + groovyScript.mergeSourceIntoTarget('sourceRepository', 'sourceAuthor', 'sourceBranches', 'targetRepository', 'targetAuthor', 'targetBranches') + then: + 1 * getPipelineMock('checkout')(repositoryScmInformation) + 1 * getPipelineMock('usernameColonPassword.call')([credentialsId: 'kie-ci', variable: 'kieCiUserPassword']) >> 'userNamePassword' + 1 * getPipelineMock("withCredentials")(['userNamePassword'], _ as Closure) + 1 * getPipelineMock('sh')('git pull https://user:password@github.com/sourceAuthor/sourceRepository sourceBranches') + 2 * getPipelineMock("sh")(['returnStdout': true, 'script': 'git log --oneline -1']) >> 'git commit information' + } + + def "[githubscm.groovy] mergeSourceIntoTarget with different credentialsID"() { + setup: + groovyScript.getBinding().setVariable("kieCiUserPassword", 'user:password') + def repositoryScmInformation = [$class: "GitSCM", branches: [[name: "targetBranches"]], doGenerateSubmoduleConfigurations: false, extensions: [[$class: "CleanBeforeCheckout"], [$class: "SubmoduleOption", disableSubmodules: false, parentCredentials: true, recursiveSubmodules: true, reference: "", trackingSubmodules: false], [$class: "RelativeTargetDirectory", relativeTargetDir: "./"]], "submoduleCfg": [], "userRemoteConfigs": [["credentialsId": "ci-usernamePassword", "url": "https://github.com/targetAuthor/targetRepository.git"]]] + when: + groovyScript.mergeSourceIntoTarget('sourceRepository', 'sourceAuthor', 'sourceBranches', 'targetRepository', 'targetAuthor', 'targetBranches', 'ci-usernamePassword') + then: + 1 * getPipelineMock('checkout')(repositoryScmInformation) + 1 * getPipelineMock('usernameColonPassword.call')([credentialsId: 'ci-usernamePassword', variable: 'kieCiUserPassword']) >> 'userNamePassword' + 1 * getPipelineMock("withCredentials")(['userNamePassword'], _ as Closure) + 1 * getPipelineMock('sh')('git pull https://user:password@github.com/sourceAuthor/sourceRepository sourceBranches') + 2 * getPipelineMock("sh")(['returnStdout': true, 'script': 'git log --oneline -1']) >> 'git commit information' + } + + def "[githubscm.groovy] mergeSourceIntoTarget throw exception"() { + setup: + groovyScript.getBinding().setVariable("kieCiUserPassword", 'user:password') + def repositoryScmInformation = [$class: "GitSCM", branches: [[name: "targetBranches"]], doGenerateSubmoduleConfigurations: false, extensions: [[$class: "CleanBeforeCheckout"], [$class: "SubmoduleOption", disableSubmodules: false, parentCredentials: true, recursiveSubmodules: true, reference: "", trackingSubmodules: false], [$class: "RelativeTargetDirectory", relativeTargetDir: "./"]], "submoduleCfg": [], "userRemoteConfigs": [["credentialsId": "kie-ci", "url": "https://github.com/targetAuthor/targetRepository.git"]]] + when: + groovyScript.mergeSourceIntoTarget('sourceRepository', 'sourceAuthor', 'sourceBranches', 'targetRepository', 'targetAuthor', 'targetBranches') + then: + 1 * getPipelineMock('checkout')(repositoryScmInformation) + 1 * getPipelineMock('usernameColonPassword.call')([credentialsId: 'kie-ci', variable: 'kieCiUserPassword']) >> 'userNamePassword' + 1 * getPipelineMock("withCredentials")(['userNamePassword'], _ as Closure) + 1 * getPipelineMock('sh')('git pull https://user:password@github.com/sourceAuthor/sourceRepository sourceBranches') >> { throw new Exception('git error') } + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': 'git log --oneline -1']) >> 'git commit information' + thrown(Exception) + } + + def "[githubscm.groovy] createBranch"() { + when: + groovyScript.createBranch('branchName') + then: + 1 * getPipelineMock("sh")('git checkout -b branchName') + } + + def "[githubscm.groovy] createBranch exception"() { + when: + groovyScript.createBranch('branchName') + then: + 1 * getPipelineMock("sh")('git checkout -b branchName') >> { throw new Exception('git error') } + thrown(Exception) + } + + def "[githubscm.groovy] isBranchExist ok"() { + when: + def result = groovyScript.isBranchExist('origin', 'BRANCH') + then: + 1 * getPipelineMock("sh")('git fetch origin') + 1 * getPipelineMock("sh")([returnStatus: true, script: "git rev-parse origin/BRANCH"]) >> 0 + result + } + + def "[githubscm.groovy] isBranchExist not existing"() { + when: + def result = groovyScript.isBranchExist('origin', 'BRANCH') + then: + 1 * getPipelineMock("sh")('git fetch origin') + 1 * getPipelineMock("sh")([returnStatus: true, script: "git rev-parse origin/BRANCH"]) >> 1 + !result + } + + def "[githubscm.groovy] removeRemoteBranch without credentialsId"() { + setup: + groovyScript.getBinding().setVariable("GITHUB_USER", 'user') + groovyScript.getBinding().setVariable("GITHUB_TOKEN", 'password') + when: + groovyScript.removeRemoteBranch('remote', 'BRANCH') + then: + 1 * getPipelineMock("usernamePassword.call").call(['credentialsId': 'kie-ci', 'usernameVariable': 'GITHUB_USER', 'passwordVariable': 'GITHUB_TOKEN']) >> 'userNamePassword' + 1 * getPipelineMock("withCredentials")(['userNamePassword'], _ as Closure) + 1 * getPipelineMock("sh")('git config user.email user@jenkins.kie.apache.org') + 1 * getPipelineMock("sh")('git config user.name user') + 1 * getPipelineMock("sh")('git config --local credential.helper "!f() { echo username=\\user; echo password=\\password; }; f"') + 1 * getPipelineMock("sh")("git push --delete remote BRANCH") + } + + def "[githubscm.groovy] removeRemoteBranch with credentialsId"() { + setup: + groovyScript.getBinding().setVariable("GITHUB_USER", 'user') + groovyScript.getBinding().setVariable("GITHUB_TOKEN", 'password') + when: + groovyScript.removeRemoteBranch('remote', 'BRANCH', 'credsId') + then: + 1 * getPipelineMock("usernamePassword.call").call(['credentialsId': 'credsId', 'usernameVariable': 'GITHUB_USER', 'passwordVariable': 'GITHUB_TOKEN']) >> 'userNamePassword' + 1 * getPipelineMock("withCredentials")(['userNamePassword'], _ as Closure) + 1 * getPipelineMock("sh")('git config user.email user@jenkins.kie.apache.org') + 1 * getPipelineMock("sh")('git config user.name user') + 1 * getPipelineMock("sh")('git config --local credential.helper "!f() { echo username=\\user; echo password=\\password; }; f"') + 1 * getPipelineMock("sh")("git push --delete remote BRANCH") + } + + def "[githubscm.groovy] removeLocalBranch"() { + when: + groovyScript.removeLocalBranch('BRANCH') + then: + 1 * getPipelineMock("sh")("git branch -D BRANCH") + } + + def "[githubscm.groovy] commitChanges without files to add"() { + when: + groovyScript.commitChanges('commit message') + then: + 1 * getPipelineMock("sh")('git add -u') + 1 * getPipelineMock("sh")("git commit -m 'commit message'") + } + + def "[githubscm.groovy] commitChanges with files to add"() { + when: + groovyScript.commitChanges('commit message', 'src/*') + then: + 1 * getPipelineMock("sh")('git add src/*') + 1 * getPipelineMock("sh")("git commit -m 'commit message'") + } + + def "[githubscm.groovy] commitChanges with precommit closure"() { + when: + groovyScript.commitChanges('commit message', { + sh 'whatever' + }) + then: + 1 * getPipelineMock("sh")('whatever') + 1 * getPipelineMock("sh")("git commit -m 'commit message'") + } + + def "[githubscm.groovy] commitChanges without precommit closure"() { + when: + groovyScript.commitChanges('commit message') + then: + 1 * getPipelineMock("sh")("git commit -m 'commit message'") + } + + def "[githubscm.groovy] addRemote simple"() { + when: + groovyScript.addRemote('prod', 'https://github.com/kiegroup/droolsjbpm-build-bootstrap.git') + then: + 1 * getPipelineMock("sh")("git remote add prod https://github.com/kiegroup/droolsjbpm-build-bootstrap.git") + } + + def "[githubscm.groovy] squashCommits simple"() { + when: + groovyScript.squashCommits('main', 'COMMIT_MSG') + then: + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': "git rev-parse --abbrev-ref HEAD"]) >> 'BRANCH' + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': "git merge-base main BRANCH"]) >> 'MERGE_NAME' + 1 * getPipelineMock("sh")("git reset MERGE_NAME") + 1 * getPipelineMock("sh")("git add -A") + 1 * getPipelineMock("sh")('git commit -m "COMMIT_MSG"') + } + + + def "[githubscm.groovy] forkRepo without credentials"() { + setup: + groovyScript.getBinding().setVariable("GITHUB_USER", 'user') + groovyScript.getBinding().setVariable("GITHUB_TOKEN", 'password') + when: + groovyScript.forkRepo() + then: + 1 * getPipelineMock("sh")("rm -rf ~/.config/hub") + 1 * getPipelineMock('usernamePassword.call')([credentialsId: 'kie-ci', usernameVariable: 'GITHUB_USER', passwordVariable: 'GITHUB_TOKEN']) >> 'userNamePassword' + 1 * getPipelineMock("withCredentials")(['userNamePassword'], _ as Closure) + + 1 * getPipelineMock("sh")('git config user.email user@jenkins.kie.apache.org') + 1 * getPipelineMock("sh")('git config user.name user') + 1 * getPipelineMock("sh")('git config hub.protocol https') + 1 * getPipelineMock("sh")('hub fork --remote-name=origin') + 1 * getPipelineMock("sh")('git remote -v') + } + + def "[githubscm.groovy] forkRepo with credentials"() { + setup: + groovyScript.getBinding().setVariable("GITHUB_USER", 'user') + groovyScript.getBinding().setVariable("GITHUB_TOKEN", 'password') + when: + groovyScript.forkRepo('credentialsId') + then: + 1 * getPipelineMock("sh")("rm -rf ~/.config/hub") + 1 * getPipelineMock('usernamePassword.call')([credentialsId: 'credentialsId', usernameVariable: 'GITHUB_USER', passwordVariable: 'GITHUB_TOKEN']) >> 'userNamePassword' + 1 * getPipelineMock("withCredentials")(['userNamePassword'], _ as Closure) + + 1 * getPipelineMock("sh")('git config user.email user@jenkins.kie.apache.org') + 1 * getPipelineMock("sh")('git config user.name user') + 1 * getPipelineMock("sh")('git config hub.protocol https') + 1 * getPipelineMock("sh")('hub fork --remote-name=origin') + 1 * getPipelineMock("sh")('git remote -v') + } + + def "[githubscm.groovy] createPR without body, Credentials and target branch"() { + setup: + groovyScript.getBinding().setVariable("GITHUB_USER", 'user') + groovyScript.getBinding().setVariable("GITHUB_TOKEN", 'password') + when: + def result = groovyScript.createPR('PR Title') + then: + 1 * getPipelineMock("sh")("rm -rf ~/.config/hub") + 1 * getPipelineMock('usernamePassword.call')([credentialsId: 'kie-ci', usernameVariable: 'GITHUB_USER', passwordVariable: 'GITHUB_TOKEN']) >> 'userNamePassword' + 1 * getPipelineMock("withCredentials")(['userNamePassword'], _ as Closure) + 1 * getPipelineMock("sh")('git config user.email user@jenkins.kie.apache.org') + 1 * getPipelineMock("sh")('git config user.name user') + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': "hub pull-request -m 'PR Title' -m '' -b 'main'"]) >> 'shResult' + } + + def "[githubscm.groovy] createPR without Credentials and target branch"() { + setup: + groovyScript.getBinding().setVariable("GITHUB_USER", 'user') + groovyScript.getBinding().setVariable("GITHUB_TOKEN", 'password') + when: + def result = groovyScript.createPR('PR Title', 'PR body.') + then: + 1 * getPipelineMock("sh")("rm -rf ~/.config/hub") + 1 * getPipelineMock('usernamePassword.call')([credentialsId: 'kie-ci', usernameVariable: 'GITHUB_USER', passwordVariable: 'GITHUB_TOKEN']) >> 'userNamePassword' + 1 * getPipelineMock("withCredentials")(['userNamePassword'], _ as Closure) + 1 * getPipelineMock("sh")('git config user.email user@jenkins.kie.apache.org') + 1 * getPipelineMock("sh")('git config user.name user') + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': "hub pull-request -m 'PR Title' -m 'PR body.' -b 'main'"]) >> 'shResult' + } + + def "[githubscm.groovy] createPR without Credentials and target branch throwing exception"() { + setup: + groovyScript.getBinding().setVariable("GITHUB_USER", 'user') + groovyScript.getBinding().setVariable("GITHUB_TOKEN", 'password') + when: + def result = groovyScript.createPR('PR Title') + then: + 1 * getPipelineMock("sh")("rm -rf ~/.config/hub") + 1 * getPipelineMock('usernamePassword.call')([credentialsId: 'kie-ci', usernameVariable: 'GITHUB_USER', passwordVariable: 'GITHUB_TOKEN']) >> 'userNamePassword' + 1 * getPipelineMock("withCredentials")(['userNamePassword'], _ as Closure) + 1 * getPipelineMock("sh")('git config user.email user@jenkins.kie.apache.org') + 1 * getPipelineMock("sh")('git config user.name user') + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': "hub pull-request -m 'PR Title' -m '' -b 'main'"]) >> { throw new Exception('error') } + thrown(Exception) + } + + def "[githubscm.groovy] createPR with body, Credentials and target branch"() { + setup: + groovyScript.getBinding().setVariable("GITHUB_USER", 'user') + groovyScript.getBinding().setVariable("GITHUB_TOKEN", 'password') + when: + def result = groovyScript.createPR('PR Title', 'PR body.', 'targetBranch', 'credentialsId') + then: + 1 * getPipelineMock("sh")("rm -rf ~/.config/hub") + 1 * getPipelineMock('usernamePassword.call')([credentialsId: 'credentialsId', usernameVariable: 'GITHUB_USER', passwordVariable: 'GITHUB_TOKEN']) >> 'userNamePassword' + 1 * getPipelineMock("withCredentials")(['userNamePassword'], _ as Closure) + 1 * getPipelineMock("sh")('git config user.email user@jenkins.kie.apache.org') + 1 * getPipelineMock("sh")('git config user.name user') + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': "hub pull-request -m 'PR Title' -m 'PR body.' -b 'targetBranch'"]) >> 'shResult' + } + + def "[githubscm.groovy] createPrAsDraft without body, Credentials and target branch"() { + setup: + groovyScript.getBinding().setVariable("GITHUB_USER", 'user') + groovyScript.getBinding().setVariable("GITHUB_TOKEN", 'password') + when: + def result = groovyScript.createPrAsDraft('PR Title') + then: + 1 * getPipelineMock("sh")("rm -rf ~/.config/hub") + 1 * getPipelineMock('usernamePassword.call')([credentialsId: 'kie-ci', usernameVariable: 'GITHUB_USER', passwordVariable: 'GITHUB_TOKEN']) >> 'userNamePassword' + 1 * getPipelineMock("withCredentials")(['userNamePassword'], _ as Closure) + 1 * getPipelineMock("sh")('git config user.email user@jenkins.kie.apache.org') + 1 * getPipelineMock("sh")('git config user.name user') + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': "hub pull-request -d -m 'PR Title' -m '' -b 'main'"]) >> 'shResult' + } + + def "[githubscm.groovy] createPrAsDraft without Credentials and target branch"() { + setup: + groovyScript.getBinding().setVariable("GITHUB_USER", 'user') + groovyScript.getBinding().setVariable("GITHUB_TOKEN", 'password') + when: + def result = groovyScript.createPrAsDraft('PR Title', 'PR body.') + then: + 1 * getPipelineMock("sh")("rm -rf ~/.config/hub") + 1 * getPipelineMock('usernamePassword.call')([credentialsId: 'kie-ci', usernameVariable: 'GITHUB_USER', passwordVariable: 'GITHUB_TOKEN']) >> 'userNamePassword' + 1 * getPipelineMock("withCredentials")(['userNamePassword'], _ as Closure) + 1 * getPipelineMock("sh")('git config user.email user@jenkins.kie.apache.org') + 1 * getPipelineMock("sh")('git config user.name user') + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': "hub pull-request -d -m 'PR Title' -m 'PR body.' -b 'main'"]) >> 'shResult' + } + + def "[githubscm.groovy] createPrAsDraft without Credentials and target branch throwing exception"() { + setup: + groovyScript.getBinding().setVariable("GITHUB_USER", 'user') + groovyScript.getBinding().setVariable("GITHUB_TOKEN", 'password') + when: + def result = groovyScript.createPrAsDraft('PR Title') + then: + 1 * getPipelineMock("sh")("rm -rf ~/.config/hub") + 1 * getPipelineMock('usernamePassword.call')([credentialsId: 'kie-ci', usernameVariable: 'GITHUB_USER', passwordVariable: 'GITHUB_TOKEN']) >> 'userNamePassword' + 1 * getPipelineMock("withCredentials")(['userNamePassword'], _ as Closure) + 1 * getPipelineMock("sh")('git config user.email user@jenkins.kie.apache.org') + 1 * getPipelineMock("sh")('git config user.name user') + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': "hub pull-request -d -m 'PR Title' -m '' -b 'main'"]) >> { throw new Exception('draft error') } + thrown(Exception) + } + + def "[githubscm.groovy] createPrAsDraft with body, Credentials and target branch"() { + setup: + groovyScript.getBinding().setVariable("GITHUB_USER", 'user') + groovyScript.getBinding().setVariable("GITHUB_TOKEN", 'password') + when: + def result = groovyScript.createPrAsDraft('PR Title', 'PR body.', 'targetBranch', 'credentialsId') + then: + 1 * getPipelineMock("sh")("rm -rf ~/.config/hub") + 1 * getPipelineMock('usernamePassword.call')([credentialsId: 'credentialsId', usernameVariable: 'GITHUB_USER', passwordVariable: 'GITHUB_TOKEN']) >> 'userNamePassword' + 1 * getPipelineMock("withCredentials")(['userNamePassword'], _ as Closure) + 1 * getPipelineMock("sh")('git config user.email user@jenkins.kie.apache.org') + 1 * getPipelineMock("sh")('git config user.name user') + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': "hub pull-request -d -m 'PR Title' -m 'PR body.' -b 'targetBranch'"]) >> 'shResult' + } + + def "[githubscm.groovy] createPRWithLabels with body, Credentials and target branch"() { + setup: + groovyScript.getBinding().setVariable("GITHUB_USER", 'user') + groovyScript.getBinding().setVariable("GITHUB_TOKEN", 'password') + when: + def result = groovyScript.createPRWithLabels('PR Title', 'PR body.', 'targetBranch', ['label1', 'label2'] as String[], 'credentialsId') + then: + 1 * getPipelineMock("sh")("rm -rf ~/.config/hub") + 1 * getPipelineMock('usernamePassword.call')([credentialsId: 'credentialsId', usernameVariable: 'GITHUB_USER', passwordVariable: 'GITHUB_TOKEN']) >> 'userNamePassword' + 1 * getPipelineMock("withCredentials")(['userNamePassword'], _ as Closure) + 1 * getPipelineMock("sh")('git config user.email user@jenkins.kie.apache.org') + 1 * getPipelineMock("sh")('git config user.name user') + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': "hub pull-request -m 'PR Title' -m 'PR body.' -b 'targetBranch' -l 'label1','label2'"]) >> 'shResult' + } + + def "[githubscm.groovy] executeHub with credentials"() { + setup: + groovyScript.getBinding().setVariable("GITHUB_USER", 'user') + groovyScript.getBinding().setVariable("GITHUB_TOKEN", 'password') + when: + def result = groovyScript.executeHub('hubCommand', 'credentialsId') + then: + 1 * getPipelineMock("sh")("rm -rf ~/.config/hub") + 1 * getPipelineMock('usernamePassword.call')([credentialsId: 'credentialsId', usernameVariable: 'GITHUB_USER', passwordVariable: 'GITHUB_TOKEN']) >> 'userNamePassword' + 1 * getPipelineMock("withCredentials")(['userNamePassword'], _ as Closure) + 1 * getPipelineMock("sh")('git config user.email user@jenkins.kie.apache.org') + 1 * getPipelineMock("sh")('git config user.name user') + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': "hubCommand"]) >> 'shResult' + } + + def "[githubscm.groovy] mergePR without Credentials"() { + setup: + groovyScript.getBinding().setVariable("GITHUB_USER", 'user') + groovyScript.getBinding().setVariable("GITHUB_TOKEN", 'password') + when: + groovyScript.mergePR('pullRequestLink') + then: + 1 * getPipelineMock("sh")("rm -rf ~/.config/hub") + 1 * getPipelineMock('usernamePassword.call')([credentialsId: 'kie-ci', usernameVariable: 'GITHUB_USER', passwordVariable: 'GITHUB_TOKEN']) >> 'userNamePassword' + 1 * getPipelineMock("withCredentials")(['userNamePassword'], _ as Closure) + 1 * getPipelineMock("sh")('git config user.email user@jenkins.kie.apache.org') + 1 * getPipelineMock("sh")('git config user.name user') + 1 * getPipelineMock("sh")('hub merge pullRequestLink') + } + + def "[githubscm.groovy] mergePR without Credentials throwing exception"() { + setup: + groovyScript.getBinding().setVariable("GITHUB_USER", 'user') + groovyScript.getBinding().setVariable("GITHUB_TOKEN", 'password') + when: + groovyScript.mergePR('pullRequestLink') + then: + 1 * getPipelineMock("sh")("rm -rf ~/.config/hub") + 1 * getPipelineMock('usernamePassword.call')([credentialsId: 'kie-ci', usernameVariable: 'GITHUB_USER', passwordVariable: 'GITHUB_TOKEN']) >> 'userNamePassword' + 1 * getPipelineMock("withCredentials")(['userNamePassword'], _ as Closure) + 1 * getPipelineMock("sh")('git config user.email user@jenkins.kie.apache.org') + 1 * getPipelineMock("sh")('git config user.name user') + 1 * getPipelineMock("sh")('hub merge pullRequestLink') >> { throw new Exception('hub error') } + thrown(Exception) + } + + def "[githubscm.groovy] mergePR with Credentials"() { + setup: + groovyScript.getBinding().setVariable("GITHUB_USER", 'user') + groovyScript.getBinding().setVariable("GITHUB_TOKEN", 'password') + when: + groovyScript.mergePR('pullRequestLink', 'credentialsId') + then: + 1 * getPipelineMock("sh")("rm -rf ~/.config/hub") + 1 * getPipelineMock('usernamePassword.call')([credentialsId: 'credentialsId', usernameVariable: 'GITHUB_USER', passwordVariable: 'GITHUB_TOKEN']) >> 'userNamePassword' + 1 * getPipelineMock("withCredentials")(['userNamePassword'], _ as Closure) + 1 * getPipelineMock("sh")('git config user.email user@jenkins.kie.apache.org') + 1 * getPipelineMock("sh")('git config user.name user') + 1 * getPipelineMock("sh")('hub merge pullRequestLink') + } + + def "[githubscm.groovy] tagRepository with buildTag"() { + when: + groovyScript.tagRepository('tagName', 'buildTag') + then: + 1 * getPipelineMock("sh")("git tag -a 'tagName' -m 'Tagged by Jenkins in build \"buildTag\".'") + } + + def "[githubscm.groovy] tagRepository without buildTag"() { + when: + groovyScript.tagRepository('tagName') + then: + 1 * getPipelineMock("sh")("git tag -a 'tagName' -m 'Tagged by Jenkins.'") + } + + def "[githubscm.groovy] pushRemoteTag without credentialsId"() { + setup: + groovyScript.getBinding().setVariable("GITHUB_USER", 'user') + groovyScript.getBinding().setVariable("GITHUB_TOKEN", 'password') + when: + groovyScript.pushRemoteTag('remote', 'tagName') + then: + 1 * getPipelineMock("usernamePassword.call").call(['credentialsId': 'kie-ci', 'usernameVariable': 'GITHUB_USER', 'passwordVariable': 'GITHUB_TOKEN']) >> 'userNamePassword' + 1 * getPipelineMock("withCredentials")(['userNamePassword'], _ as Closure) + 1 * getPipelineMock("sh")('git config user.email user@jenkins.kie.apache.org') + 1 * getPipelineMock("sh")('git config user.name user') + 1 * getPipelineMock("sh")('git config --local credential.helper "!f() { echo username=\\user; echo password=\\password; }; f"') + 1 * getPipelineMock("sh")("git push remote --tags tagName") + } + + def "[githubscm.groovy] pushRemoteTag with credentialsId"() { + setup: + groovyScript.getBinding().setVariable("GITHUB_USER", 'user') + groovyScript.getBinding().setVariable("GITHUB_TOKEN", 'password') + when: + groovyScript.pushRemoteTag('remote', 'tagName', 'credsId') + then: + 1 * getPipelineMock("usernamePassword.call").call(['credentialsId': 'credsId', 'usernameVariable': 'GITHUB_USER', 'passwordVariable': 'GITHUB_TOKEN']) >> 'userNamePassword' + 1 * getPipelineMock("withCredentials")(['userNamePassword'], _ as Closure) + 1 * getPipelineMock("sh")('git config user.email user@jenkins.kie.apache.org') + 1 * getPipelineMock("sh")('git config user.name user') + 1 * getPipelineMock("sh")('git config --local credential.helper "!f() { echo username=\\user; echo password=\\password; }; f"') + 1 * getPipelineMock("sh")("git push remote --tags tagName") + } + + def "[githubscm.groovy] isTagExist ok"() { + when: + def output = groovyScript.isTagExist('remote', 'tagName') + then: + 1 * getPipelineMock("sh")("git fetch remote --tags") + 1 * getPipelineMock("sh")([returnStatus: true, script: "git rev-parse tagName"]) >> 0 + output + } + + def "[githubscm.groovy] isTagExist ko"() { + when: + def output = groovyScript.isTagExist('remote', 'tagName') + then: + 1 * getPipelineMock("sh")("git fetch remote --tags") + 1 * getPipelineMock("sh")([returnStatus: true, script: "git rev-parse tagName"]) >> 130 + !output + } + + def "[githubscm.groovy] removeLocalTag"() { + when: + groovyScript.removeLocalTag('tagName') + then: + 1 * getPipelineMock("sh")("git tag -d tagName") + } + + def "[githubscm.groovy] removeRemoteTag without credentialsId"() { + setup: + groovyScript.getBinding().setVariable("GITHUB_USER", 'user') + groovyScript.getBinding().setVariable("GITHUB_TOKEN", 'password') + when: + groovyScript.removeRemoteTag('remote', 'tagName') + then: + 1 * getPipelineMock("usernamePassword.call").call(['credentialsId': 'kie-ci', 'usernameVariable': 'GITHUB_USER', 'passwordVariable': 'GITHUB_TOKEN']) >> 'userNamePassword' + 1 * getPipelineMock("withCredentials")(['userNamePassword'], _ as Closure) + 1 * getPipelineMock("sh")('git config user.email user@jenkins.kie.apache.org') + 1 * getPipelineMock("sh")('git config user.name user') + 1 * getPipelineMock("sh")('git config --local credential.helper "!f() { echo username=\\user; echo password=\\password; }; f"') + 1 * getPipelineMock("sh")("git push --delete remote tagName") + } + + def "[githubscm.groovy] removeRemoteTag with credentialsId"() { + setup: + groovyScript.getBinding().setVariable("GITHUB_USER", 'user') + groovyScript.getBinding().setVariable("GITHUB_TOKEN", 'password') + when: + groovyScript.removeRemoteTag('remote', 'tagName', 'credsId') + then: + 1 * getPipelineMock("usernamePassword.call").call(['credentialsId': 'credsId', 'usernameVariable': 'GITHUB_USER', 'passwordVariable': 'GITHUB_TOKEN']) >> 'userNamePassword' + 1 * getPipelineMock("withCredentials")(['userNamePassword'], _ as Closure) + 1 * getPipelineMock("sh")('git config user.email user@jenkins.kie.apache.org') + 1 * getPipelineMock("sh")('git config user.name user') + 1 * getPipelineMock("sh")('git config --local credential.helper "!f() { echo username=\\user; echo password=\\password; }; f"') + 1 * getPipelineMock("sh")("git push --delete remote tagName") + } + + def "[githubscm.groovy] tagLocalAndRemoteRepository default"() { + setup: + groovyScript.getBinding().setVariable("GITHUB_USER", 'user') + groovyScript.getBinding().setVariable("GITHUB_TOKEN", 'password') + when: + groovyScript.tagLocalAndRemoteRepository('remote', 'tagName') + then: + 1 * getPipelineMock("sh")("git tag -a 'tagName' -m 'Tagged by Jenkins.'") + 1 * getPipelineMock("usernamePassword.call").call(['credentialsId': 'kie-ci', 'usernameVariable': 'GITHUB_USER', 'passwordVariable': 'GITHUB_TOKEN']) >> 'userNamePassword' + 1 * getPipelineMock("withCredentials")(['userNamePassword'], _ as Closure) + 1 * getPipelineMock("sh")('git config user.email user@jenkins.kie.apache.org') + 1 * getPipelineMock("sh")('git config user.name user') + 1 * getPipelineMock("sh")('git config --local credential.helper "!f() { echo username=\\user; echo password=\\password; }; f"') + 1 * getPipelineMock("sh")("git push remote --tags tagName") + } + + def "[githubscm.groovy] tagLocalAndRemoteRepository all params and tag exists"() { + setup: + groovyScript.getBinding().setVariable("GITHUB_USER", 'user') + groovyScript.getBinding().setVariable("GITHUB_TOKEN", 'password') + when: + groovyScript.tagLocalAndRemoteRepository('remote', 'tagName', 'credsId', 'buildTag', true) + then: + 1 * getPipelineMock("sh")("git fetch remote --tags") + 1 * getPipelineMock("sh")([returnStatus: true, script: "git rev-parse tagName"]) >> 0 + 1 * getPipelineMock("sh")("git tag -d tagName") + 1 * getPipelineMock("sh")("git push --delete remote tagName") + 1 * getPipelineMock("sh")("git tag -a 'tagName' -m 'Tagged by Jenkins in build \"buildTag\".'") + 2 * getPipelineMock("usernamePassword.call").call(['credentialsId': 'credsId', 'usernameVariable': 'GITHUB_USER', 'passwordVariable': 'GITHUB_TOKEN']) >> 'userNamePassword' + 2 * getPipelineMock("withCredentials")(['userNamePassword'], _ as Closure) + 2 * getPipelineMock("sh")('git config user.email user@jenkins.kie.apache.org') + 2 * getPipelineMock("sh")('git config user.name user') + 2 * getPipelineMock("sh")('git config --local credential.helper "!f() { echo username=\\user; echo password=\\password; }; f"') + 1 * getPipelineMock("sh")("git push remote --tags tagName") + } + + def "[githubscm.groovy] tagLocalAndRemoteRepository all params and tag not exists"() { + setup: + groovyScript.getBinding().setVariable("GITHUB_USER", 'user') + groovyScript.getBinding().setVariable("GITHUB_TOKEN", 'password') + when: + groovyScript.tagLocalAndRemoteRepository('remote', 'tagName', 'credsId', 'buildTag', true) + then: + 1 * getPipelineMock("sh")("git fetch remote --tags") + 1 * getPipelineMock("sh")([returnStatus: true, script: "git rev-parse tagName"]) >> 130 + 0 * getPipelineMock("sh")("git tag -d tagName") + 0 * getPipelineMock("sh")("git push --delete remote tagName") + 1 * getPipelineMock("sh")("git tag -a 'tagName' -m 'Tagged by Jenkins in build \"buildTag\".'") + 1 * getPipelineMock("usernamePassword.call").call(['credentialsId': 'credsId', 'usernameVariable': 'GITHUB_USER', 'passwordVariable': 'GITHUB_TOKEN']) >> 'userNamePassword' + 1 * getPipelineMock("withCredentials")(['userNamePassword'], _ as Closure) + 1 * getPipelineMock("sh")('git config user.email user@jenkins.kie.apache.org') + 1 * getPipelineMock("sh")('git config user.name user') + 1 * getPipelineMock("sh")('git config --local credential.helper "!f() { echo username=\\user; echo password=\\password; }; f"') + 1 * getPipelineMock("sh")("git push remote --tags tagName") + } + + def "[githubscm.groovy] createRelease with all params"() { + setup: + groovyScript.getBinding().setVariable("GH_USER", 'user') + groovyScript.getBinding().setVariable("GH_TOKEN", 'password') + when: + groovyScript.createRelease('tag','branch','releaseTag','credsId') + then: + 1 * getPipelineMock("usernamePassword.call").call(['credentialsId': 'credsId', 'usernameVariable': 'GH_USER', 'passwordVariable': 'GH_TOKEN']) >> 'userNamePassword' + 1 * getPipelineMock("withCredentials")(['userNamePassword'], _ as Closure) + 1 * getPipelineMock("sh")('gh release create tag --target branch --title tag --notes "releaseTag"') + } + + def "[githubscm.groovy] createRelease without credentialId"() { + setup: + groovyScript.getBinding().setVariable("GH_USER", 'user') + groovyScript.getBinding().setVariable("GH_TOKEN", 'password') + when: + groovyScript.createRelease('tag','branch','releaseTag') + then: + 1 * getPipelineMock("usernamePassword.call").call(['credentialsId': 'kie-ci', 'usernameVariable': 'GH_USER', 'passwordVariable': 'GH_TOKEN']) >> 'userNamePassword' + 1 * getPipelineMock("withCredentials")(['userNamePassword'], _ as Closure) + 1 * getPipelineMock("sh")('gh release create tag --target branch --title tag --notes "releaseTag"') + } + + def "[githubscm.groovy] createRelease without credentialId and description"() { + setup: + groovyScript.getBinding().setVariable("GH_USER", 'user') + groovyScript.getBinding().setVariable("GH_TOKEN", 'password') + when: + groovyScript.createRelease('tag','branch') + then: + 1 * getPipelineMock("usernamePassword.call").call(['credentialsId': 'kie-ci', 'usernameVariable': 'GH_USER', 'passwordVariable': 'GH_TOKEN']) >> 'userNamePassword' + 1 * getPipelineMock("withCredentials")(['userNamePassword'], _ as Closure) + 1 * getPipelineMock("sh")('gh release create tag --target branch --title tag --notes "Release tag"') + } + + def "[githubscm.groovy] createReleaseWithReleaseNotes with all params"() { + setup: + groovyScript.getBinding().setVariable("GH_USER", 'user') + groovyScript.getBinding().setVariable("GH_TOKEN", 'password') + when: + groovyScript.createReleaseWithReleaseNotes('tag','branch','releaseNotes','credsId') + then: + 1 * getPipelineMock("usernamePassword.call").call(['credentialsId': 'credsId', 'usernameVariable': 'GH_USER', 'passwordVariable': 'GH_TOKEN']) >> 'userNamePassword' + 1 * getPipelineMock("withCredentials")(['userNamePassword'], _ as Closure) + 1 * getPipelineMock("sh")('gh release create tag --target branch --title tag -F releaseNotes') + } + + def "[githubscm.groovy] createReleaseWithReleaseNotes without credentialId"() { + setup: + groovyScript.getBinding().setVariable("GH_USER", 'user') + groovyScript.getBinding().setVariable("GH_TOKEN", 'password') + when: + groovyScript.createReleaseWithReleaseNotes('tag','branch','releaseNotes') + then: + 1 * getPipelineMock("usernamePassword.call").call(['credentialsId': 'kie-ci', 'usernameVariable': 'GH_USER', 'passwordVariable': 'GH_TOKEN']) >> 'userNamePassword' + 1 * getPipelineMock("withCredentials")(['userNamePassword'], _ as Closure) + 1 * getPipelineMock("sh")('gh release create tag --target branch --title tag -F releaseNotes') + } + + def "[githubscm.groovy] createReleaseWithReleaseNotes without credentialId and without release notes"() { + setup: + groovyScript.getBinding().setVariable("GH_USER", 'user') + groovyScript.getBinding().setVariable("GH_TOKEN", 'password') + when: + groovyScript.createReleaseWithReleaseNotes('tag','branch') + then: + 1 * getPipelineMock("usernamePassword.call").call(['credentialsId': 'kie-ci', 'usernameVariable': 'GH_USER', 'passwordVariable': 'GH_TOKEN']) >> 'userNamePassword' + 1 * getPipelineMock("withCredentials")(['userNamePassword'], _ as Closure) + 1 * getPipelineMock("sh")('gh release create tag --target branch --title tag -F Release Notes') + } + + def "[githubscm.groovy] createReleaseWithGeneratedReleaseNotes with all params"() { + setup: + groovyScript.getBinding().setVariable("GH_USER", 'user') + groovyScript.getBinding().setVariable("GH_TOKEN", 'password') + when: + groovyScript.createReleaseWithGeneratedReleaseNotes('tag','branch','previoustag','credsId') + then: + 1 * getPipelineMock("usernamePassword.call").call(['credentialsId': 'credsId', 'usernameVariable': 'GH_USER', 'passwordVariable': 'GH_TOKEN']) >> 'userNamePassword' + 1 * getPipelineMock("withCredentials")(['userNamePassword'], _ as Closure) + 1 * getPipelineMock("sh")('gh release create tag --target branch --title tag --generate-notes --notes-start-tag previoustag') + } + + def "[githubscm.groovy] createReleaseWithGeneratedReleaseNotes without credentialId"() { + setup: + groovyScript.getBinding().setVariable("GH_USER", 'user') + groovyScript.getBinding().setVariable("GH_TOKEN", 'password') + when: + groovyScript.createReleaseWithGeneratedReleaseNotes('tag','branch','anothertag') + then: + 1 * getPipelineMock("usernamePassword.call").call(['credentialsId': 'kie-ci', 'usernameVariable': 'GH_USER', 'passwordVariable': 'GH_TOKEN']) >> 'userNamePassword' + 1 * getPipelineMock("withCredentials")(['userNamePassword'], _ as Closure) + 1 * getPipelineMock("sh")('gh release create tag --target branch --title tag --generate-notes --notes-start-tag anothertag') + } + + def "[githubscm.groovy] deleteRelease with credentialId"() { + setup: + groovyScript.getBinding().setVariable("GH_USER", 'user') + groovyScript.getBinding().setVariable("GH_TOKEN", 'password') + when: + groovyScript.deleteRelease('tag','credsId') + then: + 1 * getPipelineMock("usernamePassword.call").call(['credentialsId': 'credsId', 'usernameVariable': 'GH_USER', 'passwordVariable': 'GH_TOKEN']) >> 'userNamePassword' + 1 * getPipelineMock("withCredentials")(['userNamePassword'], _ as Closure) + 1 * getPipelineMock("sh")('gh release delete tag -y') + } + + def "[githubscm.groovy] deleteRelease without credentialId"() { + setup: + groovyScript.getBinding().setVariable("GH_USER", 'user') + groovyScript.getBinding().setVariable("GH_TOKEN", 'password') + when: + groovyScript.deleteRelease('tag') + then: + 1 * getPipelineMock("usernamePassword.call").call(['credentialsId': 'kie-ci', 'usernameVariable': 'GH_USER', 'passwordVariable': 'GH_TOKEN']) >> 'userNamePassword' + 1 * getPipelineMock("withCredentials")(['userNamePassword'], _ as Closure) + 1 * getPipelineMock("sh")('gh release delete tag -y') + } + + def "[githubscm.groovy] deleteReleaseAndTag with credentialId"() { + setup: + groovyScript.getBinding().setVariable("GH_USER", 'user') + groovyScript.getBinding().setVariable("GH_TOKEN", 'password') + when: + groovyScript.deleteReleaseAndTag('tag','credsId') + then: + 1 * getPipelineMock("usernamePassword.call").call(['credentialsId': 'credsId', 'usernameVariable': 'GH_USER', 'passwordVariable': 'GH_TOKEN']) >> 'userNamePassword' + 1 * getPipelineMock("withCredentials")(['userNamePassword'], _ as Closure) + 1 * getPipelineMock("sh")('gh release delete --cleanup-tag tag -y') + } + + def "[githubscm.groovy] deleteReleaseAndTag without credentialId"() { + setup: + groovyScript.getBinding().setVariable("GH_USER", 'user') + groovyScript.getBinding().setVariable("GH_TOKEN", 'password') + when: + groovyScript.deleteReleaseAndTag('tag') + then: + 1 * getPipelineMock("usernamePassword.call").call(['credentialsId': 'kie-ci', 'usernameVariable': 'GH_USER', 'passwordVariable': 'GH_TOKEN']) >> 'userNamePassword' + 1 * getPipelineMock("withCredentials")(['userNamePassword'], _ as Closure) + 1 * getPipelineMock("sh")('gh release delete --cleanup-tag tag -y') + } + + def "[githubscm.groovy] isReleaseExist with credentialId"() { + setup: + groovyScript.getBinding().setVariable("GH_USER", 'user') + groovyScript.getBinding().setVariable("GH_TOKEN", 'password') + when: + def result = groovyScript.isReleaseExist('tag','credsId') + then: + 1 * getPipelineMock("usernamePassword.call").call(['credentialsId': 'credsId', 'usernameVariable': 'GH_USER', 'passwordVariable': 'GH_TOKEN']) >> 'userNamePassword' + 1 * getPipelineMock("withCredentials")(['userNamePassword'], _ as Closure) + 1 * getPipelineMock("sh")([returnStatus: true, 'script':'gh release view tag']) >> 0 + result == true + } + + def "[githubscm.groovy] isReleaseExist without credentialId"() { + setup: + groovyScript.getBinding().setVariable("GH_USER", 'user') + groovyScript.getBinding().setVariable("GH_TOKEN", 'password') + when: + def result = groovyScript.isReleaseExist('tag') + then: + 1 * getPipelineMock("usernamePassword.call").call(['credentialsId': 'kie-ci', 'usernameVariable': 'GH_USER', 'passwordVariable': 'GH_TOKEN']) >> 'userNamePassword' + 1 * getPipelineMock("withCredentials")(['userNamePassword'], _ as Closure) + 1 * getPipelineMock("sh")([returnStatus: true, 'script':'gh release view tag']) >> 0 + result == true + } + + def "[githubscm.groovy] pushObject without credentialsId"() { + setup: + groovyScript.getBinding().setVariable("GITHUB_USER", 'user') + groovyScript.getBinding().setVariable("GITHUB_TOKEN", 'password') + when: + groovyScript.pushObject('remote', 'object') + then: + 1 * getPipelineMock("usernamePassword.call").call(['credentialsId': 'kie-ci', 'usernameVariable': 'GITHUB_USER', 'passwordVariable': 'GITHUB_TOKEN']) >> 'userNamePassword' + 1 * getPipelineMock("withCredentials")(['userNamePassword'], _ as Closure) + 1 * getPipelineMock("sh")('git config user.email user@jenkins.kie.apache.org') + 1 * getPipelineMock("sh")('git config user.name user') + 1 * getPipelineMock("sh")('git config --local credential.helper "!f() { echo username=\\user; echo password=\\password; }; f"') + 1 * getPipelineMock("sh")("git push remote object") + } + + def "[githubscm.groovy] pushObject with credentialsId"() { + setup: + groovyScript.getBinding().setVariable("GITHUB_USER", 'user') + groovyScript.getBinding().setVariable("GITHUB_TOKEN", 'password') + when: + groovyScript.pushObject('remote', 'object', 'credsId') + then: + 1 * getPipelineMock("usernamePassword.call").call(['credentialsId': 'credsId', 'usernameVariable': 'GITHUB_USER', 'passwordVariable': 'GITHUB_TOKEN']) >> 'userNamePassword' + 1 * getPipelineMock("withCredentials")(['userNamePassword'], _ as Closure) + 1 * getPipelineMock("sh")('git config user.email user@jenkins.kie.apache.org') + 1 * getPipelineMock("sh")('git config user.name user') + 1 * getPipelineMock("sh")('git config --local credential.helper "!f() { echo username=\\user; echo password=\\password; }; f"') + 1 * getPipelineMock("sh")("git push remote object") + } + + def "[githubscm.groovy] pushObject exception"() { + setup: + groovyScript.getBinding().setVariable("GITHUB_USER", 'user') + groovyScript.getBinding().setVariable("GITHUB_TOKEN", 'password') + getPipelineMock("sh")("git push remote object") >> { + throw new Exception("error when pushing") + } + when: + groovyScript.pushObject('remote', 'object') + then: + 1 * getPipelineMock("usernamePassword.call").call(['credentialsId': 'kie-ci', 'usernameVariable': 'GITHUB_USER', 'passwordVariable': 'GITHUB_TOKEN']) >> 'userNamePassword' + 1 * getPipelineMock("withCredentials")(['userNamePassword'], _ as Closure) + 1 * getPipelineMock("sh")('git config user.email user@jenkins.kie.apache.org') + 1 * getPipelineMock("sh")('git config user.name user') + 1 * getPipelineMock("sh")('git config --local credential.helper "!f() { echo username=\\user; echo password=\\password; }; f"') + thrown(Exception) + } + + def "[githubscm.groovy] getCommit"() { + when: + def result = groovyScript.getCommit() + then: + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': 'git log --oneline -1']) >> { return 'cd13a88 (HEAD -> BXMSPROD-819, upstream/main, upstream/HEAD, main) [KOGITO-2285] Shared libraries: Git tagging (#41)' } + result == 'cd13a88 (HEAD -> BXMSPROD-819, upstream/main, upstream/HEAD, main) [KOGITO-2285] Shared libraries: Git tagging (#41)' + } + + def "[githubscm.groovy] getCommitHash"() { + when: + def result = groovyScript.getCommitHash() + then: + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': 'git rev-parse HEAD']) >> { return 'ac36137f12d1bcfa5cdf02b796a1a33d251b48e1' } + result == 'ac36137f12d1bcfa5cdf02b796a1a33d251b48e1' + } + + def "[githubscm.groovy] getTagCommitHash"() { + when: + def result = groovyScript.getTagCommitHash('TAG') + then: + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': "git rev-list -n 1 TAG"]) >> { return 'ac36137f12d1bcfa5cdf02b796a1a33d251b48e1' } + result == 'ac36137f12d1bcfa5cdf02b796a1a33d251b48e1' + } + + def "[githubscm.groovy] getGitRepositoryURL"() { + when: + def result = groovyScript.getGitRepositoryURL() + then: + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': 'git config --get remote.origin.url | head -n 1']) >> { return 'REPO_URL' } + result == 'REPO_URL' + } + + def "[githubscm.groovy] getGitRepositoryName"() { + when: + def result = groovyScript.getGitRepositoryName() + then: + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': 'git config --get remote.origin.url | head -n 1']) >> { return 'REPO_URL' } + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': "basename REPO_URL | sed 's|\\.git||g'"]) >> { return 'REPO-NAME' } + result == 'REPO-NAME' + } + + def "[githubscm.groovy] getGitRepositoryAuthor"() { + when: + def result = groovyScript.getGitRepositoryAuthor() + then: + 2 * getPipelineMock("sh")(['returnStdout': true, 'script': 'git config --get remote.origin.url | head -n 1']) >> { return 'REPO_URL' } + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': "basename REPO_URL | sed 's|\\.git||g'"]) >> { return 'REPO-NAME' } + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': "echo REPO_URL | sed 's|/REPO-NAME.*||g' | sed 's|.*github.com.\\?||g'"]) >> { return 'AUTHOR' } + result == 'AUTHOR' + } + + def "[githubscm.groovy] getBranch"() { + when: + def result = groovyScript.getBranch() + then: + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': 'git branch --all --contains HEAD']) >> { return '* BXMSPROD-819' } + result == '* BXMSPROD-819' + } + + def "[githubscm.groovy] cleanHubAuth"() { + when: + groovyScript.cleanHubAuth() + then: + 1 * getPipelineMock("sh")("rm -rf ~/.config/hub") + } + + def "[githubscm.groovy] cleanWorkingTree"() { + when: + groovyScript.cleanWorkingTree() + then: + 1 * getPipelineMock("sh")("git clean -xdf") + } + + def "[githubscm.groovy] getRemoteInfo"() { + when: + def result = groovyScript.getRemoteInfo('remoteName', 'configName') + then: + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': 'git config --get remote.remoteName.configName']) >> { return '+refs/heads/*:refs/remotes/origin/*' } + result == '+refs/heads/*:refs/remotes/origin/*' + } + + def "[githubscm.groovy] hasForkPullRequest with pull requests"() { + when: + def result = groovyScript.hasForkPullRequest('group', 'repository', 'author', 'branch') + then: + 1 * getPipelineMock("string.call")(['credentialsId': 'kie-ci1-token', 'variable': 'OAUTHTOKEN']) + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': "curl --globoff -H \"Authorization: token oauth_token\" 'https://api.github.com/repos/group/repository/pulls?head=author:branch&state=open'"]) >> pullRequestInfo + result + } + + def "[githubscm.groovy] hasForkPullRequest with pull requests different credentials"() { + when: + def result = groovyScript.hasForkPullRequest('group', 'repository', 'author', 'branch', 'credentials2') + then: + 1 * getPipelineMock("string.call")(['credentialsId': 'credentials2', 'variable': 'OAUTHTOKEN']) + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': "curl --globoff -H \"Authorization: token oauth_token\" 'https://api.github.com/repos/group/repository/pulls?head=author:branch&state=open'"]) >> pullRequestInfo + result + } + + def "[githubscm.groovy] hasForkPullRequest without pull requests"() { + when: + def result = groovyScript.hasForkPullRequest('group', 'repository', 'author', 'branch') + then: + 1 * getPipelineMock("string.call")(['credentialsId': 'kie-ci1-token', 'variable': 'OAUTHTOKEN']) + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': "curl --globoff -H \"Authorization: token oauth_token\" 'https://api.github.com/repos/group/repository/pulls?head=author:branch&state=open'"]) >> pullRequestInfoEmpty + !result + } + + def "[githubscm.groovy] hasOriginPullRequest with pull requests"() { + when: + def result = groovyScript.hasOriginPullRequest('group', 'repository', 'branch') + then: + 1 * getPipelineMock("string.call")(['credentialsId': 'kie-ci1-token', 'variable': 'OAUTHTOKEN']) + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': "curl --globoff -H \"Authorization: token oauth_token\" 'https://api.github.com/repos/group/repository/pulls?head=group:branch&state=open'"]) >> pullRequestInfo + result + } + + def "[githubscm.groovy] hasOriginPullRequest without pull requests"() { + when: + def result = groovyScript.hasOriginPullRequest('group', 'repository', 'branch') + then: + 1 * getPipelineMock("string.call")(['credentialsId': 'kie-ci1-token', 'variable': 'OAUTHTOKEN']) + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': "curl --globoff -H \"Authorization: token oauth_token\" 'https://api.github.com/repos/group/repository/pulls?head=group:branch&state=open'"]) >> pullRequestInfoEmpty + !result + } + + def "[githubscm.groovy] hasPullRequest with fork PR"() { + when: + def result = groovyScript.hasPullRequest('group', 'repository', 'author', 'branch') + then: + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': "curl --globoff -H \"Authorization: token oauth_token\" 'https://api.github.com/repos/group/repository/pulls?head=author:branch&state=open'"]) >> pullRequestInfo + 0 * getPipelineMock("sh")(['returnStdout': true, 'script': "curl --globoff -H \"Authorization: token oauth_token\" 'https://api.github.com/repos/group/repository/pulls?head=group:branch&state=open'"]) + result + } + + def "[githubscm.groovy] hasPullRequest with origin PR"() { + when: + def result = groovyScript.hasPullRequest('group', 'repository', 'author', 'branch') + then: + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': "curl --globoff -H \"Authorization: token oauth_token\" 'https://api.github.com/repos/group/repository/pulls?head=author:branch&state=open'"]) >> pullRequestInfoEmpty + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': "curl --globoff -H \"Authorization: token oauth_token\" 'https://api.github.com/repos/group/repository/pulls?head=group:branch&state=open'"]) >> pullRequestInfo + result + } + + def "[githubscm.groovy] hasPullRequest without PR"() { + when: + def result = groovyScript.hasPullRequest('group', 'repository', 'author', 'branch') + then: + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': "curl --globoff -H \"Authorization: token oauth_token\" 'https://api.github.com/repos/group/repository/pulls?head=author:branch&state=open'"]) >> pullRequestInfoEmpty + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': "curl --globoff -H \"Authorization: token oauth_token\" 'https://api.github.com/repos/group/repository/pulls?head=group:branch&state=open'"]) >> pullRequestInfoEmpty + !result + } + + def "[githubscm.groovy] getForkedProject exists"() { + when: + def result = groovyScript.getForkedProjectName('groupx', 'repox', 'irtyamine') + then: + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': "curl -H \"Authorization: token oauth_token\" 'https://api.github.com/repos/groupx/repox/forks?per_page=100&page=1'"]) >> forkListInfo + 0 * getPipelineMock("sh")(['returnStdout': true, 'script': "curl -H \"Authorization: token oauth_token\" 'https://api.github.com/repos/groupx/repox/forks?per_page=100&page=2'"]) + 'github-action-build-chain' == result + } + + def "[githubscm.groovy] getForkedProject no existing"() { + when: + def result = groovyScript.getForkedProjectName('groupx', 'repox', 'unknownuser') + then: + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': "curl -H \"Authorization: token oauth_token\" 'https://api.github.com/repos/groupx/repox/forks?per_page=100&page=1'"]) >> forkListInfo + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': "curl -H \"Authorization: token oauth_token\" 'https://api.github.com/repos/groupx/repox/forks?per_page=100&page=2'"]) >> forkListInfoEmpty + 0 * getPipelineMock("sh")(['returnStdout': true, 'script': "curl -H \"Authorization: token oauth_token\" 'https://api.github.com/repos/groupx/repox/forks?per_page=100&page=3'"]) + null == result + } + + def "[githubscm.groovy] getForkedProject empty"() { + when: + def result = groovyScript.getForkedProjectName('groupx', 'repox', 'irtyamine') + then: + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': "curl -H \"Authorization: token oauth_token\" 'https://api.github.com/repos/groupx/repox/forks?per_page=100&page=1'"]) >> forkListInfoEmpty + 0 * getPipelineMock("sh")(['returnStdout': true, 'script': "curl -H \"Authorization: token oauth_token\" 'https://api.github.com/repos/groupx/repox/forks?per_page=100&page=2'"]) + null == result + } + + def "[githubscm.groovy] getForkedProject pagination"() { + when: + def result = groovyScript.getForkedProjectName('groupx', 'repox', 'LeonidLapshin') + then: + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': "curl -H \"Authorization: token oauth_token\" 'https://api.github.com/repos/groupx/repox/forks?per_page=100&page=1'"]) >> forkListInfo + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': "curl -H \"Authorization: token oauth_token\" 'https://api.github.com/repos/groupx/repox/forks?per_page=100&page=2'"]) >> forkListInfo + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': "curl -H \"Authorization: token oauth_token\" 'https://api.github.com/repos/groupx/repox/forks?per_page=100&page=3'"]) >> forkListInfoPage3 + 0 * getPipelineMock("sh")(['returnStdout': true, 'script': "curl -H \"Authorization: token oauth_token\" 'https://api.github.com/repos/groupx/repox/forks?per_page=100&page=4'"]) + 'appformer' == result + } + + def "[githubscm.groovy] getForkedProject pagination not present"() { + when: + def result = groovyScript.getForkedProjectName('groupx', 'repox', 'notexistinguser') + then: + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': "curl -H \"Authorization: token oauth_token\" 'https://api.github.com/repos/groupx/repox/forks?per_page=100&page=1'"]) >> forkListInfo + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': "curl -H \"Authorization: token oauth_token\" 'https://api.github.com/repos/groupx/repox/forks?per_page=100&page=2'"]) >> forkListInfoEmpty + 0 * getPipelineMock("sh")(['returnStdout': true, 'script': "curl -H \"Authorization: token oauth_token\" 'https://api.github.com/repos/groupx/repox/forks?per_page=100&page=3'"]) + null == result + } + + def "[githubscm.groovy] getForkedProject same group and owner"() { + when: + def result = groovyScript.getForkedProjectName('groupx', 'repox', 'groupx') + then: + 0 * getPipelineMock("sh")(_) + 'repox' == result + } + + def "[githubscm.groovy] getForkedProject missing property exception"() { + when: + groovyScript.getForkedProjectName('groupx', 'repox', 'irtyamine') + then: + 3 * getPipelineMock("sh")(['returnStdout': true, 'script': "curl -H \"Authorization: token oauth_token\" 'https://api.github.com/repos/groupx/repox/forks?per_page=100&page=1'"]) >> forkListInfoMissingOwner + thrown(Exception) + } + + def "[githubscm.groovy] getForkedProject missing property 2 replays"() { + when: + def result = groovyScript.getForkedProjectName('groupx', 'repox', 'irtyamine') + then: + 2 * getPipelineMock("sh")(['returnStdout': true, 'script': "curl -H \"Authorization: token oauth_token\" 'https://api.github.com/repos/groupx/repox/forks?per_page=100&page=1'"]) >> forkListInfoMissingOwner + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': "curl -H \"Authorization: token oauth_token\" 'https://api.github.com/repos/groupx/repox/forks?per_page=100&page=1'"]) >> forkListInfo + 'github-action-build-chain' == result + } + + def "[githubscm.groovy] isThereAnyChanges no change"() { + when: + def result = groovyScript.isThereAnyChanges() + then: + 1 * getPipelineMock("sh")(['script': 'git status --porcelain', 'returnStdout': true]) >> '' + } + + def "[githubscm.groovy] isThereAnyChanges with changes"() { + when: + def result = groovyScript.isThereAnyChanges() + then: + 1 * getPipelineMock("sh")(['script': 'git status --porcelain', 'returnStdout': true]) >> 'anything' + } + + def "[githubscm.groovy] updateReleaseBody with all params"() { + setup: + groovyScript.getBinding().setVariable("GH_USER", 'user') + groovyScript.getBinding().setVariable("GH_TOKEN", 'password') + when: + groovyScript.updateReleaseBody('tag','credsId') + then: + 1 * getPipelineMock("usernamePassword.call").call(['credentialsId': 'credsId', 'usernameVariable': 'GH_USER', 'passwordVariable': 'GH_TOKEN']) >> 'userNamePassword' + 1 * getPipelineMock("withCredentials")(['userNamePassword'], _ as Closure) + 1 * getPipelineMock("sh")('gh release edit tag -F release_notes') + } + + def "[githubscm.groovy] updateReleaseBody without credentialId"() { + setup: + groovyScript.getBinding().setVariable("GH_USER", 'user') + groovyScript.getBinding().setVariable("GH_TOKEN", 'password') + when: + groovyScript.updateReleaseBody('tag') + then: + 1 * getPipelineMock("usernamePassword.call").call(['credentialsId': 'kie-ci', 'usernameVariable': 'GH_USER', 'passwordVariable': 'GH_TOKEN']) >> 'userNamePassword' + 1 * getPipelineMock("withCredentials")(['userNamePassword'], _ as Closure) + 1 * getPipelineMock("sh")('gh release edit tag -F release_notes') + } + + def "[githubscm.groovy] getPreviousTag when tag does not exist"() { + when: + def result = groovyScript.getPreviousTag('tag') + then: + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': 'git tag --sort=-taggerdate | head -n 1']) >> { return '1.39.0.Final' } + result == '1.39.0.Final' + } + + def "[githubscm.groovy] getPreviousTag when tag exists"() { + when: + def result = groovyScript.getPreviousTag('tag') + then: + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': 'git tag --sort=-taggerdate | head -n 1']) >> 'tag' + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': 'git tag --sort=-taggerdate | head -n 2 | tail -n 1']) >> { return '1.39.0.Final' } + result == '1.39.0.Final' + } + + def "[githubscm.groovy] getLatestTag default"() { + when: + def result = groovyScript.getLatestTag() + then: + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': "git tag --sort=-taggerdate | head -n 1"]) >> 'TAG' + result == 'TAG' + } + + def "[githubscm.groovy] getLatestTag with startsWith"() { + when: + def result = groovyScript.getLatestTag('START') + then: + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': "git tag --sort=-taggerdate | grep '^START' | head -n 1"]) >> 'TAG ' + result == 'TAG' + } + + def "[githubscm.groovy] getLatestTag with endsWith"() { + when: + def result = groovyScript.getLatestTag('', 'END') + then: + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': "git tag --sort=-taggerdate | grep 'END\$' | head -n 1"]) >> 'TAG ' + result == 'TAG' + } + + def "[githubscm.groovy] getLatestTag with ignoreTags"() { + when: + def result = groovyScript.getLatestTag('', '', [ 'TAG1', 'TAG2' ]) + then: + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': "git tag --sort=-taggerdate | grep -v 'TAG1' | grep -v 'TAG2' | head -n 1"]) >> 'TAG ' + result == 'TAG' + } + + def "[githubscm.groovy] getLatestTag with all params"() { + when: + def result = groovyScript.getLatestTag('START', 'END', [ 'TAG1', 'TAG2' ]) + then: + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': "git tag --sort=-taggerdate | grep -v 'TAG1' | grep -v 'TAG2' | grep '^START' | grep 'END\$' | head -n 1"]) >> 'TAG ' + result == 'TAG' + } + + def "[githubscm.groovy] getPreviousTagFromVersion multiple results"() { + setup: + def previousVersions = """ +1.42.0 +1.41.0 +1.40.0 +""" + when: + def result = groovyScript.getPreviousTagFromVersion('1.43.0') + then: + 1 * getPipelineMock("util.parseVersion")('1.43.0') >> [ 1, 43, 0 ] + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': "git tag --sort=-committerdate | grep '^1.42.' | sort -V"]) >> previousVersions + result == '1.42.0' + } + + def "[githubscm.groovy] getPreviousTagFromVersion previous micro"() { + when: + def result = groovyScript.getPreviousTagFromVersion('1.43.2') + then: + 1 * getPipelineMock("util.parseVersion")('1.43.2') >> [ 1, 43, 2 ] + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': "git tag --sort=-committerdate | grep '^1.43.1' | sort -V -r"]) >> '' + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': "git tag --sort=-committerdate | grep '^1.43.0' | sort -V -r"]) >> '1.43.0' + result == '1.43.0' + } + + def "[githubscm.groovy] getPreviousTagFromVersion previous minor, no micro"() { + when: + def result = groovyScript.getPreviousTagFromVersion('1.43.0') + then: + 1 * getPipelineMock("util.parseVersion")('1.43.0') >> [ 1, 43, 0 ] + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': "git tag --sort=-committerdate | grep '^1.42.' | sort -V"]) >> '1.42.1' + result == '1.42.1' + } + + def "[githubscm.groovy] getPreviousTagFromVersion previous minor, with micro"() { + when: + def result = groovyScript.getPreviousTagFromVersion('1.43.1') + then: + 1 * getPipelineMock("util.parseVersion")('1.43.1') >> [ 1, 43, 1 ] + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': "git tag --sort=-committerdate | grep '^1.43.0' | sort -V -r"]) >> '' + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': "git tag --sort=-committerdate | grep '^1.42.' | sort -V"]) >> '1.42.1' + result == '1.42.1' + } + + def "[githubscm.groovy] getPreviousTagFromVersion previous major, no micro/minor"() { + when: + def result = groovyScript.getPreviousTagFromVersion('2.0.0') + then: + 1 * getPipelineMock("util.parseVersion")('2.0.0') >> [ 2, 0, 0 ] + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': "git tag --sort=-committerdate | grep '^1.' | sort -V -r"]) >> '1.524.568' + result == '1.524.568' + } + + def "[githubscm.groovy] getPreviousTagFromVersion previous major, with micro/minor"() { + when: + def result = groovyScript.getPreviousTagFromVersion('2.2.2') + then: + 1 * getPipelineMock("util.parseVersion")('2.2.2') >> [ 2, 2, 2 ] + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': "git tag --sort=-committerdate | grep '^2.2.1' | sort -V -r"]) >> '' + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': "git tag --sort=-committerdate | grep '^2.2.0' | sort -V -r"]) >> '' + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': "git tag --sort=-committerdate | grep '^2.1.' | sort -V"]) >> '' + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': "git tag --sort=-committerdate | grep '^2.0.' | sort -V"]) >> '' + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': "git tag --sort=-committerdate | grep '^1.' | sort -V -r"]) >> '1.524.568' + result == '1.524.568' + } + + def "[githubscm.groovy] getPreviousTagFromVersion with startsWith"() { + when: + def result = groovyScript.getPreviousTagFromVersion('2.2.2', 'any') + then: + 1 * getPipelineMock("util.parseVersion")('2.2.2') >> [ 2, 2, 2 ] + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': "git tag --sort=-committerdate | grep '^any2.2.1' | sort -V -r"]) >> '' + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': "git tag --sort=-committerdate | grep '^any2.2.0' | sort -V -r"]) >> '' + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': "git tag --sort=-committerdate | grep '^any2.1.' | sort -V"]) >> '' + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': "git tag --sort=-committerdate | grep '^any2.0.' | sort -V"]) >> '' + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': "git tag --sort=-committerdate | grep '^any1.' | sort -V -r"]) >> '1.524.568' + result == '1.524.568' + } + + def "[githubscm.groovy] getPreviousTagFromVersion with endsWith"() { + when: + def result = groovyScript.getPreviousTagFromVersion('2.2.2', '', 'any') + then: + 1 * getPipelineMock("util.parseVersion")('2.2.2') >> [ 2, 2, 2 ] + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': "git tag --sort=-committerdate | grep 'any\$' | grep '^2.2.1' | sort -V -r"]) >> '' + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': "git tag --sort=-committerdate | grep 'any\$' | grep '^2.2.0' | sort -V -r"]) >> '' + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': "git tag --sort=-committerdate | grep 'any\$' | grep '^2.1.' | sort -V"]) >> '' + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': "git tag --sort=-committerdate | grep 'any\$' | grep '^2.0.' | sort -V"]) >> '' + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': "git tag --sort=-committerdate | grep 'any\$' | grep '^1.' | sort -V -r"]) >> '1.524.568' + result == '1.524.568' + } + + def "[githubscm.groovy] getPreviousTagFromVersion with filterOutGrep"() { + when: + def result = groovyScript.getPreviousTagFromVersion('2.2.2', '', '', ['/', ']']) + then: + 1 * getPipelineMock("util.parseVersion")('2.2.2') >> [ 2, 2, 2 ] + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': "git tag --sort=-committerdate | grep -v '/' | grep -v ']' | grep '^2.2.1' | sort -V -r"]) >> '' + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': "git tag --sort=-committerdate | grep -v '/' | grep -v ']' | grep '^2.2.0' | sort -V -r"]) >> '' + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': "git tag --sort=-committerdate | grep -v '/' | grep -v ']' | grep '^2.1.' | sort -V"]) >> '' + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': "git tag --sort=-committerdate | grep -v '/' | grep -v ']' | grep '^2.0.' | sort -V"]) >> '' + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': "git tag --sort=-committerdate | grep -v '/' | grep -v ']' | grep '^1.' | sort -V -r"]) >> '1.524.568' + result == '1.524.568' + } + + def "[githubscm.groovy] getPreviousTagFromVersion with all"() { + when: + def result = groovyScript.getPreviousTagFromVersion('2.2.2', 'any', 'end', ['/', ']']) + then: + 1 * getPipelineMock("util.parseVersion")('2.2.2') >> [ 2, 2, 2 ] + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': "git tag --sort=-committerdate | grep 'end\$' | grep -v '/' | grep -v ']' | grep '^any2.2.1' | sort -V -r"]) >> '' + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': "git tag --sort=-committerdate | grep 'end\$' | grep -v '/' | grep -v ']' | grep '^any2.2.0' | sort -V -r"]) >> '' + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': "git tag --sort=-committerdate | grep 'end\$' | grep -v '/' | grep -v ']' | grep '^any2.1.' | sort -V"]) >> '' + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': "git tag --sort=-committerdate | grep 'end\$' | grep -v '/' | grep -v ']' | grep '^any2.0.' | sort -V"]) >> '' + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': "git tag --sort=-committerdate | grep 'end\$' | grep -v '/' | grep -v ']' | grep '^any1.' | sort -V -r"]) >> '1.524.568' + result == '1.524.568' + } + + def "[githubscm.groovy] prepareCommitStatusInformation default"() { + setup: + def env = [:] + groovyScript.getBinding().setVariable("env", env) + when: + groovyScript.prepareCommitStatusInformation('REPO-NAME', 'AUTHOR', 'BRANCH') + then: + 1 * getPipelineMock("dir")('githubscm-prepare-commit-REPO-NAME', _) + 1 * getPipelineMock("checkout")([$class: "GitSCM", branches: [[name: "BRANCH"]], doGenerateSubmoduleConfigurations: false, extensions: [[$class: "CleanBeforeCheckout"], [$class: "SubmoduleOption", disableSubmodules: false, parentCredentials: true, recursiveSubmodules: true, reference: "", trackingSubmodules: false], [$class: "RelativeTargetDirectory", relativeTargetDir: "./"]], "submoduleCfg": [], "userRemoteConfigs": [["credentialsId": "kie-ci", "url": "https://github.com/AUTHOR/REPO-NAME.git"]]]) + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': 'git config --get remote.origin.url | head -n 1']) >> 'REPO_URL' + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': 'git rev-parse HEAD']) >> 'COMMIT_SHA ' + groovyScript.getBinding().getVariable("env")['REPO-NAME_COMMIT_STATUS_REPO_URL'] == "REPO_URL" + groovyScript.getBinding().getVariable("env")['REPO-NAME_COMMIT_STATUS_SHA'] == "COMMIT_SHA" + } + + def "[githubscm.groovy] prepareCommitStatusInformation with credentials"() { + setup: + def env = [:] + groovyScript.getBinding().setVariable("env", env) + when: + groovyScript.prepareCommitStatusInformation('REPO-NAME', 'AUTHOR', 'BRANCH', 'CREDS_ID') + then: + 1 * getPipelineMock("dir")('githubscm-prepare-commit-REPO-NAME', _) + 1 * getPipelineMock("checkout")([$class: "GitSCM", branches: [[name: "BRANCH"]], doGenerateSubmoduleConfigurations: false, extensions: [[$class: "CleanBeforeCheckout"], [$class: "SubmoduleOption", disableSubmodules: false, parentCredentials: true, recursiveSubmodules: true, reference: "", trackingSubmodules: false], [$class: "RelativeTargetDirectory", relativeTargetDir: "./"]], "submoduleCfg": [], "userRemoteConfigs": [["credentialsId": "CREDS_ID", "url": "https://github.com/AUTHOR/REPO-NAME.git"]]]) + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': 'git config --get remote.origin.url | head -n 1']) >> 'REPO_URL' + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': 'git rev-parse HEAD']) >> 'COMMIT_SHA ' + groovyScript.getBinding().getVariable("env")['REPO-NAME_COMMIT_STATUS_REPO_URL'] == "REPO_URL" + groovyScript.getBinding().getVariable("env")['REPO-NAME_COMMIT_STATUS_SHA'] == "COMMIT_SHA" + } + + def "[githubscm.groovy] prepareCommitStatusInformationForPullRequest default"() { + setup: + def env = [:] + groovyScript.getBinding().setVariable("env", env) + when: + groovyScript.prepareCommitStatusInformationForPullRequest('REPO-NAME', 'AUTHOR', 'BRANCH', 'TARGET_AUTHOR') + then: + 1 * getPipelineMock("dir")('githubscm-prepare-commit-REPO-NAME', _) + 1 * getPipelineMock("checkout")([$class: "GitSCM", branches: [[name: "BRANCH"]], doGenerateSubmoduleConfigurations: false, extensions: [[$class: "CleanBeforeCheckout"], [$class: "SubmoduleOption", disableSubmodules: false, parentCredentials: true, recursiveSubmodules: true, reference: "", trackingSubmodules: false], [$class: "RelativeTargetDirectory", relativeTargetDir: "./"]], "submoduleCfg": [], "userRemoteConfigs": [["credentialsId": "kie-ci", "url": "https://github.com/AUTHOR/REPO-NAME.git"]]]) + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': 'git config --get remote.origin.url | head -n 1']) >> 'REPO_URL' + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': 'git rev-parse HEAD']) >> 'COMMIT_SHA ' + groovyScript.getBinding().getVariable("env")['REPO-NAME_COMMIT_STATUS_REPO_URL'] == "https://github.com/TARGET_AUTHOR/REPO-NAME" + groovyScript.getBinding().getVariable("env")['REPO-NAME_COMMIT_STATUS_SHA'] == "COMMIT_SHA" + } + + def "[githubscm.groovy] prepareCommitStatusInformationForPullRequest with credentials"() { + setup: + def env = [:] + groovyScript.getBinding().setVariable("env", env) + when: + groovyScript.prepareCommitStatusInformationForPullRequest('REPO-NAME', 'AUTHOR', 'BRANCH', 'TARGET_AUTHOR', 'CREDS_ID') + then: + 1 * getPipelineMock("dir")('githubscm-prepare-commit-REPO-NAME', _) + 1 * getPipelineMock("checkout")([$class: "GitSCM", branches: [[name: "BRANCH"]], doGenerateSubmoduleConfigurations: false, extensions: [[$class: "CleanBeforeCheckout"], [$class: "SubmoduleOption", disableSubmodules: false, parentCredentials: true, recursiveSubmodules: true, reference: "", trackingSubmodules: false], [$class: "RelativeTargetDirectory", relativeTargetDir: "./"]], "submoduleCfg": [], "userRemoteConfigs": [["credentialsId": "CREDS_ID", "url": "https://github.com/AUTHOR/REPO-NAME.git"]]]) + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': 'git config --get remote.origin.url | head -n 1']) >> 'REPO_URL' + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': 'git rev-parse HEAD']) >> 'COMMIT_SHA ' + groovyScript.getBinding().getVariable("env")['REPO-NAME_COMMIT_STATUS_REPO_URL'] == "https://github.com/TARGET_AUTHOR/REPO-NAME" + groovyScript.getBinding().getVariable("env")['REPO-NAME_COMMIT_STATUS_SHA'] == "COMMIT_SHA" + } + + def "[githubscm.groovy] setCommitStatusRepoURLEnv default"() { + setup: + def env = [:] + groovyScript.getBinding().setVariable("env", env) + when: + groovyScript.setCommitStatusRepoURLEnv('repo-with-dash') + then: + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': 'git config --get remote.origin.url | head -n 1']) >> 'REPO_URL' + groovyScript.getBinding().getVariable("env")['REPO-WITH-DASH_COMMIT_STATUS_REPO_URL'] == "REPO_URL" + } + + def "[githubscm.groovy] setCommitStatusRepoURLEnv with given url"() { + setup: + def env = [:] + groovyScript.getBinding().setVariable("env", env) + when: + groovyScript.setCommitStatusRepoURLEnv('repo-with-dash', 'URL') + then: + 0 * getPipelineMock("sh")(['returnStdout': true, 'script': 'git config --get remote.origin.url | head -n 1']) >> 'REPO_URL' + groovyScript.getBinding().getVariable("env")['REPO-WITH-DASH_COMMIT_STATUS_REPO_URL'] == "URL" + } + + def "[githubscm.groovy] setCommitStatusShaEnv default"() { + setup: + def env = [:] + groovyScript.getBinding().setVariable("env", env) + when: + groovyScript.setCommitStatusShaEnv('repo-with-dash') + then: + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': 'git rev-parse HEAD']) >> 'COMMIT_SHA ' + groovyScript.getBinding().getVariable("env")['REPO-WITH-DASH_COMMIT_STATUS_SHA'] == "COMMIT_SHA" + } + + def "[githubscm.groovy] setCommitStatusShaEnv with given url"() { + setup: + def env = [:] + groovyScript.getBinding().setVariable("env", env) + when: + groovyScript.setCommitStatusShaEnv('repo-with-dash', 'SHA') + then: + 0 * getPipelineMock("sh")(['returnStdout': true, 'script': 'git config --get remote.origin.url | head -n 1']) >> 'REPO_URL' + groovyScript.getBinding().getVariable("env")['REPO-WITH-DASH_COMMIT_STATUS_SHA'] == "SHA" + } + + def "[githubscm.groovy] updateGithubCommitStatus default, no env"() { + setup: + def env = [:] + groovyScript.getBinding().setVariable("env", env) + when: + groovyScript.updateGithubCommitStatus('CHECK_NAME', 'STATE', 'MESSAGE') + then: + 2 * getPipelineMock("sh")(['returnStdout': true, 'script': 'git config --get remote.origin.url | head -n 1']) >> 'REPO_URL' + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': "basename REPO_URL | sed 's|\\.git||g'" ]) >> 'REPO-NAME' + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': 'git rev-parse HEAD']) >> 'COMMIT_SHA ' + 1 * getPipelineMock("step")([ + $class: 'GitHubCommitStatusSetter', + commitShaSource: [$class: 'ManuallyEnteredShaSource', sha: 'COMMIT_SHA'], + contextSource: [$class: 'ManuallyEnteredCommitContextSource', context: 'CHECK_NAME'], + reposSource: [$class: 'ManuallyEnteredRepositorySource', url: 'REPO_URL'], + statusResultSource: [ $class: 'ConditionalStatusResultSource', results: [[$class: 'AnyBuildResult', message: 'MESSAGE', state: 'STATE']] ], + ]) + groovyScript.getBinding().getVariable("env")['REPO-NAME_COMMIT_STATUS_REPO_URL'] == "REPO_URL" + groovyScript.getBinding().getVariable("env")['REPO-NAME_COMMIT_STATUS_SHA'] == "COMMIT_SHA" + } + + def "[githubscm.groovy] updateGithubCommitStatus default, with full env"() { + setup: + def env = [:] + env."REPO-NAME_COMMIT_STATUS_REPO_URL" = 'PREVIOUS_REPO_URL' + env."REPO-NAME_COMMIT_STATUS_SHA" = 'PREVIOUS_COMMIT_SHA' + groovyScript.getBinding().setVariable("env", env) + when: + groovyScript.updateGithubCommitStatus('CHECK_NAME', 'STATE', 'MESSAGE') + then: + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': 'git config --get remote.origin.url | head -n 1']) >> 'REPO_URL' + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': "basename REPO_URL | sed 's|\\.git||g'" ]) >> 'REPO-NAME' + 0 * getPipelineMock("sh")(['returnStdout': true, 'script': 'git rev-parse HEAD']) >> 'COMMIT_SHA ' + 1 * getPipelineMock("step")([ + $class: 'GitHubCommitStatusSetter', + commitShaSource: [$class: 'ManuallyEnteredShaSource', sha: 'PREVIOUS_COMMIT_SHA'], + contextSource: [$class: 'ManuallyEnteredCommitContextSource', context: 'CHECK_NAME'], + reposSource: [$class: 'ManuallyEnteredRepositorySource', url: 'PREVIOUS_REPO_URL'], + statusResultSource: [ $class: 'ConditionalStatusResultSource', results: [[$class: 'AnyBuildResult', message: 'MESSAGE', state: 'STATE']] ], + ]) + groovyScript.getBinding().getVariable("env")['REPO-NAME_COMMIT_STATUS_REPO_URL'] == "PREVIOUS_REPO_URL" + groovyScript.getBinding().getVariable("env")['REPO-NAME_COMMIT_STATUS_SHA'] == "PREVIOUS_COMMIT_SHA" + } + + def "[githubscm.groovy] updateGithubCommitStatus default, with only COMMIT_STATUS_REPO_URL env defined"() { + setup: + def env = [:] + env."REPO-NAME_COMMIT_STATUS_REPO_URL" = 'PREVIOUS_REPO_URL' + groovyScript.getBinding().setVariable("env", env) + when: + groovyScript.updateGithubCommitStatus('CHECK_NAME', 'STATE', 'MESSAGE') + then: + 2 * getPipelineMock("sh")(['returnStdout': true, 'script': 'git config --get remote.origin.url | head -n 1']) >> 'REPO_URL' + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': "basename REPO_URL | sed 's|\\.git||g'" ]) >> 'REPO-NAME' + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': 'git rev-parse HEAD']) >> 'COMMIT_SHA ' + 1 * getPipelineMock("step")([ + $class: 'GitHubCommitStatusSetter', + commitShaSource: [$class: 'ManuallyEnteredShaSource', sha: 'COMMIT_SHA'], + contextSource: [$class: 'ManuallyEnteredCommitContextSource', context: 'CHECK_NAME'], + reposSource: [$class: 'ManuallyEnteredRepositorySource', url: 'REPO_URL'], + statusResultSource: [ $class: 'ConditionalStatusResultSource', results: [[$class: 'AnyBuildResult', message: 'MESSAGE', state: 'STATE']] ], + ]) + groovyScript.getBinding().getVariable("env")['REPO-NAME_COMMIT_STATUS_REPO_URL'] == "REPO_URL" + groovyScript.getBinding().getVariable("env")['REPO-NAME_COMMIT_STATUS_SHA'] == "COMMIT_SHA" + } + + def "[githubscm.groovy] updateGithubCommitStatus default, with only COMMIT_STATUS_SHA env defined"() { + setup: + def env = [:] + env."REPO-NAME_COMMIT_STATUS_SHA" = 'PREVIOUS_COMMIT_SHA' + groovyScript.getBinding().setVariable("env", env) + when: + groovyScript.updateGithubCommitStatus('CHECK_NAME', 'STATE', 'MESSAGE') + then: + 2 * getPipelineMock("sh")(['returnStdout': true, 'script': 'git config --get remote.origin.url | head -n 1']) >> 'REPO_URL' + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': "basename REPO_URL | sed 's|\\.git||g'" ]) >> 'REPO-NAME' + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': 'git rev-parse HEAD']) >> 'COMMIT_SHA ' + 1 * getPipelineMock("step")([ + $class: 'GitHubCommitStatusSetter', + commitShaSource: [$class: 'ManuallyEnteredShaSource', sha: 'COMMIT_SHA'], + contextSource: [$class: 'ManuallyEnteredCommitContextSource', context: 'CHECK_NAME'], + reposSource: [$class: 'ManuallyEnteredRepositorySource', url: 'REPO_URL'], + statusResultSource: [ $class: 'ConditionalStatusResultSource', results: [[$class: 'AnyBuildResult', message: 'MESSAGE', state: 'STATE']] ], + ]) + groovyScript.getBinding().getVariable("env")['REPO-NAME_COMMIT_STATUS_REPO_URL'] == "REPO_URL" + groovyScript.getBinding().getVariable("env")['REPO-NAME_COMMIT_STATUS_SHA'] == "COMMIT_SHA" + } + + def "[githubscm.groovy] updateGithubCommitStatus default, with repository"() { + setup: + def env = [:] + groovyScript.getBinding().setVariable("env", env) + when: + groovyScript.updateGithubCommitStatus('CHECK_NAME', 'STATE', 'MESSAGE', 'repository') + then: + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': 'git config --get remote.origin.url | head -n 1']) >> 'REPO_URL' + 0 * getPipelineMock("sh")(['returnStdout': true, 'script': "basename REPO_URL | sed 's|\\.git||g'" ]) >> 'REPO-NAME' + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': 'git rev-parse HEAD']) >> 'COMMIT_SHA ' + 1 * getPipelineMock("step")([ + $class: 'GitHubCommitStatusSetter', + commitShaSource: [$class: 'ManuallyEnteredShaSource', sha: 'COMMIT_SHA'], + contextSource: [$class: 'ManuallyEnteredCommitContextSource', context: 'CHECK_NAME'], + reposSource: [$class: 'ManuallyEnteredRepositorySource', url: 'REPO_URL'], + statusResultSource: [ $class: 'ConditionalStatusResultSource', results: [[$class: 'AnyBuildResult', message: 'MESSAGE', state: 'STATE']] ], + ]) + groovyScript.getBinding().getVariable("env")['REPOSITORY_COMMIT_STATUS_REPO_URL'] == "REPO_URL" + groovyScript.getBinding().getVariable("env")['REPOSITORY_COMMIT_STATUS_SHA'] == "COMMIT_SHA" + } + + def "[githubscm.groovy] updateGithubCommitStatusFromBuildResult and result SUCCESS"() { + setup: + groovyScript.getBinding().setVariable("env", [:]) + groovyScript.getBinding().setVariable("currentBuild", [ currentResult: 'SUCCESS' ]) + when: + groovyScript.updateGithubCommitStatusFromBuildResult('CHECK_NAME') + then: + 2 * getPipelineMock("sh")(['returnStdout': true, 'script': 'git config --get remote.origin.url | head -n 1']) >> 'REPO_URL' + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': "basename REPO_URL | sed 's|\\.git||g'" ]) >> 'REPO-NAME' + 1 * getPipelineMock("util.retrieveTestResults")() >> [passCount:10, skipCount: 3, failCount: 2] + 1 * getPipelineMock("util.getJobDurationInSeconds")() >> 3824 + 1 * getPipelineMock("util.displayDurationFromSeconds")(3824) >> '1h2m4s' + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': 'git rev-parse HEAD']) >> 'COMMIT_SHA ' + 1 * getPipelineMock("step")([ + $class: 'GitHubCommitStatusSetter', + commitShaSource: [$class: 'ManuallyEnteredShaSource', sha: 'COMMIT_SHA'], + contextSource: [$class: 'ManuallyEnteredCommitContextSource', context: 'CHECK_NAME'], + reposSource: [$class: 'ManuallyEnteredRepositorySource', url: 'REPO_URL'], + statusResultSource: [ $class: 'ConditionalStatusResultSource', results: [[$class: 'AnyBuildResult', message: "(1h2m4s) Check is successful. 15 tests run, 2 failed, 3 skipped.", state: 'SUCCESS']] ], + ]) + groovyScript.getBinding().getVariable("env")['REPO-NAME_COMMIT_STATUS_REPO_URL'] == "REPO_URL" + groovyScript.getBinding().getVariable("env")['REPO-NAME_COMMIT_STATUS_SHA'] == "COMMIT_SHA" + } + + def "[githubscm.groovy] updateGithubCommitStatusFromBuildResult and result UNSTABLE"() { + setup: + groovyScript.getBinding().setVariable("env", [:]) + groovyScript.getBinding().setVariable("currentBuild", [ currentResult: 'UNSTABLE' ]) + when: + groovyScript.updateGithubCommitStatusFromBuildResult('CHECK_NAME') + then: + 2 * getPipelineMock("sh")(['returnStdout': true, 'script': 'git config --get remote.origin.url | head -n 1']) >> 'REPO_URL' + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': "basename REPO_URL | sed 's|\\.git||g'" ]) >> 'REPO-NAME' + 1 * getPipelineMock("util.retrieveTestResults")() >> [passCount:10, skipCount: 3, failCount: 2] + 1 * getPipelineMock("util.getJobDurationInSeconds")() >> 3824 + 1 * getPipelineMock("util.displayDurationFromSeconds")(3824) >> '1h2m4s' + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': 'git rev-parse HEAD']) >> 'COMMIT_SHA ' + 1 * getPipelineMock("step")([ + $class: 'GitHubCommitStatusSetter', + commitShaSource: [$class: 'ManuallyEnteredShaSource', sha: 'COMMIT_SHA'], + contextSource: [$class: 'ManuallyEnteredCommitContextSource', context: 'CHECK_NAME'], + reposSource: [$class: 'ManuallyEnteredRepositorySource', url: 'REPO_URL'], + statusResultSource: [ $class: 'ConditionalStatusResultSource', results: [[$class: 'AnyBuildResult', message: "(1h2m4s) Test failures occurred. 15 tests run, 2 failed, 3 skipped.", state: 'FAILURE']] ], + ]) + groovyScript.getBinding().getVariable("env")['REPO-NAME_COMMIT_STATUS_REPO_URL'] == "REPO_URL" + groovyScript.getBinding().getVariable("env")['REPO-NAME_COMMIT_STATUS_SHA'] == "COMMIT_SHA" + } + + def "[githubscm.groovy] updateGithubCommitStatusFromBuildResult and result ABORTED"() { + setup: + groovyScript.getBinding().setVariable("env", [:]) + groovyScript.getBinding().setVariable("currentBuild", [ currentResult: 'ABORTED' ]) + when: + groovyScript.updateGithubCommitStatusFromBuildResult('CHECK_NAME') + then: + 2 * getPipelineMock("sh")(['returnStdout': true, 'script': 'git config --get remote.origin.url | head -n 1']) >> 'REPO_URL' + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': "basename REPO_URL | sed 's|\\.git||g'" ]) >> 'REPO-NAME' + 1 * getPipelineMock("util.retrieveTestResults")() >> [passCount:10, skipCount: 3, failCount: 2] + 1 * getPipelineMock("util.getJobDurationInSeconds")() >> 3824 + 1 * getPipelineMock("util.displayDurationFromSeconds")(3824) >> '1h2m4s' + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': 'git rev-parse HEAD']) >> 'COMMIT_SHA ' + 1 * getPipelineMock("step")([ + $class: 'GitHubCommitStatusSetter', + commitShaSource: [$class: 'ManuallyEnteredShaSource', sha: 'COMMIT_SHA'], + contextSource: [$class: 'ManuallyEnteredCommitContextSource', context: 'CHECK_NAME'], + reposSource: [$class: 'ManuallyEnteredRepositorySource', url: 'REPO_URL'], + statusResultSource: [ $class: 'ConditionalStatusResultSource', results: [[$class: 'AnyBuildResult', message: "(1h2m4s) Job aborted. 15 tests run, 2 failed, 3 skipped.", state: 'ERROR']] ], + ]) + groovyScript.getBinding().getVariable("env")['REPO-NAME_COMMIT_STATUS_REPO_URL'] == "REPO_URL" + groovyScript.getBinding().getVariable("env")['REPO-NAME_COMMIT_STATUS_SHA'] == "COMMIT_SHA" + } + + def "[githubscm.groovy] updateGithubCommitStatusFromBuildResult and result FAILURE"() { + setup: + groovyScript.getBinding().setVariable("env", [:]) + groovyScript.getBinding().setVariable("currentBuild", [ currentResult: 'FAILURE' ]) + when: + groovyScript.updateGithubCommitStatusFromBuildResult('CHECK_NAME') + then: + 2 * getPipelineMock("sh")(['returnStdout': true, 'script': 'git config --get remote.origin.url | head -n 1']) >> 'REPO_URL' + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': "basename REPO_URL | sed 's|\\.git||g'" ]) >> 'REPO-NAME' + 1 * getPipelineMock("util.retrieveTestResults")() >> [passCount:10, skipCount: 3, failCount: 2] + 1 * getPipelineMock("util.getJobDurationInSeconds")() >> 3824 + 1 * getPipelineMock("util.displayDurationFromSeconds")(3824) >> '1h2m4s' + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': 'git rev-parse HEAD']) >> 'COMMIT_SHA ' + 1 * getPipelineMock("step")([ + $class: 'GitHubCommitStatusSetter', + commitShaSource: [$class: 'ManuallyEnteredShaSource', sha: 'COMMIT_SHA'], + contextSource: [$class: 'ManuallyEnteredCommitContextSource', context: 'CHECK_NAME'], + reposSource: [$class: 'ManuallyEnteredRepositorySource', url: 'REPO_URL'], + statusResultSource: [ $class: 'ConditionalStatusResultSource', results: [[$class: 'AnyBuildResult', message: "(1h2m4s) Issue in pipeline. 15 tests run, 2 failed, 3 skipped.", state: 'ERROR']] ], + ]) + groovyScript.getBinding().getVariable("env")['REPO-NAME_COMMIT_STATUS_REPO_URL'] == "REPO_URL" + groovyScript.getBinding().getVariable("env")['REPO-NAME_COMMIT_STATUS_SHA'] == "COMMIT_SHA" + } + + def "[githubscm.groovy] updateGithubCommitStatusFromBuildResult and any other result"() { + setup: + groovyScript.getBinding().setVariable("env", [:]) + groovyScript.getBinding().setVariable("currentBuild", [ currentResult: 'anyother' ]) + when: + groovyScript.updateGithubCommitStatusFromBuildResult('CHECK_NAME') + then: + 2 * getPipelineMock("sh")(['returnStdout': true, 'script': 'git config --get remote.origin.url | head -n 1']) >> 'REPO_URL' + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': "basename REPO_URL | sed 's|\\.git||g'" ]) >> 'REPO-NAME' + 1 * getPipelineMock("util.retrieveTestResults")() >> [passCount:10, skipCount: 3, failCount: 2] + 1 * getPipelineMock("util.getJobDurationInSeconds")() >> 3824 + 1 * getPipelineMock("util.displayDurationFromSeconds")(3824) >> '1h2m4s' + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': 'git rev-parse HEAD']) >> 'COMMIT_SHA ' + 1 * getPipelineMock("step")([ + $class: 'GitHubCommitStatusSetter', + commitShaSource: [$class: 'ManuallyEnteredShaSource', sha: 'COMMIT_SHA'], + contextSource: [$class: 'ManuallyEnteredCommitContextSource', context: 'CHECK_NAME'], + reposSource: [$class: 'ManuallyEnteredRepositorySource', url: 'REPO_URL'], + statusResultSource: [ $class: 'ConditionalStatusResultSource', results: [[$class: 'AnyBuildResult', message: "(1h2m4s) Issue in pipeline. 15 tests run, 2 failed, 3 skipped.", state: 'ERROR']] ], + ]) + groovyScript.getBinding().getVariable("env")['REPO-NAME_COMMIT_STATUS_REPO_URL'] == "REPO_URL" + groovyScript.getBinding().getVariable("env")['REPO-NAME_COMMIT_STATUS_SHA'] == "COMMIT_SHA" + } + + def "[githubscm.groovy] updateGithubCommitStatusFromBuildResult without test results"() { + setup: + groovyScript.getBinding().setVariable("env", [:]) + groovyScript.getBinding().setVariable("currentBuild", [ currentResult: 'SUCCESS' ]) + when: + groovyScript.updateGithubCommitStatusFromBuildResult('CHECK_NAME') + then: + 2 * getPipelineMock("sh")(['returnStdout': true, 'script': 'git config --get remote.origin.url | head -n 1']) >> 'REPO_URL' + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': "basename REPO_URL | sed 's|\\.git||g'" ]) >> 'REPO-NAME' + 1 * getPipelineMock("util.retrieveTestResults")() + 1 * getPipelineMock("util.getJobDurationInSeconds")() >> 3824 + 1 * getPipelineMock("util.displayDurationFromSeconds")(3824) >> '1h2m4s' + 1 * getPipelineMock("sh")(['returnStdout': true, 'script': 'git rev-parse HEAD']) >> 'COMMIT_SHA ' + 1 * getPipelineMock("step")([ + $class: 'GitHubCommitStatusSetter', + commitShaSource: [$class: 'ManuallyEnteredShaSource', sha: 'COMMIT_SHA'], + contextSource: [$class: 'ManuallyEnteredCommitContextSource', context: 'CHECK_NAME'], + reposSource: [$class: 'ManuallyEnteredRepositorySource', url: 'REPO_URL'], + statusResultSource: [ $class: 'ConditionalStatusResultSource', results: [[$class: 'AnyBuildResult', message: "(1h2m4s) Check is successful. No test results found.", state: 'SUCCESS']] ], + ]) + groovyScript.getBinding().getVariable("env")['REPO-NAME_COMMIT_STATUS_REPO_URL'] == "REPO_URL" + groovyScript.getBinding().getVariable("env")['REPO-NAME_COMMIT_STATUS_SHA'] == "COMMIT_SHA" + } +} diff --git a/jenkins-pipeline-shared-libraries/test/vars/LocalShellSpec .groovy b/jenkins-pipeline-shared-libraries/test/vars/LocalShellSpec .groovy new file mode 100644 index 000000000..bf53b2cb7 --- /dev/null +++ b/jenkins-pipeline-shared-libraries/test/vars/LocalShellSpec .groovy @@ -0,0 +1,101 @@ +import com.homeaway.devtools.jenkins.testing.JenkinsPipelineSpecification +import org.jenkinsci.plugins.workflow.steps.Step +import org.jenkinsci.plugins.workflow.steps.StepContext +import org.jenkinsci.plugins.workflow.steps.StepExecution +import org.kie.jenkins.shell.LocalShell +import org.kie.jenkins.shell.installation.Installation + +class LocalShellSpec extends JenkinsPipelineSpecification { + + def steps + def env = [:] + + def setup() { + steps = new Step() { + + @Override + StepExecution start(StepContext stepContext) throws Exception { + return null + } + + } + } + + def "[LocalShell.groovy] getFullCommand default"() { + setup: + def shell = new LocalShell(steps) + when: + def result = shell.getFullCommand('whatever') + then: + result == 'whatever' + } + + def "[LocalShell.groovy] getFullCommand with installations"() { + setup: + def install1 = Mock(Installation) + def install2 = Mock(Installation) + 1 * getPipelineMock('sh')([returnStdout: true, script: 'mktemp -d']) >> 'TMP_FOLDER' + def shell = new LocalShell(steps) + shell.install(install1) + shell.install(install2) + 1 * install1.getBinaryPaths() >> ['PATH1', 'PATH2'] + 1 * install2.getBinaryPaths() >> ['PATH3'] + 1 * install1.getExtraEnvVars() >> [:] + 1 * install2.getExtraEnvVars() >> [ install2key : 'install2value' ] + when: + def result = shell.getFullCommand('whatever') + then: + result == """export PATH=\${PATH}:PATH1:PATH2:PATH3 +export install2key=install2value +whatever""" + } + + def "[LocalShell.groovy] getFullCommand with environment variables"() { + setup: + def shell = new LocalShell(steps) + shell.addEnvironmentVariable('KEY1', 'VALUE1') + shell.addEnvironmentVariable('key2', 'value2') + when: + def result = shell.getFullCommand('whatever') + then: + result == '''export KEY1=VALUE1 +export key2=value2 +whatever''' + } + + def "[LocalShell.groovy] getFullCommand with directory"() { + setup: + def shell = new LocalShell(steps) + when: + def result = shell.getFullCommand('whatever', 'DIR') + then: + result == """mkdir -p DIR && cd DIR +whatever""" + } + + def "[LocalShell.groovy] getFullCommand with all"() { + setup: + def install1 = Mock(Installation) + def install2 = Mock(Installation) + 1 * getPipelineMock('sh')([returnStdout: true, script: 'mktemp -d']) >> 'TMP_FOLDER' + def shell = new LocalShell(steps) + shell.install(install1) + shell.install(install2) + shell.addEnvironmentVariable('KEY1', 'VALUE1') + shell.addEnvironmentVariable('key2', 'value2') + 1 * install1.getBinaryPaths() >> ['PATH1', 'PATH2'] + 1 * install2.getBinaryPaths() >> ['PATH3'] + 1 * install1.getExtraEnvVars() >> [:] + 1 * install2.getExtraEnvVars() >> [ install2key : 'install2value' ] + when: + def result = shell.getFullCommand('whatever', 'DIR') + then: + result == """mkdir -p DIR && cd DIR +export PATH=\${PATH}:PATH1:PATH2:PATH3 +export install2key=install2value +export KEY1=VALUE1 +export key2=value2 +whatever""" + } + +} diff --git a/jenkins-pipeline-shared-libraries/test/vars/MailerSpec.groovy b/jenkins-pipeline-shared-libraries/test/vars/MailerSpec.groovy new file mode 100644 index 000000000..76801de09 --- /dev/null +++ b/jenkins-pipeline-shared-libraries/test/vars/MailerSpec.groovy @@ -0,0 +1,233 @@ +import com.homeaway.devtools.jenkins.testing.JenkinsPipelineSpecification + +class MailerSpec extends JenkinsPipelineSpecification { + + def groovyScript = null + + def setup() { + groovyScript = loadPipelineScriptForTest('vars/mailer.groovy') + groovyScript.metaClass.WORKSPACE = '/' + } + + def "[mailer.groovy] sendEmailFailure without CHANGE_BRANCH"() { + setup: + def env = [:] + env['ghprbSourceBranch'] = 'PR_1' + groovyScript.getBinding().setVariable('env', env) + groovyScript.getBinding().setVariable('BUILD_URL', 'https://redhat.com/') + when: + groovyScript.sendEmailFailure() + then: + 1 * getPipelineMock('emailext')(['subject': 'Build PR_1 failed', body: 'Build PR_1 failed! For more information see https://redhat.com/', 'recipientProviders': [['$class': 'DevelopersRecipientProvider'], ['$class': 'RequesterRecipientProvider']]]) + } + + def "[mailer.groovy] sendEmailFailure with CHANGE_BRANCH"() { + setup: + def env = [:] + env['CHANGE_BRANCH'] = 'main' + groovyScript.getBinding().setVariable('env', env) + groovyScript.getBinding().setVariable('BUILD_URL', 'https://redhat.com/') + when: + groovyScript.sendEmailFailure() + then: + 1 * getPipelineMock('emailext')(['subject': 'Build main failed', body: 'Build main failed! For more information see https://redhat.com/', 'recipientProviders': [['$class': 'DevelopersRecipientProvider'], ['$class': 'RequesterRecipientProvider']]]) + } + + def "[mailer.groovy] sendEmail_failedPR with additional subject"() { + setup: + groovyScript.getBinding().setVariable('ghprbPullId', '1') + groovyScript.getBinding().setVariable('ghprbGhRepository', '2') + groovyScript.getBinding().setVariable('ghprbPullTitle', '3') + groovyScript.getBinding().setVariable('BUILD_URL', 'https://redhat.com/') + when: + groovyScript.sendEmail_failedPR('additional subject') + then: + 1 * getPipelineMock('emailext')(['subject': 'additional subject #1 of 2: 3 failed', body: ''' + Pull request #1 of 2: 3 FAILED + Build log: https://redhat.com/consoleText + Failed tests ${TEST_COUNTS,var="fail"}: https://redhat.com/testReport + (IMPORTANT: For visiting the links you need to have access to Red Hat VPN. In case you don't have access to RedHat VPN please download and decompress attached file.) + ''', 'attachmentsPattern': 'error.log.gz', 'recipientProviders': [['$class': 'DevelopersRecipientProvider'], ['$class': 'RequesterRecipientProvider']]]) + } + + def "[mailer.groovy] sendEmail_failedPR without additional subject"() { + setup: + groovyScript.getBinding().setVariable('ghprbPullId', '1') + groovyScript.getBinding().setVariable('ghprbGhRepository', '2') + groovyScript.getBinding().setVariable('ghprbPullTitle', '3') + groovyScript.getBinding().setVariable('BUILD_URL', 'https://redhat.com/') + when: + groovyScript.sendEmail_failedPR() + then: + 1 * getPipelineMock('emailext')(['subject': 'PR #1 of 2: 3 failed', body: ''' + Pull request #1 of 2: 3 FAILED + Build log: https://redhat.com/consoleText + Failed tests ${TEST_COUNTS,var="fail"}: https://redhat.com/testReport + (IMPORTANT: For visiting the links you need to have access to Red Hat VPN. In case you don't have access to RedHat VPN please download and decompress attached file.) + ''', 'attachmentsPattern': 'error.log.gz', 'recipientProviders': [['$class': 'DevelopersRecipientProvider'], ['$class': 'RequesterRecipientProvider']]]) + } + + def "[mailer.groovy] sendEmail_unstablePR with additional subject"() { + setup: + groovyScript.getBinding().setVariable('ghprbPullId', '1') + groovyScript.getBinding().setVariable('ghprbGhRepository', '2') + groovyScript.getBinding().setVariable('ghprbPullTitle', '3') + groovyScript.getBinding().setVariable('BUILD_URL', 'https://redhat.com/') + when: + groovyScript.sendEmail_unstablePR('additional subject') + then: + 1 * getPipelineMock('emailext')(['subject': 'additional subject #1 of 2: 3 was unstable', body: ''' + Pull request #1 of 2: 3 was UNSTABLE + Build log: https://redhat.com/consoleText + Failed tests ${TEST_COUNTS,var="fail"}: https://redhat.com/testReport + (IMPORTANT: For visiting the links you need to have access to Red Hat VPN) + *********************************************************************************************************************************************************** + ${FAILED_TESTS} + ''', 'recipientProviders': [['$class': 'DevelopersRecipientProvider'], ['$class': 'RequesterRecipientProvider']]]) + } + + def "[mailer.groovy] sendEmail_unstablePR without additional subject"() { + setup: + groovyScript.getBinding().setVariable('ghprbPullId', '1') + groovyScript.getBinding().setVariable('ghprbGhRepository', '2') + groovyScript.getBinding().setVariable('ghprbPullTitle', '3') + groovyScript.getBinding().setVariable('BUILD_URL', 'https://redhat.com/') + when: + groovyScript.sendEmail_unstablePR() + then: + 1 * getPipelineMock('emailext')(['subject': 'PR #1 of 2: 3 was unstable', body: ''' + Pull request #1 of 2: 3 was UNSTABLE + Build log: https://redhat.com/consoleText + Failed tests ${TEST_COUNTS,var="fail"}: https://redhat.com/testReport + (IMPORTANT: For visiting the links you need to have access to Red Hat VPN) + *********************************************************************************************************************************************************** + ${FAILED_TESTS} + ''', 'recipientProviders': [['$class': 'DevelopersRecipientProvider'], ['$class': 'RequesterRecipientProvider']]]) + } + + def "[mailer.groovy] sendEmail_fixedPR with additional subject"() { + setup: + groovyScript.getBinding().setVariable('ghprbPullId', '1') + groovyScript.getBinding().setVariable('ghprbGhRepository', '2') + groovyScript.getBinding().setVariable('ghprbPullTitle', '3') + when: + groovyScript.sendEmail_fixedPR('additional subject') + then: + 1 * getPipelineMock('emailext')(['subject': 'additional subject #1 of 2: 3 is fixed and was SUCCESSFUL', 'body': '', 'recipientProviders': [['$class': 'DevelopersRecipientProvider'], ['$class': 'RequesterRecipientProvider']]]) + } + + def "[mailer.groovy] sendEmail_fixedPR without additional subject"() { + setup: + groovyScript.getBinding().setVariable('ghprbPullId', '1') + groovyScript.getBinding().setVariable('ghprbGhRepository', '2') + groovyScript.getBinding().setVariable('ghprbPullTitle', '3') + when: + groovyScript.sendEmail_fixedPR() + then: + 1 * getPipelineMock('emailext')(['subject': 'PR #1 of 2: 3 is fixed and was SUCCESSFUL', 'body': '', 'recipientProviders': [['$class': 'DevelopersRecipientProvider'], ['$class': 'RequesterRecipientProvider']]]) + } + + def "[mailer.groovy] sendEmail_abortedPR with additional subject"() { + setup: + groovyScript.getBinding().setVariable('ghprbPullId', '1') + groovyScript.getBinding().setVariable('ghprbGhRepository', '2') + groovyScript.getBinding().setVariable('ghprbPullTitle', '3') + when: + groovyScript.sendEmail_abortedPR('additional subject') + then: + 1 * getPipelineMock('emailext')(['subject': 'additional subject #1 of 2: 3 was ABORTED', 'body': '', 'recipientProviders': [['$class': 'DevelopersRecipientProvider'], ['$class': 'RequesterRecipientProvider']]]) + } + + def "[mailer.groovy] sendEmail_abortedPR without additional subject"() { + setup: + groovyScript.getBinding().setVariable('ghprbPullId', '1') + groovyScript.getBinding().setVariable('ghprbGhRepository', '2') + groovyScript.getBinding().setVariable('ghprbPullTitle', '3') + when: + groovyScript.sendEmail_abortedPR() + then: + 1 * getPipelineMock('emailext')(['subject': 'PR #1 of 2: 3 was ABORTED', 'body': '', 'recipientProviders': [['$class': 'DevelopersRecipientProvider'], ['$class': 'RequesterRecipientProvider']]]) + } + + def "[mailer.groovy] build Log Script PR"() { + when: + groovyScript.buildLogScriptPR() + then: + 1 * getPipelineMock('sh')('touch trace.sh') + 1 * getPipelineMock('sh')('chmod 755 trace.sh') + 1 * getPipelineMock('sh')('echo "wget --no-check-certificate ${BUILD_URL}consoleText" >> trace.sh') + 1 * getPipelineMock('sh')('echo "tail -n 750 consoleText >> error.log" >> trace.sh') + 1 * getPipelineMock('sh')('echo "gzip error.log" >> trace.sh') + } + + def "[mailer.groovy] sendMarkdownTestSummaryNotification with no job id and no build url"() { + setup: + groovyScript.getBinding().setVariable('BUILD_URL', 'URL/') + when: + groovyScript.sendMarkdownTestSummaryNotification('SUBJECT', ['email@anything.com']) + then: + 1 * getPipelineMock('util.getMarkdownTestSummary')('', '', 'URL/') >> 'CONTENT' + 1 * getPipelineMock('emailext')([ subject: 'SUBJECT', to: 'email@anything.com', body: 'CONTENT']) + } + + def "[mailer.groovy] sendMarkdownTestSummaryNotification with no job id and build url"() { + setup: + groovyScript.getBinding().setVariable('BUILD_URL', 'URL/') + when: + groovyScript.sendMarkdownTestSummaryNotification('SUBJECT', ['email@anything.com'], '', 'BUILD_URL/') + then: + 1 * getPipelineMock('util.getMarkdownTestSummary')('', '', 'BUILD_URL/') >> 'CONTENT' + 1 * getPipelineMock('emailext')([ subject: 'SUBJECT', to: 'email@anything.com', body: 'CONTENT']) + } + + def "[mailer.groovy] sendMarkdownTestSummaryNotification with empty and no build url"() { + setup: + groovyScript.getBinding().setVariable('BUILD_URL', 'URL/') + when: + groovyScript.sendMarkdownTestSummaryNotification('', 'SUBJECT', ['email@anything.com']) + then: + 1 * getPipelineMock('util.getMarkdownTestSummary')('', '', 'URL/') >> 'CONTENT' + 1 * getPipelineMock('emailext')([ subject: 'SUBJECT', to: 'email@anything.com', body: 'CONTENT']) + } + + def "[mailer.groovy] sendMarkdownTestSummaryNotification with no build url"() { + setup: + groovyScript.getBinding().setVariable('BUILD_URL', 'URL/') + when: + groovyScript.sendMarkdownTestSummaryNotification('JOB_ID', 'SUBJECT', ['email@anything.com']) + then: + 1 * getPipelineMock('util.getMarkdownTestSummary')('', '', 'URL/') >> 'CONTENT' + 1 * getPipelineMock('emailext')([ subject: 'SUBJECT - JOB_ID', to: 'email@anything.com', body: 'CONTENT']) + } + + def "[mailer.groovy] sendMarkdownTestSummaryNotification with build url"() { + setup: + groovyScript.getBinding().setVariable('BUILD_URL', 'URL/') + when: + groovyScript.sendMarkdownTestSummaryNotification('JOB_ID', 'SUBJECT', ['email@anything.com'], '', 'BUILD_URL/') + then: + 1 * getPipelineMock('util.getMarkdownTestSummary')('', '', 'BUILD_URL/') >> 'CONTENT' + 1 * getPipelineMock('emailext')([ subject: 'SUBJECT - JOB_ID', to: 'email@anything.com', body: 'CONTENT']) + } + + def "[mailer.groovy] sendMarkdownTestSummaryNotification with multiple emails"() { + setup: + groovyScript.getBinding().setVariable('BUILD_URL', 'URL/') + when: + groovyScript.sendMarkdownTestSummaryNotification('JOB_ID', 'SUBJECT', ['email@anything.com', 'email@otherthing.com']) + then: + 1 * getPipelineMock('util.getMarkdownTestSummary')('', '', 'URL/') >> 'CONTENT' + 1 * getPipelineMock('emailext')([ subject: 'SUBJECT - JOB_ID', to: 'email@anything.com,email@otherthing.com', body: 'CONTENT']) + } + + def "[mailer.groovy] sendMarkdownTestSummaryNotification with additional info"() { + setup: + groovyScript.getBinding().setVariable('BUILD_URL', 'URL/') + when: + groovyScript.sendMarkdownTestSummaryNotification('JOB_ID', 'SUBJECT', ['email@anything.com'], 'ADDITIONAL_INFO') + then: + 1 * getPipelineMock('util.getMarkdownTestSummary')('', 'ADDITIONAL_INFO', 'URL/') >> 'CONTENT' + 1 * getPipelineMock('emailext')([ subject: 'SUBJECT - JOB_ID', to: 'email@anything.com', body: 'CONTENT']) + } + +} diff --git a/jenkins-pipeline-shared-libraries/test/vars/MavenCommandSpec.groovy b/jenkins-pipeline-shared-libraries/test/vars/MavenCommandSpec.groovy new file mode 100644 index 000000000..dcd93b0a4 --- /dev/null +++ b/jenkins-pipeline-shared-libraries/test/vars/MavenCommandSpec.groovy @@ -0,0 +1,472 @@ +import com.homeaway.devtools.jenkins.testing.JenkinsPipelineSpecification +import org.jenkinsci.plugins.workflow.steps.Step +import org.jenkinsci.plugins.workflow.steps.StepContext +import org.jenkinsci.plugins.workflow.steps.StepExecution +import org.kie.jenkins.MavenCommand + +class MavenCommandSpec extends JenkinsPipelineSpecification { + + def steps + def env = [:] + + def setup() { + steps = new Step() { + + @Override + StepExecution start(StepContext stepContext) throws Exception { + return null + } + + } + } + + def "[MavenCommand.groovy] run simple"() { + setup: + def mvnCommand = new MavenCommand(steps) + when: + mvnCommand.run('whatever') + then: + 1 * getPipelineMock('sh')([script: 'mvn -B whatever', returnStdout: false]) + 0 * getPipelineMock('error')(_) + } + + def "[MavenCommand.groovy] run with all set"() { + setup: + steps.env = ['MAVEN_SETTINGS_XML':'settingsFileId'] + def props = new Properties() + props.put('key1', 'value1') + def mvnCommand = new MavenCommand(steps) + when: + mvnCommand + .withProperties(props) + .withProperty('key2') + .withDependencyRepositoryInSettings('ID', 'URL') + .withMirrorDisabledForRepoInSettings('DISABLED_ID') + .withSnapshotsDisabledInSettings() + .withSettingsXmlId('anyId') + .withOptions(['hello', 'bonjour']) + .withProfiles(['p1']) + .skipTests(true) + .withLogFileName('LOG_FILE') + .withDeployRepository('REPOSITORY') + .useMavenWrapper() + .run('whatever') + then: + 1 * getPipelineMock("sh")([returnStdout: true, script: 'mktemp --suffix -settings.xml']) >> 'anything-settings.xml' + 1 * getPipelineMock("sh").call(['script':'./mnw -B -s settingsFileId hello bonjour whatever -Pp1 -Dkey1=value1 -Dkey2 -Denforcer.skip=true -DskipTests=true -DaltDeploymentRepository=runtimes-artifacts::default::REPOSITORY | tee $WORKSPACE/LOG_FILE ; test ${PIPESTATUS[0]} -eq 0', 'returnStdout':false]) + 1 * getPipelineMock('sh')(""" + sed -i 's||IDIDURLdefaulttruetrue|g' settingsFileId + sed -i 's||IDIDURLdefaulttruetrue|g' settingsFileId + """) + 1 * getPipelineMock('sh')("sed -i 's||,!ID|g' settingsFileId") + 1 * getPipelineMock('sh')("sed -i 's||,!DISABLED_ID|g' settingsFileId") + 1 * getPipelineMock('sh')("sed -i '//,/<\\/repository>/ { //,/<\\/snapshots>/ { s|true|false|; }}' settingsFileId") + 1 * getPipelineMock('sh')("sed -i '//,/<\\/pluginRepository>/ { //,/<\\/snapshots>/ { s|true|false|; }}' settingsFileId") + } + + def "[MavenCommand.groovy] inDirectory"() { + setup: + def mvnCommand = new MavenCommand(steps) + when: + mvnCommand.inDirectory('DIR').run('whatever') + then: + 1 * getPipelineMock('dir')(_) + 1 * getPipelineMock('sh')([script: 'mvn -B whatever', returnStdout: false]) + } + + def "[MavenCommand.groovy] inDirectory empty"() { + setup: + def mvnCommand = new MavenCommand(steps) + when: + mvnCommand.inDirectory('').run('whatever') + then: + 0 * getPipelineMock('dir')(_) + 1 * getPipelineMock('sh')([script: 'mvn -B whatever', returnStdout: false]) + } + + def "[MavenCommand.groovy] inDirectory with output"() { + setup: + def mvnCommand = new MavenCommand(steps) + when: + def output = mvnCommand.inDirectory('').returnOutput().run('whatever') + then: + 0 * getPipelineMock('dir')(_) + 1 * getPipelineMock('sh')([script: 'mvn -B whatever', returnStdout: true]) >> 'output' + output.length() > 0 + } + + def "[MavenCommand.groovy] useMavenWrapper"() { + setup: + def mvnCommand = new MavenCommand(steps) + when: + mvnCommand.useMavenWrapper().run('whatever') + then: + 1 * getPipelineMock("sh").call(['script':'./mnw -B whatever', 'returnStdout':false]) + } + + def "[MavenCommand.groovy] withSettingsXmlId"() { + setup: + steps.env = ['MAVEN_SETTINGS_XML':'settingsFileId'] + def mvnCommand = new MavenCommand(steps) + when: + mvnCommand.withSettingsXmlId('anyId').run('whatever') + then: + 1 * getPipelineMock("sh")([returnStdout: true, script: 'mktemp --suffix -settings.xml']) >> 'anything-settings.xml' + 1 * getPipelineMock('sh')([script: 'mvn -B -s settingsFileId whatever', returnStdout: false]) + } + + def "[MavenCommand.groovy] withSettingsXmlId not existing"() { + setup: + def mvnCommand = new MavenCommand(steps) + when: + mvnCommand.withSettingsXmlId('anyId').run('whatever') + then: + 1 * getPipelineMock("sh")([returnStdout: true, script: 'mktemp --suffix -settings.xml']) >> 'anything-settings.xml' + 0 * getPipelineMock('sh')([script: 'mvn -B -s settingsFileId whatever', returnStdout: false]) + } + + def "[MavenCommand.groovy] withSettingsXmlFile"() { + setup: + def mvnCommand = new MavenCommand(steps) + when: + mvnCommand.withSettingsXmlFile('FILE').run('whatever') + then: + 1 * getPipelineMock('sh')([script: 'mvn -B -s FILE whatever', returnStdout: false]) + } + + def "[MavenCommand.groovy] withSettingsXmlFile empty value"() { + setup: + def mvnCommand = new MavenCommand(steps) + when: + mvnCommand.withSettingsXmlFile('').run('whatever') + then: + thrown(AssertionError) + } + + def "[MavenCommand.groovy] withSettingsXmlFile and withSettingsXmlId"() { + setup: + steps.env = ['MAVEN_SETTINGS_XML':'settingsFileId'] + def mvnCommand = new MavenCommand(steps) + when: + mvnCommand.withSettingsXmlFile('FILE').withSettingsXmlId('anyId').run('whatever') + then: + 1 * getPipelineMock("sh")([returnStdout: true, script: 'mktemp --suffix -settings.xml']) >> 'anything-settings.xml' + 0 * getPipelineMock('sh')([script: 'mvn -B -s FILE whatever', returnStdout: false]) + 1 * getPipelineMock('sh')([script: 'mvn -B -s settingsFileId whatever', returnStdout: false]) + } + + def "[MavenCommand.groovy] withDependencyRepositoryInSettings"() { + setup: + def mvnCommand = new MavenCommand(steps) + when: + mvnCommand.withSettingsXmlFile('FILE').withDependencyRepositoryInSettings('ID', 'URL').run('whatever') + then: + 1 * getPipelineMock('sh')(""" + sed -i 's||IDIDURLdefaulttruetrue|g' FILE + sed -i 's||IDIDURLdefaulttruetrue|g' FILE + """) + 1 * getPipelineMock('sh')("sed -i 's||,!ID|g' FILE") + 1 * getPipelineMock('sh')([script: 'mvn -B -s FILE whatever -Denforcer.skip=true', returnStdout: false]) + } + + def "[MavenCommand.groovy] withMirrorDisabledForRepoInSettings"() { + setup: + def mvnCommand = new MavenCommand(steps) + when: + mvnCommand.withSettingsXmlFile('FILE').withMirrorDisabledForRepoInSettings('ID').run('whatever') + then: + 1 * getPipelineMock('sh')("sed -i 's||,!ID|g' FILE") + 1 * getPipelineMock('sh')([script: 'mvn -B -s FILE whatever', returnStdout: false]) + } + + def "[MavenCommand.groovy] withSnapshotsDisabledInSettings"() { + setup: + def mvnCommand = new MavenCommand(steps) + when: + mvnCommand.withSettingsXmlFile('FILE').withSnapshotsDisabledInSettings().run('whatever') + then: + 1 * getPipelineMock('sh')("sed -i '//,/<\\/repository>/ { //,/<\\/snapshots>/ { s|true|false|; }}' FILE") + 1 * getPipelineMock('sh')("sed -i '//,/<\\/pluginRepository>/ { //,/<\\/snapshots>/ { s|true|false|; }}' FILE") + } + + def "[MavenCommand.groovy] withOptions"() { + setup: + def mvnCommand = new MavenCommand(steps) + when: + mvnCommand.withOptions(['opt1', 'opt2']).run('whatever') + then: + 1 * getPipelineMock('sh')([script: 'mvn -B opt1 opt2 whatever', returnStdout: false]) + } + + def "[MavenCommand.groovy] withOptions null"() { + setup: + def mvnCommand = new MavenCommand(steps) + when: + mvnCommand.withOptions(null).run('whatever') + then: + thrown(Exception) + } + + def "[MavenCommand.groovy] skipTests no value"() { + setup: + def mvnCommand = new MavenCommand(steps) + when: + mvnCommand.skipTests().run('whatever') + then: + 1 * getPipelineMock('sh')([script: 'mvn -B whatever -DskipTests=true', returnStdout: false]) + } + + def "[MavenCommand.groovy] skipTests with value"() { + setup: + def mvnCommand = new MavenCommand(steps) + .skipTests(false) + when: + mvnCommand.run('whatever') + then: + 1 * getPipelineMock('sh')([script: 'mvn -B whatever -DskipTests=false', returnStdout: false]) + } + + def "[MavenCommand.groovy] withProfiles"() { + setup: + def mvnCommand = new MavenCommand(steps) + when: + mvnCommand.withProfiles(['profile1', 'profile2']).run('whatever') + then: + 1 * getPipelineMock('sh')([script: 'mvn -B whatever -Pprofile1,profile2', returnStdout: false]) + } + + def "[MavenCommand.groovy] withProfiles null"() { + setup: + def mvnCommand = new MavenCommand(steps) + when: + mvnCommand.withProfiles(null).run('whatever') + then: + thrown(Exception) + } + + def "[MavenCommand.groovy] withProperty no value"() { + setup: + def mvnCommand = new MavenCommand(steps) + when: + mvnCommand.withProperty('prop').run('whatever') + then: + 1 * getPipelineMock('sh')([script: 'mvn -B whatever -Dprop', returnStdout: false]) + } + + def "[MavenCommand.groovy] withProperty empty value"() { + setup: + def mvnCommand = new MavenCommand(steps) + when: + mvnCommand.withProperty('prop', '').run('whatever') + then: + 1 * getPipelineMock('sh')([script: 'mvn -B whatever -Dprop', returnStdout: false]) + } + + def "[MavenCommand.groovy] withProperty with value"() { + setup: + def mvnCommand = new MavenCommand(steps) + when: + mvnCommand.withProperty('prop', 'value').run('whatever') + then: + 1 * getPipelineMock('sh')([script: 'mvn -B whatever -Dprop=value', returnStdout: false]) + } + + def "[MavenCommand.groovy] withProperties"() { + setup: + def props = new Properties() + props.put('key1', 'value1') + props.put('key2', 'value2') + def mvnCommand = new MavenCommand(steps) + when: + mvnCommand.withProperties(props).run('whatever') + then: + 1 * getPipelineMock('sh')([script: 'mvn -B whatever -Dkey2=value2 -Dkey1=value1', returnStdout: false]) + } + + def "[MavenCommand.groovy] withProperties null value"() { + setup: + def mvnCommand = new MavenCommand(steps) + when: + mvnCommand.withProperties(null).run('whatever') + then: + 1 * getPipelineMock('sh')([script: 'mvn -B whatever', returnStdout: false]) + } + + def "[MavenCommand.groovy] withPropertyMap"() { + setup: + def props = [ + 'key1' : 'value1', + 'key2' : 'value2' + ] + def mvnCommand = new MavenCommand(steps) + when: + mvnCommand.withPropertyMap(props).run('whatever') + then: + 1 * getPipelineMock('sh')([script: 'mvn -B whatever -Dkey1=value1 -Dkey2=value2', returnStdout: false]) + } + + def "[MavenCommand.groovy] withPropertyMap null value"() { + setup: + def mvnCommand = new MavenCommand(steps) + when: + mvnCommand.withPropertyMap(null).run('whatever') + then: + thrown(Exception) + } + + def "[MavenCommand.groovy] withLogFileName"() { + setup: + def mvnCommand = new MavenCommand(steps) + when: + mvnCommand.withLogFileName('LOGFILE').run('whatever') + then: + 1 * getPipelineMock('sh')([script: 'mvn -B whatever | tee $WORKSPACE/LOGFILE ; test ${PIPESTATUS[0]} -eq 0', returnStdout: false]) + } + + def "[MavenCommand.groovy] withLogFileName empty value"() { + setup: + def mvnCommand = new MavenCommand(steps) + when: + mvnCommand.withLogFileName('').run('whatever') + then: + 1 * getPipelineMock('sh')([script: 'mvn -B whatever', returnStdout: false]) + } + + def "[MavenCommand.groovy] withDeployRepository"() { + setup: + def mvnCommand = new MavenCommand(steps) + when: + mvnCommand.withDeployRepository('REPOSITORY').run('whatever') + then: + 1 * getPipelineMock('sh')([script: 'mvn -B whatever -DaltDeploymentRepository=runtimes-artifacts::default::REPOSITORY -Denforcer.skip=true', returnStdout: false]) + } + + def "[MavenCommand.groovy] withDeployRepository empty value"() { + setup: + def mvnCommand = new MavenCommand(steps) + when: + mvnCommand.withDeployRepository('').run('whatever') + then: + thrown(AssertionError) + } + + def "[MavenCommand.groovy] withLocalDeployFolder"() { + setup: + def mvnCommand = new MavenCommand(steps) + when: + mvnCommand.withLocalDeployFolder('LOCAL_FOLDER').run('whatever') + then: + 1 * getPipelineMock('sh')([script: 'mvn -B whatever -DaltDeploymentRepository=local::default::file://LOCAL_FOLDER', returnStdout: false]) + } + + def "[MavenCommand.groovy] withLocalDeployFolder empty value"() { + setup: + def mvnCommand = new MavenCommand(steps) + when: + mvnCommand.withLocalDeployFolder('').run('whatever') + then: + thrown(AssertionError) + } + + def "[MavenCommand.groovy] withServerInSettings"() { + setup: + def mvnCommand = new MavenCommand(steps) + when: + mvnCommand.withSettingsXmlFile('FILE').withServerInSettings('testId', 'USERNAME', 'PASSWORD').run('whatever') + then: + 1 * getPipelineMock('sh')("sed -i 's||testIdUSERNAMEPASSWORD|g' FILE") + } + + def "[MavenCommand.groovy] clone ok"() { + setup: + steps.env = ['MAVEN_SETTINGS_XML':'settingsFileId'] + def props = new Properties() + props.put('key1', 'value1') + def mvnCommand = new MavenCommand(steps) + .withProperties(props) + .withProperty('key2') + .withSettingsXmlFile('SETTINGS_FILE') + .withOptions(['hello', 'bonjour']) + .withProfiles(['p1']) + .skipTests(true) + .withLocalDeployFolder('LOCAL_FOLDER') + .withDependencyRepositoryInSettings('ID','URL') + .withMirrorDisabledForRepoInSettings('DISABLED_ID') + .withSnapshotsDisabledInSettings() + .withServerInSettings('testId', 'USERNAME', 'PASSWORD') + when: + mvnCommand.run('clean deploy') + def newCmd = mvnCommand + .clone() + .withProperty('key3', 'value3') + .withSettingsXmlId('anyId') + .withLogFileName('LOG_FILE') + .withProfiles(['p2']) + .withDeployRepository('REPOSITORY') + .withServerInSettings('testId2', 'USERNAME2', 'PASSWORD2') + newCmd.run('clean deploy') + mvnCommand.run('clean deploy') + then: + 1 * getPipelineMock("sh")([returnStdout: true, script: 'mktemp --suffix -settings.xml']) >> 'anything-settings.xml' + 2 * getPipelineMock('sh')([script: 'mvn -B -s SETTINGS_FILE hello bonjour clean deploy -Pp1 -Dkey1=value1 -Dkey2 -DskipTests=true -DaltDeploymentRepository=local::default::file://LOCAL_FOLDER -Denforcer.skip=true', returnStdout: false]) + 2 * getPipelineMock('sh')(""" + sed -i 's||IDIDURLdefaulttruetrue|g' SETTINGS_FILE + sed -i 's||IDIDURLdefaulttruetrue|g' SETTINGS_FILE + """) + 2 * getPipelineMock('sh')("sed -i 's||,!ID|g' SETTINGS_FILE") + 2 * getPipelineMock('sh')("sed -i 's||,!DISABLED_ID|g' SETTINGS_FILE") + 2 * getPipelineMock('sh')("sed -i '//,/<\\/repository>/ { //,/<\\/snapshots>/ { s|true|false|; }}' SETTINGS_FILE") + 2 * getPipelineMock('sh')("sed -i '//,/<\\/pluginRepository>/ { //,/<\\/snapshots>/ { s|true|false|; }}' SETTINGS_FILE") + 1 * getPipelineMock('sh')([script: 'mvn -B -s settingsFileId hello bonjour clean deploy -Pp1,p2 -Dkey1=value1 -Dkey2 -DskipTests=true -DaltDeploymentRepository=runtimes-artifacts::default::REPOSITORY -Denforcer.skip=true -Dkey3=value3 | tee $WORKSPACE/LOG_FILE ; test ${PIPESTATUS[0]} -eq 0', returnStdout: false]) + 2 * getPipelineMock('sh')("sed -i 's||testIdUSERNAMEPASSWORD|g' SETTINGS_FILE") + + 1 * getPipelineMock('sh')(""" + sed -i 's||IDIDURLdefaulttruetrue|g' settingsFileId + sed -i 's||IDIDURLdefaulttruetrue|g' settingsFileId + """) + 1 * getPipelineMock('sh')("sed -i 's||,!ID|g' settingsFileId") + 1 * getPipelineMock('sh')("sed -i 's||,!DISABLED_ID|g' settingsFileId") + 1 * getPipelineMock('sh')("sed -i '//,/<\\/repository>/ { //,/<\\/snapshots>/ { s|true|false|; }}' settingsFileId") + 1 * getPipelineMock('sh')("sed -i '//,/<\\/pluginRepository>/ { //,/<\\/snapshots>/ { s|true|false|; }}' settingsFileId") + 1 * getPipelineMock('sh')("sed -i 's||testIdUSERNAMEPASSWORD|g' settingsFileId") + 1 * getPipelineMock('sh')("sed -i 's||testId2USERNAME2PASSWORD2|g' settingsFileId") + } + + def "[MavenCommand.groovy] returnOutput"() { + setup: + def mvnCommand = new MavenCommand(steps) + when: + def output = mvnCommand.returnOutput().run('whatever') + then: + 1 * getPipelineMock('sh')([script: 'mvn -B whatever', returnStdout: true]) >> { return 'This is output' } + 'This is output' == output + } + + def "[MavenCommand.groovy] returnOutput false"() { + setup: + def mvnCommand = new MavenCommand(steps) + when: + def output = mvnCommand.returnOutput(false).run('whatever') + then: + 1 * getPipelineMock('sh')([script: 'mvn -B whatever', returnStdout: false]) + } + + def "[MavenCommand.groovy] printSettings"() { + setup: + def mvnCommand = new MavenCommand(steps).withSettingsXmlFile('SETTINGS_FILE') + when: + mvnCommand.printSettings().run('whatever') + then: + 1 * getPipelineMock('sh')('cat SETTINGS_FILE') + } + + def "[MavenCommand.groovy] printSettings false"() { + setup: + def mvnCommand = new MavenCommand(steps).withSettingsXmlFile('SETTINGS_FILE') + when: + mvnCommand.printSettings(false).run('whatever') + then: + 0 * getPipelineMock('sh')('cat SETTINGS_FILE') + } + +} diff --git a/jenkins-pipeline-shared-libraries/test/vars/MavenSettingsServiceSpec.groovy b/jenkins-pipeline-shared-libraries/test/vars/MavenSettingsServiceSpec.groovy new file mode 100644 index 000000000..58448a2d5 --- /dev/null +++ b/jenkins-pipeline-shared-libraries/test/vars/MavenSettingsServiceSpec.groovy @@ -0,0 +1,208 @@ +import com.homeaway.devtools.jenkins.testing.JenkinsPipelineSpecification +import org.jenkinsci.plugins.workflow.steps.Step +import org.jenkinsci.plugins.workflow.steps.StepContext +import org.jenkinsci.plugins.workflow.steps.StepExecution +import org.kie.jenkins.MavenSettingsConfigBuilder +import org.kie.jenkins.MavenSettingsService + +class MavenSettingsServiceSpec extends JenkinsPipelineSpecification { + + def steps + def env = [:] + + def setup() { + steps = new Step() { + + @Override + StepExecution start(StepContext stepContext) throws Exception { + return null + } + + } + } + + def "[MavenSettingsService.groovy] create with all set"() { + setup: + steps.env = ['MAVEN_SETTINGS_XML':'settingsFileId'] + def mvnCfg = new MavenSettingsConfigBuilder() + .dependenciesRepositoriesInSettings([ID: 'URL']) + .disabledMirrorRepoInSettings(['DISABLED_ID'] as Set) + .disableSnapshotsInSettings(true) + .settingsXmlConfigFileId('anyId') + .build() + when: + new MavenSettingsService(steps, mvnCfg).createSettingsFile() + then: + 1 * getPipelineMock("sh")([returnStdout: true, script: 'mktemp --suffix -settings.xml']) >> 'anything-settings.xml' + 1 * getPipelineMock('sh')(""" + sed -i 's||IDIDURLdefaulttruetrue|g' settingsFileId + sed -i 's||IDIDURLdefaulttruetrue|g' settingsFileId + """) + 1 * getPipelineMock('sh')("sed -i 's||,!ID|g' settingsFileId") + 1 * getPipelineMock('sh')("sed -i 's||,!DISABLED_ID|g' settingsFileId") + 1 * getPipelineMock('sh')("sed -i '//,/<\\/repository>/ { //,/<\\/snapshots>/ { s|true|false|; }}' settingsFileId") + 1 * getPipelineMock('sh')("sed -i '//,/<\\/pluginRepository>/ { //,/<\\/snapshots>/ { s|true|false|; }}' settingsFileId") + } + + def "[MavenSettingsService.groovy] settingsXmlConfigFileId"() { + setup: + steps.env = ['MAVEN_SETTINGS_XML':'settingsFileId'] + def mvnCfg = new MavenSettingsConfigBuilder() + .settingsXmlConfigFileId('anyId') + .build() + when: + def settingsFile = new MavenSettingsService(steps, mvnCfg).createSettingsFile() + then: + 1 * getPipelineMock("sh")([returnStdout: true, script: 'mktemp --suffix -settings.xml']) >> 'anything-settings.xml' + settingsFile == 'settingsFileId' + } + + def "[MavenSettingsService.groovy] settingsXmlConfigFileId not existing"() { + setup: + def mvnCfg = new MavenSettingsConfigBuilder() + .settingsXmlConfigFileId('anyId') + .build() + when: + def settingsFile = new MavenSettingsService(steps, mvnCfg).createSettingsFile() + then: + 1 * getPipelineMock("sh")([returnStdout: true, script: 'mktemp --suffix -settings.xml']) >> 'anything-settings.xml' + settingsFile == '' + } + + def "[MavenSettingsService.groovy] settingsXmlPath"() { + setup: + def mvnCfg = new MavenSettingsConfigBuilder() + .settingsXmlPath('FILE') + .build() + when: + def settingsFile = new MavenSettingsService(steps, mvnCfg).createSettingsFile() + then: + settingsFile == 'FILE' + } + + def "[MavenSettingsService.groovy] settingsXmlPath and settingsXmlConfigFileId"() { + setup: + steps.env = ['MAVEN_SETTINGS_XML':'settingsFileId'] + def mvnCfg = new MavenSettingsConfigBuilder() + .settingsXmlPath('FILE') + .settingsXmlConfigFileId('anyId') + .build() + when: + def settingsFile = new MavenSettingsService(steps, mvnCfg).createSettingsFile() + then: + 1 * getPipelineMock("sh")([returnStdout: true, script: 'mktemp --suffix -settings.xml']) >> 'anything-settings.xml' + settingsFile == 'settingsFileId' + } + + def "[MavenSettingsService.groovy] dependenciesRepositoriesInSettings"() { + setup: + def mvnCfg = new MavenSettingsConfigBuilder() + .settingsXmlPath('FILE') + .dependenciesRepositoriesInSettings([ID: 'URL']) + .build() + when: + def settingsFile = new MavenSettingsService(steps, mvnCfg).createSettingsFile() + then: + 1 * getPipelineMock('sh')(""" + sed -i 's||IDIDURLdefaulttruetrue|g' FILE + sed -i 's||IDIDURLdefaulttruetrue|g' FILE + """) + 1 * getPipelineMock('sh')("sed -i 's||,!ID|g' FILE") + settingsFile == 'FILE' + } + + def "[MavenSettingsService.groovy] disabledMirrorRepoInSettings"() { + setup: + def mvnCfg = new MavenSettingsConfigBuilder() + .settingsXmlPath('FILE') + .disabledMirrorRepoInSettings(['ID'] as Set) + .build() + when: + def settingsFile = new MavenSettingsService(steps, mvnCfg).createSettingsFile() + then: + 1 * getPipelineMock('sh')("sed -i 's||,!ID|g' FILE") + settingsFile == 'FILE' + } + + def "[MavenSettingsService.groovy] disableSnapshotsInSettings"() { + setup: + def mvnCfg = new MavenSettingsConfigBuilder() + .settingsXmlPath('FILE') + .disableSnapshotsInSettings(true) + .build() + when: + def settingsFile = new MavenSettingsService(steps, mvnCfg).createSettingsFile() + then: + 1 * getPipelineMock('sh')("sed -i '//,/<\\/repository>/ { //,/<\\/snapshots>/ { s|true|false|; }}' FILE") + 1 * getPipelineMock('sh')("sed -i '//,/<\\/pluginRepository>/ { //,/<\\/snapshots>/ { s|true|false|; }}' FILE") + settingsFile == 'FILE' + } + + def "[MavenSettingsService.groovy] servers"() { + setup: + def mvnCfg = new MavenSettingsConfigBuilder() + .settingsXmlPath('FILE') + .servers([ + [id: 'testId', username: 'USERNAME', password: 'PASSWORD'], + [id: 'testId2', username: 'USERNAME2', password: 'PASSWORD2'], + ]) + .build() + when: + def settingsFile = new MavenSettingsService(steps, mvnCfg).createSettingsFile() + then: + 1 * getPipelineMock('sh')("sed -i 's||testIdUSERNAMEPASSWORD|g' FILE") + 1 * getPipelineMock('sh')("sed -i 's||testId2USERNAME2PASSWORD2|g' FILE") + settingsFile == 'FILE' + } + + def "[MavenSettingsService.groovy] clone ok"() { + setup: + steps.env = ['MAVEN_SETTINGS_XML':'settingsFileId'] + def props = new Properties() + props.put('key1', 'value1') + def mvnCfg = new MavenSettingsConfigBuilder() + .settingsXmlPath('SETTINGS_FILE') + .dependenciesRepositoriesInSettings([ID: 'URL']) + .disabledMirrorRepoInSettings(['DISABLED_ID'] as Set) + .disableSnapshotsInSettings(true) + .servers([ + [id: 'testId', username: 'USERNAME', password: 'PASSWORD'], + ]) + .build() + when: + new MavenSettingsService(steps, mvnCfg).createSettingsFile() + def newCfg = MavenSettingsConfigBuilder.from(mvnCfg) + .settingsXmlConfigFileId('anyId') + .servers([ + [id: 'testId2', username: 'USERNAME2', password: 'PASSWORD2'], + ]) + .build() + def newSettingsFile = new MavenSettingsService(steps, newCfg).createSettingsFile() + def settingsFile = new MavenSettingsService(steps, mvnCfg).createSettingsFile() + then: + 1 * getPipelineMock("sh")([returnStdout: true, script: 'mktemp --suffix -settings.xml']) >> 'anything-settings.xml' + 2 * getPipelineMock('sh')(""" + sed -i 's||IDIDURLdefaulttruetrue|g' SETTINGS_FILE + sed -i 's||IDIDURLdefaulttruetrue|g' SETTINGS_FILE + """) + 2 * getPipelineMock('sh')("sed -i 's||,!ID|g' SETTINGS_FILE") + 2 * getPipelineMock('sh')("sed -i 's||,!DISABLED_ID|g' SETTINGS_FILE") + 2 * getPipelineMock('sh')("sed -i '//,/<\\/repository>/ { //,/<\\/snapshots>/ { s|true|false|; }}' SETTINGS_FILE") + 2 * getPipelineMock('sh')("sed -i '//,/<\\/pluginRepository>/ { //,/<\\/snapshots>/ { s|true|false|; }}' SETTINGS_FILE") + 2 * getPipelineMock('sh')("sed -i 's||testIdUSERNAMEPASSWORD|g' SETTINGS_FILE") + + 1 * getPipelineMock('sh')(""" + sed -i 's||IDIDURLdefaulttruetrue|g' settingsFileId + sed -i 's||IDIDURLdefaulttruetrue|g' settingsFileId + """) + 1 * getPipelineMock('sh')("sed -i 's||,!ID|g' settingsFileId") + 1 * getPipelineMock('sh')("sed -i 's||,!DISABLED_ID|g' settingsFileId") + 1 * getPipelineMock('sh')("sed -i '//,/<\\/repository>/ { //,/<\\/snapshots>/ { s|true|false|; }}' settingsFileId") + 1 * getPipelineMock('sh')("sed -i '//,/<\\/pluginRepository>/ { //,/<\\/snapshots>/ { s|true|false|; }}' settingsFileId") + 1 * getPipelineMock('sh')("sed -i 's||testIdUSERNAMEPASSWORD|g' settingsFileId") + 1 * getPipelineMock('sh')("sed -i 's||testId2USERNAME2PASSWORD2|g' settingsFileId") + settingsFile == 'SETTINGS_FILE' + newSettingsFile == 'settingsFileId' + } + +} diff --git a/jenkins-pipeline-shared-libraries/test/vars/MavenSpec.groovy b/jenkins-pipeline-shared-libraries/test/vars/MavenSpec.groovy new file mode 100644 index 000000000..8e21c523a --- /dev/null +++ b/jenkins-pipeline-shared-libraries/test/vars/MavenSpec.groovy @@ -0,0 +1,370 @@ +import com.homeaway.devtools.jenkins.testing.JenkinsPipelineSpecification +import groovy.util.slurpersupport.GPathResult +import groovy.util.slurpersupport.NodeChild +import groovy.util.slurpersupport.NodeChildren + +class MavenSpec extends JenkinsPipelineSpecification { + def mavenGroovy = null + + class VersionChildNode { + String version + + VersionChildNode(String version) { + this.version = version; + } + + def text() { + return this.version; + } + } + + def setup() { + mavenGroovy = loadPipelineScriptForTest("vars/maven.groovy") + } + + def "[maven.groovy] run Maven"() { + when: + mavenGroovy.runMaven("clean install") + then: + 1 * getPipelineMock("sh")([script: 'mvn -B clean install', returnStdout: false]) + } + + def "[maven.groovy] run Maven with option"() { + when: + mavenGroovy.runMaven("clean install", ['-fae']) + then: + 1 * getPipelineMock("sh")([script: 'mvn -B -fae clean install', returnStdout: false]) + } + + def "[maven.groovy] run Maven with log file"() { + setup: + Properties props = new Properties() + props.setProperty("anykey", "anyvalue") + when: + mavenGroovy.runMaven("clean install", ['-fae'], props, "logFile.txt") + then: + 1 * getPipelineMock("sh")([script: 'mvn -B -fae clean install -Danykey=anyvalue | tee $WORKSPACE/logFile.txt ; test ${PIPESTATUS[0]} -eq 0', returnStdout: false]) + } + + def "[maven.groovy] run Maven with skip tests"() { + when: + mavenGroovy.runMaven("clean install", true, ['-fae'], "logFile.txt") + then: + 1 * getPipelineMock("sh")([script: 'mvn -B -fae clean install -DskipTests=true | tee $WORKSPACE/logFile.txt ; test ${PIPESTATUS[0]} -eq 0', returnStdout: false]) + } + + def "[maven.groovy] run Maven without skip tests"() { + when: + mavenGroovy.runMaven("clean install", false, ['-fae'], "logFile.txt") + then: + 1 * getPipelineMock("sh")([script: 'mvn -B -fae clean install -DskipTests=false | tee $WORKSPACE/logFile.txt ; test ${PIPESTATUS[0]} -eq 0', returnStdout: false]) + } + + def "[maven.groovy] run Maven without log file"() { + setup: + Properties props = new Properties() + props.setProperty("anykey", "anyvalue") + when: + mavenGroovy.runMaven("clean install", ['-fae'], props) + then: + 1 * getPipelineMock("sh")([script: 'mvn -B -fae clean install -Danykey=anyvalue', returnStdout: false]) + } + + def "[maven.groovy] run Maven with settings with log file"() { + setup: + mavenGroovy.getBinding().setVariable('MAVEN_SETTINGS_XML','settingsFileId') + Properties properties = new Properties() + properties.put('property1', 'value1') + when: + mavenGroovy.runMavenWithSettings("settings.xml", "clean install", properties, "logFile.txt") + then: + 1 * getPipelineMock("sh")([script: 'mvn -B -s settingsFileId -fae clean install -Dproperty1=value1 | tee $WORKSPACE/logFile.txt ; test ${PIPESTATUS[0]} -eq 0', returnStdout: false]) + } + + def "[maven.groovy] run Maven with settings without log file"() { + setup: + mavenGroovy.getBinding().setVariable('MAVEN_SETTINGS_XML','settingsFileId') + Properties properties = new Properties() + properties.put('property1b', 'value1b') + when: + mavenGroovy.runMavenWithSettings("settings.xml", "clean install", properties) + then: + 1 * getPipelineMock("sh")([script: 'mvn -B -s settingsFileId -fae clean install -Dproperty1b=value1b', returnStdout: false]) + } + + def "[maven.groovy] run Maven with settings without properties"() { + setup: + mavenGroovy.getBinding().setVariable('MAVEN_SETTINGS_XML', 'settingsFileId') + Properties properties = new Properties() + when: + mavenGroovy.runMavenWithSettings("settings.xml", "clean install", properties, "logFile.txt") + then: + 1 * getPipelineMock("sh")([script: 'mvn -B -s settingsFileId -fae clean install | tee $WORKSPACE/logFile.txt ; test ${PIPESTATUS[0]} -eq 0', returnStdout: false]) + } + + def "[maven.groovy] run Maven sonar settings with log file"() { + setup: + mavenGroovy.getBinding().setVariable('MAVEN_SETTINGS_XML', 'settingsFileId') + mavenGroovy.getBinding().setVariable("TOKEN", 'tokenId') + when: + mavenGroovy.runMavenWithSettingsSonar("settings.xml", "clean install", "sonarCloudId", "logFile.txt") + then: + 1 * getPipelineMock("sh")([script: 'mvn -B -s settingsFileId clean install -Dsonar.login=tokenId | tee $WORKSPACE/logFile.txt ; test ${PIPESTATUS[0]} -eq 0', returnStdout: false]) + } + + def "[maven.groovy] run Maven sonar settings without log file"() { + setup: + mavenGroovy.getBinding().setVariable('MAVEN_SETTINGS_XML', 'settingsFileId') + mavenGroovy.getBinding().setVariable("TOKEN", 'tokenId') + when: + mavenGroovy.runMavenWithSettingsSonar("settings.xml", "clean install", "sonarCloudId") + then: + 1 * getPipelineMock("sh")([script: 'mvn -B -s settingsFileId clean install -Dsonar.login=tokenId', returnStdout: false]) + } + + def "[maven.groovy] run with Settings with log file"() { + setup: + mavenGroovy.getBinding().setVariable('MAVEN_SETTINGS_XML', 'settingsFileId') + Properties properties = new Properties() + properties.put('skipTests', true) + when: + mavenGroovy.runMavenWithSettings("settings.xml", "clean install", true, "logFile.txt") + then: + 1 * getPipelineMock("sh")([script: 'mvn -B -s settingsFileId -fae clean install -DskipTests=true | tee $WORKSPACE/logFile.txt ; test ${PIPESTATUS[0]} -eq 0', returnStdout: false]) + } + + def "[maven.groovy] run with Settings without log file"() { + setup: + mavenGroovy.getBinding().setVariable('MAVEN_SETTINGS_XML', 'settingsFileId') + when: + mavenGroovy.runMavenWithSettings("settings.xml", "clean install", false) + then: + 1 * getPipelineMock("sh")([script: 'mvn -B -s settingsFileId -fae clean install -DskipTests=false', returnStdout: false]) + } + + def "[maven.groovy] run mvn versions set"() { + setup: + def String newVersion = '3.6.2' + when: + mavenGroovy.mvnVersionsSet(newVersion) + then: + 1 * getPipelineMock("sh")([script: "mvn -B -N -e versions:set -Dfull -DnewVersion=${newVersion} -DallowSnapshots=false -DgenerateBackupPoms=false", returnStdout: false]) + } + + def "[maven.groovy] run mvn versions set with allow snapshots"() { + setup: + def String newVersion = '3.6.2' + when: + mavenGroovy.mvnVersionsSet(newVersion, true) + then: + 1 * getPipelineMock("sh")([script: "mvn -B -N -e versions:set -Dfull -DnewVersion=${newVersion} -DallowSnapshots=true -DgenerateBackupPoms=false", returnStdout: false]) + } + + def "[maven.groovy] run mvn versions update parent"() { + setup: + def String newVersion = '3.6.2' + when: + mavenGroovy.mvnVersionsUpdateParent(newVersion) + then: + 1 * getPipelineMock("sh")([script: "mvn -B -N -e versions:update-parent -Dfull -DparentVersion=[${newVersion}] -DallowSnapshots=false -DgenerateBackupPoms=false", returnStdout: false]) + } + + def "[maven.groovy] run mvn versions update parent with allow snapshots"() { + setup: + def String newVersion = '3.6.2' + when: + mavenGroovy.mvnVersionsUpdateParent(newVersion, true) + then: + 1 * getPipelineMock("sh")([script: "mvn -B -N -e versions:update-parent -Dfull -DparentVersion=[${newVersion}] -DallowSnapshots=true -DgenerateBackupPoms=false", returnStdout: false]) + } + + def "[maven.groovy] run mvn versions update child modules"() { + when: + mavenGroovy.mvnVersionsUpdateChildModules() + then: + 1 * getPipelineMock("sh")([script: 'mvn -B -N -e versions:update-child-modules -Dfull -DallowSnapshots=false -DgenerateBackupPoms=false', returnStdout: false]) + } + + def "[maven.groovy] run mvn versions update child modules with allow snapshots"() { + when: + mavenGroovy.mvnVersionsUpdateChildModules(true) + then: + 1 * getPipelineMock("sh")([script: 'mvn -B -N -e versions:update-child-modules -Dfull -DallowSnapshots=true -DgenerateBackupPoms=false', returnStdout: false]) + } + + def "[maven.groovy] run mvn versions update parent and child modules"() { + setup: + def String newVersion = '3.6.2' + when: + mavenGroovy.mvnVersionsUpdateParentAndChildModules(newVersion) + then: + 1 * getPipelineMock("sh")([script: "mvn -B -N -e versions:update-parent -Dfull -DparentVersion=[${newVersion}] -DallowSnapshots=false -DgenerateBackupPoms=false", returnStdout: false]) + 1 * getPipelineMock("sh")([script: 'mvn -B -N -e versions:update-child-modules -Dfull -DallowSnapshots=false -DgenerateBackupPoms=false', returnStdout: false]) + } + + def "[maven.groovy] run mvn versions update parent and child modules with allow snapshots"() { + setup: + def String newVersion = '3.6.2' + when: + mavenGroovy.mvnVersionsUpdateParentAndChildModules(newVersion, true) + then: + 1 * getPipelineMock("sh")([script: "mvn -B -N -e versions:update-parent -Dfull -DparentVersion=[${newVersion}] -DallowSnapshots=true -DgenerateBackupPoms=false", returnStdout: false]) + 1 * getPipelineMock("sh")([script: 'mvn -B -N -e versions:update-child-modules -Dfull -DallowSnapshots=true -DgenerateBackupPoms=false', returnStdout: false]) + } + + def "[maven.groovy] run mvn get version property"() { + setup: + String propertyName = 'version.org.kie.kogito' + String expectedPropertyValue = 'some-property-value' + when: + def result = mavenGroovy.mvnGetVersionProperty(propertyName) + then: + 1 * getPipelineMock("sh")([script: "mvn -B -q -f pom.xml help:evaluate -Dexpression=$propertyName -DforceStdout", returnStdout: true]) >> ' some-property-value ' + expectedPropertyValue == result + } + + def "[maven.groovy] run mvn get version property with custom pom"() { + setup: + String propertyName = 'version.org.kie.kogito' + String pomFile = 'path/to/pom.xml' + String expectedPropertyValue = 'some-property-value' + when: + def result = mavenGroovy.mvnGetVersionProperty(propertyName, pomFile) + then: + 1 * getPipelineMock("sh")([script: "mvn -B -q -f $pomFile help:evaluate -Dexpression=$propertyName -DforceStdout", returnStdout: true]) >> ' some-property-value ' + expectedPropertyValue == result + } + + def "[maven.groovy] run mvn set version property"() { + setup: + String newVersion = '1.2.3' + String propertyName = 'version.org.kie.kogito' + when: + mavenGroovy.mvnSetVersionProperty(propertyName, newVersion) + then: + 1 * getPipelineMock("sh")([script: "mvn -B -e versions:set-property -Dproperty=$propertyName -DnewVersion=$newVersion -DallowSnapshots=true -DgenerateBackupPoms=false", returnStdout: false]) + } + + def "[maven.groovy] uploadLocalArtifacts"() { + setup: + String mvnUploadCredsId = 'mvnUploadCredsId' + String mvnUploadCreds = 'user:password' + mavenGroovy.getBinding().setVariable('kieUnpack', mvnUploadCreds) + String artifactDir = '/tmp' + String repoUrl = 'https://redhat.com' + when: + mavenGroovy.uploadLocalArtifacts(mvnUploadCredsId, artifactDir, repoUrl) + then: + 1 * getPipelineMock('usernameColonPassword.call')([credentialsId: mvnUploadCredsId, variable: 'kieUnpack']) >> mvnUploadCreds + 1 * getPipelineMock('withCredentials')([mvnUploadCreds], _ as Closure) + 1 * getPipelineMock('dir')(artifactDir, _ as Closure) + 1 * getPipelineMock('sh')('zip -r artifacts .') + 1 * getPipelineMock('sh')("curl --silent --upload-file artifacts.zip -u ${mvnUploadCreds} -v ${repoUrl}") + } + + def "[maven.groovy] getLatestArtifactVersionFromRepository OK"() { + setup: + String expectedVersion = '7.11.0.redhat-210426' + String repositoryUrl = 'http://repoUrl' + String groupId = 'org.kie.rhba' + String artifactId = 'rhdm' + def xmlSlurper = GroovySpy(XmlSlurper.class, global: true) + def gPathResult = Mock(GPathResult.class) + when: + def result = mavenGroovy.getLatestArtifactVersionFromRepository(repositoryUrl, groupId, artifactId) + then: + 1 * xmlSlurper.parse('http://repoUrl/org/kie/rhba/rhdm/maven-metadata.xml') >> gPathResult + 1 * gPathResult.getProperty('versioning') >> gPathResult + 1 * gPathResult.getProperty('latest') >> gPathResult + 1 * gPathResult.text() >> expectedVersion + expectedVersion == result + } + + def "[maven.groovy] getLatestArtifactVersionFromRepository null"() { + setup: + String expectedVersion = null + String repositoryUrl = 'http://repoUrl' + String groupId = 'org.kie.rhba' + String artifactId = 'rhdm' + def xmlSlurper = GroovySpy(XmlSlurper.class, global: true) + def gPathResult = Mock(GPathResult.class) + when: + def result = mavenGroovy.getLatestArtifactVersionFromRepository(repositoryUrl, groupId, artifactId) + then: + 1 * xmlSlurper.parse('http://repoUrl/org/kie/rhba/rhdm/maven-metadata.xml') >> gPathResult + 1 * gPathResult.getProperty('versioning') >> null + expectedVersion == result + } + + def "[maven.groovy] getLatestArtifactVersionPrefixFromRepository OK"() { + setup: + String expectedVersion = '7.52.0.Final-redhat-00004' + String repositoryUrl = 'http://repoUrl' + String groupId = 'org.kie' + String artifactId = 'kie-api' + def xmlSlurper = GroovySpy(XmlSlurper.class, global: true) + def gPathResult = Mock(GPathResult.class) + def versionIterator = [new VersionChildNode('7.52.0.Final'), new VersionChildNode('7.52.0.Final-redhat-00001'), new VersionChildNode('7.52.0.Final-redhat-00004'), new VersionChildNode('7.52.0.Final-redhat-00003'), new VersionChildNode('7.53.0.Final-redhat-00009')].iterator() + when: + def result = mavenGroovy.getLatestArtifactVersionPrefixFromRepository(repositoryUrl, groupId, artifactId, '7.52.0.Final-redhat') + then: + 1 * xmlSlurper.parse('http://repoUrl/org/kie/kie-api/maven-metadata.xml') >> gPathResult + 1 * gPathResult.getProperty('versioning') >> gPathResult + 1 * gPathResult.getProperty('versions') >> gPathResult + 1 * gPathResult.childNodes() >> versionIterator + expectedVersion == result + } + + def "[maven.groovy] getLatestArtifactVersionPrefixFromRepository null"() { + setup: + String expectedVersion = null + String repositoryUrl = 'http://repoUrl' + String groupId = 'org.kie' + String artifactId = 'kie-api' + def xmlSlurper = GroovySpy(XmlSlurper.class, global: true) + def gPathResult = Mock(GPathResult.class) + def versionIterator = [new VersionChildNode('7.52.0.Final'), new VersionChildNode('7.52.0.Final-redhat-00001'), new VersionChildNode('7.52.0.Final-redhat-00004'), new VersionChildNode('7.52.0.Final-redhat-00003'), new VersionChildNode('7.53.0.Final-redhat-00009')].iterator() + when: + def result = mavenGroovy.getLatestArtifactVersionPrefixFromRepository(repositoryUrl, groupId, artifactId, '8.0.0.Final-redhat') + then: + 1 * xmlSlurper.parse('http://repoUrl/org/kie/kie-api/maven-metadata.xml') >> gPathResult + 1 * gPathResult.getProperty('versioning') >> gPathResult + 1 * gPathResult.getProperty('versions') >> gPathResult + 1 * gPathResult.childNodes() >> versionIterator + expectedVersion == result + } + + def "[maven.groovy] getProjectPomFromBuildCmd command with -f"() { + setup: + String buildCmd = "mvn install -f scripts/logic/pom.xml -DskipTests" + when: + def result = mavenGroovy.getProjectPomFromBuildCmd(buildCmd) + then: + result == "scripts/logic/pom.xml" + } + + def "[maven.groovy] getProjectPomFromBuildCmd command with --file="() { + setup: + String buildCmd = "mvn install --file=scripts/logic/pom.xml -DskipTests" + when: + def result = mavenGroovy.getProjectPomFromBuildCmd(buildCmd) + then: + result == "scripts/logic/pom.xml" + } + + def "[maven.groovy] getProjectPomFromBuildCmd command without -f/--file="() { + setup: + String buildCmd = "mvn install -DskipTests" + when: + def result = mavenGroovy.getProjectPomFromBuildCmd(buildCmd) + then: + result == "pom.xml" + } + + def "[maven.groovy] cleanRepository"() { + when: + mavenGroovy.cleanRepository() + then: + 1 * getPipelineMock('sh')('rm -rf $HOME/.m2/repository') + } +} diff --git a/jenkins-pipeline-shared-libraries/test/vars/MavenStagingHelperSpec.groovy b/jenkins-pipeline-shared-libraries/test/vars/MavenStagingHelperSpec.groovy new file mode 100644 index 000000000..11e39bd34 --- /dev/null +++ b/jenkins-pipeline-shared-libraries/test/vars/MavenStagingHelperSpec.groovy @@ -0,0 +1,98 @@ +import com.homeaway.devtools.jenkins.testing.JenkinsPipelineSpecification +import org.jenkinsci.plugins.workflow.steps.Step +import org.jenkinsci.plugins.workflow.steps.StepContext +import org.jenkinsci.plugins.workflow.steps.StepExecution +import org.kie.jenkins.MavenStagingHelper + +class MavenStagingHelperSpec extends JenkinsPipelineSpecification { + def steps + + def setup() { + steps = new Step() { + @Override + StepExecution start(StepContext stepContext) throws Exception { + return null + } + } + } + + def "[MavenStagingHelper.groovy] stageLocalArtifacts"() { + setup: + def helper = new MavenStagingHelper(steps) + when: + helper.stageLocalArtifacts('ID', 'FOLDER') + then: + 1 * getPipelineMock("sh")([script: 'mvn -B -q help:evaluate -Dexpression=project.artifactId -DforceStdout', returnStdout: true]) >> { return 'NAME' } + 1 * getPipelineMock("sh")([script: 'mvn -B -q help:evaluate -Dexpression=project.version -DforceStdout', returnStdout: true]) >> { return 'VS' } + 1 * getPipelineMock("sh")([script: "mvn -B --projects :NAME org.sonatype.plugins:nexus-staging-maven-plugin:1.6.13:deploy-staged-repository -DnexusUrl=https://repository.jboss.org/nexus -DserverId=jboss-releases-repository -DstagingDescription='NAME VS' -DkeepStagingRepositoryOnCloseRuleFailure=true -DstagingProgressTimeoutMinutes=10 -DrepositoryDirectory=FOLDER -DstagingProfileId=ID", returnStdout: false]) + 1 * getPipelineMock("sh")([script: 'find FOLDER -name *.properties', returnStdout: true]) >> { return 'file.properties' } + 1 * getPipelineMock("readProperties")([file: 'file.properties']) >> { return ['stagingRepository.id':'STAGING_ID'] } + 'STAGING_ID' == helper.stagingRepositoryId + } + + def "[MavenStagingHelper.groovy] stageLocalArtifacts empty stagingProfileId"() { + setup: + def helper = new MavenStagingHelper(steps) + when: + helper.stageLocalArtifacts('', 'FOLDER') + then: + thrown(AssertionError) + } + + def "[MavenStagingHelper.groovy] stageLocalArtifacts empty artifacts folder"() { + setup: + def helper = new MavenStagingHelper(steps) + when: + helper.stageLocalArtifacts('ID', '') + then: + thrown(AssertionError) + } + + def "[MavenStagingHelper.groovy] promoteStagingRepository"() { + setup: + def helper = new MavenStagingHelper(steps) + when: + helper.withStagingRepositoryId('STAGING_ID') + .promoteStagingRepository('ID') + then: + 1 * getPipelineMock("sh")([script: 'mvn -B -q help:evaluate -Dexpression=project.artifactId -DforceStdout', returnStdout: true]) >> { return 'NAME' } + 1 * getPipelineMock("sh")([script: 'mvn -B -q help:evaluate -Dexpression=project.version -DforceStdout', returnStdout: true]) >> { return 'VS' } + 1 * getPipelineMock("sh")([script: "mvn -B --projects :NAME org.sonatype.plugins:nexus-staging-maven-plugin:1.6.13:promote -DnexusUrl=https://repository.jboss.org/nexus -DserverId=jboss-releases-repository -DstagingDescription='NAME VS' -DbuildPromotionProfileId=ID -DstagingRepositoryId=STAGING_ID", returnStdout: false]) + } + + def "[MavenStagingHelper.groovy] promoteStagingRepository empty buildPromotionProfileId"() { + setup: + def helper = new MavenStagingHelper(steps) + when: + helper.withStagingRepositoryId('STAGING_ID') + .promoteStagingRepository('') + then: + thrown(AssertionError) + } + + def "[MavenStagingHelper.groovy] promoteStagingRepository no stagingRepositoryId"() { + setup: + def helper = new MavenStagingHelper(steps) + when: + helper.promoteStagingRepository('ID') + then: + thrown(AssertionError) + } + + def "[MavenStagingHelper.groovy] full process"() { + setup: + def helper = new MavenStagingHelper(steps) + .withNexusReleaseUrl('NEXUS_URL') + .withNexusReleaseRepositoryId('NEXUS_REPOSITORY_ID') + .withStagingDescription('DESCRIPTION') + getPipelineMock("sh")([script: 'mvn -B -q help:evaluate -Dexpression=project.artifactId -DforceStdout', returnStdout: true]) >> { return 'NAME' } + getPipelineMock("sh")([script: 'mvn -B -q help:evaluate -Dexpression=project.version -DforceStdout', returnStdout: true]) >> { return 'VS' } + getPipelineMock("sh")([script: 'find FOLDER -name *.properties', returnStdout: true]) >> { return 'file.properties' } + getPipelineMock("readProperties")([file: 'file.properties']) >> { return ['stagingRepository.id':'STAGING_ID'] } + helper.stageLocalArtifacts('STAGE_PROFILE_ID', 'FOLDER') + when: + helper.promoteStagingRepository('BUILD_PROMOTE_ID') + then: + 1 * getPipelineMock("sh")([script: "mvn -B --projects :NAME org.sonatype.plugins:nexus-staging-maven-plugin:1.6.13:promote -DnexusUrl=NEXUS_URL -DserverId=NEXUS_REPOSITORY_ID -DstagingDescription='DESCRIPTION' -DbuildPromotionProfileId=BUILD_PROMOTE_ID -DstagingRepositoryId=STAGING_ID", returnStdout: false]) + } +} diff --git a/jenkins-pipeline-shared-libraries/test/vars/PullRequestSpec.groovy b/jenkins-pipeline-shared-libraries/test/vars/PullRequestSpec.groovy new file mode 100644 index 000000000..2545e8e59 --- /dev/null +++ b/jenkins-pipeline-shared-libraries/test/vars/PullRequestSpec.groovy @@ -0,0 +1,35 @@ +import com.homeaway.devtools.jenkins.testing.JenkinsPipelineSpecification + +class PullRequestSpec extends JenkinsPipelineSpecification { + def groovyScript = null + def projectCollection = ['projectA', 'projectB', 'projectC'] + + def setup() { + groovyScript = loadPipelineScriptForTest("vars/pullrequest.groovy") + explicitlyMockPipelineVariable("out") + groovyScript.metaClass.WORKSPACE = '/' + } + + def "PR from fork getAuthorAndRepoForPr" () { + setup: + def env = [:] + env['CHANGE_FORK']='contributor/fork' + env['CHANGE_URL']='https://github.com/owner/repo/pull/1' + groovyScript.getBinding().setVariable('env', env) + when: + def result = groovyScript.getAuthorAndRepoForPr() + then: + result == 'contributor/fork' + } + + def "PR from origin getAuthorAndRepoForPr" () { + setup: + def env = [:] + env['CHANGE_URL']='https://github.com/owner/repo/pull/1' + groovyScript.getBinding().setVariable('env', env) + when: + def result = groovyScript.getAuthorAndRepoForPr() + then: + result == 'owner/repo' + } +} \ No newline at end of file diff --git a/jenkins-pipeline-shared-libraries/test/vars/SSHShellSpec.groovy b/jenkins-pipeline-shared-libraries/test/vars/SSHShellSpec.groovy new file mode 100644 index 000000000..8a2299f39 --- /dev/null +++ b/jenkins-pipeline-shared-libraries/test/vars/SSHShellSpec.groovy @@ -0,0 +1,128 @@ +import com.homeaway.devtools.jenkins.testing.JenkinsPipelineSpecification +import org.jenkinsci.plugins.workflow.steps.Step +import org.jenkinsci.plugins.workflow.steps.StepContext +import org.jenkinsci.plugins.workflow.steps.StepExecution +import org.kie.jenkins.shell.SSHShell +import org.kie.jenkins.shell.installation.Installation + +class SSHShellSpec extends JenkinsPipelineSpecification { + + def steps + def env = [:] + + def setup() { + steps = new Step() { + + @Override + StepExecution start(StepContext stepContext) throws Exception { + return null + } + + } + } + + def "[SSHShell.groovy] getFullCommand default"() { + setup: + def shell = new SSHShell(steps, 'SERVER') + when: + def result = shell.getFullCommand('whatever') + then: + result == "ssh SERVER \"whatever\"" + } + + def "[SSHShell.groovy] getFullCommand ssh options"() { + setup: + def shell = new SSHShell(steps, 'SERVER', 'SSH_OPTIONS') + when: + def result = shell.getFullCommand('whatever') + then: + result == "ssh SSH_OPTIONS SERVER \"whatever\"" + } + + def "[SSHShell.groovy] getFullCommand with installations"() { + setup: + def install1 = Mock(Installation) + def install2 = Mock(Installation) + 1 * getPipelineMock('sh')([returnStdout: true, script: 'mktemp -d']) >> 'TMP_FOLDER' + def shell = new SSHShell(steps, 'SERVER') + shell.install(install1) + shell.install(install2) + 1 * install1.getBinaryPaths() >> ['PATH1', 'PATH2'] + 1 * install2.getBinaryPaths() >> ['PATH3'] + 1 * install1.getExtraEnvVars() >> [:] + 1 * install2.getExtraEnvVars() >> [ install2key : 'install2value' ] + when: + def result = shell.getFullCommand('whatever') + then: + result == """ssh SERVER \"export PATH=\${PATH}:PATH1:PATH2:PATH3 +export install2key=install2value +whatever\"""" + } + + def "[SSHShell.groovy] getFullCommand with environment variables"() { + setup: + def shell = new SSHShell(steps, 'SERVER') + shell.addEnvironmentVariable('KEY1', 'VALUE1') + shell.addEnvironmentVariable('key2', 'value2') + when: + def result = shell.getFullCommand('whatever') + then: + result == """ssh SERVER \"export KEY1=VALUE1 +export key2=value2 +whatever\"""" + } + + def "[SSHShell.groovy] getFullCommand with directory"() { + setup: + def shell = new SSHShell(steps, 'SERVER') + when: + def result = shell.getFullCommand('whatever', 'DIR') + then: + result == """ssh SERVER \"mkdir -p DIR && cd DIR +whatever\"""" + } + + def "[SSHShell.groovy] getFullCommand with all"() { + setup: + def install1 = Mock(Installation) + def install2 = Mock(Installation) + 1 * getPipelineMock('sh')([returnStdout: true, script: 'mktemp -d']) >> 'TMP_FOLDER' + def shell = new SSHShell(steps, 'SERVER', 'SSH_OPTIONS') + shell.install(install1) + shell.install(install2) + shell.addEnvironmentVariable('KEY1', 'VALUE1') + shell.addEnvironmentVariable('key2', 'value2') + 1 * install1.getBinaryPaths() >> ['PATH1', 'PATH2'] + 1 * install2.getBinaryPaths() >> ['PATH3'] + 1 * install1.getExtraEnvVars() >> [:] + 1 * install2.getExtraEnvVars() >> [ install2key : 'install2value' ] + when: + def result = shell.getFullCommand('whatever', 'DIR') + then: + result == """ssh SSH_OPTIONS SERVER \"mkdir -p DIR && cd DIR +export PATH=\${PATH}:PATH1:PATH2:PATH3 +export install2key=install2value +export KEY1=VALUE1 +export key2=value2 +whatever\"""" + } + + def "[SSHShell.groovy] copyFilesFromRemote"() { + setup: + def shell = new SSHShell(steps, 'SERVER', 'SSH_OPTIONS') + when: + shell.copyFilesFromRemote('REMOTE', 'LOCAL') + then: + 1 * getPipelineMock('sh')("scp -r -p SSH_OPTIONS SERVER:REMOTE LOCAL") + } + + def "[SSHShell.groovy] copyFilesToRemote"() { + setup: + def shell = new SSHShell(steps, 'SERVER', 'SSH_OPTIONS') + when: + shell.copyFilesToRemote('LOCAL', 'REMOTE') + then: + 1 * getPipelineMock('sh')("scp -r -p SSH_OPTIONS LOCAL SERVER:REMOTE") + } + +} diff --git a/jenkins-pipeline-shared-libraries/test/vars/UtilSpec.groovy b/jenkins-pipeline-shared-libraries/test/vars/UtilSpec.groovy new file mode 100644 index 000000000..f91bb290c --- /dev/null +++ b/jenkins-pipeline-shared-libraries/test/vars/UtilSpec.groovy @@ -0,0 +1,1609 @@ +import com.homeaway.devtools.jenkins.testing.JenkinsPipelineSpecification +import groovy.json.JsonSlurper +import hudson.plugins.git.GitSCM + +class UtilSpec extends JenkinsPipelineSpecification { + def groovyScript = null + def projectBranchMappingProperties = null + + def setup() { + groovyScript = loadPipelineScriptForTest("vars/util.groovy") + explicitlyMockPipelineVariable("out") + explicitlyMockPipelineVariable("KEYTAB_FILE") + } + + def "[util.groovy] getProject"() { + when: + def project = groovyScript.getProject('https://github.com/apache/incubator-kie-kogito-pipelines.git') + then: + project == 'apache/incubator-kie-kogito-pipelines' + } + + def "[util.groovy] getGroup"() { + when: + def group = groovyScript.getGroup('https://github.com/apache/incubator-kie-kogito-pipelines.git') + then: + group == 'apache' + } + + def "[util.groovy] getProjectGroupName with group"() { + when: + def groupName = groovyScript.getProjectGroupName('name', 'group') + then: + groupName[0] == 'group' + groupName[1] == 'name' + } + + def "[util.groovy] getProjectGroupName without group"() { + when: + def groupName = groovyScript.getProjectGroupName('name') + then: + groupName[0] == 'apache' + groupName[1] == 'name' + } + + def "[util.groovy] storeGitInformation no previous values"() { + setup: + def projectGroupName = ['group', 'name'] + def env = [:] + groovyScript.getBinding().setVariable("env", env) + when: + groovyScript.storeGitInformation('projectName') + then: + 1 * getPipelineMock('githubscm.getCommit')() >> 'kiegroup/lienzo-core: 0f917d4 Expose zoom and pan filters (#102)' + 1 * getPipelineMock('githubscm.getCommitHash')() >> 'ac36137f12d1bcfa5cdf02b796a1a33d251b48e1' + 1 * getPipelineMock('githubscm.getBranch')() >> '* (detached from 0f917d4) remotes/origin/main' + 1 * getPipelineMock('githubscm.getRemoteInfo')('origin', 'url') >> 'https://github.com/kiegroup/lienzo-core.git' + env['GIT_INFORMATION_REPORT'] == "projectName=kiegroup/lienzo-core: 0f917d4 Expose zoom and pan filters (#102) Branch [* (detached from 0f917d4) remotes/origin/main] Remote [https://github.com/kiegroup/lienzo-core.git]" + env['GIT_INFORMATION_HASHES'] == "projectName=ac36137f12d1bcfa5cdf02b796a1a33d251b48e1" + } + + def "[util.groovy] storeGitInformation with previous values"() { + setup: + def projectGroupName = ['group', 'name'] + def env = [:] + env['GIT_INFORMATION_REPORT'] = 'projectName=kiegroup/lienzo-tests: 45c16e1 Fix tests (#84) Branch [* (detached from 45c16e1) remotes/origin/main] Remote [https://github.com/kiegroup/lienzo-tests.git]' + env['GIT_INFORMATION_HASHES'] = 'projectName=45c16e1' + groovyScript.getBinding().setVariable("env", env) + when: + groovyScript.storeGitInformation('projectName') + then: + 1 * getPipelineMock('githubscm.getCommit')() >> 'kiegroup/lienzo-core: 0f917d4 Expose zoom and pan filters (#102)' + 1 * getPipelineMock('githubscm.getCommitHash')() >> '11111111111111111111111111111111' + 1 * getPipelineMock('githubscm.getBranch')() >> '* (detached from 0f917d4) remotes/origin/main' + 1 * getPipelineMock('githubscm.getRemoteInfo')('origin', 'url') >> 'https://github.com/kiegroup/lienzo-core.git' + env['GIT_INFORMATION_REPORT'] == 'projectName=kiegroup/lienzo-tests: 45c16e1 Fix tests (#84) Branch [* (detached from 45c16e1) remotes/origin/main] Remote [https://github.com/kiegroup/lienzo-tests.git]; projectName=kiegroup/lienzo-core: 0f917d4 Expose zoom and pan filters (#102) Branch [* (detached from 0f917d4) remotes/origin/main] Remote [https://github.com/kiegroup/lienzo-core.git]' + env['GIT_INFORMATION_HASHES'] == 'projectName=45c16e1;projectName=11111111111111111111111111111111' + } + + def "[util.groovy] printGitInformationReport GIT_INFORMATION_REPORT null"() { + setup: + def projectGroupName = ['group', 'name'] + def env = [:] + groovyScript.getBinding().setVariable("env", env) + when: + groovyScript.printGitInformationReport() + then: + true + } + + def "[util.groovy] getProjectDirPath without group"() { + setup: + def projectGroupName = ['group', 'name'] + def env = [:] + env.put('WORKSPACE', '/workspacefolder') + groovyScript.getBinding().setVariable("env", env) + when: + def result = groovyScript.getProjectDirPath('projectA') + then: + result == "/workspacefolder/apache_projectA" + } + + def "[util.groovy] getNextVersionMicro"() { + when: + def snapshotVersion = groovyScript.getNextVersion('0.12.0', 'micro') + then: + '0.12.1-SNAPSHOT' == snapshotVersion + + } + + def "[util.groovy] getNextVersionMinor"() { + when: + def snapshotVersion = groovyScript.getNextVersion('0.12.1', 'minor') + then: + '0.13.0-SNAPSHOT' == snapshotVersion + } + + def "[util.groovy] getNextVersionMinor no resetSubVersions"() { + when: + def snapshotVersion = groovyScript.getNextVersion('0.12.1', 'minor', 'SNAPSHOT', false) + then: + '0.13.1-SNAPSHOT' == snapshotVersion + } + + def "[util.groovy] getNextVersionMajor"() { + when: + def snapshotVersion = groovyScript.getNextVersion('0.12.1', 'major') + then: + '1.0.0-SNAPSHOT' == snapshotVersion + } + + def "[util.groovy] getNextVersionMajor no resetSubVersions"() { + when: + def snapshotVersion = groovyScript.getNextVersion('0.12.1', 'major', 'SNAPSHOT', false) + then: + '1.12.1-SNAPSHOT' == snapshotVersion + } + + def "[util.groovy] getNextVersionSuffixTest"() { + when: + def snapshotVersion = groovyScript.getNextVersion('0.12.1', 'major', 'whatever') + then: + '1.0.0-whatever' == snapshotVersion + } + + def "[util.groovy] getNextVersionSuffixTest no resetSubVersions"() { + when: + def snapshotVersion = groovyScript.getNextVersion('0.12.1', 'major', 'whatever', false) + then: + '1.12.1-whatever' == snapshotVersion + } + + def "[util.groovy] getNextVersionErrorContainsAlphabets"() { + when: + def checkForAlphabets = groovyScript.getNextVersion('a.12.0', 'micro') + then: + 1 * getPipelineMock("error").call('Version a.12.0 is not in the required format. The major, minor, and micro parts should contain only numeric characters.') + } + + def "[util.groovy] getNextVersionErrorFormat"() { + when: + def checkForFormatError = groovyScript.getNextVersion('0.12.0.1', 'micro') + then: + 1 * getPipelineMock("error").call('Version 0.12.0.1 is not in the required format X.Y.Z or X.Y.Z.suffix.') + } + + def "[util.groovy] getNextVersion null"() { + when: + def version = groovyScript.getNextVersion('0.12.0', 'micro', null) + then: + '0.12.1' == version + } + + def "[util.groovy] getNextVersionAssertErrorCheck"() { + when: + groovyScript.getNextVersion('0.12.0', 'microo') + then: + thrown(AssertionError) + } + + def "[util.groovy] parseVersion correct"() { + when: + def version = groovyScript.parseVersion('0.12.6598') + then: + version[0] == 0 + version[1] == 12 + version[2] == 6598 + } + + def "[util.groovy] parseVersion With Suffix Correct"() { + when: + def version = groovyScript.parseVersion('1.0.0.Final') + then: + version[0] == 1 + version[1] == 0 + version[2] == 0 + } + + def "[util.groovy] parseVersion Error Contains Alphabets"() { + when: + groovyScript.parseVersion('a.12.0') + then: + 1 * getPipelineMock("error").call('Version a.12.0 is not in the required format. The major, minor, and micro parts should contain only numeric characters.') + } + + def "[util.groovy] parseVersion Error Format"() { + when: + groovyScript.parseVersion('0.12.0.1') + then: + 1 * getPipelineMock("error").call('Version 0.12.0.1 is not in the required format X.Y.Z or X.Y.Z.suffix.') + } + + def "[util.groovy] getMajorMinorVersion with correct version"() { + when: + def result = groovyScript.getMajorMinorVersion('1.36.0') + then: + result == "1.36" + } + + def "[util.groovy] getMajorMinorVersion with incorrect version raises error"() { + when: + groovyScript.getMajorMinorVersion('ANY_VERSION') + then: + thrown(Exception) + } + + + def "[util.groovy] getReleaseBranchFromVersion"() { + when: + def output = groovyScript.getReleaseBranchFromVersion('1.50.425.Final') + then: + output == '1.50.x' + } + + def "[util.groovy] calculateTargetReleaseBranch default"() { + when: + def version = groovyScript.calculateTargetReleaseBranch('56.34.x') + then: + version == '56.34.x' + } + + def "[util.groovy] calculateTargetReleaseBranch not release branch"() { + when: + def version = groovyScript.calculateTargetReleaseBranch('anything') + then: + version == 'anything' + } + + def "[util.groovy] calculateTargetReleaseBranch addMajor"() { + when: + def version = groovyScript.calculateTargetReleaseBranch('56.34.x', 10) + then: + version == '66.34.x' + } + + def "[util.groovy] calculateTargetReleaseBranch addMinor"() { + when: + def version = groovyScript.calculateTargetReleaseBranch('56.34.x', 0, 15) + then: + version == '56.49.x' + } + + def "[util.groovy] calculateTargetReleaseBranch addMajor addMinor"() { + when: + def version = groovyScript.calculateTargetReleaseBranch('56.34.x', 10, 15) + then: + version == '66.49.x' + } + + def "[util.groovy] calculateTargetReleaseBranch prod branch"() { + when: + def version = groovyScript.calculateTargetReleaseBranch('56.34.x-prod') + then: + version == '56.34.x-prod' + } + + def "[util.groovy] calculateTargetReleaseBranch prod branch addMajor"() { + when: + def version = groovyScript.calculateTargetReleaseBranch('56.34.x-prod', 7) + then: + version == '63.34.x-prod' + } + + def "[util.groovy] calculateTargetReleaseBranch prod branch addMajor addMinor"() { + when: + def version = groovyScript.calculateTargetReleaseBranch('56.34.x-prod', 7, 10) + then: + version == '63.44.x-prod' + } + + def "[util.groovy] calculateTargetReleaseBranch not release branch with -prod"() { + when: + def version = groovyScript.calculateTargetReleaseBranch('56.34.x-prod-anything', 7) + then: + version == '56.34.x-prod-anything' + } + + def "[util.groovy] generateHashSize9"() { + when: + def hash9 = groovyScript.generateHash(9) + then: + hash9.length() == 9 + } + + def "[util.groovy] generateHashSize1000"() { + when: + def hash1000 = groovyScript.generateHash(1000) + then: + hash1000.length() == 1000 + } + + def "[util.groovy] generateTempFile"() { + when: + def result = groovyScript.generateTempFile() + then: + 1 * getPipelineMock("sh")([returnStdout: true, script: 'mktemp']) >> 'file' + result == 'file' + } + + def "[util.groovy] generateTempFolder"() { + when: + def result = groovyScript.generateTempFolder() + then: + 1 * getPipelineMock("sh")([returnStdout: true, script: 'mktemp -d']) >> 'folder' + result == 'folder' + } + + def "[util.groovy] executeWithCredentialsMap with token"() { + when: + groovyScript.executeWithCredentialsMap([token: 'TOKEN']) { + sh 'hello' + } + then: + 1 * getPipelineMock('string.call')([credentialsId: 'TOKEN', variable: 'QUAY_TOKEN']) >> 'token' + 1 * getPipelineMock('withCredentials')(['token'], _ as Closure) + 1 * getPipelineMock("sh")('hello') + 0 * getPipelineMock('error').call('No credentials given to execute the given closure') + } + + def "[util.groovy] executeWithCredentialsMap with usernamePassword"() { + when: + groovyScript.executeWithCredentialsMap([usernamePassword: 'USERNAME_PASSWORD']) { + sh 'hello' + } + then: + 1 * getPipelineMock('usernamePassword.call')([credentialsId: 'USERNAME_PASSWORD', usernameVariable: 'QUAY_USER', passwordVariable: 'QUAY_TOKEN']) >> 'usernamePassword' + 1 * getPipelineMock('withCredentials')(['usernamePassword'], _ as Closure) + 1 * getPipelineMock("sh")('hello') + 0 * getPipelineMock('error').call('No credentials given to execute the given closure') + } + + def "[util.groovy] executeWithCredentialsMap with token and usernamePassword"() { + when: + groovyScript.executeWithCredentialsMap([token: 'TOKEN', usernamePassword: 'USERNAME_PASSWORD']) { + sh 'hello' + } + then: + 1 * getPipelineMock('string.call')([credentialsId: 'TOKEN', variable: 'QUAY_TOKEN']) >> 'token' + 1 * getPipelineMock('withCredentials')(['token'], _ as Closure) + 1 * getPipelineMock("sh")('hello') + 0 * getPipelineMock('error').call('No credentials given to execute the given closure') + } + + def "[util.groovy] executeWithCredentialsMap empty"() { + when: + groovyScript.executeWithCredentialsMap([:]) { + sh 'hello' + } + then: + 0 * getPipelineMock('string.call')([credentialsId: 'TOKEN', variable: 'QUAY_TOKEN']) >> 'token' + 0 * getPipelineMock('withCredentials')(['token'], _ as Closure) + 0 * getPipelineMock('usernamePassword.call')([credentialsId: 'USERNAME_PASSWORD', usernameVariable: 'QUAY_USER', passwordVariable: 'QUAY_TOKEN']) >> 'usernamePassword' + 0 * getPipelineMock('withCredentials')(['usernamePassword'], _ as Closure) + 0 * getPipelineMock("sh")('hello') + 1 * getPipelineMock('error').call('No credentials given to execute the given closure') + } + + def "[util.groovy] cleanNode"() { + when: + groovyScript.cleanNode() + then: + 0 * getPipelineMock('cloud.cleanContainersAndImages')(_) + 1 * getPipelineMock('maven.cleanRepository')() + 1 * getPipelineMock('cleanWs.call')() + } + + def "[util.groovy] cleanNode with docker"() { + when: + groovyScript.cleanNode('docker') + then: + 1 * getPipelineMock('cloud.cleanContainersAndImages')('docker') + 1 * getPipelineMock('maven.cleanRepository')() + 1 * getPipelineMock('cleanWs.call')() + } + + def "[util.groovy] cleanNode with podman"() { + when: + groovyScript.cleanNode('podman') + then: + 1 * getPipelineMock('cloud.cleanContainersAndImages')('podman') + 1 * getPipelineMock('maven.cleanRepository')() + 1 * getPipelineMock('cleanWs.call')() + } + + def "[util.groovy] replaceInAllFilesRecursive"() { + when: + groovyScript.replaceInAllFilesRecursive('pattern*', 'sedpatternval\\', 'newValue') + then: + 1 * getPipelineMock('sh')('find . -name \'pattern*\' -type f -exec sed -i \'s/sedpatternval\\/newValue/g\' {} \\;') + } + + def "[util.groovy] rmPartialDeps"() { + setup: + def env = [:] + env.put('WORKSPACE', '/workspacefolderrmPartialDeps') + groovyScript.getBinding().setVariable("env", env) + + when: + groovyScript.rmPartialDeps() + then: + 1 * getPipelineMock('dir')('/workspacefolderrmPartialDeps/.m2', _ as Closure) + 1 * getPipelineMock("sh").call('find . -regex ".*\\.part\\(\\.lock\\)?" -exec rm -rf {} \\;') + } + + + + def "[util.groovy] retrieveConsoleLog no arg"() { + setup: + groovyScript.getBinding().setVariable('BUILD_URL', 'URL/') + when: + def result = groovyScript.retrieveConsoleLog() + then: + 1 * getPipelineMock('sh')([returnStdout: true, script: 'wget --no-check-certificate -qO - URL/consoleText | tail -n 100']) >> 'CONTENT' + result == 'CONTENT' + } + + def "[util.groovy] retrieveConsoleLog with number of lines"() { + setup: + groovyScript.getBinding().setVariable('BUILD_URL', 'URL/') + when: + def result = groovyScript.retrieveConsoleLog(3) + then: + 1 * getPipelineMock('sh')([returnStdout: true, script: 'wget --no-check-certificate -qO - URL/consoleText | tail -n 3']) >> 'CONTENT' + result == 'CONTENT' + } + + def "[util.groovy] retrieveConsoleLog with number of lines and build url"() { + setup: + groovyScript.getBinding().setVariable('BUILD_URL', 'URL/') + when: + def result = groovyScript.retrieveConsoleLog(2, "BUILD_URL/") + then: + 1 * getPipelineMock('sh')([returnStdout: true, script: 'wget --no-check-certificate -qO - BUILD_URL/consoleText | tail -n 2']) >> 'CONTENT' + result == 'CONTENT' + } + + def "[util.groovy] archiveConsoleLog no arg"() { + setup: + groovyScript.getBinding().setVariable('BUILD_URL', 'URL/') + when: + def result = groovyScript.archiveConsoleLog() + then: + 1 * getPipelineMock('sh')('rm -rf console.log') + 1 * getPipelineMock('sh')([returnStdout: true, script: 'wget --no-check-certificate -qO - URL/consoleText | tail -n 100']) >> 'CONTENT' + 1 * getPipelineMock('writeFile')([text: 'CONTENT', file: 'console.log']) + 1 * getPipelineMock('archiveArtifacts.call')([artifacts: 'console.log']) + } + + def "[util.groovy] archiveConsoleLog with id"() { + setup: + groovyScript.getBinding().setVariable('BUILD_URL', 'URL/') + when: + def result = groovyScript.archiveConsoleLog('ID', 3) + then: + 1 * getPipelineMock('sh')('rm -rf ID_console.log') + 1 * getPipelineMock('sh')([returnStdout: true, script: 'wget --no-check-certificate -qO - URL/consoleText | tail -n 3']) >> 'CONTENT' + 1 * getPipelineMock('writeFile')([text: 'CONTENT', file: 'ID_console.log']) + 1 * getPipelineMock('archiveArtifacts.call')([artifacts: 'ID_console.log']) + } + + def "[util.groovy] archiveConsoleLog with id and number of lines"() { + setup: + groovyScript.getBinding().setVariable('BUILD_URL', 'URL/') + groovyScript.metaClass.generateHash = { int size -> + return 'GENERATED_ID' + } + when: + def result = groovyScript.archiveConsoleLog('ID', 3) + then: + 1 * getPipelineMock('sh')('rm -rf ID_console.log') + 1 * getPipelineMock('sh')([returnStdout: true, script: 'wget --no-check-certificate -qO - URL/consoleText | tail -n 3']) >> 'CONTENT' + 1 * getPipelineMock('writeFile')([text: 'CONTENT', file: 'ID_console.log']) + 1 * getPipelineMock('archiveArtifacts.call')([artifacts: 'ID_console.log']) + } + + def "[util.groovy] archiveConsoleLog with id, number of lines and build url"() { + setup: + groovyScript.getBinding().setVariable('BUILD_URL', 'URL/') + groovyScript.metaClass.generateHash = { int size -> + return 'GENERATED_ID' + } + when: + def result = groovyScript.archiveConsoleLog('ID', 3, 'BUILD_URL/') + then: + 1 * getPipelineMock('sh')('rm -rf ID_console.log') + 1 * getPipelineMock('sh')([returnStdout: true, script: 'wget --no-check-certificate -qO - BUILD_URL/consoleText | tail -n 3']) >> 'CONTENT' + 1 * getPipelineMock('writeFile')([text: 'CONTENT', file: 'ID_console.log']) + 1 * getPipelineMock('archiveArtifacts.call')([artifacts: 'ID_console.log']) + } + + def "[util.groovy] retrieveTestResults no arg"() { + setup: + groovyScript.getBinding().setVariable('BUILD_URL', 'URL/') + when: + def result = groovyScript.retrieveTestResults() + then: + 1 * getPipelineMock('sh')([returnStdout: true, script: 'wget --no-check-certificate -qO - URL/testReport/api/json?depth=1']) >> 'CONTENT' + 1 * getPipelineMock('readJSON')([text: 'CONTENT']) >> [ hello : 'anything' ] + result.hello == 'anything' + } + + def "[util.groovy] retrieveTestResults with build url"() { + setup: + groovyScript.getBinding().setVariable('BUILD_URL', 'URL/') + when: + def result = groovyScript.retrieveTestResults("BUILD_URL/") + then: + 1 * getPipelineMock('sh')([returnStdout: true, script: 'wget --no-check-certificate -qO - BUILD_URL/testReport/api/json?depth=1']) >> 'CONTENT' + 1 * getPipelineMock('readJSON')([text: 'CONTENT']) >> [ hello : 'anything' ] + result.hello == 'anything' + } + + def "[util.groovy] retrieveFailedTests no arg"() { + setup: + groovyScript.getBinding().setVariable('BUILD_URL', 'URL/') + def failedTests = [ + suites: [ + [ + cases: [ + [ + status: 'FAILED', + className: 'package1.class1', + name: 'test', + errorDetails: 'details package1.class1.test1', + errorStackTrace: 'trace package1.class1.test1' + ], + [ + status: 'SKIPPED', + className: 'package1.class2.', + name: 'test' + ] + ] + ], + [ + cases: [ + [ + status: 'FAILED', + className: 'package2.class1', + name: '(?)', + errorDetails: 'details package2.class1.(?)', + errorStackTrace: 'trace package2.class1.(?)' + ], + [ + status: 'PASSED', + className: 'package2.class2', + name: 'test' + ], + [ + status: 'REGRESSION', + className: 'package2.class2', + name: 'test2', + errorDetails: 'details package2.class2.test2', + errorStackTrace: 'trace package2.class2.test2' + ], + ] + ] + ] + ] + when: + def result = groovyScript.retrieveFailedTests() + then: + 1 * getPipelineMock('sh')([returnStdout: true, script: 'wget --no-check-certificate -qO - URL/testReport/api/json?depth=1']) >> 'CONTENT' + 1 * getPipelineMock('readJSON')([text: 'CONTENT']) >> failedTests + result.size() == 3 + result.find { it.packageName == 'package1' && it.className == 'class1' && it.name == 'test'} + result.find { it.packageName == 'package1' && it.className == 'class1' && it.name == 'test'}.fullName == 'package1.class1.test' + result.find { it.packageName == 'package1' && it.className == 'class1' && it.name == 'test'}.url == 'URL/testReport/package1/class1/test/' + result.find { it.packageName == 'package1' && it.className == 'class1' && it.name == 'test'}.details == 'details package1.class1.test1' + result.find { it.packageName == 'package1' && it.className == 'class1' && it.name == 'test'}.stacktrace == 'trace package1.class1.test1' + + result.find { it.packageName == 'package2' && it.className == 'class1' && it.name == '(?)'} + result.find { it.packageName == 'package2' && it.className == 'class1' && it.name == '(?)'}.fullName == 'package2.class1.(?)' + result.find { it.packageName == 'package2' && it.className == 'class1' && it.name == '(?)'}.url == 'URL/testReport/package2/class1/___/' + result.find { it.packageName == 'package2' && it.className == 'class1' && it.name == '(?)'}.details == 'details package2.class1.(?)' + result.find { it.packageName == 'package2' && it.className == 'class1' && it.name == '(?)'}.stacktrace == 'trace package2.class1.(?)' + + result.find { it.packageName == 'package2' && it.className == 'class2' && it.name == 'test2'} + result.find { it.packageName == 'package2' && it.className == 'class2' && it.name == 'test2'}.fullName == 'package2.class2.test2' + result.find { it.packageName == 'package2' && it.className == 'class2' && it.name == 'test2'}.url == 'URL/testReport/package2/class2/test2/' + result.find { it.packageName == 'package2' && it.className == 'class2' && it.name == 'test2'}.details == 'details package2.class2.test2' + result.find { it.packageName == 'package2' && it.className == 'class2' && it.name == 'test2'}.stacktrace == 'trace package2.class2.test2' + } + + def "[util.groovy] retrieveFailedTests with build url"() { + setup: + groovyScript.getBinding().setVariable('BUILD_URL/', 'URL/') + def failedTests = [ + suites: [ + [ + cases: [ + [ + status: 'FAILED', + className: 'package1.class1', + name: 'test', + errorDetails: 'details package1.class1.test1', + errorStackTrace: 'trace package1.class1.test1' + ], + [ + status: 'SKIPPED', + className: 'package1.class2.', + name: 'test' + ] + ] + ], + [ + cases: [ + [ + status: 'FAILED', + className: 'package2.class1', + name: '(?)', + errorDetails: 'details package2.class1.(?)', + errorStackTrace: 'trace package2.class1.(?)' + ], + [ + status: 'PASSED', + className: 'package2.class2', + name: 'test' + ], + [ + status: 'REGRESSION', + className: 'package2.class2', + name: 'test2', + errorDetails: 'details package2.class2.test2', + errorStackTrace: 'trace package2.class2.test2' + ], + ] + ] + ] + ] + when: + def result = groovyScript.retrieveFailedTests('BUILD_URL/') + then: + 1 * getPipelineMock('sh')([returnStdout: true, script: 'wget --no-check-certificate -qO - BUILD_URL/testReport/api/json?depth=1']) >> 'CONTENT' + 1 * getPipelineMock('readJSON')([text: 'CONTENT']) >> failedTests + result.size() == 3 + + result.find { it.packageName == 'package1' && it.className == 'class1' && it.name == 'test'} + result.find { it.packageName == 'package1' && it.className == 'class1' && it.name == 'test'}.fullName == 'package1.class1.test' + result.find { it.packageName == 'package1' && it.className == 'class1' && it.name == 'test'}.url == 'BUILD_URL/testReport/package1/class1/test/' + result.find { it.packageName == 'package1' && it.className == 'class1' && it.name == 'test'}.details == 'details package1.class1.test1' + result.find { it.packageName == 'package1' && it.className == 'class1' && it.name == 'test'}.stacktrace == 'trace package1.class1.test1' + + result.find { it.packageName == 'package2' && it.className == 'class1' && it.name == '(?)'} + result.find { it.packageName == 'package2' && it.className == 'class1' && it.name == '(?)'}.fullName == 'package2.class1.(?)' + result.find { it.packageName == 'package2' && it.className == 'class1' && it.name == '(?)'}.url == 'BUILD_URL/testReport/package2/class1/___/' + result.find { it.packageName == 'package2' && it.className == 'class1' && it.name == '(?)'}.details == 'details package2.class1.(?)' + result.find { it.packageName == 'package2' && it.className == 'class1' && it.name == '(?)'}.stacktrace == 'trace package2.class1.(?)' + + result.find { it.packageName == 'package2' && it.className == 'class2' && it.name == 'test2'} + result.find { it.packageName == 'package2' && it.className == 'class2' && it.name == 'test2'}.fullName == 'package2.class2.test2' + result.find { it.packageName == 'package2' && it.className == 'class2' && it.name == 'test2'}.url == 'BUILD_URL/testReport/package2/class2/test2/' + result.find { it.packageName == 'package2' && it.className == 'class2' && it.name == 'test2'}.details == 'details package2.class2.test2' + result.find { it.packageName == 'package2' && it.className == 'class2' && it.name == 'test2'}.stacktrace == 'trace package2.class2.test2' + } + + def "[util.groovy] retrieveFailedTests with multiple test cases with same name"() { + setup: + groovyScript.getBinding().setVariable('BUILD_URL', 'URL/') + def failedTests = [ + suites: [ + [ + cases: [ + [ + status: 'FAILED', + className: 'package1.class1', + name: 'test', + errorDetails: 'details package1.class1.test1', + errorStackTrace: 'trace package1.class1.test1' + ], + [ + status: 'SKIPPED', + className: 'package1.class2.', + name: 'test' + ] + ], + enclosingBlockNames : [ + 'Test kogito-runtime-jvm', + 'Build&Test kogito-runtime-jvm', + 'Build & Test Images' + ] + ], + [ + cases: [ + [ + status: 'FAILED', + className: 'package1.class1', + name: 'test', + errorDetails: 'details package1.class1.test1', + errorStackTrace: 'trace package1.class1.test1' + ], + [ + status: 'SKIPPED', + className: 'package1.class2.', + name: 'test' + ] + ], + enclosingBlockNames : [ + 'Test kogito-runtime-native', + 'Build&Test kogito-runtime-native', + 'Build & Test Images' + ] + ] + ] + ] + when: + def result = groovyScript.retrieveFailedTests() + then: + 1 * getPipelineMock('sh')([returnStdout: true, script: 'wget --no-check-certificate -qO - URL/testReport/api/json?depth=1']) >> 'CONTENT' + 1 * getPipelineMock('readJSON')([text: 'CONTENT']) >> failedTests + result.size() == 2 + result.find { it.packageName == 'package1' && it.className == 'class1' && it.name == 'test' && it.enclosingBlockNames == 'Build & Test Images / Build&Test kogito-runtime-jvm / Test kogito-runtime-jvm'} + result.find { it.packageName == 'package1' && it.className == 'class1' && it.name == 'test' && it.enclosingBlockNames == 'Build & Test Images / Build&Test kogito-runtime-jvm / Test kogito-runtime-jvm'}.fullName == 'Build & Test Images / Build&Test kogito-runtime-jvm / Test kogito-runtime-jvm / package1.class1.test' + result.find { it.packageName == 'package1' && it.className == 'class1' && it.name == 'test' && it.enclosingBlockNames == 'Build & Test Images / Build&Test kogito-runtime-jvm / Test kogito-runtime-jvm'}.url == 'URL/testReport/package1/class1/Build___Test_Images___Build_Test_kogito_runtime_jvm___Test_kogito_runtime_jvm___test/' + result.find { it.packageName == 'package1' && it.className == 'class1' && it.name == 'test' && it.enclosingBlockNames == 'Build & Test Images / Build&Test kogito-runtime-jvm / Test kogito-runtime-jvm'}.details == 'details package1.class1.test1' + result.find { it.packageName == 'package1' && it.className == 'class1' && it.name == 'test' && it.enclosingBlockNames == 'Build & Test Images / Build&Test kogito-runtime-jvm / Test kogito-runtime-jvm'}.stacktrace == 'trace package1.class1.test1' + + result.find { it.packageName == 'package1' && it.className == 'class1' && it.name == 'test' && it.enclosingBlockNames == 'Build & Test Images / Build&Test kogito-runtime-native / Test kogito-runtime-native'} + result.find { it.packageName == 'package1' && it.className == 'class1' && it.name == 'test' && it.enclosingBlockNames == 'Build & Test Images / Build&Test kogito-runtime-native / Test kogito-runtime-native'}.fullName == 'Build & Test Images / Build&Test kogito-runtime-native / Test kogito-runtime-native / package1.class1.test' + result.find { it.packageName == 'package1' && it.className == 'class1' && it.name == 'test' && it.enclosingBlockNames == 'Build & Test Images / Build&Test kogito-runtime-native / Test kogito-runtime-native'}.url == 'URL/testReport/package1/class1/Build___Test_Images___Build_Test_kogito_runtime_native___Test_kogito_runtime_native___test/' + result.find { it.packageName == 'package1' && it.className == 'class1' && it.name == 'test' && it.enclosingBlockNames == 'Build & Test Images / Build&Test kogito-runtime-native / Test kogito-runtime-native'}.details == 'details package1.class1.test1' + result.find { it.packageName == 'package1' && it.className == 'class1' && it.name == 'test' && it.enclosingBlockNames == 'Build & Test Images / Build&Test kogito-runtime-native / Test kogito-runtime-native'}.stacktrace == 'trace package1.class1.test1' + } + + def "[util.groovy] retrieveFailedTests with enclosingBlockNames"() { + setup: + groovyScript.getBinding().setVariable('BUILD_URL', 'URL/') + def failedTests = [ + suites: [ + [ + cases: [ + [ + status: 'FAILED', + className: 'package1.class1', + name: 'test', + errorDetails: 'details package1.class1.test1', + errorStackTrace: 'trace package1.class1.test1' + ], + [ + status: 'SKIPPED', + className: 'package1.class2.', + name: 'test' + ] + ], + enclosingBlockNames : [ + 'Test kogito-runtime-jvm', + 'Build&Test kogito-runtime-jvm', + 'Build & Test Images' + ] + ] + ] + ] + when: + def result = groovyScript.retrieveFailedTests() + then: + 1 * getPipelineMock('sh')([returnStdout: true, script: 'wget --no-check-certificate -qO - URL/testReport/api/json?depth=1']) >> 'CONTENT' + 1 * getPipelineMock('readJSON')([text: 'CONTENT']) >> failedTests + result.size() == 1 + result.find { it.packageName == 'package1' && it.className == 'class1' && it.name == 'test' && it.enclosingBlockNames == 'Build & Test Images / Build&Test kogito-runtime-jvm / Test kogito-runtime-jvm'} + result.find { it.packageName == 'package1' && it.className == 'class1' && it.name == 'test' && it.enclosingBlockNames == 'Build & Test Images / Build&Test kogito-runtime-jvm / Test kogito-runtime-jvm'}.fullName == 'package1.class1.test' + result.find { it.packageName == 'package1' && it.className == 'class1' && it.name == 'test' && it.enclosingBlockNames == 'Build & Test Images / Build&Test kogito-runtime-jvm / Test kogito-runtime-jvm'}.url == 'URL/testReport/package1/class1/test/' + result.find { it.packageName == 'package1' && it.className == 'class1' && it.name == 'test' && it.enclosingBlockNames == 'Build & Test Images / Build&Test kogito-runtime-jvm / Test kogito-runtime-jvm'}.details == 'details package1.class1.test1' + result.find { it.packageName == 'package1' && it.className == 'class1' && it.name == 'test' && it.enclosingBlockNames == 'Build & Test Images / Build&Test kogito-runtime-jvm / Test kogito-runtime-jvm'}.stacktrace == 'trace package1.class1.test1' + } + + def "[util.groovy] retrieveArtifact default file exists"() { + setup: + groovyScript.getBinding().setVariable('BUILD_URL', 'URL/') + when: + def result = groovyScript.retrieveArtifact('PATH') + then: + 1 * getPipelineMock('sh')([returnStdout: true, script: "curl -o /dev/null --silent -Iw '%{http_code}' URL/artifact/PATH"]) >> '200' + 1 * getPipelineMock('sh')([returnStdout: true, script: 'wget --no-check-certificate -qO - URL/artifact/PATH']) >> 'CONTENT' + result == 'CONTENT' + } + + + def "[util.groovy] retrieveArtifact default file NOT exists"() { + setup: + groovyScript.getBinding().setVariable('BUILD_URL', 'URL/') + when: + def result = groovyScript.retrieveArtifact('PATH') + then: + 1 * getPipelineMock('sh')([returnStdout: true, script: "curl -o /dev/null --silent -Iw '%{http_code}' URL/artifact/PATH"]) >> '404' + 0 * getPipelineMock('sh')([returnStdout: true, script: 'wget --no-check-certificate -qO - URL/artifact/PATH']) >> 'CONTENT' + result == '' + } + + + def "[util.groovy] retrieveArtifact with build url and file exists"() { + setup: + groovyScript.getBinding().setVariable('BUILD_URL', 'URL/') + when: + def result = groovyScript.retrieveArtifact('PATH', 'BUILD_URL/') + then: + 1 * getPipelineMock('sh')([returnStdout: true, script: "curl -o /dev/null --silent -Iw '%{http_code}' BUILD_URL/artifact/PATH"]) >> '200' + 1 * getPipelineMock('sh')([returnStdout: true, script: 'wget --no-check-certificate -qO - BUILD_URL/artifact/PATH']) >> 'CONTENT' + result == 'CONTENT' + } + + + def "[util.groovy] retrieveArtifact with build url and file NOT exists"() { + setup: + groovyScript.getBinding().setVariable('BUILD_URL', 'URL/') + when: + def result = groovyScript.retrieveArtifact('PATH', 'BUILD_URL/') + then: + 1 * getPipelineMock('sh')([returnStdout: true, script: "curl -o /dev/null --silent -Iw '%{http_code}' BUILD_URL/artifact/PATH"]) >> '404' + 0 * getPipelineMock('sh')([returnStdout: true, script: 'wget --no-check-certificate -qO - BUILD_URL/artifact/PATH']) >> 'CONTENT' + result == '' + } + + def "[util.groovy] retrieveJobInformation no arg"() { + setup: + groovyScript.getBinding().setVariable('BUILD_URL', 'URL/') + def jobMock = [ + url: 'ANY_URL' + ] + when: + def result = groovyScript.retrieveJobInformation() + then: + 1 * getPipelineMock('sh')([returnStdout: true, script: 'wget --no-check-certificate -qO - URL/api/json?depth=0']) >> 'CONTENT' + 1 * getPipelineMock('readJSON')([text: 'CONTENT']) >> jobMock + result.url == 'ANY_URL' + } + + def "[util.groovy] retrieveJobInformation with build url"() { + setup: + groovyScript.getBinding().setVariable('BUILD_URL', 'URL/') + def jobMock = [ + url: 'ANY_URL' + ] + when: + def result = groovyScript.retrieveJobInformation('BUILD_URL/') + then: + 1 * getPipelineMock('sh')([returnStdout: true, script: 'wget --no-check-certificate -qO - BUILD_URL/api/json?depth=0']) >> 'CONTENT' + 1 * getPipelineMock('readJSON')([text: 'CONTENT']) >> jobMock + result.url == 'ANY_URL' + } + + def "[util.groovy] getMarkdownTestSummary job success with no job id and no build url"() { + setup: + groovyScript.getBinding().setVariable("BUILD_URL", 'URL/') + groovyScript.getBinding().setVariable("BUILD_NUMBER", '256') + def jobMock = [ result: 'SUCCESS' ] + when: + def result = groovyScript.getMarkdownTestSummary() + then: + // retrieveConsoleLog + 1 * getPipelineMock('sh')([returnStdout: true, script: 'wget --no-check-certificate -qO - URL/consoleText | tail -n 50']) >> 'this is the console' + // retrieveJobInformation + 1 * getPipelineMock('sh')([returnStdout: true, script: 'wget --no-check-certificate -qO - URL/api/json?depth=0']) >> 'CONTENT' + 1 * getPipelineMock('readJSON')([text: 'CONTENT']) >> jobMock + + // check result + result == ''' +Job #256 was: **SUCCESS** +''' + } + + def "[util.groovy] getMarkdownTestSummary job success with no build url"() { + setup: + groovyScript.getBinding().setVariable("BUILD_URL", 'URL/') + groovyScript.getBinding().setVariable("BUILD_NUMBER", '256') + def jobMock = [ result: 'SUCCESS' ] + when: + def result = groovyScript.getMarkdownTestSummary('JOB_ID') + then: + // retrieveConsoleLog + 1 * getPipelineMock('sh')([returnStdout: true, script: 'wget --no-check-certificate -qO - URL/consoleText | tail -n 50']) >> 'this is the console' + // retrieveJobInformation + 1 * getPipelineMock('sh')([returnStdout: true, script: 'wget --no-check-certificate -qO - URL/api/json?depth=0']) >> 'CONTENT' + 1 * getPipelineMock('readJSON')([text: 'CONTENT']) >> jobMock + + // check result + result == ''' +**JOB_ID job** #256 was: **SUCCESS** +''' + } + + def "[util.groovy] getMarkdownTestSummary job success with additional info"() { + setup: + groovyScript.getBinding().setVariable("BUILD_URL", 'URL/') + groovyScript.getBinding().setVariable("BUILD_NUMBER", '256') + def jobMock = [ result: 'SUCCESS' ] + when: + def result = groovyScript.getMarkdownTestSummary('JOB_ID', 'ADDITIONAL_INFO') + then: + // retrieveConsoleLog + 1 * getPipelineMock('sh')([returnStdout: true, script: 'wget --no-check-certificate -qO - URL/consoleText | tail -n 50']) >> 'this is the console' + // retrieveJobInformation + 1 * getPipelineMock('sh')([returnStdout: true, script: 'wget --no-check-certificate -qO - URL/api/json?depth=0']) >> 'CONTENT' + 1 * getPipelineMock('readJSON')([text: 'CONTENT']) >> jobMock + + // check result + result == ''' +**JOB_ID job** #256 was: **SUCCESS** + +ADDITIONAL_INFO +''' + } + + def "[util.groovy] getMarkdownTestSummary job success with additonal info, build url"() { + setup: + groovyScript.getBinding().setVariable("BUILD_URL", 'URL/') + groovyScript.getBinding().setVariable("BUILD_NUMBER", '256') + def jobMock = [ result: 'SUCCESS' ] + when: + def result = groovyScript.getMarkdownTestSummary('JOB_ID', 'ADDITIONAL_INFO', 'BUILD_URL/') + then: + // retrieveConsoleLog + 1 * getPipelineMock('sh')([returnStdout: true, script: 'wget --no-check-certificate -qO - BUILD_URL/consoleText | tail -n 50']) >> 'this is the console' + // retrieveJobInformation + 1 * getPipelineMock('sh')([returnStdout: true, script: 'wget --no-check-certificate -qO - BUILD_URL/api/json?depth=0']) >> 'CONTENT' + 1 * getPipelineMock('readJSON')([text: 'CONTENT']) >> jobMock + + // check result + result == ''' +**JOB_ID job** #256 was: **SUCCESS** + +ADDITIONAL_INFO +''' + } + + def "[util.groovy] getMarkdownTestSummary job failure"() { + setup: + groovyScript.getBinding().setVariable("BUILD_URL", 'URL/') + groovyScript.getBinding().setVariable("BUILD_NUMBER", '256') + def jobMock = [ result: 'FAILURE' ] + def testResultsMock = [ passCount: 254, failCount: 0 ] + def failedTestsMock = [:] + when: + def result = groovyScript.getMarkdownTestSummary('JOB_ID') + then: + // retrieveJobInformation + 1 * getPipelineMock('sh')([returnStdout: true, script: 'wget --no-check-certificate -qO - URL/api/json?depth=0']) >> 'JOB_INFO' + 1 * getPipelineMock('readJSON')([text: 'JOB_INFO']) >> jobMock + // retrieveConsoleLog + 1 * getPipelineMock('sh')([returnStdout: true, script: 'wget --no-check-certificate -qO - URL/consoleText | tail -n 50']) >> 'this is the console' + // retrieveTestResults + 1 * getPipelineMock('sh')([returnStdout: true, script: 'wget --no-check-certificate -qO - URL/testReport/api/json?depth=1']) >> 'TEST_RESULTS' + 1 * getPipelineMock('readJSON')([text: 'TEST_RESULTS']) >> testResultsMock + // retrieveFailedTests + 1 * getPipelineMock('sh')([returnStdout: true, script: 'wget --no-check-certificate -qO - URL/testReport/api/json?depth=1']) >> 'FAILED_TESTS' + 1 * getPipelineMock('readJSON')([text: 'FAILED_TESTS']) >> failedTestsMock + + // check result + result == ''' +**JOB_ID job** #256 was: **FAILURE** +Possible explanation: Pipeline failure or project build failure + +Please look here: URL/display/redirect + +**Test results:** +- PASSED: 254 +- FAILED: 0 + +Those are the test failures: none + +See console log: +```spoiler Console Logs +this is the console +``` +''' + } + + def "[util.groovy] getMarkdownTestSummary job failure with additional info"() { + setup: + groovyScript.getBinding().setVariable("BUILD_URL", 'URL/') + groovyScript.getBinding().setVariable("BUILD_NUMBER", '256') + def jobMock = [ result: 'FAILURE' ] + def testResultsMock = [ passCount: 254, failCount: 0 ] + def failedTestsMock = [:] + when: + def result = groovyScript.getMarkdownTestSummary('JOB_ID', 'ADDITIONAL_INFO') + then: + // retrieveConsoleLog + 1 * getPipelineMock('sh')([returnStdout: true, script: 'wget --no-check-certificate -qO - URL/consoleText | tail -n 50']) >> 'this is the console' + // retrieveJobInformation + 1 * getPipelineMock('sh')([returnStdout: true, script: 'wget --no-check-certificate -qO - URL/api/json?depth=0']) >> 'JOB_INFO' + 1 * getPipelineMock('readJSON')([text: 'JOB_INFO']) >> jobMock + // retrieveTestResults + 1 * getPipelineMock('sh')([returnStdout: true, script: 'wget --no-check-certificate -qO - URL/testReport/api/json?depth=1']) >> 'TEST_RESULTS' + 1 * getPipelineMock('readJSON')([text: 'TEST_RESULTS']) >> testResultsMock + // retrieveFailedTests + 1 * getPipelineMock('sh')([returnStdout: true, script: 'wget --no-check-certificate -qO - URL/testReport/api/json?depth=1']) >> 'FAILED_TESTS' + 1 * getPipelineMock('readJSON')([text: 'FAILED_TESTS']) >> failedTestsMock + + // check result + result == ''' +**JOB_ID job** #256 was: **FAILURE** +Possible explanation: Pipeline failure or project build failure + +ADDITIONAL_INFO + +Please look here: URL/display/redirect + +**Test results:** +- PASSED: 254 +- FAILED: 0 + +Those are the test failures: none + +See console log: +```spoiler Console Logs +this is the console +``` +''' + } + + def "[util.groovy] getMarkdownTestSummary job failure with additional info and build url"() { + setup: + groovyScript.getBinding().setVariable("BUILD_URL", 'URL/') + groovyScript.getBinding().setVariable("BUILD_NUMBER", '256') + def jobMock = [ + result: 'FAILURE', + artifacts: [ + [ + fileName: 'this_should_not_be_handled_console.log.me' + ], + ] + ] + def testResultsMock = [ passCount: 254, failCount: 0 ] + def failedTestsMock = [:] + when: + def result = groovyScript.getMarkdownTestSummary('JOB_ID', 'ADDITIONAL_INFO', 'BUILD_URL/') + then: + // retrieveJobInformation + 1 * getPipelineMock('sh')([returnStdout: true, script: 'wget --no-check-certificate -qO - BUILD_URL/api/json?depth=0']) >> 'JOB_INFO' + 1 * getPipelineMock('readJSON')([text: 'JOB_INFO']) >> jobMock + // retrieveConsoleLog + 1 * getPipelineMock('sh')([returnStdout: true, script: 'wget --no-check-certificate -qO - BUILD_URL/consoleText | tail -n 50']) >> 'this is the console' + // retrieveTestResults + 1 * getPipelineMock('sh')([returnStdout: true, script: 'wget --no-check-certificate -qO - BUILD_URL/testReport/api/json?depth=1']) >> 'TEST_RESULTS' + 1 * getPipelineMock('readJSON')([text: 'TEST_RESULTS']) >> testResultsMock + // retrieveFailedTests + 1 * getPipelineMock('sh')([returnStdout: true, script: 'wget --no-check-certificate -qO - BUILD_URL/testReport/api/json?depth=1']) >> 'FAILED_TESTS' + 1 * getPipelineMock('readJSON')([text: 'FAILED_TESTS']) >> failedTestsMock + + // check result + result == ''' +**JOB_ID job** #256 was: **FAILURE** +Possible explanation: Pipeline failure or project build failure + +ADDITIONAL_INFO + +Please look here: BUILD_URL/display/redirect + +**Test results:** +- PASSED: 254 +- FAILED: 0 + +Those are the test failures: none + +See console log: +```spoiler Console Logs +this is the console +``` +''' + } + + def "[util.groovy] getMarkdownTestSummary job failure with console artifact existing"() { + setup: + groovyScript.getBinding().setVariable("BUILD_URL", 'URL/') + groovyScript.getBinding().setVariable("BUILD_NUMBER", '256') + def jobMock = [ + result: 'FAILURE', + artifacts: [ + [ + fileName: 'console.log' + ], + [ + fileName: 'any_console.log' + ], + [ + fileName: 'Another_console.log' + ], + [ + fileName: 'this_should_not_be_handled_console.log.me' + ], + ] + ] + def testResultsMock = [ passCount: 254, failCount: 0 ] + def failedTestsMock = [:] + when: + def result = groovyScript.getMarkdownTestSummary('JOB_ID') + then: + // retrieveJobInformation + 1 * getPipelineMock('sh')([returnStdout: true, script: 'wget --no-check-certificate -qO - URL/api/json?depth=0']) >> 'JOB_INFO' + 1 * getPipelineMock('readJSON')([text: 'JOB_INFO']) >> jobMock + // retrieveArtifact + 1 * getPipelineMock('sh')([returnStdout: true, script: "curl -o /dev/null --silent -Iw '%{http_code}' URL/artifact/console.log"]) >> '200' + 1 * getPipelineMock('sh')([returnStdout: true, script: 'wget --no-check-certificate -qO - URL/artifact/console.log']) >> 'this is the console artifact' + 1 * getPipelineMock('sh')([returnStdout: true, script: "curl -o /dev/null --silent -Iw '%{http_code}' URL/artifact/any_console.log"]) >> '200' + 1 * getPipelineMock('sh')([returnStdout: true, script: 'wget --no-check-certificate -qO - URL/artifact/any_console.log']) >> 'this is the any_console artifact' + 1 * getPipelineMock('sh')([returnStdout: true, script: "curl -o /dev/null --silent -Iw '%{http_code}' URL/artifact/Another_console.log"]) >> '200' + 1 * getPipelineMock('sh')([returnStdout: true, script: 'wget --no-check-certificate -qO - URL/artifact/Another_console.log']) >> 'this is the Another_console artifact' + 0 * getPipelineMock('sh')([returnStdout: true, script: "curl -o /dev/null --silent -Iw '%{http_code}' URL/artifact/this_should_not_be_handled_console.log.me"]) >> '200' + 0 * getPipelineMock('sh')([returnStdout: true, script: 'wget --no-check-certificate -qO - URL/artifact/this_should_not_be_handled_console.log.me']) + // retrieveConsoleLog + 0 * getPipelineMock('sh')([returnStdout: true, script: 'wget --no-check-certificate -qO - URL/consoleText | tail -n 50']) >> 'this is the console' + // retrieveTestResults + 1 * getPipelineMock('sh')([returnStdout: true, script: 'wget --no-check-certificate -qO - URL/testReport/api/json?depth=1']) >> 'TEST_RESULTS' + 1 * getPipelineMock('readJSON')([text: 'TEST_RESULTS']) >> testResultsMock + // retrieveFailedTests + 1 * getPipelineMock('sh')([returnStdout: true, script: 'wget --no-check-certificate -qO - URL/testReport/api/json?depth=1']) >> 'FAILED_TESTS' + 1 * getPipelineMock('readJSON')([text: 'FAILED_TESTS']) >> failedTestsMock + + // check result + result == ''' +**JOB_ID job** #256 was: **FAILURE** +Possible explanation: Pipeline failure or project build failure + +Please look here: URL/display/redirect + +**Test results:** +- PASSED: 254 +- FAILED: 0 + +Those are the test failures: none + +See console log: +```spoiler Console Logs +this is the console artifact +``` +```spoiler any +this is the any_console artifact +``` +```spoiler Another +this is the Another_console artifact +``` +''' + } + + def "[util.groovy] getMarkdownTestSummary job failure with no test results"() { + setup: + groovyScript.getBinding().setVariable("BUILD_URL", 'URL/') + groovyScript.getBinding().setVariable("BUILD_NUMBER", '256') + def jobMock = [ result: 'FAILURE' ] + def testResultsMock = [ passCount: 254, failCount: 635 ] + def failedTestsMock = [:] + when: + def result = groovyScript.getMarkdownTestSummary('JOB_ID') + then: + // retrieveJobInformation + 1 * getPipelineMock('sh')([returnStdout: true, script: 'wget --no-check-certificate -qO - URL/api/json?depth=0']) >> 'JOB_INFO' + 1 * getPipelineMock('readJSON')([text: 'JOB_INFO']) >> jobMock + // retrieveConsoleLog + 1 * getPipelineMock('sh')([returnStdout: true, script: 'wget --no-check-certificate -qO - URL/consoleText | tail -n 50']) >> 'this is the console' + // retrieveTestResults + 1 * getPipelineMock('sh')([returnStdout: true, script: 'wget --no-check-certificate -qO - URL/testReport/api/json?depth=1']) >> { throw new Exception('unknown URL') } + 0 * getPipelineMock('readJSON')([text: 'TEST_RESULTS']) >> testResultsMock + // retrieveFailedTests + 0 * getPipelineMock('sh')([returnStdout: true, script: 'wget --no-check-certificate -qO - URL/testReport/api/json?depth=1']) >> 'FAILED_TESTS' + 0 * getPipelineMock('readJSON')([text: 'FAILED_TESTS']) >> failedTestsMock + + // check result + result == ''' +**JOB_ID job** #256 was: **FAILURE** +Possible explanation: Pipeline failure or project build failure + +Please look here: URL/display/redirect +See console log: +```spoiler Console Logs +this is the console +``` +''' + } + + def "[util.groovy] getMarkdownTestSummary job failure with failed tests"() { + setup: + groovyScript.getBinding().setVariable("BUILD_URL", 'URL/') + groovyScript.getBinding().setVariable("BUILD_NUMBER", '256') + def jobMock = [ result: 'FAILURE' ] + def testResultsMock = [ passCount: 254, failCount: 2 ] + def failedTestsMock = [ + suites: [ + [ + cases: [ + [ + status: 'FAILED', + className: 'package1.class1', + name: 'test', + errorDetails: 'details package1.class1.test', + errorStackTrace: 'trace package1.class1.test' + ], + [ + status: 'FAILED', + className: 'package1.class2', + name: 'test', + errorDetails: null, + errorStackTrace: 'trace package1.class2.test' + ] + ] + ] + ] + ] + when: + def result = groovyScript.getMarkdownTestSummary('JOB_ID') + then: + // retrieveJobInformation + 1 * getPipelineMock('sh')([returnStdout: true, script: 'wget --no-check-certificate -qO - URL/api/json?depth=0']) >> 'JOB_INFO' + 1 * getPipelineMock('readJSON')([text: 'JOB_INFO']) >> jobMock + // retrieveConsoleLog + 1 * getPipelineMock('sh')([returnStdout: true, script: 'wget --no-check-certificate -qO - URL/consoleText | tail -n 50']) >> 'this is the console' + // retrieveTestResults + 1 * getPipelineMock('sh')([returnStdout: true, script: 'wget --no-check-certificate -qO - URL/testReport/api/json?depth=1']) >> 'TEST_RESULTS' + 1 * getPipelineMock('readJSON')([text: 'TEST_RESULTS']) >> testResultsMock + // retrieveFailedTests + 1 * getPipelineMock('sh')([returnStdout: true, script: 'wget --no-check-certificate -qO - URL/testReport/api/json?depth=1']) >> 'FAILED_TESTS' + 1 * getPipelineMock('readJSON')([text: 'FAILED_TESTS']) >> failedTestsMock + + // check result + result == ''' +**JOB_ID job** #256 was: **FAILURE** +Possible explanation: Pipeline failure or project build failure + +Please look here: URL/display/redirect + +**Test results:** +- PASSED: 254 +- FAILED: 2 + +Those are the test failures: +```spoiler [package1.class1.test](URL/testReport/package1/class1/test/) +details package1.class1.test +``` +```spoiler [package1.class2.test](URL/testReport/package1/class2/test/) +trace package1.class2.test +``` + +See console log: +```spoiler Console Logs +this is the console +``` +''' + } + + def "[util.groovy] getMarkdownTestSummary job unstable with failed tests"() { + setup: + groovyScript.getBinding().setVariable("BUILD_URL", 'URL/') + groovyScript.getBinding().setVariable("BUILD_NUMBER", '256') + def jobMock = [ result: 'UNSTABLE' ] + def testResultsMock = [ passCount: 254, failCount: 2 ] + def failedTestsMock = [ + suites: [ + [ + cases: [ + [ + status: 'FAILED', + className: 'package1.class1', + name: 'test', + errorDetails: 'details package1.class1.test' + ], + [ + status: 'FAILED', + className: 'package1.class2', + name: 'test', + errorDetails: 'details package1.class2.test' + ] + ] + ] + ] + ] + when: + def result = groovyScript.getMarkdownTestSummary('JOB_ID') + then: + // retrieveJobInformation + 1 * getPipelineMock('sh')([returnStdout: true, script: 'wget --no-check-certificate -qO - URL/api/json?depth=0']) >> 'JOB_INFO' + 1 * getPipelineMock('readJSON')([text: 'JOB_INFO']) >> jobMock + // retrieveConsoleLog + 1 * getPipelineMock('sh')([returnStdout: true, script: 'wget --no-check-certificate -qO - URL/consoleText | tail -n 50']) >> 'this is the console' + // retrieveTestResults + 1 * getPipelineMock('sh')([returnStdout: true, script: 'wget --no-check-certificate -qO - URL/testReport/api/json?depth=1']) >> 'TEST_RESULTS' + 1 * getPipelineMock('readJSON')([text: 'TEST_RESULTS']) >> testResultsMock + // retrieveFailedTests + 1 * getPipelineMock('sh')([returnStdout: true, script: 'wget --no-check-certificate -qO - URL/testReport/api/json?depth=1']) >> 'FAILED_TESTS' + 1 * getPipelineMock('readJSON')([text: 'FAILED_TESTS']) >> failedTestsMock + + // check result + result == ''' +**JOB_ID job** #256 was: **UNSTABLE** +Possible explanation: This should be test failures + +Please look here: URL/display/redirect + +**Test results:** +- PASSED: 254 +- FAILED: 2 + +Those are the test failures: +```spoiler [package1.class1.test](URL/testReport/package1/class1/test/) +details package1.class1.test +``` +```spoiler [package1.class2.test](URL/testReport/package1/class2/test/) +details package1.class2.test +``` +''' + } + + def "[util.groovy] getMarkdownTestSummary job unstable with failed tests and GITHUB output"() { + setup: + groovyScript.getBinding().setVariable("BUILD_URL", 'URL/') + groovyScript.getBinding().setVariable("BUILD_NUMBER", '256') + def jobMock = [ result: 'FAILURE' ] + def testResultsMock = [ passCount: 254, failCount: 2 ] + def failedTestsMock = [ + suites: [ + [ + cases: [ + [ + status: 'FAILED', + className: 'package1.class1', + name: 'test', + errorDetails: 'details package1.class1.test' + ], + [ + status: 'FAILED', + className: 'package1.class2', + name: 'test', + errorStackTrace: 'stacktrace package1.class2.test\nstacktrace line 2\nstacktrace line 3' + ] + ] + ] + ] + ] + when: + def result = groovyScript.getMarkdownTestSummary('JOB_ID', '', "URL/", 'GITHUB') + then: + // retrieveJobInformation + 1 * getPipelineMock('sh')([returnStdout: true, script: 'wget --no-check-certificate -qO - URL/api/json?depth=0']) >> 'JOB_INFO' + 1 * getPipelineMock('readJSON')([text: 'JOB_INFO']) >> jobMock + // retrieveConsoleLog + 1 * getPipelineMock('sh')([returnStdout: true, script: 'wget --no-check-certificate -qO - URL/consoleText | tail -n 50']) >> 'this is the console\nanother line' + // retrieveTestResults + 1 * getPipelineMock('sh')([returnStdout: true, script: 'wget --no-check-certificate -qO - URL/testReport/api/json?depth=1']) >> 'TEST_RESULTS' + 1 * getPipelineMock('readJSON')([text: 'TEST_RESULTS']) >> testResultsMock + // retrieveFailedTests + 1 * getPipelineMock('sh')([returnStdout: true, script: 'wget --no-check-certificate -qO - URL/testReport/api/json?depth=1']) >> 'FAILED_TESTS' + 1 * getPipelineMock('readJSON')([text: 'FAILED_TESTS']) >> failedTestsMock + + // check result + result == ''' +**JOB_ID job** `#256` was: **FAILURE** +Possible explanation: Pipeline failure or project build failure + +Please look here: URL/display/redirect + +**Test results:** +- PASSED: 254 +- FAILED: 2 + +Those are the test failures: +
+package1.class1.test +details package1.class1.test +
+
+package1.class2.test +stacktrace package1.class2.test
stacktrace line 2
stacktrace line 3 +
+ +See console log: +
+Console Logs +this is the console
another line +
+''' + } + + def "[util.groovy] multiple serializeQueryParams serialize map to query url"() { + setup: + def params = [q: 'value', k: 3] + when: + def result = groovyScript.serializeQueryParams(params) + then: + result == 'q=value&k=3' + } + def "[util.groovy] single serializeQueryParams serialize map to query url"() { + setup: + def params = [q: 'value'] + when: + def result = groovyScript.serializeQueryParams(params) + then: + result == 'q=value' + } + + def "[util.groovy] withKerberos using default succeeded"() { + setup: + def env = [:] + groovyScript.getBinding().setVariable("env", env) + // simulate withCredentials binding + groovyScript.getBinding().setVariable('KEYTAB_FILE', 'path/to/file') + when: + groovyScript.withKerberos('keytab-id') { + sh 'hello' + } + then: + 1 * getPipelineMock('file.call')([credentialsId: 'keytab-id', variable: 'KEYTAB_FILE']) >> 'path/to/file' + 1 * getPipelineMock('withCredentials')(['path/to/file'], _ as Closure) + 1 * getPipelineMock('sh')([returnStdout: true, script: "klist -kt path/to/file |grep REDHAT.COM | awk -F' ' 'NR==1{print \$4}' "]) >> 'service-account' + 1 * getPipelineMock('sh')([returnStdout: true, script: "klist | grep -i 'Default principal' | awk -F':' 'NR==1{print \$2}' "]) >> '' + 1 * getPipelineMock("sh")('hello') + 1 * getPipelineMock('sh')([returnStatus: true, script: "kinit service-account -kt path/to/file"]) >> 0 + env['KERBEROS_PRINCIPAL'] == 'service-account' + noExceptionThrown() + } + + def "[util.groovy] withKerberos using default succeeded after 2 failure"() { + setup: + def env = [:] + groovyScript.getBinding().setVariable("env", env) + // simulate withCredentials binding + groovyScript.getBinding().setVariable('KEYTAB_FILE', 'path/to/file') + when: + groovyScript.withKerberos('keytab-id') { + sh 'hello' + } + then: + 1 * getPipelineMock('file.call')([credentialsId: 'keytab-id', variable: 'KEYTAB_FILE']) >> 'path/to/file' + 1 * getPipelineMock('withCredentials')(['path/to/file'], _ as Closure) + 1 * getPipelineMock('sh')([returnStdout: true, script: "klist -kt path/to/file |grep REDHAT.COM | awk -F' ' 'NR==1{print \$4}' "]) >> 'service-account' + 1 * getPipelineMock('sh')([returnStdout: true, script: "klist | grep -i 'Default principal' | awk -F':' 'NR==1{print \$2}' "]) >> '' + 1 * getPipelineMock("sh")('hello') + 2 * getPipelineMock('sh')([returnStatus: true, script: "kinit service-account -kt path/to/file"]) >> 1 + 1 * getPipelineMock('sh')([returnStatus: true, script: "kinit service-account -kt path/to/file"]) >> 0 + env['KERBEROS_PRINCIPAL'] == 'service-account' + noExceptionThrown() + } + + + def "[util.groovy] withKerberos using custom domain succeeded"() { + setup: + def env = [:] + groovyScript.getBinding().setVariable("env", env) + // simulate withCredentials binding + groovyScript.getBinding().setVariable('KEYTAB_FILE', 'path/to/file') + when: + groovyScript.withKerberos('keytab-id', {sh 'hello'}, 'CUSTOM.COM') + then: + 1 * getPipelineMock('file.call')([credentialsId: 'keytab-id', variable: 'KEYTAB_FILE']) >> 'path/to/file' + 1 * getPipelineMock('withCredentials')(['path/to/file'], _ as Closure) + 1 * getPipelineMock('sh')([returnStdout: true, script: "klist -kt path/to/file |grep CUSTOM.COM | awk -F' ' 'NR==1{print \$4}' "]) >> 'service-account' + 1 * getPipelineMock('sh')([returnStdout: true, script: "klist | grep -i 'Default principal' | awk -F':' 'NR==1{print \$2}' "]) >> '' + 1 * getPipelineMock("sh")('hello') + 1 * getPipelineMock('sh')([returnStatus: true, script: "kinit service-account -kt path/to/file"]) >> 0 + env['KERBEROS_PRINCIPAL'] == 'service-account' + noExceptionThrown() + } + + def "[util.groovy] withKerberos when blank kerberos principal"() { + setup: + def env = [:] + groovyScript.getBinding().setVariable("env", env) + // simulate withCredentials binding + groovyScript.getBinding().setVariable('KEYTAB_FILE', 'path/to/file') + when: + groovyScript.withKerberos('keytab-id') { + sh 'hello' + } + then: + 1 * getPipelineMock('file.call')([credentialsId: 'keytab-id', variable: 'KEYTAB_FILE']) >> 'path/to/file' + 1 * getPipelineMock('withCredentials')(['path/to/file'], _ as Closure) + 1 * getPipelineMock('sh')([returnStdout: true, script: "klist -kt path/to/file |grep REDHAT.COM | awk -F' ' 'NR==1{print \$4}' "]) >> '' + 0 * getPipelineMock('sh')([returnStdout: true, script: "klist | grep -i 'Default principal' | awk -F':' 'NR==1{print \$2}' "]) + // closure not being executed + 0 * getPipelineMock("sh")('hello') + 0 * getPipelineMock('sh')([returnStatus: true, script: "kinit -kt path/to/file"]) >> 0 + thrown(Exception) + } + + def "[util.groovy] withKerberos when kinit fails"() { + setup: + def env = [:] + groovyScript.getBinding().setVariable("env", env) + // simulate withCredentials binding + groovyScript.getBinding().setVariable('KEYTAB_FILE', 'path/to/file') + when: + groovyScript.withKerberos('keytab-id') { + sh 'hello' + } + then: + 1 * getPipelineMock('file.call')([credentialsId: 'keytab-id', variable: 'KEYTAB_FILE']) >> 'path/to/file' + 1 * getPipelineMock('withCredentials')(['path/to/file'], _ as Closure) + 1 * getPipelineMock('sh')([returnStdout: true, script: "klist -kt path/to/file |grep REDHAT.COM | awk -F' ' 'NR==1{print \$4}' "]) >> 'service-account' + 1 * getPipelineMock('sh')([returnStdout: true, script: "klist | grep -i 'Default principal' | awk -F':' 'NR==1{print \$2}' "]) >> '' + // closure not being executed + 0 * getPipelineMock("sh")('hello') + 5 * getPipelineMock('sh')([returnStatus: true, script: "kinit service-account -kt path/to/file"]) >> 1 + env['KERBEROS_PRINCIPAL'] == 'service-account' + thrown(Exception) + } + + def "[util.groovy] withKerberos when kinit fails after one try"() { + setup: + def env = [:] + groovyScript.getBinding().setVariable("env", env) + // simulate withCredentials binding + groovyScript.getBinding().setVariable('KEYTAB_FILE', 'path/to/file') + when: + groovyScript.withKerberos('keytab-id', { sh 'hello' }, 'REDHAT.COM', 1) + then: + 1 * getPipelineMock('file.call')([credentialsId: 'keytab-id', variable: 'KEYTAB_FILE']) >> 'path/to/file' + 1 * getPipelineMock('withCredentials')(['path/to/file'], _ as Closure) + 1 * getPipelineMock('sh')([returnStdout: true, script: "klist -kt path/to/file |grep REDHAT.COM | awk -F' ' 'NR==1{print \$4}' "]) >> 'service-account' + 1 * getPipelineMock('sh')([returnStdout: true, script: "klist | grep -i 'Default principal' | awk -F':' 'NR==1{print \$2}' "]) >> '' + // closure not being executed + 0 * getPipelineMock("sh")('hello') + 1 * getPipelineMock('sh')([returnStatus: true, script: "kinit service-account -kt path/to/file"]) >> 1 + env['KERBEROS_PRINCIPAL'] == 'service-account' + thrown(Exception) + } + + def "[util.groovy] withKerberos when principal already authenticated"() { + setup: + def env = [:] + groovyScript.getBinding().setVariable("env", env) + // simulate withCredentials binding + groovyScript.getBinding().setVariable('KEYTAB_FILE', 'path/to/file') + when: + groovyScript.withKerberos('keytab-id') { + sh 'hello' + } + then: + 1 * getPipelineMock('file.call')([credentialsId: 'keytab-id', variable: 'KEYTAB_FILE']) >> 'path/to/file' + 1 * getPipelineMock('withCredentials')(['path/to/file'], _ as Closure) + 1 * getPipelineMock('sh')([returnStdout: true, script: "klist -kt path/to/file |grep REDHAT.COM | awk -F' ' 'NR==1{print \$4}' "]) >> 'service-account' + 1 * getPipelineMock('sh')([returnStdout: true, script: "klist | grep -i 'Default principal' | awk -F':' 'NR==1{print \$2}' "]) >> ' service-account' + 1 * getPipelineMock("sh")('hello') + // kinit not being executed + 0 * getPipelineMock('sh')([returnStatus: true, script: "kinit service-account -kt path/to/file"]) + env['KERBEROS_PRINCIPAL'] == 'service-account' + noExceptionThrown() + } + + def "[util.groovy] runWithPythonVirtualEnv all params"() { + when: + def result = groovyScript.runWithPythonVirtualEnv('CMD', 'anyenv', true) + then: + 1 * getPipelineMock('sh')([returnStdout: true, script: """ +source ~/virtenvs/anyenv/bin/activate +CMD +"""]) >> 'output' + result == 'output' + } + + def "[util.groovy] runWithPythonVirtualEnv default"() { + when: + def result = groovyScript.runWithPythonVirtualEnv('CMD', 'anyenv') + then: + 1 * getPipelineMock('sh')([returnStdout: false, script: """ +source ~/virtenvs/anyenv/bin/activate +CMD +"""]) + } + + def "[util.groovy] displayDurationFromSeconds with seconds"() { + when: + def result = groovyScript.displayDurationFromSeconds(5) + then: + result == "5s" + } + + def "[util.groovy] displayDurationFromSeconds with minutes"() { + when: + def result = groovyScript.displayDurationFromSeconds(300) + then: + result == "5m0s" + } + + def "[util.groovy] displayDurationFromSeconds with minutes and seconds"() { + when: + def result = groovyScript.displayDurationFromSeconds(301) + then: + result == "5m1s" + } + + def "[util.groovy] displayDurationFromSeconds with hours"() { + when: + def result = groovyScript.displayDurationFromSeconds(3600) + then: + result == "1h0m0s" + } + + def "[util.groovy] displayDurationFromSeconds with hours, minutes and seconds"() { + when: + def result = groovyScript.displayDurationFromSeconds(3723) + then: + result == "1h2m3s" + } +} diff --git a/jenkins-pipeline-shared-libraries/vars/buildChain.groovy b/jenkins-pipeline-shared-libraries/vars/buildChain.groovy new file mode 100644 index 000000000..62ead57ea --- /dev/null +++ b/jenkins-pipeline-shared-libraries/vars/buildChain.groovy @@ -0,0 +1,15 @@ +/* +* It gets buildChain verion from composite action.yml file +*/ +def getBuildChainVersionFromCompositeActionFile(String actionFilePath = '.ci/actions/build-chain/action.yml', String usesContainingString = 'github-action-build-chain@') { + def actionObject = readYaml(file: actionFilePath) + + def uses = actionObject.runs.steps.uses + def action = uses != null ? uses.find({ it.contains(usesContainingString) }) : null + if (action == null) { + throw new RuntimeException("There's not steps with 'uses' for build-chain ${usesContainingString}") + } + + def buildChainScmRevision = action.substring(action.indexOf('@') + 1) + return "^${buildChainScmRevision}" +} diff --git a/jenkins-pipeline-shared-libraries/vars/cloud.groovy b/jenkins-pipeline-shared-libraries/vars/cloud.groovy new file mode 100644 index 000000000..e6dc8c331 --- /dev/null +++ b/jenkins-pipeline-shared-libraries/vars/cloud.groovy @@ -0,0 +1,331 @@ +// Quay.io:BEGIN // + +/* +* Make a quay.io image public if not already +*/ +void makeQuayImagePublic(String namespace, String repository, Map credentials = [ 'token': '', 'usernamePassword': '' ]) { + if (!isQuayImagePublic(namespace, repository, credentials) + && !setQuayImagePublic(namespace, repository, credentials)) { + error "Cannot set image quay.io/${namespace}/${repository} as visible" + } +} + +/* +* Checks whether a quay image is public +*/ +boolean isQuayImagePublic(String namespace, String repository, Map credentials = [ 'token': '', 'usernamePassword': '' ]) { + def output = 'false' + util.executeWithCredentialsMap(credentials) { + output = sh(returnStdout: true, script: "curl -H 'Authorization: Bearer ${QUAY_TOKEN}' -X GET https://quay.io/api/v1/repository/${namespace}/${repository} | jq '.is_public'").trim() + } + return output == 'true' +} + +/* +* Sets a Quay repository as public +* +* return false if any problem occurs +*/ +boolean setQuayImagePublic(String namespace, String repository, Map credentials = [ 'token': '', 'usernamePassword': '' ]) { + def output = 'false' + util.executeWithCredentialsMap(credentials) { + output = sh(returnStdout: true, script: "curl -H 'Content-Type: application/json' -H 'Authorization: Bearer ${QUAY_TOKEN}' -X POST --data '{\"visibility\": \"public\"}' https://quay.io/api/v1/repository/${namespace}/${repository}/changevisibility | jq '.success'").trim() + } + return output == 'true' +} + +/* +* Update image description on quay.io +* descriptionString = string content that will be the description of the image +*/ +void updateQuayImageDescription(String descriptionString, String namespace, String repository, Map credentials = [ 'token': '', 'usernamePassword': '' ]) { + util.executeWithCredentialsMap(credentials) { + def json = [ + description: descriptionString + ] + writeJSON(file: "description.json", json: json) + archiveArtifacts(artifacts: 'description.json') + sh(script: "curl -H 'Content-type: application/json' -H 'Authorization: Bearer ${QUAY_TOKEN}' -X PUT --data-binary '@description.json' https://quay.io/api/v1/repository/${namespace}/${repository}") + } +} + +// Quay.io:END // + +/* +* Login to given OpenShift API +*/ +void loginOpenShift(String openShiftAPI, String openShiftCredsId) { + withCredentials([usernamePassword(credentialsId: openShiftCredsId, usernameVariable: 'OC_USER', passwordVariable: 'OC_PWD')]) { + sh "oc login --username=${OC_USER} --password=${OC_PWD} --server=${openShiftAPI} --insecure-skip-tls-verify" + } +} + +/* +* Login to current OpenShift registry +* +* It considers that you are already authenticated to OpenShift +*/ +void loginOpenShiftRegistry(String containerEngine = 'docker', String containerEngineTlsOptions = '') { + // username can be anything. See https://docs.openshift.com/container-platform/4.4/registry/accessing-the-registry.html#registry-accessing-directly_accessing-the-registry + sh "set +x && ${containerEngine} login -u anything -p \$(oc whoami -t) ${containerEngineTlsOptions} ${getOpenShiftRegistryURL()}" +} + +/* +* Retrieve the OpenShift registry URL +* +* It considers that you are already authenticated to OpenShift +*/ +String getOpenShiftRegistryURL() { + return sh(returnStdout: true, script: "oc get routes -n openshift-image-registry | tail -1 | awk '{print \$2}'")?.trim() +} + +/* +* Login to a container registry +*/ +void loginContainerRegistry(String registry, String credsId, String containerEngine = 'docker', String containerEngineTlsOptions = '') { + withCredentials([usernamePassword(credentialsId: credsId, usernameVariable: 'REGISTRY_USER', passwordVariable: 'REGISTRY_PWD')]) { + sh "set +x && ${containerEngine} login -u ${REGISTRY_USER} -p ${REGISTRY_PWD} ${containerEngineTlsOptions} ${registry}" + } +} + +void pullImage(String imageTag, int retries = 3, String containerEngine = 'docker', String containerEngineTlsOptions = '') { + retry(retries) { + sh "${containerEngine} pull ${containerEngineTlsOptions} ${imageTag}" + } +} + +void pushImage(String imageTag, int retries = 3, String containerEngine = 'docker', String containerEngineTlsOptions = '') { + retry(retries) { + sh "${containerEngine} push ${containerEngineTlsOptions} ${imageTag}" + } +} + +void tagImage(String oldImageTag, String newImageTag, String containerEngine = 'docker') { + sh "${containerEngine} tag ${oldImageTag} ${newImageTag}" +} + +/* +* Get reduced tag, aka X.Y, from the given tag +*/ +String getReducedTag(String originalTag) { + try { + String[] versionSplit = originalTag.split("\\.") + return "${versionSplit[0]}.${versionSplit[1]}" + } catch (err) { + println "[ERROR] ${originalTag} cannot be reduced to the format X.Y" + throw err + } +} + +/* +* Cleanup all containers and images +*/ +void cleanContainersAndImages(String containerEngine = 'podman') { + println '[INFO] Cleaning up running containers and images. Any error here can be ignored' + sh(script: "${containerEngine } ps -a -q | tr '\\n' ','", returnStdout: true).trim().split(',').findAll { it != '' }.each { + sh "${containerEngine} rm -f ${it} || date" +} + sh(script: "${containerEngine } images -q | tr '\\n' ','", returnStdout: true).trim().split(',').findAll { it != '' }.each { + sh "${containerEngine} rmi -f ${it} || date" + } +} + +/* +* Start local docker registry +* +* Accessible on `localhost:${port}`. Default port is 5000. +*/ +String startLocalRegistry(int port = 5000) { + cleanLocalRegistry(port) + sh "docker run -d -p ${port}:5000 --restart=always --name registry-${port} registry:2" + sh 'docker ps' + return "localhost:${port}" +} + +/* +* Find an open local port. +*/ +int findFreePort() { + return Integer.valueOf(sh( script: """ echo \$(python -c 'import socket; s=socket.socket(); s.bind(("", 0)); print(s.getsockname()[1]); s.close()') """, returnStdout: true ).trim()) +} + +/* +* Clean local registry +*/ +void cleanLocalRegistry(int port = 5000) { + sh "docker rm -f registry-${port} || true" + sh 'docker ps' +} + +/* +* Squash a docker image +* +* If `replaceCurrentImage` is disabled, the `-squashed` suffix is added to the returned image name +*/ +String dockerSquashImage(String baseImage, String squashMessage = "${baseImage} squashed", boolean replaceCurrentImage = true) { + String squashedPlatformImage = replaceCurrentImage ? "${baseImage}" : "${baseImage}-squashed" + + // Squash images + def nbLayers = Integer.parseInt(sh(returnStdout: true, script: "docker history ${baseImage} | grep buildkit.dockerfile | wc -l").trim()) + nbLayers++ // Get the next layer not done by buildkit + echo "Got ${nbLayers} layers to squash" + // Use message option in docker-squash due to https://github.com/goldmann/docker-squash/issues/220 + def dockerSquashShellCmd = "docker-squash -v -m '${squashMessage}' -f ${nbLayers} -t ${squashedPlatformImage} ${baseImage}" + sh dockerSquashShellCmd + sh "docker push ${squashedPlatformImage}" + + return squashedPlatformImage +} + +/* +* Print some debugging for a specific image +*/ +void dockerDebugImage(String imageTag) { + sh 'docker images' + sh "docker history ${imageTag}" + sh "docker inspect ${imageTag}" +} + + +// Multiplatform build:BEGIN // + +/* +* Build an image for multiple platforms and create a manifest to gather under a same name +* +* You should have run `prepareForDockerMultiplatformBuild` method before executing this method +*/ +void dockerBuildMultiPlatformImages(String buildImageTag, List platforms, boolean squashImages = true, String squashMessage = "Squashed ${buildImageTag}", boolean debug = false, boolean outputToFile = false) { + // Build image locally in tgz file + List buildPlatformImages = platforms.collect { platform -> + String os_arch = platform.replaceAll('/', '-') + String platformImage = "${buildImageTag}-${os_arch}" + String finalPlatformImage = platformImage + + // Build + dockerBuildPlatformImage(platformImage, platform, outputToFile) + if (debug) { dockerDebugImage(platformImage) } + + if (squashImages) { + finalPlatformImage = dockerSquashImage(platformImage, squashMessage) + if (debug) { dockerDebugImage(platformImage) } + } + + return finalPlatformImage + } + + dockerCreateManifest(buildImageTag, buildPlatformImages) + if (debug) { dockerDebugImage(buildImageTag) } +} + +/* +* Build an image for a specific platform +* +* You should have run `prepareForDockerMultiplatformBuild` method before executing this method +*/ +void dockerBuildPlatformImage(String buildImageTag, String platform, boolean outputToFile = false) { + def logFileName = (buildImageTag + '-' + platform + '-build.log') + .replaceAll('/','_') + .replaceAll(':','_') + sh "docker buildx build --push --sbom=false --provenance=false --platform ${platform} -t ${buildImageTag} .${outputToFile ? ' 2> ' + "${WORKSPACE}/${logFileName}" : ''}" + sh "docker buildx imagetools inspect ${buildImageTag}" + sh "docker pull --platform ${platform} ${buildImageTag}" +} + +/* +* Create a multiplatform manifest based on the given images +*/ +void dockerCreateManifest(String buildImageTag, List manifestImages) { + sh "docker manifest rm ${buildImageTag} || true" + sh "docker manifest create ${buildImageTag} --insecure ${manifestImages.join(' ')}" + sh "docker manifest push ${buildImageTag}" +} + +/* +* Prepare the node for Docker multiplatform build +* +* Each element of the `mirrorRegistriesConfig` should contain: +* - name: Name of the registry to mirror +* - mirrors: List of mirrors for that registry, containing: +* - url: mirror url +* - insecure: whether the mirror is insecure +*/ +void prepareForDockerMultiplatformBuild(List insecureRegistries = [], List mirrorRegistriesConfig = [], boolean debug = false) { + cleanDockerMultiplatformBuild(debug) + + // For multiplatform build + sh 'docker run --rm --privileged --name binfmt docker.io/tonistiigi/binfmt --install all' + + if (debug) { debugDockerMultiplatformBuild() } + + String buildkitdtomlConfig = "debug = ${debug}\n" + + insecureRegistries.each { + buildkitdtomlConfig += "${getBuildkitRegistryConfigStr(it, true)}" + } + + mirrorRegistriesConfig.each { mirrorRegistryCfg -> + buildkitdtomlConfig += "[registry.\"${ mirrorRegistryCfg.name }\"]\n" + buildkitdtomlConfig += "mirrors = [${ mirrorRegistryCfg.mirrors.collect { "\"${it.url }\"" }.join(',')}]\n" + mirrorRegistryCfg.mirrors.each { mirror -> + buildkitdtomlConfig += "${getBuildkitRegistryConfigStr(mirror.url, mirror.insecure)}" + } + } + + writeFile(file: 'buildkitd.toml', text: buildkitdtomlConfig) + if (debug) { + sh 'cat buildkitd.toml' + } + + sh 'docker buildx create --name mybuilder --driver docker-container --driver-opt network=host --bootstrap --config ${WORKSPACE}/buildkitd.toml' + sh 'docker buildx use mybuilder' + + if (debug) { debugDockerMultiplatformBuild() } +} + +String getBuildkitRegistryConfigStr(String registryURL, boolean insecure) { + return """[registry."${registryURL}"] +http = ${insecure} +""" +} + +/* +* Return the mirror registry config for `docker.io` +* +* This checks for internal registry defined as env `DOCKER_REGISTRY_MIRROR`. +* Fallback to `mirror.gcr.io` is none defined. +*/ +Map getDockerIOMirrorRegistryConfig() { + return [ + name: 'docker.io', + mirrors: [ + [ + url : env.DOCKER_REGISTRY_MIRROR ?: 'mirror.gcr.io', + insecure: env.DOCKER_REGISTRY_MIRROR ? true : false, + ] + ], + ] +} + +/* +* Helpful commands to debug `docker buildx` preparation +*/ +void debugDockerMultiplatformBuild() { + sh 'docker context ls' + sh 'docker buildx inspect' + sh 'docker buildx ls' +} + +/* +* Clean the node from Docker multiplatform configuration +*/ +void cleanDockerMultiplatformBuild(boolean debug = false) { + sh 'docker buildx rm mybuilder || true' + sh 'docker rm -f binfmt || true' + if (debug) { debugDockerMultiplatformBuild() } +} + +// Multiplatform build:END // + +void skopeoCopyRegistryImages(String oldImageName, String newImageName, int retries = 3) { + sh "skopeo copy --retry-times ${retries} --tls-verify=false --all docker://${oldImageName} docker://${newImageName}" +} \ No newline at end of file diff --git a/jenkins-pipeline-shared-libraries/vars/githubscm.groovy b/jenkins-pipeline-shared-libraries/vars/githubscm.groovy new file mode 100644 index 000000000..e2874a78d --- /dev/null +++ b/jenkins-pipeline-shared-libraries/vars/githubscm.groovy @@ -0,0 +1,670 @@ +def resolveRepository(String repository, String author, String branches, boolean ignoreErrors, String credentialID = 'kie-ci') { + println "[INFO] Resolving Repository https://github.com/${author}/${repository}:${branches}. CredentialsID: ${credentialID}" + return [$class : 'GitSCM', + branches : [[name: branches]], + doGenerateSubmoduleConfigurations: false, + extensions : [[$class: 'CleanBeforeCheckout'], + [$class : 'SubmoduleOption', + disableSubmodules : false, + parentCredentials : true, + recursiveSubmodules: true, + reference : '', + trackingSubmodules : false], + [$class : 'RelativeTargetDirectory', + relativeTargetDir: './']], + submoduleCfg : [], + userRemoteConfigs : [[credentialsId: credentialID, url: "https://github.com/${author}/${repository}.git"]] + ] +} + +def checkoutIfExists(String repository, String author, String branches, String defaultAuthor, String defaultBranches, boolean mergeTarget = false, def credentials = ['token': 'kie-ci1-token', 'usernamePassword': 'kie-ci']) { + assert credentials['token'] + assert credentials['usernamePassword'] + def sourceAuthor = author + def sourceRepository = getForkedProjectName(defaultAuthor, repository, sourceAuthor, credentials['token']) ?: repository + // Checks source group and branch (for cases where the branch has been created in the author's forked project) + def repositoryScm = getRepositoryScm(sourceRepository, author, branches, credentials['usernamePassword']) + if (repositoryScm == null) { + // Checks target group and and source branch (for cases where the branch has been created in the target project itself + repositoryScm = getRepositoryScm(repository, defaultAuthor, branches, credentials['usernamePassword']) + sourceAuthor = repositoryScm ? defaultAuthor : author + } + if (repositoryScm != null && (!mergeTarget || hasPullRequest(defaultAuthor, repository, author, branches, credentials['token']))) { + if (mergeTarget) { + mergeSourceIntoTarget(sourceRepository, sourceAuthor, branches, repository, defaultAuthor, defaultBranches, credentials['usernamePassword']) + } else { + checkout repositoryScm + } + } else { + checkout(resolveRepository(repository, defaultAuthor, defaultBranches, false, credentials['usernamePassword'])) + } +} + +def getRepositoryScm(String repository, String author, String branches, String credentialId = 'kie-ci') { + def repositoryScm = resolveRepository(repository, author, branches, true, credentialId) + dir("githubscm-get-repository-${repository}") { + try { + checkout repositoryScm + } catch (Exception ex) { + println "[WARNING] Branches [${branches}] from repository ${repository} not found in ${author} organisation." + repositoryScm = null + } finally { + deleteDir() + } + } + return repositoryScm +} + +def mergeSourceIntoTarget(String sourceRepository, String sourceAuthor, String sourceBranches, String targetRepository, String targetAuthor, String targetBranches, String credentialId = 'kie-ci') { + println "[INFO] Merging source [${sourceAuthor}/${sourceRepository}:${sourceBranches}] into target [${targetAuthor}/${targetRepository}:${targetBranches}]..." + checkout(resolveRepository(targetRepository, targetAuthor, targetBranches, false, credentialId)) + setUserConfigFromCreds(credentialId) + def targetCommit = getCommit() + + try { + withCredentials([usernameColonPassword(credentialsId: credentialId, variable: 'kieCiUserPassword')]) { + sh "git pull https://${kieCiUserPassword}@github.com/${sourceAuthor}/${sourceRepository} ${sourceBranches}" + } + } catch (Exception e) { + println """ + ------------------------------------------------------------- + [ERROR] Can't merge source into Target. Please rebase PR branch. + ------------------------------------------------------------- + Source: git://github.com/${sourceAuthor}/${sourceRepository} ${sourceBranches} + Target: ${targetCommit} + ------------------------------------------------------------- + """ + throw e + } + def mergedCommit = getCommit() + + println """ + ------------------------------------------------------------- + [INFO] Source merged into Target + ------------------------------------------------------------- + Target: ${targetCommit} + Produced: ${mergedCommit} + ------------------------------------------------------------- + """ +} + +def createBranch(String branchName) { + try { + sh "git checkout -b ${branchName}" + } catch (Exception e) { + println "[ERROR] Can't create branch ${branchName} on repo." + throw e + } + println "[INFO] Created branch '${branchName}' on repo." +} + +boolean isBranchExist(String remote, String branch) { + sh "git fetch ${remote}" + return sh(returnStatus: true, script: "git rev-parse ${remote}/${branch}") == 0 +} + +/* +* Remove a branch from the remote +* +* You need correct rights to delete the remote tag +* +* Will fail if the branch does not exist +*/ +def removeRemoteBranch(String remote, String branch, String credentialsId = 'kie-ci') { + pushObject("--delete ${remote}", "${branch}", credentialsId) + println "[INFO] Deleted remote branch ${branch}." +} + +void removeLocalBranch(String branch) { + sh "git branch -D ${branch}" + println "[INFO] Deleted branch ${branch}." +} + +def commitChanges(String commitMessage, Closure preCommit) { + preCommit() + sh "git commit -m '${commitMessage}'" +} + +def commitChanges(String commitMessage, String filesToAdd = '-u') { + commitChanges(commitMessage, { sh "git add ${filesToAdd}" }) +} + +def addRemote(String remoteName, String remoteUrl) { + sh "git remote add ${remoteName} ${remoteUrl}" +} + +def squashCommits(String baseBranch, String newCommitMsg) { + String branchName = sh(returnStdout: true, script: 'git rev-parse --abbrev-ref HEAD').trim() + String mergeName = sh(returnStdout: true, script: "git merge-base ${baseBranch} ${branchName}").trim() + sh "git reset ${mergeName}" + sh 'git add -A' + sh "git commit -m \"${newCommitMsg}\"" +} + +def forkRepo(String credentialID = 'kie-ci') { + cleanHubAuth() + withCredentials([usernamePassword(credentialsId: credentialID, usernameVariable: 'GITHUB_USER', passwordVariable: 'GITHUB_TOKEN')]) { + setUserConfig("${GITHUB_USER}") + sh 'git config hub.protocol https' + sh 'hub fork --remote-name=origin' + sh 'git remote -v' + } +} + +def createPR(String pullRequestTitle, String pullRequestBody = '', String targetBranch = 'main', String credentialID = 'kie-ci') { + def pullRequestLink + try { + pullRequestLink = executeHub("hub pull-request -m '${pullRequestTitle}' -m '${pullRequestBody}' -b '${targetBranch}'", credentialID) + } catch (Exception e) { + println "[ERROR] Unable to create PR. Please make sure the targetBranch ${targetBranch} is correct." + throw e + } + println "Please see the created PR at: ${pullRequestLink}" + return pullRequestLink +} + +def createPrAsDraft(String pullRequestTitle, String pullRequestBody = '', String targetBranch = 'main', String credentialID = 'kie-ci') { + def pullRequestLink + try { + pullRequestLink = executeHub("hub pull-request -d -m '${pullRequestTitle}' -m '${pullRequestBody}' -b '${targetBranch}'", credentialID) + } catch (Exception e) { + println "[ERROR] Unable to create Draft PR. Please make sure the targetBranch ${targetBranch} is correct." + throw e + } + println "Please see the created Draft PR at: ${pullRequestLink}" + return pullRequestLink +} + +def createPRWithLabels(String pullRequestTitle, String pullRequestBody = '', String targetBranch = 'main', String[] labels, String credentialID = 'kie-ci') { + def pullRequestLink + try { + pullRequestLink = executeHub("hub pull-request -m '${pullRequestTitle }' -m '${pullRequestBody }' -b '${targetBranch }' -l ${labels.collect { it -> "'${it }'" }.join(',')}", credentialID) + } catch (Exception e) { + println "[ERROR] Unable to create PR. Please make sure the targetBranch ${targetBranch} is correct." + throw e + } + println "Please see the created PR at: ${pullRequestLink}" + return pullRequestLink +} + +def executeHub(String hubCommand, String credentialID = 'kie-ci') { + cleanHubAuth() + withCredentials([usernamePassword(credentialsId: credentialID, usernameVariable: 'GITHUB_USER', passwordVariable: 'GITHUB_TOKEN')]) { + setUserConfig("${GITHUB_USER}") + return sh(returnStdout: true, script: hubCommand).trim() + } +} + +def mergePR(String pullRequestLink, String credentialID = 'kie-ci') { + cleanHubAuth() + withCredentials([usernamePassword(credentialsId: credentialID, usernameVariable: 'GITHUB_USER', passwordVariable: 'GITHUB_TOKEN')]) { + try { + setUserConfig("${GITHUB_USER}") + sh "hub merge ${pullRequestLink}" + } catch (Exception e) { + println "[ERROR] Can't merge PR ${pullRequestLink} on repo." + throw e + } + println "[INFO] Merged PR '${pullRequestLink}' on repo." + } +} + +// Optional: Pass in env.BUILD_TAG as buildTag in pipeline script +// to trace back the build from which this tag came from. +def tagRepository(String tagName, String buildTag = '') { + def currentCommit = getCommit() + def tagMessageEnding = buildTag ? " in build \"${buildTag}\"." : '.' + def tagMessage = "Tagged by Jenkins${tagMessageEnding}" + sh "git tag -a '${tagName}' -m '${tagMessage}'" + println """ +------------------------------------------------------------- +[INFO] Tagged current repository +------------------------------------------------------------- +Commit: ${currentCommit} +Tagger: ${env.GIT_COMMITTER_NAME} (${env.GIT_COMMITTER_EMAIL}) +Tag: ${tagName} +Tag Message: ${tagMessage} +------------------------------------------------------------- +""" +} + +/* +* Push a tag to the remote +* +* You need correct rights to create the tag +*/ + +def pushRemoteTag(String remote, String tagName, String credentialsId = 'kie-ci') { + pushObject(remote, "--tags ${tagName}", credentialsId) + println "[INFO] Pushed remote tag ${tagName}." +} + +boolean isTagExist(String remote, String tagName) { + sh "git fetch ${remote} --tags" + return sh(returnStatus: true, script: "git rev-parse ${tagName}") == 0 +} + +void removeLocalTag(String tagName) { + sh "git tag -d ${tagName}" + println "[INFO] Deleted tag ${tagName}." +} + +/* +* Remove a tag from the remote +* +* You need correct rights to delete the remote tag +* +* Will fail if the tag does not exist +*/ + +def removeRemoteTag(String remote, String tagName, String credentialsId = 'kie-ci') { + pushObject("--delete ${remote}", "${tagName}", credentialsId) + println "[INFO] Deleted remote tag ${tagName}." +} + +/* +* Creates a new release on GitHub +*/ +void createRelease(String tagName, String buildBranch, String description = "Release ${tagName}", String credentialsId = 'kie-ci') { + withCredentials([usernamePassword(credentialsId: "${credentialsId}", usernameVariable: 'GH_USER', passwordVariable: 'GH_TOKEN')]) { + sh "gh release create ${tagName} --target ${buildBranch} --title ${tagName} --notes \"${description}\"" + } +} + +/* +* Creates a new release on GitHub with release notes +*/ +void createReleaseWithReleaseNotes(String tagName, String buildBranch, String releaseNotes = 'Release Notes', String credentialsId = 'kie-ci') { + withCredentials([usernamePassword(credentialsId: "${credentialsId}", usernameVariable: 'GH_USER', passwordVariable: 'GH_TOKEN')]) { + sh "gh release create ${tagName} --target ${buildBranch} --title ${tagName} -F ${releaseNotes}" + } +} + +/* +* Creates a new release on GitHub with GH generated release notes +*/ +void createReleaseWithGeneratedReleaseNotes(String tagName, String buildBranch, String previousTag, String credentialsId = 'kie-ci') { + withCredentials([usernamePassword(credentialsId: "${credentialsId}", usernameVariable: 'GH_USER', passwordVariable: 'GH_TOKEN')]) { + sh "gh release create ${tagName} --target ${buildBranch} --title ${tagName} --generate-notes --notes-start-tag ${previousTag}" + } +} + +/* +* Removes a release on GitHub +*/ +void deleteRelease(String tagName, String credentialsId = 'kie-ci') { + withCredentials([usernamePassword(credentialsId: "${credentialsId}", usernameVariable: 'GH_USER', passwordVariable: 'GH_TOKEN')]) { + sh "gh release delete ${tagName} -y" + } +} + +/* +* Removes a release and its tag on GitHub +*/ +void deleteReleaseAndTag(String tagName, String credentialsId = 'kie-ci') { + withCredentials([usernamePassword(credentialsId: "${credentialsId}", usernameVariable: 'GH_USER', passwordVariable: 'GH_TOKEN')]) { + sh "gh release delete --cleanup-tag ${tagName} -y" + } +} + +/* +* Checks whether a release exists on GitHub +*/ +boolean isReleaseExist(String tagName, String credentialsId = 'kie-ci') { + withCredentials([usernamePassword(credentialsId: "${credentialsId}", usernameVariable: 'GH_USER', passwordVariable: 'GH_TOKEN')]) { + // checks if the release is already existing + exist = sh(script: "gh release view ${tagName}", returnStatus: true) == 0 + } + return exist +} + +/* +* Tag Local and remote repository +* +* You need correct rights to create or delete (in case of override) the tag +*/ + +def tagLocalAndRemoteRepository(String remote, String tagName, String credentialsId = 'kie-ci', String buildTag = '', boolean override = false) { + if (override && isTagExist(remote, tagName)) { + println "[INFO] Tag ${tagName} exists... Overriding it." + removeLocalTag(tagName) + removeRemoteTag(remote, tagName, credentialsId) + } + + tagRepository(tagName, buildTag) + pushRemoteTag(remote, tagName, credentialsId) +} + +def pushObject(String remote, String object, String credentialsId = 'kie-ci') { + try { + withCredentials([usernamePassword(credentialsId: "${credentialsId}", usernameVariable: 'GITHUB_USER', passwordVariable: 'GITHUB_TOKEN')]) { + setUserConfig("${GITHUB_USER}") + sh("git config --local credential.helper \"!f() { echo username=\\$GITHUB_USER; echo password=\\$GITHUB_TOKEN; }; f\"") + sh("git push ${remote} ${object}") + } + } catch (Exception e) { + println "[ERROR] Couldn't push object '${object}' to ${remote}." + throw e + } + println "[INFO] Pushed object '${object}' to ${remote}." +} + +def setUserConfig(String username, String domain = 'jenkins.kie.apache.org', boolean global=false) { + sh "git config ${(global?'--global ':'')}user.email ${username}@${domain}" + sh "git config ${(global?'--global ':'')}user.name ${username}" +} + +def setUserConfigFromCreds(String credentialsId = 'kie-ci') { + withCredentials([usernamePassword(credentialsId: "${credentialsId}", usernameVariable: 'GITHUB_USER', passwordVariable: 'GITHUB_TOKEN')]) { + setUserConfig("${GITHUB_USER}") + } +} + +def getCommit() { + return sh(returnStdout: true, script: 'git log --oneline -1').trim() +} + +def getCommitHash() { + return sh(returnStdout: true, script: 'git rev-parse HEAD').trim() +} + +String getTagCommitHash(String tagName) { + return sh(returnStdout: true, script: "git rev-list -n 1 ${tagName}").trim() +} + +/* +* Retrieve the Git repository URL from current dir +*/ +def getGitRepositoryURL() { + return sh(returnStdout: true, script: 'git config --get remote.origin.url | head -n 1').trim() +} + +def getGitRepositoryName() { + return sh(returnStdout: true, script: "basename ${getGitRepositoryURL()} | sed 's|\\.git||g'").trim() +} + +def getGitRepositoryAuthor() { + return sh(returnStdout: true, script: "echo ${getGitRepositoryURL()} | sed 's|/${getGitRepositoryName()}.*||g' | sed 's|.*github.com.\\?||g'").trim() +} + +def getBranch() { + return sh(returnStdout: true, script: 'git branch --all --contains HEAD').trim() +} + +def getRemoteInfo(String remoteName, String configName) { + return sh(returnStdout: true, script: "git config --get remote.${remoteName}.${configName}").trim() +} + +def hasPullRequest(String group, String repository, String author, String branch, String credentialsId = 'kie-ci1-token') { + return hasForkPullRequest(group, repository, author, branch, credentialsId) || hasOriginPullRequest(group, repository, branch, credentialsId) +} + +def hasOriginPullRequest(String group, String repository, String branch, String credentialsId = 'kie-ci1-token') { + return hasForkPullRequest(group, repository, group, branch, credentialsId) +} + +def hasForkPullRequest(String group, String repository, String author, String branch, String credentialsId = 'kie-ci1-token') { + def result = false + withCredentials([string(credentialsId: credentialsId, variable: 'OAUTHTOKEN')]) { + def curlResult = sh(returnStdout: true, script: "curl --globoff -H \"Authorization: token ${OAUTHTOKEN}\" 'https://api.github.com/repos/${group}/${repository}/pulls?head=${author}:${branch}&state=open'")?.trim() + if (curlResult) { + def pullRequestJsonObject = readJSON text: curlResult + result = pullRequestJsonObject.size() > 0 + } + } + println "[INFO] has pull request for ${group}/${repository}:${author}:${branch} -> ${result}" + return result +} + +def getForkedProjectName(String group, String repository, String owner, String credentialsId = 'kie-ci1-token', int page = 1, int perPage = 100, replays = 3) { + if (group == owner) { + return repository + } + def result = null + withCredentials([string(credentialsId: credentialsId, variable: 'OAUTHTOKEN')]) { + def forkedProjects = null + + def curlResult = sh(returnStdout: true, script: "curl -H \"Authorization: token ${OAUTHTOKEN}\" 'https://api.github.com/repos/${group}/${repository}/forks?per_page=${perPage}&page=${page}'")?.trim() + if (curlResult) { + forkedProjects = readJSON text: curlResult + } + if (result == null && forkedProjects != null && forkedProjects.size() > 0) { + try { + def forkedProject = forkedProjects.find { it.owner.login == owner } + result = forkedProject ? forkedProject.name : getForkedProjectName(group, repository, owner, credentialsId, ++page, perPage) + } catch (MissingPropertyException e) { + if (--replays <= 0) { + throw new Exception("Error getting forked project name for ${group}/${repository}/forks?per_page=${perPage}&page=${page}. Communication error, please relaunch job.") + } else { + println("[ERROR] Getting forked project name for ${group}/${repository}/forks?per_page=${perPage}&page=${page}. Replaying... [${replays}]") + result = getForkedProjectName(group, repository, owner, credentialsId, page, perPage, replays) + } + } + } + } + return result +} + +def cleanHubAuth() { + sh 'rm -rf ~/.config/hub' +} + +def cleanWorkingTree() { + sh 'git clean -xdf' +} + +/** + * Uses `find` command to stage all files matching the pattern and which are not in .gitignore + */ +def findAndStageNotIgnoredFiles(String findNamePattern) { + // based on https://stackoverflow.com/a/59888964/8811872 + sh """ + find . -type f -name '${findNamePattern}' > found_files.txt + files_to_add="" + while IFS= read -r file; do + if ! git check-ignore -q "\$file"; then + files_to_add="\$files_to_add \$file" + fi + done < found_files.txt + rm found_files.txt + if [ ! -z "\$files_to_add" ]; then + git add \$files_to_add + fi + git status + """ +} + +boolean isThereAnyChanges() { + return sh(script: 'git status --porcelain', returnStdout: true).trim() != '' +} + +def updateReleaseBody(String tagName, String credsId = 'kie-ci') { + String releaseNotesFile = 'release_notes' + withCredentials([usernamePassword(credentialsId: credsId, usernameVariable: 'GH_USER', passwordVariable: 'GH_TOKEN')]) { + sh "gh release view ${tagName} --json body --jq .body > ${releaseNotesFile}" + + sh """ + #!/bin/bash + sed -i -r 's|\\[((incubator-)?kie-issues[-#][0-9]*)\\](.*)|\\1\\3|g' ${releaseNotesFile} + sed -i -r 's|(incubator-)?kie-issues[-#]([0-9]*)(.*)|\\[kie-issues#\\2\\](https\\://github\\.com/apache/incubator-kie-issues/issues/\\2)\\3|g' ${releaseNotesFile} + """ + sh "gh release edit ${tagName} -F ${releaseNotesFile}" + } +} + +/* +* DEPRECATED +* +* Should use `getLatestTag` method instead which is more flexible +*/ +@Deprecated +def getPreviousTag(String ignoreTag) { + String latestTag = sh(returnStdout: true, script: 'git tag --sort=-taggerdate | head -n 1').trim() + if (latestTag == ignoreTag) { + latestTag = sh(returnStdout: true, script: 'git tag --sort=-taggerdate | head -n 2 | tail -n 1').trim() + } + echo "Got latestTag = ${latestTag}" + return latestTag +} + +def getLatestTag(String startsWith = '', String endsWith = '', List ignoreTags = []) { + String cmd = 'git tag --sort=-taggerdate' + cmd += ignoreTags.collect { tag -> " | grep -v '${tag}'" }.join('') + if (startsWith) { + cmd += " | grep '^${startsWith}'" + } + if (endsWith) { + cmd += " | grep '${endsWith}\$'" + } + cmd += ' | head -n 1' + return sh(returnStdout: true, script: cmd).trim() +} + +def getPreviousTagFromVersion(String version, String startsWith = '', String endsWith = '', List filterOutGrep = [], boolean debug = false) { + if (debug) { println "getPreviousTagFromVersion for version = ${version}" } + String cmd = 'git tag --sort=-committerdate' + if (endsWith) { + cmd += " | grep '${endsWith}\$'" + } + if (filterOutGrep) { + cmd += " ${filterOutGrep.collect { "| grep -v '${it}'" }.join(' ')}" + } + Integer[] versionSplit = util.parseVersion(version) + + Closure searchTag = { tagToSearch, reverse -> + if (debug) { println "Searching tag ${tagToSearch}" } + String foundTag = sh(returnStdout: true, script: "${cmd} | grep '${tagToSearch}' | sort -V${reverse ? ' -r' : ''}")?.trim() + if (debug) { println "Found tag ${foundTag}" } + return foundTag ? foundTag.split('\n')[0] : '' + } + + // Previous micro search + int micro = versionSplit[2] + while (micro-- > 0) { + String foundTag = searchTag("^${startsWith}${versionSplit[0]}.${versionSplit[1]}.${micro}", true) + if (foundTag) { return foundTag } + } + + // Previous minor search + int minor = versionSplit[1] + while (minor-- > 0) { + String foundTag = searchTag("^${startsWith}${versionSplit[0]}.${minor}.", false) + if (foundTag) { return foundTag } + } + + // Previous major search (different looking for) + int major = versionSplit[0] + while (major-- > 0) { + String foundTag = searchTag("^${startsWith}${major}.", true) + if (foundTag) { return foundTag } + } + + return '' +} + +/* +* Store in env the commit info needed to update the commit status +*/ +void prepareCommitStatusInformation(String repository, String author, String branch, String credentialsId = 'kie-ci') { + dir("githubscm-prepare-commit-${repository}") { + try { + checkout(resolveRepository(repository, author, branch, false, credentialsId)) + setCommitStatusRepoURLEnv(repository) + setCommitStatusShaEnv(repository) + } finally { + deleteDir() + } + } +} + +/* +* Store in env the commit info needed to update the commit status of a PR +*/ +void prepareCommitStatusInformationForPullRequest(String repository, String author, String branch, String targetAuthor, String credentialsId = 'kie-ci') { + prepareCommitStatusInformation(repository, author, branch, credentialsId) + setCommitStatusRepoURLEnv(repository, "https://github.com/${targetAuthor}/${repository}") +} + +String getCommitStatusRepoURLEnv(String repository) { + return env."${repository.toUpperCase()}_COMMIT_STATUS_REPO_URL" +} + +void setCommitStatusRepoURLEnv(String repository, String url = '') { + env."${repository.toUpperCase()}_COMMIT_STATUS_REPO_URL" = url ?: getGitRepositoryURL() +} + +String getCommitStatusShaEnv(String repository) { + return env."${repository.toUpperCase()}_COMMIT_STATUS_SHA" +} + +void setCommitStatusShaEnv(String repository, String sha = '') { + env."${repository.toUpperCase()}_COMMIT_STATUS_SHA" = sha ?: getCommitHash() +} + +/* +* UpdateGithubCommitStatus for the given repository +* +* (Run `prepareCommitStatusInformation` before if you need to set specific commit info before updating. Useful when working with `checkoutIfExists`) +* +* @params checkName Name of the check to appear into GH check status page +* @params state State of the check: 'PENDING' / 'SUCCESS' / 'ERROR' / 'FAILURE' +* @params message Message to display next to the check +*/ +def updateGithubCommitStatus(String checkName, String state, String message, String repository = '') { + println "[INFO] Update commit status for check ${checkName}: state = ${state} and message = ${message}" + + if (!repository) { + println '[INFO] No given repository... Trying to guess it from current directory' + repository = getGitRepositoryName() + } + println "[DEBUG] repository name = ${repository}" + + if (!getCommitStatusRepoURLEnv(repository) || !getCommitStatusShaEnv(repository)) { + println '[INFO] Commit status info are not stored, guessing from current repository' + setCommitStatusRepoURLEnv(repository) + setCommitStatusShaEnv(repository) + } + println "[DEBUG] repo url = ${getCommitStatusRepoURLEnv(repository)}" + println "[DEBUG] commit sha = ${getCommitStatusShaEnv(repository)}" + + try { + step([ + $class: 'GitHubCommitStatusSetter', + commitShaSource: [$class: 'ManuallyEnteredShaSource', sha: getCommitStatusShaEnv(repository)], + contextSource: [$class: 'ManuallyEnteredCommitContextSource', context: checkName], + reposSource: [$class: 'ManuallyEnteredRepositorySource', url: getCommitStatusRepoURLEnv(repository)], + statusResultSource: [ $class: 'ConditionalStatusResultSource', results: [[$class: 'AnyBuildResult', message: message, state: state]] ], + ]) + } catch(err) { + println "Error updating commit status: ${err}" + } +} + +def updateGithubCommitStatusFromBuildResult(String checkName) { + println "[INFO] Update commit status for check ${checkName} from build result" + String buildResult = currentBuild.currentResult + println "[DEBUG] Got build result ${buildResult}" + + def testResults = util.retrieveTestResults() + println "[DEBUG] Got test results ${testResults}" + String testsInfo = testResults ? "${testResults.passCount + testResults.skipCount + testResults.failCount} tests run, ${testResults.failCount} failed, ${testResults.skipCount} skipped." : 'No test results found.' + + int jobDuration = util.getJobDurationInSeconds() + println "[DEBUG] Got job duration ${jobDuration} seconds" + String timeInfo = util.displayDurationFromSeconds(jobDuration) + + switch (buildResult) { + case 'SUCCESS': + updateGithubCommitStatus(checkName, 'SUCCESS', "(${timeInfo}) Check is successful. ${testsInfo}".trim()) + break + case 'UNSTABLE': + updateGithubCommitStatus(checkName, 'FAILURE', "(${timeInfo}) Test failures occurred. ${testsInfo}".trim()) + break + case 'ABORTED': + updateGithubCommitStatus(checkName, 'ERROR', "(${timeInfo}) Job aborted. ${testsInfo}".trim()) + break + default: + updateGithubCommitStatus(checkName, 'ERROR', "(${timeInfo}) Issue in pipeline. ${testsInfo}".trim()) + break + } +} diff --git a/jenkins-pipeline-shared-libraries/vars/mailer.groovy b/jenkins-pipeline-shared-libraries/vars/mailer.groovy new file mode 100644 index 000000000..9d1133b75 --- /dev/null +++ b/jenkins-pipeline-shared-libraries/vars/mailer.groovy @@ -0,0 +1,68 @@ +def sendEmailFailure() { + def branch = env.CHANGE_BRANCH ?: env.ghprbSourceBranch + emailext ( + subject: "Build $branch failed", + body: "Build $branch failed! For more information see $BUILD_URL", + recipientProviders: [[$class: 'DevelopersRecipientProvider'], [$class: 'RequesterRecipientProvider']]) +} + +def sendEmail_failedPR(String additionalSubject = null ) { + emailext( + subject: "${additionalSubject?.trim() || additionalSubject?.trim() != null ? additionalSubject?.trim() : 'PR'} #$ghprbPullId of $ghprbGhRepository: $ghprbPullTitle failed", + body: """ + Pull request #$ghprbPullId of $ghprbGhRepository: $ghprbPullTitle FAILED + Build log: ${BUILD_URL}consoleText + Failed tests \${TEST_COUNTS,var=\"fail\"}: ${BUILD_URL}testReport + (IMPORTANT: For visiting the links you need to have access to Red Hat VPN. In case you don\'t have access to RedHat VPN please download and decompress attached file.) + """, + attachmentsPattern: 'error.log.gz', + recipientProviders: [[$class: 'DevelopersRecipientProvider'], [$class: 'RequesterRecipientProvider']]) +} + +def sendEmail_unstablePR(String additionalSubject = null ) { + emailext( + subject: "${additionalSubject?.trim() || additionalSubject?.trim() != null ? additionalSubject?.trim() : 'PR'} #$ghprbPullId of $ghprbGhRepository: $ghprbPullTitle was unstable", + body: """ + Pull request #$ghprbPullId of $ghprbGhRepository: $ghprbPullTitle was UNSTABLE + Build log: ${BUILD_URL}consoleText + Failed tests \${TEST_COUNTS,var=\"fail\"}: ${BUILD_URL}testReport + (IMPORTANT: For visiting the links you need to have access to Red Hat VPN) + *********************************************************************************************************************************************************** + \${FAILED_TESTS} + """, + recipientProviders: [[$class: 'DevelopersRecipientProvider'], [$class: 'RequesterRecipientProvider']]) +} + +def sendEmail_fixedPR(String additionalSubject = null ) { + emailext( + subject: "${additionalSubject?.trim() || additionalSubject?.trim() != null ? additionalSubject?.trim() : 'PR'} #$ghprbPullId of $ghprbGhRepository: $ghprbPullTitle is fixed and was SUCCESSFUL", + body: '', + recipientProviders: [[$class: 'DevelopersRecipientProvider'], [$class: 'RequesterRecipientProvider']]) +} + +def sendEmail_abortedPR(String additionalSubject = null ) { + emailext( + subject: "${additionalSubject?.trim() || additionalSubject?.trim() != null ? additionalSubject?.trim() : 'PR'} #$ghprbPullId of $ghprbGhRepository: $ghprbPullTitle was ABORTED", + body: '', + recipientProviders: [[$class: 'DevelopersRecipientProvider'], [$class: 'RequesterRecipientProvider']]) +} + +def buildLogScriptPR () { + dir("$WORKSPACE") { + sh 'touch trace.sh' + sh 'chmod 755 trace.sh' + sh 'echo "wget --no-check-certificate ${BUILD_URL}consoleText" >> trace.sh' + sh 'echo "tail -n 750 consoleText >> error.log" >> trace.sh' + sh 'echo "gzip error.log" >> trace.sh' + } +} + +void sendMarkdownTestSummaryNotification(String jobId, String subject, List recipients, String additionalInfo = '', String buildUrl = "${BUILD_URL}") { + emailext subject: (jobId ? "${subject} - ${jobId}" : subject), + to: recipients.join(','), + body: util.getMarkdownTestSummary('', additionalInfo, buildUrl) +} + +void sendMarkdownTestSummaryNotification(String subject, List recipients, String additionalInfo = '', String buildUrl = "${BUILD_URL}") { + sendMarkdownTestSummaryNotification('', subject, recipients, additionalInfo, buildUrl) +} diff --git a/jenkins-pipeline-shared-libraries/vars/maven.groovy b/jenkins-pipeline-shared-libraries/vars/maven.groovy new file mode 100644 index 000000000..1ac1f77de --- /dev/null +++ b/jenkins-pipeline-shared-libraries/vars/maven.groovy @@ -0,0 +1,196 @@ +import java.util.Properties +import org.kie.jenkins.MavenCommand + +def runMaven(String goals, List options = [], Properties properties = null, String logFileName = null) { + new MavenCommand(this) + .withOptions(options) + .withProperties(properties) + .withLogFileName(logFileName) + .run(goals) +} + +def runMaven(String goals, boolean skipTests, List options = [], String logFileName = null) { + new MavenCommand(this) + .withOptions(options) + .skipTests(skipTests) + .withLogFileName(logFileName) + .run(goals) +} + +def runMavenWithSettings(String settingsXmlId, String goals, Properties properties, String logFileName = null) { + configFileProvider([configFile(fileId: settingsXmlId, variable: 'MAVEN_SETTINGS_XML')]) { + new MavenCommand(this, ['-fae']) + .withSettingsXmlFile(MAVEN_SETTINGS_XML) + .withProperties(properties) + .withLogFileName(logFileName) + .run(goals) + } +} + +def runMavenWithSettings(String settingsXmlId, String goals, boolean skipTests, String logFileName = null) { + configFileProvider([configFile(fileId: settingsXmlId, variable: 'MAVEN_SETTINGS_XML')]) { + new MavenCommand(this, ['-fae']) + .withSettingsXmlFile(MAVEN_SETTINGS_XML) + .skipTests(skipTests) + .withLogFileName(logFileName) + .run(goals) + } +} + +/** + * + * @param settingsXmlId settings.xml file + * @param goals maven gals + * @param sonarCloudId Jenkins token for SonarCloud* + */ +def runMavenWithSettingsSonar(String settingsXmlId, String goals, String sonarCloudId, String logFileName = null) { + configFileProvider([configFile(fileId: settingsXmlId, variable: 'MAVEN_SETTINGS_XML')]) { + withCredentials([string(credentialsId: sonarCloudId, variable: 'TOKEN')]) { + new MavenCommand(this) + .withSettingsXmlFile(MAVEN_SETTINGS_XML) + .withProperty('sonar.login', "${TOKEN}") + .withLogFileName(logFileName) + .run(goals) + } + } +} + +def mvnVersionsSet(String newVersion, boolean allowSnapshots = false) { + mvnVersionsSet(new MavenCommand(this), newVersion, allowSnapshots) +} + +def mvnVersionsSet(MavenCommand mvnCmd, String newVersion, boolean allowSnapshots = false) { + mvnCmd.clone() + .withOptions(['-N', '-e']) + .withProperty('full') + .withProperty('newVersion', newVersion) + .withProperty('allowSnapshots', allowSnapshots) + .withProperty('generateBackupPoms', false) + .run('versions:set') +} + +def mvnVersionsUpdateParent(String newVersion, boolean allowSnapshots = false) { + mvnVersionsUpdateParent(new MavenCommand(this), newVersion, allowSnapshots) +} + +def mvnVersionsUpdateParent(MavenCommand mvnCmd, String newVersion, boolean allowSnapshots = false) { + mvnCmd.clone() + .withOptions(['-N', '-e']) + .withProperty('full') + .withProperty('parentVersion', "[${newVersion}]") + .withProperty('allowSnapshots', allowSnapshots) + .withProperty('generateBackupPoms', false) + .run('versions:update-parent') +} + +def mvnVersionsUpdateChildModules(boolean allowSnapshots = false) { + mvnVersionsUpdateChildModules(new MavenCommand(this), allowSnapshots) +} + +def mvnVersionsUpdateChildModules(MavenCommand mvnCmd, boolean allowSnapshots = false) { + mvnCmd.clone() + .withOptions(['-N', '-e']) + .withProperty('full') + .withProperty('allowSnapshots', allowSnapshots) + .withProperty('generateBackupPoms', false) + .run('versions:update-child-modules') +} + +def mvnVersionsUpdateParentAndChildModules(String newVersion, boolean allowSnapshots = false) { + mvnVersionsUpdateParentAndChildModules(new MavenCommand(this), newVersion, allowSnapshots) +} + +def mvnVersionsUpdateParentAndChildModules(MavenCommand mvnCmd, String newVersion, boolean allowSnapshots = false) { + mvnVersionsUpdateParent(mvnCmd, newVersion, allowSnapshots) + mvnVersionsUpdateChildModules(mvnCmd, allowSnapshots) +} + +def mvnGetVersionProperty(String property, String pomFile = 'pom.xml') { + mvnGetVersionProperty(new MavenCommand(this), property, pomFile) +} + +def mvnGetVersionProperty(MavenCommand mvnCmd, String property, String pomFile = 'pom.xml') { + mvnCmd.clone() + .withOptions(['-q', '-f', "${pomFile}"]) + .withProperty('expression', property) + .withProperty('forceStdout') + .returnOutput() + .run('help:evaluate') + .trim() +} + +def mvnSetVersionProperty(String property, String newVersion) { + mvnSetVersionProperty(new MavenCommand(this), property, newVersion) +} + +def mvnSetVersionProperty(MavenCommand mvnCmd, String property, String newVersion) { + mvnCmd.clone() + .withOptions(['-e']) + .withProperty('property', property) + .withProperty('newVersion', newVersion) + .withProperty('allowSnapshots', true) + .withProperty('generateBackupPoms', false) + .run('versions:set-property') +} + +def mvnCompareDependencies(String remotePom, String project = '', boolean updateDependencies = false, boolean updatePropertyVersions = false) { + mvnCompareDependencies(new MavenCommand(this), remotePom, project, updateDependencies, updatePropertyVersions) +} + +def mvnCompareDependencies(MavenCommand mvnCmd, String remotePom, String project = '', boolean updateDependencies = false, boolean updatePropertyVersions=false) { + def newMvnCmd = mvnCmd.clone() + .withProperty('remotePom', remotePom) + .withProperty('updatePropertyVersions', updatePropertyVersions) + .withProperty('updateDependencies', updateDependencies) + .withProperty('generateBackupPoms', false) + + if(project) { + newMvnCmd.withOptions(["-pl ${project}"]) + } + + newMvnCmd.run('versions:compare-dependencies') +} + +def uploadLocalArtifacts(String mvnUploadCredsId, String artifactDir, String repoUrl) { + def zipFileName = 'artifacts' + withCredentials([usernameColonPassword(credentialsId: mvnUploadCredsId, variable: 'kieUnpack')]) { + dir(artifactDir) { + sh "zip -r ${zipFileName} ." + sh "curl --silent --upload-file ${zipFileName}.zip -u ${kieUnpack} -v ${repoUrl}" + } + } +} + +def getLatestArtifactVersionFromRepository(String repositoryUrl, String groupId, String artifactId) { + return getMavenMetadata(repositoryUrl, groupId, artifactId).versioning?.latest?.text() +} + +def getLatestArtifactVersionPrefixFromRepository(String repositoryUrl, String groupId, String artifactId, String versionPrefix) { + return getMavenMetadata(repositoryUrl, groupId, artifactId).versioning?.versions?.childNodes().collect{ it.text() }.findAll{ it.startsWith(versionPrefix) }.max() +} + +def getMavenMetadata(String repositoryUrl, String groupId, String artifactId) { + def groupIdArtifactId = "${groupId.replaceAll("\\.", "/")}/${artifactId}" + return new XmlSlurper().parse("${repositoryUrl}/${groupIdArtifactId}/maven-metadata.xml") +} + +String getProjectPomFromBuildCmd(String buildCmd) { + def pom = "pom.xml" + def fileOption = "-f" + + def projectPom = "pom.xml" + regexF = "-f[ =]" + regexFile = "--file[ =]" + if (buildCmd =~ regexF || buildCmd =~ regexFile) { + projectPom = buildCmd.substring(buildCmd.indexOf(fileOption), buildCmd.indexOf(pom) + pom.length()) + projectPom = projectPom.split("=| ")[1] + } + return projectPom; +} + +/* +* Clean Maven repository on the node +*/ +void cleanRepository() { + sh 'rm -rf $HOME/.m2/repository' +} \ No newline at end of file diff --git a/jenkins-pipeline-shared-libraries/vars/pullrequest.groovy b/jenkins-pipeline-shared-libraries/vars/pullrequest.groovy new file mode 100644 index 000000000..ef2b111ce --- /dev/null +++ b/jenkins-pipeline-shared-libraries/vars/pullrequest.groovy @@ -0,0 +1,33 @@ +/** +* This method add a comment to current PR (for either ghprb or Github Branch Source plugin) +*/ +void postComment(String commentText, String githubTokenCredsId = "kie-ci3-token") { + if (!CHANGE_ID && !ghprbPullId) { + error "Pull Request Id variable (ghprbPullId or CHANGE_ID) is not set. Are you sure you are running with Github Branch Source plugin or ghprb plugin?" + } + def changeId = CHANGE_ID ?: ghprbPullId + def changeRepository = CHANGE_ID ? getAuthorAndRepoForPr() : ghprbGhRepository + String filename = "${util.generateHash(10)}.build.summary" + def jsonComment = [ + body : commentText + ] + writeJSON(json: jsonComment, file: filename) + sh "cat ${filename}" + withCredentials([string(credentialsId: githubTokenCredsId, variable: 'GITHUB_TOKEN')]) { + sh "curl -s -H \"Authorization: token ${GITHUB_TOKEN}\" -X POST -d '@${filename}' \"https://api.github.com/repos/${changeRepository}/issues/${changeId}/comments\"" + } + sh "rm ${filename}" +} + +String getAuthorAndRepoForPr() { + if (!env.CHANGE_FORK && !env.CHANGE_URL) { + error "CHANGE_FORK neither CHANGE_URL variables are set. Are you sure you're running with Github Branch Source plugin?" + } + if (env.CHANGE_FORK) { + return env.CHANGE_FORK + } + String fullUrl = env.CHANGE_URL + String urlWithoutProtocol = fullUrl.split('://')[1] + String path = urlWithoutProtocol.substring(urlWithoutProtocol.indexOf('/')) + return path.substring(1, path.indexOf('/pull/')) +} \ No newline at end of file diff --git a/jenkins-pipeline-shared-libraries/vars/util.groovy b/jenkins-pipeline-shared-libraries/vars/util.groovy new file mode 100644 index 000000000..30e00587f --- /dev/null +++ b/jenkins-pipeline-shared-libraries/vars/util.groovy @@ -0,0 +1,549 @@ +/** + * + * @param projectUrl the github project url + */ +def getProject(String projectUrl) { + return (projectUrl =~ /((git|ssh|http(s)?)|(git@[\w\.]+))(:(\/\/)?(github.com\/))([\w\.@\:\/\-~]+)(\.git)(\/)?/)[0][8] +} + +/** + * + * @param projectUrl the github project url + */ +def getGroup(String projectUrl) { + return getProjectGroupName(getProject(projectUrl))[0] +} + +/** + * Returns an array containing group and name + * + * @param project the project + * @param defaultGroup the default project group. Optional. + */ +def getProjectGroupName(String project, String defaultGroup = "apache") { + def projectNameGroup = project.split("\\/") + def group = projectNameGroup.size() > 1 ? projectNameGroup[0] : defaultGroup + def name = projectNameGroup.size() > 1 ? projectNameGroup[1] : project + return [group, name] +} + +/** + * Returns the path to the project dir + * @param projectGroupName + * @return + */ +def getProjectDirPath(String project, String defaultGroup = "apache") { + def projectGroupName = getProjectGroupName(project, defaultGroup) + return "${env.WORKSPACE}/${projectGroupName[0]}_${projectGroupName[1]}" +} + +/** + * + * Stores git information into an env variable to be retrievable at any point of the pipeline + * + * @param projectName to store commit + */ +def storeGitInformation(String projectName) { + def gitInformationReport = env.GIT_INFORMATION_REPORT ? "${env.GIT_INFORMATION_REPORT}; " : "" + gitInformationReport += "${projectName}=${githubscm.getCommit().replace(';', '').replace('=', '')} Branch [${githubscm.getBranch().replace(';', '').replace('=', '')}] Remote [${githubscm.getRemoteInfo('origin', 'url').replace(';', '').replace('=', '')}]" + env.GIT_INFORMATION_REPORT = gitInformationReport + + def gitHashes = env.GIT_INFORMATION_HASHES ? "${env.GIT_INFORMATION_HASHES};" : "" + gitHashes += "${projectName}=${githubscm.getCommitHash()}" + env.GIT_INFORMATION_HASHES = gitHashes +} + +/** + * + * prints GIT_INFORMATION_REPORT variable + */ +def printGitInformationReport() { + if (env.GIT_INFORMATION_REPORT?.trim()) { + def result = env.GIT_INFORMATION_REPORT.split(';').inject([:]) { map, token -> + token.split('=').with { key, value -> + map[key.trim()] = value.trim() + } + map + } + def report = ''' +------------------------------------------ +GIT INFORMATION REPORT +------------------------------------------ +''' + result.each { key, value -> + report += "${key}: ${value}\n" + } + println report + } else { + println '[WARNING] The variable GIT_INFORMATION_REPORT does not exist' + } +} + +/* + * Get the next major/minor/micro version, with a specific suffix if needed. + * The version string needs to be in the form X.Y.Z +*/ + +def getNextVersion(String version, String type, String suffix = 'SNAPSHOT', boolean resetSubVersions = true) { + assert ['major', 'minor', 'micro'].contains(type) + Integer[] versionSplit = parseVersion(version) + if (versionSplit != null) { + int majorVersion = versionSplit[0] + (type == 'major' ? 1 : 0) + int minorVersion = resetSubVersions && type == 'major' ? 0 : (versionSplit[1] + (type == 'minor' ? 1 : 0)) + int microVersion = resetSubVersions && (type == 'major' || type == 'minor') ? 0 : (versionSplit[2] + (type == 'micro' ? 1 : 0)) + return "${majorVersion}.${minorVersion}.${microVersion}${suffix ? '-' + suffix : ''}" + } else { + return null + } +} + +String getMajorMinorVersion(String version) { + try { + String[] versionSplit = version.split("\\.") + return "${versionSplit[0]}.${versionSplit[1]}" + } catch (err) { + println "[ERROR] ${version} cannot be reduced to Major.minor" + throw err + } +} + +/* + * It parses a version string, which needs to be in the format X.Y.Z or X.Y.Z.suffix and returns the 3 numbers + * in an array. The optional suffix must not be numeric. + *

+ * Valid version examples: + * 1.0.0 + * 1.0.0.Final +*/ + +Integer[] parseVersion(String version) { + String[] versionSplit = version.split("\\.") + boolean hasNonNumericSuffix = versionSplit.length == 4 && !(versionSplit[3].isNumber()) + if (versionSplit.length == 3 || hasNonNumericSuffix) { + if (versionSplit[0].isNumber() && versionSplit[1].isNumber() && versionSplit[2].isNumber()) { + Integer[] vs = new Integer[3] + vs[0] = Integer.parseInt(versionSplit[0]) + vs[1] = Integer.parseInt(versionSplit[1]) + vs[2] = Integer.parseInt(versionSplit[2]) + return vs + } else { + error "Version ${version} is not in the required format. The major, minor, and micro parts should contain only numeric characters." + } + } else { + error "Version ${version} is not in the required format X.Y.Z or X.Y.Z.suffix." + } +} + +String getReleaseBranchFromVersion(String version) { + Integer[] versionSplit = parseVersion(version) + return "${versionSplit[0]}.${versionSplit[1]}.x" +} + +String calculateTargetReleaseBranch(String currentReleaseBranch, int addToMajor = 0, int addToMinor = 0) { + String targetBranch = currentReleaseBranch + String [] versionSplit = targetBranch.split("\\.") + if (versionSplit.length == 3 + && versionSplit[0].isNumber() + && versionSplit[1].isNumber() + && (versionSplit[2] == 'x' || versionSplit[2] == 'x-prod')) { + Integer newMajor = Integer.parseInt(versionSplit[0]) + addToMajor + Integer newMinor = Integer.parseInt(versionSplit[1]) + addToMinor + targetBranch = "${newMajor}.${newMinor}.${versionSplit[2]}" + } else { + println "Cannot parse targetBranch as release branch so going further with current value: ${targetBranch}" + } + return targetBranch +} + +/** + * It prepares the environment to avoid problems with plugins. For example files from SCM pipeline are deleted during checkout + */ +def prepareEnvironment() { + println """ +[INFO] Preparing Environment +[INFO] Copying WORKSPACE content env folder + """ + def envFolderName = '.ci-env' + if (fileExists("${env.WORKSPACE}/${envFolderName}")) { + println "[WARNING] folder ${env.WORKSPACE}/${envFolderName} already exist, won't create env folder again." + } else { + dir(env.WORKSPACE) { + sh "mkdir ${envFolderName}" + sh "cp -r `ls -A | grep -v '${envFolderName}'` ${envFolderName}/" + } + } +} + +/* +* Generate a hash composed of alphanumeric characters (lowercase) of a given size +*/ + +String generateHash(int size) { + String alphabet = (('a'..'z') + ('0'..'9')).join("") + def random = new Random() + return (1..size).collect { alphabet[random.nextInt(alphabet.length())] }.join("") +} + +String generateTempFile() { + return sh(returnStdout: true, script: 'mktemp').trim() +} + +String generateTempFolder() { + return sh(returnStdout: true, script: 'mktemp -d').trim() +} + +void executeWithCredentialsMap(Map credentials, Closure closure) { + if (credentials.token) { + withCredentials([string(credentialsId: credentials.token, variable: 'QUAY_TOKEN')]) { + closure() + } + } else if (credentials.usernamePassword) { + withCredentials([usernamePassword(credentialsId: credentials.usernamePassword, usernameVariable: 'QUAY_USER', passwordVariable: 'QUAY_TOKEN')]) { + closure() + } + } else { + error 'No credentials given to execute the given closure' + } +} + +void cleanNode(String containerEngine = '') { + println '[INFO] Clean workspace' + cleanWs() + println '[INFO] Workspace cleaned' + println '[INFO] Cleanup Maven artifacts' + maven.cleanRepository() + println '[INFO] .m2/repository cleaned' + if (containerEngine) { + println "[INFO] Cleanup ${containerEngine} containers/images" + cloud.cleanContainersAndImages(containerEngine) + } +} + +def spaceLeft() { + dir(env.WORKSPACE) { + println '[INFO] space left on the machine' + sh 'df -h' + println '[INFO] space of /home/jenkins' + sh "du -h -d1 /home/jenkins" + println '[INFO] space of workspace' + sh "du -h -d3 /home/jenkins/workspace" + } +} + +def replaceInAllFilesRecursive(String findPattern, String oldValueSedPattern, String newSedValue) { + sh "find . -name '${findPattern}' -type f -exec sed -i 's/${oldValueSedPattern}/${newSedValue}/g' {} \\;" +} + +/* +* Removes any partial downloaded dependencies from .m2 if the previous run was interrupted and no post actions were +* executed (cleanRepository()) and a new build is executed on the same machine +*/ +def rmPartialDeps(){ + dir("${env.WORKSPACE}/.m2") { + sh "find . -regex \".*\\.part\\(\\.lock\\)?\" -exec rm -rf {} \\;" + } +} + +String retrieveConsoleLog(int numberOfLines = 100, String buildUrl = "${BUILD_URL}") { + return sh(returnStdout: true, script: "wget --no-check-certificate -qO - ${buildUrl}consoleText | tail -n ${numberOfLines}") +} + +String archiveConsoleLog(String id = '', int numberOfLines = 100, String buildUrl = "${BUILD_URL}") { + String filename = "${id ? "${id}_" : ''}console.log" + sh "rm -rf ${filename}" + writeFile(text: retrieveConsoleLog(numberOfLines, buildUrl), file: filename) + archiveArtifacts(artifacts: filename) +} + +def retrieveTestResults(String buildUrl = "${BUILD_URL}") { + return readJSON(text: sh(returnStdout: true, script: "wget --no-check-certificate -qO - ${buildUrl}testReport/api/json?depth=1")) +} + +def retrieveFailedTests(String buildUrl = "${BUILD_URL}") { + def testResults = retrieveTestResults(buildUrl) + + def allCases = [] + testResults.suites?.each { testSuite -> + allCases.addAll(testSuite.cases) + } + + def failedTests = [] + testResults.suites?.each { testSuite -> + testSuite.cases?.each { testCase -> + if (!['PASSED', 'SKIPPED', 'FIXED'].contains(testCase.status)) { + def failedTest = [:] + + boolean hasSameNameCases = allCases.findAll { it.name == testCase.name && it.className == testCase.className }.size() > 1 + + failedTest.status = testCase.status + + // Retrieve class name + fullClassName = testCase.className + int lastIndexOf = fullClassName.lastIndexOf('.') + packageName = fullClassName.substring(0, lastIndexOf) + className = fullClassName.substring(lastIndexOf + 1) + + failedTest.name = testCase.name + failedTest.packageName = packageName + failedTest.className = className + failedTest.enclosingBlockNames = testSuite.enclosingBlockNames?.reverse()?.join(' / ') + + failedTest.fullName = "${packageName}.${className}.${failedTest.name}" + // If other cases have the same className / name, Jenkins uses the enclosingBlockNames for the URL distinction + if (hasSameNameCases && testSuite.enclosingBlockNames) { + failedTest.fullName = "${testSuite.enclosingBlockNames.reverse().join(' / ')} / ${failedTest.fullName}" + } + + // Construct test url + String urlLeaf = '' + // If other cases have the same className / name, Jenkins uses the enclosingBlockNames for the URL distinction + if (hasSameNameCases && testSuite.enclosingBlockNames) { + urlLeaf += testSuite.enclosingBlockNames.reverse().join('___') + } + urlLeaf += urlLeaf ? '___' : urlLeaf + urlLeaf += "${failedTest.name == "(?)" ? "___" : failedTest.name}/" + urlLeaf = urlLeaf.replaceAll(' ', '_') + .replaceAll('&', '_') + .replaceAll('-', '_') + failedTest.url = "${buildUrl}testReport/${packageName}/${className}/${urlLeaf}" + + failedTest.details = [null, 'null'].contains(testCase.errorDetails) ? '' : testCase.errorDetails + failedTest.stacktrace = [null, 'null'].contains(testCase.errorStackTrace) ? '' : testCase.errorStackTrace + failedTests.add(failedTest) + } + } + } + + return failedTests +} + +String retrieveArtifact(String artifactPath, String buildUrl = "${BUILD_URL}") { + String finalUrl = "${buildUrl}artifact/${artifactPath}" + String httpCode = sh(returnStdout: true, script: "curl -o /dev/null --silent -Iw '%{http_code}' ${finalUrl}") + return httpCode == "200" ? sh(returnStdout: true, script: "wget --no-check-certificate -qO - ${finalUrl}") : '' +} + +def retrieveJobInformation(String buildUrl = "${BUILD_URL}") { + return readJSON(text: sh(returnStdout: true, script: "wget --no-check-certificate -qO - ${buildUrl}api/json?depth=0")) +} + +boolean isJobResultSuccess(String jobResult) { + return jobResult == 'SUCCESS' +} + +boolean isJobResultFailure(String jobResult) { + return jobResult == 'FAILURE' +} + +boolean isJobResultAborted(String jobResult) { + return jobResult == 'ABORTED' +} + +boolean isJobResultUnstable(String jobResult) { + return jobResult == 'UNSTABLE' +} + +/* +* Return the build/test summary of a job +* +* outputStyle possibilities: 'ZULIP' (default), 'GITHUB' +*/ +String getMarkdownTestSummary(String jobId = '', String additionalInfo = '', String buildUrl = "${BUILD_URL}", String outputStyle = 'ZULIP') { + def jobInfo = retrieveJobInformation(buildUrl) + + // Check if any *_console.log is available as artifact first + String defaultConsoleLogId = 'Console Logs' + Map consoleLogs = jobInfo.artifacts?.collect { it.fileName } + .findAll { it.endsWith('console.log') } + .collectEntries { filename -> + int index = filename.lastIndexOf('_') + String logId = index > 0 ? filename.substring(0, index) : defaultConsoleLogId + return [ (logId) : retrieveArtifact(filename, buildUrl) ] + } ?: [ (defaultConsoleLogId) : retrieveConsoleLog(50, buildUrl)] + + String jobResult = jobInfo.result + String summary = """ +${jobId ? "**${jobId} job**" : 'Job'} ${formatBuildNumber(outputStyle, BUILD_NUMBER)} was: **${jobResult}** +""" + + if (!isJobResultSuccess(jobResult)) { + summary += "Possible explanation: ${getResultExplanationMessage(jobResult)}\n" + } + + if (additionalInfo) { + summary += """ +${additionalInfo} +""" + } + + if (!isJobResultSuccess(jobResult)) { + boolean testResultsFound = false + summary += "\nPlease look here: ${buildUrl}display/redirect" + + try { + def testResults = retrieveTestResults(buildUrl) + def failedTests = retrieveFailedTests(buildUrl) + testResultsFound=true + + summary += """ +\n**Test results:** +- PASSED: ${testResults.passCount} +- FAILED: ${testResults.failCount} +""" + + summary += 'GITHUB'.equalsIgnoreCase(outputStyle) ? """ +Those are the test failures: ${failedTests.size() <= 0 ? 'none' : '\n'}${failedTests.collect { failedTest -> + return """

+${failedTest.fullName} +${formatTextForHtmlDisplay(failedTest.details ?: failedTest.stacktrace)} +
""" +}.join('\n')} +""" + : """ +Those are the test failures: ${failedTests.size() <= 0 ? 'none' : '\n'}${failedTests.collect { failedTest -> + return """```spoiler [${failedTest.fullName}](${failedTest.url}) +${failedTest.details ?: failedTest.stacktrace} +```""" +}.join('\n')} +""" + } catch (err) { + echo 'No test results found' + } + + // Display console logs if no test results found + if (!(jobResult == 'UNSTABLE' && testResultsFound)) { + summary += 'GITHUB'.equalsIgnoreCase(outputStyle) ? """ +See console log: +${consoleLogs.collect { key, value -> +return """
+${key} +${formatTextForHtmlDisplay(value)} +
+""" +}.join('')}""" + : """ +See console log: +${consoleLogs.collect { key, value -> +return """```spoiler ${key} +${value} +``` +""" +}.join('')}""" + } + } + + return summary +} + +String getResultExplanationMessage(String jobResult) { + switch (jobResult) { + case 'SUCCESS': + return 'Do I need to explain ?' + case 'UNSTABLE': + return 'This should be test failures' + case 'FAILURE': + return 'Pipeline failure or project build failure' + case 'ABORTED': + return 'Most probably a timeout, please review' + default: + return 'Woops ... I don\'t know about this result value ... Please ask maintainer.' + } +} + +String formatTextForHtmlDisplay(String text) { + return text.replaceAll('\n', '
') +} + +String formatBuildNumber(String outputStyle, String buildNumber) { + return 'GITHUB'.equalsIgnoreCase(outputStyle) ? "`#${buildNumber}`" : "#${buildNumber}" +} + +/** + * Encode the provided string value in the provided encoding + * @param value string to encode + * @param encoding [default UTF-8] + * @return the encoded string + */ +String encode(String value, String encoding='UTF-8') { + return URLEncoder.encode(value, encoding) +} + +/** + * Serialize the parameters converting a Map into an URL query string, like: + * {A: 1, B: 2} --> 'A=1&B=2' + * @param params key-value map representation of the parameters + * @return URL query string + */ +String serializeQueryParams(Map params) { + return params.collect { "${it.getKey()}=${encode(it.getValue() as String)}" }.join('&') +} + +/** + * Execute the provided closure within Kerberos authentication context + * @param keytabId id of the keytab to be used + * @param closure code to run in the kerberos auth context + * @param domain kerberos domain to look for into the keytab + * @param retry number of max retries to perform if kinit fails + */ +def withKerberos(String keytabId, Closure closure, String domain = 'REDHAT.COM', int nRetries = 5) { + withCredentials([file(credentialsId: keytabId, variable: 'KEYTAB_FILE')]) { + env.KERBEROS_PRINCIPAL = sh(returnStdout: true, script: "klist -kt $KEYTAB_FILE |grep $domain | awk -F' ' 'NR==1{print \$4}' ").trim() + + if (!env.KERBEROS_PRINCIPAL?.trim()) { + throw new Exception("[ERROR] found blank KERBEROS_PRINCIPAL, kerberos authetication failed.") + } + + // check if kerberos authentication already exists with provided principal + def currentPrincipal = sh(returnStdout: true, script: "klist | grep -i 'Default principal' | awk -F':' 'NR==1{print \$2}' ").trim() + + if (currentPrincipal != env.KERBEROS_PRINCIPAL) { + def kerberosStatus = 0 + for (int i = 0; i < nRetries; i++) { + kerberosStatus = sh(returnStatus: true, script: "kinit ${env.KERBEROS_PRINCIPAL} -kt $KEYTAB_FILE") + if (kerberosStatus == 0) { + // exit at first success + break + } + } + + // if the kerberos status is still != 0 after nRetries throw exception + if (kerberosStatus != 0) { + throw new Exception("[ERROR] kinit failed with non-zero status.") + } + } else { + println "[INFO] ${env.KERBEROS_PRINCIPAL} already authenticated, skipping kinit." + } + + closure() + } +} + +def runWithPythonVirtualEnv(String cmd, String virtualEnvName, boolean returnStdout = false) { + return sh(returnStdout: returnStdout, script: """ +source ~/virtenvs/${virtualEnvName}/bin/activate +${cmd} +""") +} + +int getJobDurationInSeconds() { + long startTimestamp = retrieveJobInformation().timestamp + long currentTimestamp = new Date().getTime() + return (int) ((currentTimestamp - startTimestamp) / 1000) +} + +String displayDurationFromSeconds(int durationInSec) { + String result = '' + int seconds = durationInSec + int minutes = durationInSec / 60 + if (minutes > 0) { + seconds = seconds - minutes * 60 + int hours = minutes / 60 + if (hours > 0) { + minutes = minutes - hours*60 + result += "${hours}h" + } + result += "${minutes}m" + } + result += "${seconds}s" + return result +}