Adds support for GitLabAPI V4 while keeping support for V3 (#614)

This commit is contained in:
Karsten Kraus 2017-10-25 19:35:47 +02:00 committed by Owen Mehegan
parent ca400c19bc
commit 17e89c3fdd
57 changed files with 2403 additions and 1147 deletions

BIN
.mvn/wrapper/maven-wrapper.jar vendored Normal file

Binary file not shown.

1
.mvn/wrapper/maven-wrapper.properties vendored Normal file
View File

@ -0,0 +1 @@
distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.3.9/apache-maven-3.3.9-bin.zip

52
.travis.yml Normal file
View File

@ -0,0 +1,52 @@
language: java
env:
global:
- DOCKER_CACHE_DIR=$HOME/.docker-images
matrix:
fast_finish: true
before_install:
- chmod +x travis/*.sh
install: true
jobs:
include:
- stage: test
script: ./travis/test.sh
- stage: integration-test
sudo: required
services:
- docker
env:
- GITLAB_VERSION=8.17.4
script: ./travis/integration-test.sh
- stage: integration-test
sudo: required
services:
- docker
env:
- GITLAB_VERSION=9.5.5
script: ./travis/integration-test.sh
- stage: integration-test
sudo: required
services:
- docker
env:
- GITLAB_VERSION=latest
script: ./travis/integration-test.sh
cache:
directories:
- $DOCKER_CACHE_DIR
- $HOME/.m2/repository
- $HOME/.m2/wrapper
before_cache:
- travis/before_cache.sh

236
mvnw vendored Executable file
View File

@ -0,0 +1,236 @@
#!/bin/sh
# ----------------------------------------------------------------------------
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
# ----------------------------------------------------------------------------
# ----------------------------------------------------------------------------
# Maven2 Start Up Batch script
#
# Required ENV vars:
# ------------------
# JAVA_HOME - location of a JDK home dir
#
# Optional ENV vars
# -----------------
# M2_HOME - location of maven2's installed home dir
# MAVEN_OPTS - parameters passed to the Java VM when running Maven
# e.g. to debug Maven itself, use
# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
# MAVEN_SKIP_RC - flag to disable loading of mavenrc files
# ----------------------------------------------------------------------------
if [ -z "$MAVEN_SKIP_RC" ] ; then
if [ -f /etc/mavenrc ] ; then
. /etc/mavenrc
fi
if [ -f "$HOME/.mavenrc" ] ; then
. "$HOME/.mavenrc"
fi
fi
# OS specific support. $var _must_ be set to either true or false.
cygwin=false;
darwin=false;
mingw=false
case "`uname`" in
CYGWIN*) cygwin=true ;;
MINGW*) mingw=true;;
Darwin*) darwin=true
#
# Look for the Apple JDKs first to preserve the existing behaviour, and then look
# for the new JDKs provided by Oracle.
#
if [ -z "$JAVA_HOME" ] && [ -L /System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK ] ; then
#
# Apple JDKs
#
export JAVA_HOME=/System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK/Home
fi
if [ -z "$JAVA_HOME" ] && [ -L /System/Library/Java/JavaVirtualMachines/CurrentJDK ] ; then
#
# Apple JDKs
#
export JAVA_HOME=/System/Library/Java/JavaVirtualMachines/CurrentJDK/Contents/Home
fi
if [ -z "$JAVA_HOME" ] && [ -L "/Library/Java/JavaVirtualMachines/CurrentJDK" ] ; then
#
# Oracle JDKs
#
export JAVA_HOME=/Library/Java/JavaVirtualMachines/CurrentJDK/Contents/Home
fi
if [ -z "$JAVA_HOME" ] && [ -x "/usr/libexec/java_home" ]; then
#
# Apple JDKs
#
export JAVA_HOME=`/usr/libexec/java_home`
fi
;;
esac
if [ -z "$JAVA_HOME" ] ; then
if [ -r /etc/gentoo-release ] ; then
JAVA_HOME=`java-config --jre-home`
fi
fi
if [ -z "$M2_HOME" ] ; then
## resolve links - $0 may be a link to maven's home
PRG="$0"
# need this for relative symlinks
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG="`dirname "$PRG"`/$link"
fi
done
saveddir=`pwd`
M2_HOME=`dirname "$PRG"`/..
# make it fully qualified
M2_HOME=`cd "$M2_HOME" && pwd`
cd "$saveddir"
# echo Using m2 at $M2_HOME
fi
# For Cygwin, ensure paths are in UNIX format before anything is touched
if $cygwin ; then
[ -n "$M2_HOME" ] &&
M2_HOME=`cygpath --unix "$M2_HOME"`
[ -n "$JAVA_HOME" ] &&
JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
[ -n "$CLASSPATH" ] &&
CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
fi
# For Migwn, ensure paths are in UNIX format before anything is touched
if $mingw ; then
[ -n "$M2_HOME" ] &&
M2_HOME="`(cd "$M2_HOME"; pwd)`"
[ -n "$JAVA_HOME" ] &&
JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
# TODO classpath?
fi
if [ -z "$JAVA_HOME" ]; then
javaExecutable="`which javac`"
if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
# readlink(1) is not available as standard on Solaris 10.
readLink=`which readlink`
if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
if $darwin ; then
javaHome="`dirname \"$javaExecutable\"`"
javaExecutable="`cd \"$javaHome\" && pwd -P`/javac"
else
javaExecutable="`readlink -f \"$javaExecutable\"`"
fi
javaHome="`dirname \"$javaExecutable\"`"
javaHome=`expr "$javaHome" : '\(.*\)/bin'`
JAVA_HOME="$javaHome"
export JAVA_HOME
fi
fi
fi
if [ -z "$JAVACMD" ] ; then
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
else
JAVACMD="`which java`"
fi
fi
if [ ! -x "$JAVACMD" ] ; then
echo "Error: JAVA_HOME is not defined correctly." >&2
echo " We cannot execute $JAVACMD" >&2
exit 1
fi
if [ -z "$JAVA_HOME" ] ; then
echo "Warning: JAVA_HOME environment variable is not set."
fi
CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
# traverses directory structure from process work directory to filesystem root
# first directory with .mvn subdirectory is considered project base directory
find_maven_basedir() {
local basedir=$(pwd)
local wdir=$(pwd)
while [ "$wdir" != '/' ] ; do
if [ -d "$wdir"/.mvn ] ; then
basedir=$wdir
break
fi
wdir=$(cd "$wdir/.."; pwd)
done
echo "${basedir}"
}
# concatenates all lines of a file
concat_lines() {
if [ -f "$1" ]; then
echo "$(tr -s '\n' ' ' < "$1")"
fi
}
export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-$(find_maven_basedir)}
MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
# For Cygwin, switch paths to Windows format before running java
if $cygwin; then
[ -n "$M2_HOME" ] &&
M2_HOME=`cygpath --path --windows "$M2_HOME"`
[ -n "$JAVA_HOME" ] &&
JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
[ -n "$CLASSPATH" ] &&
CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
[ -n "$MAVEN_PROJECTBASEDIR" ] &&
MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"`
fi
# Provide a "standardized" way to retrieve the CLI args that will
# work with both Windows and non-Windows executions.
MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@"
export MAVEN_CMD_LINE_ARGS
WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
# avoid using MAVEN_CMD_LINE_ARGS below since that would loose parameter escaping in $@
exec "$JAVACMD" \
$MAVEN_OPTS \
-classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
"-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"

146
mvnw.cmd vendored Normal file
View File

@ -0,0 +1,146 @@
@REM ----------------------------------------------------------------------------
@REM Licensed to the Apache Software Foundation (ASF) under one
@REM or more contributor license agreements. See the NOTICE file
@REM distributed with this work for additional information
@REM regarding copyright ownership. The ASF licenses this file
@REM to you under the Apache License, Version 2.0 (the
@REM "License"); you may not use this file except in compliance
@REM with the License. You may obtain a copy of the License at
@REM
@REM http://www.apache.org/licenses/LICENSE-2.0
@REM
@REM Unless required by applicable law or agreed to in writing,
@REM software distributed under the License is distributed on an
@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@REM KIND, either express or implied. See the License for the
@REM specific language governing permissions and limitations
@REM under the License.
@REM ----------------------------------------------------------------------------
@REM ----------------------------------------------------------------------------
@REM Maven2 Start Up Batch script
@REM
@REM Required ENV vars:
@REM JAVA_HOME - location of a JDK home dir
@REM
@REM Optional ENV vars
@REM M2_HOME - location of maven2's installed home dir
@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending
@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
@REM e.g. to debug Maven itself, use
@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
@REM ----------------------------------------------------------------------------
@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
@echo off
@REM enable echoing my setting MAVEN_BATCH_ECHO to 'on'
@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
@REM set %HOME% to equivalent of $HOME
if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
@REM Execute a user defined script before this one
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
@REM check for pre script, once with legacy .bat ending and once with .cmd ending
if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat"
if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd"
:skipRcPre
@setlocal
set ERROR_CODE=0
@REM To isolate internal variables from possible post scripts, we use another setlocal
@setlocal
@REM ==== START VALIDATION ====
if not "%JAVA_HOME%" == "" goto OkJHome
echo.
echo Error: JAVA_HOME not found in your environment. >&2
echo Please set the JAVA_HOME variable in your environment to match the >&2
echo location of your Java installation. >&2
echo.
goto error
:OkJHome
if exist "%JAVA_HOME%\bin\java.exe" goto init
echo.
echo Error: JAVA_HOME is set to an invalid directory. >&2
echo JAVA_HOME = "%JAVA_HOME%" >&2
echo Please set the JAVA_HOME variable in your environment to match the >&2
echo location of your Java installation. >&2
echo.
goto error
@REM ==== END VALIDATION ====
:init
set MAVEN_CMD_LINE_ARGS=%MAVEN_CONFIG% %*
@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
@REM Fallback to current working directory if not found.
set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
set EXEC_DIR=%CD%
set WDIR=%EXEC_DIR%
:findBaseDir
IF EXIST "%WDIR%"\.mvn goto baseDirFound
cd ..
IF "%WDIR%"=="%CD%" goto baseDirNotFound
set WDIR=%CD%
goto findBaseDir
:baseDirFound
set MAVEN_PROJECTBASEDIR=%WDIR%
cd "%EXEC_DIR%"
goto endDetectBaseDir
:baseDirNotFound
set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
cd "%EXEC_DIR%"
:endDetectBaseDir
IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
@setlocal EnableExtensions EnableDelayedExpansion
for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
:endReadAdditionalConfig
SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
set WRAPPER_JAR=""%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar""
set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
@REM avoid using MAVEN_CMD_LINE_ARGS below since that would loose parameter escaping in %*
%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
if ERRORLEVEL 1 goto error
goto end
:error
set ERROR_CODE=1
:end
@endlocal & set ERROR_CODE=%ERROR_CODE%
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost
@REM check for post script, once with legacy .bat ending and once with .cmd ending
if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat"
if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd"
:skipRcPost
@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
if "%MAVEN_BATCH_PAUSE%" == "on" pause
if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE%
exit /B %ERROR_CODE%

16
pom.xml
View File

@ -288,7 +288,8 @@
<profile> <profile>
<id>integration-test</id> <id>integration-test</id>
<properties> <properties>
<gitlab.version>8.5.8</gitlab.version> <gitlab.version>8.17.4</gitlab.version>
<postgres.version>9.5-1</postgres.version>
</properties> </properties>
<build> <build>
<plugins> <plugins>
@ -359,7 +360,7 @@
<verbose>true</verbose> <verbose>true</verbose>
<images> <images>
<image> <image>
<name>sameersbn/postgresql:9.4-15</name> <name>sameersbn/postgresql:${postgres.version}</name>
<alias>it-gitlab-postgres</alias> <alias>it-gitlab-postgres</alias>
<run> <run>
<namingStrategy>alias</namingStrategy> <namingStrategy>alias</namingStrategy>
@ -367,6 +368,7 @@
<DB_NAME>gitlabhq_production</DB_NAME> <DB_NAME>gitlabhq_production</DB_NAME>
<DB_USER>gitlab</DB_USER> <DB_USER>gitlab</DB_USER>
<DB_PASS>password</DB_PASS> <DB_PASS>password</DB_PASS>
<DB_EXTENSION>pg_trgm</DB_EXTENSION>
</env> </env>
<ports> <ports>
<port>${postgres.port}:5432</port> <port>${postgres.port}:5432</port>
@ -382,7 +384,9 @@
</image> </image>
<image> <image>
<name>sameersbn/gitlab:${gitlab.version}</name> <name>sameersbn/gitlab:${gitlab.version}</name>
<alias>it-gitlab-gitlab</alias>
<run> <run>
<namingStrategy>alias</namingStrategy>
<links> <links>
<link>it-gitlab-postgres:postgresql</link> <link>it-gitlab-postgres:postgresql</link>
<link>it-gitlab-redis:redisio</link> <link>it-gitlab-redis:redisio</link>
@ -392,9 +396,15 @@
<port>${gitlab.ssh.port}:22</port> <port>${gitlab.ssh.port}:22</port>
</ports> </ports>
<env> <env>
<DEBUG>false</DEBUG>
<TZ>Asia/Kolkata</TZ>
<GITLAB_TIMEZONE>Kolkata</GITLAB_TIMEZONE>
<GITLAB_PORT>${gitlab.http.port}</GITLAB_PORT> <GITLAB_PORT>${gitlab.http.port}</GITLAB_PORT>
<GITLAB_SSH_PORT>${gitlab.ssh.port}</GITLAB_SSH_PORT> <GITLAB_SSH_PORT>${gitlab.ssh.port}</GITLAB_SSH_PORT>
<GITLAB_SECRETS_DB_KEY_BASE>abc123</GITLAB_SECRETS_DB_KEY_BASE> <GITLAB_SECRETS_DB_KEY_BASE>long-and-random-alpha-numeric-string</GITLAB_SECRETS_DB_KEY_BASE>
<GITLAB_SECRETS_SECRET_KEY_BASE>long-and-random-alphanumeric-string</GITLAB_SECRETS_SECRET_KEY_BASE>
<GITLAB_SECRETS_OTP_KEY_BASE>long-and-random-alpha-numeric-string</GITLAB_SECRETS_OTP_KEY_BASE>
<GITLAB_HOST>172.17.0.1</GITLAB_HOST>
</env> </env>
<wait> <wait>
<http> <http>

View File

@ -14,7 +14,7 @@ postgresql:
gitlab: gitlab:
container_name: docker-gitlab container_name: docker-gitlab
restart: always restart: always
image: sameersbn/gitlab:9.4.1 image: sameersbn/gitlab:8.17.4
links: links:
- redis:redisio - redis:redisio
- postgresql:postgresql - postgresql:postgresql

View File

@ -1,5 +1,6 @@
package com.dabsquared.gitlabjenkins; package com.dabsquared.gitlabjenkins;
import com.dabsquared.gitlabjenkins.connection.GitLabConnection; import com.dabsquared.gitlabjenkins.connection.GitLabConnection;
import com.dabsquared.gitlabjenkins.connection.GitLabConnectionConfig; import com.dabsquared.gitlabjenkins.connection.GitLabConnectionConfig;
import com.dabsquared.gitlabjenkins.connection.GitLabConnectionProperty; import com.dabsquared.gitlabjenkins.connection.GitLabConnectionProperty;
@ -148,9 +149,11 @@ public class GitLabPushTrigger extends Trigger<Job<?, ?>> {
GitLabPushTrigger.DescriptorImpl oldConfig = Trigger.all().get(GitLabPushTrigger.DescriptorImpl.class); GitLabPushTrigger.DescriptorImpl oldConfig = Trigger.all().get(GitLabPushTrigger.DescriptorImpl.class);
if (!oldConfig.jobsMigrated) { if (!oldConfig.jobsMigrated) {
GitLabConnectionConfig gitLabConfig = (GitLabConnectionConfig) Jenkins.getInstance().getDescriptor(GitLabConnectionConfig.class); GitLabConnectionConfig gitLabConfig = (GitLabConnectionConfig) Jenkins.getInstance().getDescriptor(GitLabConnectionConfig.class);
gitLabConfig.getConnections().add(new GitLabConnection(oldConfig.gitlabHostUrl, gitLabConfig.getConnections().add(new GitLabConnection(
oldConfig.gitlabHostUrl,
oldConfig.gitlabHostUrl, oldConfig.gitlabHostUrl,
oldConfig.gitlabApiToken, oldConfig.gitlabApiToken,
"autodetect",
oldConfig.ignoreCertificateErrors, oldConfig.ignoreCertificateErrors,
10, 10,
10)); 10));

View File

@ -1,39 +1,83 @@
package com.dabsquared.gitlabjenkins.connection; package com.dabsquared.gitlabjenkins.connection;
import com.cloudbees.plugins.credentials.CredentialsMatchers;
import com.cloudbees.plugins.credentials.CredentialsProvider; import com.cloudbees.plugins.credentials.CredentialsProvider;
import com.cloudbees.plugins.credentials.CredentialsScope; import com.cloudbees.plugins.credentials.CredentialsScope;
import com.cloudbees.plugins.credentials.CredentialsStore; import com.cloudbees.plugins.credentials.CredentialsStore;
import com.cloudbees.plugins.credentials.SystemCredentialsProvider; import com.cloudbees.plugins.credentials.SystemCredentialsProvider;
import com.cloudbees.plugins.credentials.common.StandardCredentials;
import com.cloudbees.plugins.credentials.domains.Domain; import com.cloudbees.plugins.credentials.domains.Domain;
import com.cloudbees.plugins.credentials.domains.DomainRequirement;
import com.dabsquared.gitlabjenkins.gitlab.api.GitLabClient;
import com.dabsquared.gitlabjenkins.gitlab.api.GitLabClientBuilder;
import com.dabsquared.gitlabjenkins.gitlab.api.impl.AutodetectGitLabClientBuilder;
import hudson.init.InitMilestone; import hudson.init.InitMilestone;
import hudson.init.Initializer; import hudson.init.Initializer;
import hudson.model.Item;
import hudson.security.ACL;
import hudson.util.Secret; import hudson.util.Secret;
import jenkins.model.Jenkins; import jenkins.model.Jenkins;
import org.jenkinsci.plugins.plaincredentials.StringCredentials;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.NoExternalUse;
import org.kohsuke.stapler.DataBoundConstructor; import org.kohsuke.stapler.DataBoundConstructor;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
import static com.cloudbees.plugins.credentials.CredentialsProvider.lookupCredentials;
import static com.dabsquared.gitlabjenkins.gitlab.api.GitLabClientBuilder.getGitLabClientBuilderById;
/** /**
* @author Robin Müller * @author Robin Müller
*/ */
public class GitLabConnection { public class GitLabConnection {
private final String name; private final String name;
private final String url; private final String url;
private transient String apiToken; private transient String apiToken;
// TODO make final when migration code gets removed // TODO make final when migration code gets removed
private String apiTokenId; private String apiTokenId;
private GitLabClientBuilder clientBuilder;
private final boolean ignoreCertificateErrors; private final boolean ignoreCertificateErrors;
private final Integer connectionTimeout; private final Integer connectionTimeout;
private final Integer readTimeout; private final Integer readTimeout;
private transient GitLabClient apiCache;
public GitLabConnection(String name, String url, String apiTokenId, boolean ignoreCertificateErrors, Integer connectionTimeout, Integer readTimeout) {
this(
name,
url,
apiTokenId,
new AutodetectGitLabClientBuilder(),
ignoreCertificateErrors,
connectionTimeout,
readTimeout
);
}
@DataBoundConstructor @DataBoundConstructor
public GitLabConnection(String name, String url, String apiTokenId, boolean ignoreCertificateErrors, Integer connectionTimeout, Integer readTimeout) { public GitLabConnection(String name, String url, String apiTokenId, String clientBuilderId, boolean ignoreCertificateErrors, Integer connectionTimeout, Integer readTimeout) {
this(
name,
url,
apiTokenId,
getGitLabClientBuilderById(clientBuilderId),
ignoreCertificateErrors,
connectionTimeout,
readTimeout
);
}
@Restricted(NoExternalUse.class)
public GitLabConnection(String name, String url, String apiTokenId, GitLabClientBuilder clientBuilder, boolean ignoreCertificateErrors, Integer connectionTimeout, Integer readTimeout) {
this.name = name; this.name = name;
this.url = url; this.url = url;
this.apiTokenId = apiTokenId; this.apiTokenId = apiTokenId;
this.clientBuilder = clientBuilder;
this.ignoreCertificateErrors = ignoreCertificateErrors; this.ignoreCertificateErrors = ignoreCertificateErrors;
this.connectionTimeout = connectionTimeout; this.connectionTimeout = connectionTimeout;
this.readTimeout = readTimeout; this.readTimeout = readTimeout;
@ -51,6 +95,10 @@ public class GitLabConnection {
return apiTokenId; return apiTokenId;
} }
public String getClientBuilderId() {
return clientBuilder.id();
}
public boolean isIgnoreCertificateErrors() { public boolean isIgnoreCertificateErrors() {
return ignoreCertificateErrors; return ignoreCertificateErrors;
} }
@ -63,10 +111,38 @@ public class GitLabConnection {
return readTimeout; return readTimeout;
} }
public GitLabClient getClient() {
if (apiCache == null) {
apiCache = clientBuilder.buildClient(url, getApiToken(apiTokenId), ignoreCertificateErrors, connectionTimeout, readTimeout);
}
return apiCache;
}
private String getApiToken(String apiTokenId) {
StandardCredentials credentials = CredentialsMatchers.firstOrNull(
lookupCredentials(StandardCredentials.class, (Item) null, ACL.SYSTEM, new ArrayList<DomainRequirement>()),
CredentialsMatchers.withId(apiTokenId));
if (credentials != null) {
if (credentials instanceof GitLabApiToken) {
return ((GitLabApiToken) credentials).getApiToken().getPlainText();
}
if (credentials instanceof StringCredentials) {
return ((StringCredentials) credentials).getSecret().getPlainText();
}
}
throw new IllegalStateException("No credentials found for credentialsId: " + apiTokenId);
}
protected GitLabConnection readResolve() { protected GitLabConnection readResolve() {
if (connectionTimeout == null || readTimeout == null) { if (connectionTimeout == null || readTimeout == null) {
return new GitLabConnection(name, url, apiTokenId, ignoreCertificateErrors, 10, 10); return new GitLabConnection(name, url, apiTokenId, new AutodetectGitLabClientBuilder(), ignoreCertificateErrors, 10, 10);
} }
if (clientBuilder == null) {
return new GitLabConnection(name, url, apiTokenId, new AutodetectGitLabClientBuilder(), ignoreCertificateErrors, connectionTimeout, readTimeout);
}
return this; return this;
} }

View File

@ -1,13 +1,14 @@
package com.dabsquared.gitlabjenkins.connection; package com.dabsquared.gitlabjenkins.connection;
import com.cloudbees.plugins.credentials.Credentials; import com.cloudbees.plugins.credentials.Credentials;
import com.cloudbees.plugins.credentials.CredentialsMatcher; import com.cloudbees.plugins.credentials.CredentialsMatcher;
import com.cloudbees.plugins.credentials.common.AbstractIdCredentialsListBoxModel; import com.cloudbees.plugins.credentials.common.AbstractIdCredentialsListBoxModel;
import com.cloudbees.plugins.credentials.common.StandardCredentials; import com.cloudbees.plugins.credentials.common.StandardCredentials;
import com.cloudbees.plugins.credentials.common.StandardListBoxModel; import com.cloudbees.plugins.credentials.common.StandardListBoxModel;
import com.cloudbees.plugins.credentials.domains.URIRequirementBuilder; import com.cloudbees.plugins.credentials.domains.URIRequirementBuilder;
import com.dabsquared.gitlabjenkins.gitlab.GitLabClientBuilder; import com.dabsquared.gitlabjenkins.gitlab.api.GitLabClient;
import com.dabsquared.gitlabjenkins.gitlab.api.GitLabApi; import com.dabsquared.gitlabjenkins.gitlab.api.GitLabClientBuilder;
import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.NonNull;
import hudson.Extension; import hudson.Extension;
import hudson.model.Item; import hudson.model.Item;
@ -29,6 +30,9 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import static com.dabsquared.gitlabjenkins.gitlab.api.GitLabClientBuilder.getAllGitLabClientBuilders;
/** /**
* @author Robin Müller * @author Robin Müller
*/ */
@ -38,7 +42,6 @@ public class GitLabConnectionConfig extends GlobalConfiguration {
private Boolean useAuthenticatedEndpoint = true; private Boolean useAuthenticatedEndpoint = true;
private List<GitLabConnection> connections = new ArrayList<>(); private List<GitLabConnection> connections = new ArrayList<>();
private transient Map<String, GitLabConnection> connectionMap = new HashMap<>(); private transient Map<String, GitLabConnection> connectionMap = new HashMap<>();
private transient Map<String, GitLabApi> clients = new HashMap<>();
public GitLabConnectionConfig() { public GitLabConnectionConfig() {
load(); load();
@ -50,7 +53,6 @@ public class GitLabConnectionConfig extends GlobalConfiguration {
connections = req.bindJSONToList(GitLabConnection.class, json.get("connections")); connections = req.bindJSONToList(GitLabConnection.class, json.get("connections"));
useAuthenticatedEndpoint = json.getBoolean("useAuthenticatedEndpoint"); useAuthenticatedEndpoint = json.getBoolean("useAuthenticatedEndpoint");
refreshConnectionMap(); refreshConnectionMap();
clients.clear();
save(); save();
return super.configure(req, json); return super.configure(req, json);
} }
@ -80,11 +82,11 @@ public class GitLabConnectionConfig extends GlobalConfiguration {
} }
} }
public GitLabApi getClient(String connectionName) { public GitLabClient getClient(String connectionName) {
if (!clients.containsKey(connectionName) && connectionMap.containsKey(connectionName)) { if (!connectionMap.containsKey(connectionName)) {
clients.put(connectionName, GitLabClientBuilder.buildClient(connectionMap.get(connectionName))); return null;
} }
return clients.get(connectionName); return connectionMap.get(connectionName).getClient();
} }
public FormValidation doCheckName(@QueryParameter String id, @QueryParameter String value) { public FormValidation doCheckName(@QueryParameter String id, @QueryParameter String value) {
@ -131,11 +133,12 @@ public class GitLabConnectionConfig extends GlobalConfiguration {
public FormValidation doTestConnection(@QueryParameter String url, public FormValidation doTestConnection(@QueryParameter String url,
@QueryParameter String apiTokenId, @QueryParameter String apiTokenId,
@QueryParameter String clientBuilderId,
@QueryParameter boolean ignoreCertificateErrors, @QueryParameter boolean ignoreCertificateErrors,
@QueryParameter int connectionTimeout, @QueryParameter int connectionTimeout,
@QueryParameter int readTimeout) { @QueryParameter int readTimeout) {
try { try {
GitLabClientBuilder.buildClient(url, apiTokenId, ignoreCertificateErrors, connectionTimeout, readTimeout).headCurrentUser(); new GitLabConnection("", url, apiTokenId, clientBuilderId, ignoreCertificateErrors, connectionTimeout, readTimeout).getClient().headCurrentUser();
return FormValidation.ok(Messages.connection_success()); return FormValidation.ok(Messages.connection_success());
} catch (WebApplicationException e) { } catch (WebApplicationException e) {
return FormValidation.error(Messages.connection_error(e.getMessage())); return FormValidation.error(Messages.connection_error(e.getMessage()));
@ -167,6 +170,15 @@ public class GitLabConnectionConfig extends GlobalConfiguration {
return new StandardListBoxModel(); return new StandardListBoxModel();
} }
public ListBoxModel doFillClientBuilderIdItems() {
ListBoxModel model = new ListBoxModel();
for (GitLabClientBuilder builder : getAllGitLabClientBuilders()) {
model.add(builder.id());
}
return model;
}
private void refreshConnectionMap() { private void refreshConnectionMap() {
connectionMap.clear(); connectionMap.clear();
for (GitLabConnection connection : connections) { for (GitLabConnection connection : connections) {

View File

@ -1,6 +1,7 @@
package com.dabsquared.gitlabjenkins.connection; package com.dabsquared.gitlabjenkins.connection;
import com.dabsquared.gitlabjenkins.gitlab.api.GitLabApi;
import com.dabsquared.gitlabjenkins.gitlab.api.GitLabClient;
import hudson.Extension; import hudson.Extension;
import hudson.model.Job; import hudson.model.Job;
import hudson.model.JobProperty; import hudson.model.JobProperty;
@ -30,7 +31,7 @@ public class GitLabConnectionProperty extends JobProperty<Job<?, ?>> {
return gitLabConnection; return gitLabConnection;
} }
public GitLabApi getClient() { public GitLabClient getClient() {
if (StringUtils.isNotEmpty(gitLabConnection)) { if (StringUtils.isNotEmpty(gitLabConnection)) {
GitLabConnectionConfig connectionConfig = (GitLabConnectionConfig) Jenkins.getInstance().getDescriptor(GitLabConnectionConfig.class); GitLabConnectionConfig connectionConfig = (GitLabConnectionConfig) Jenkins.getInstance().getDescriptor(GitLabConnectionConfig.class);
return connectionConfig != null ? connectionConfig.getClient(gitLabConnection) : null; return connectionConfig != null ? connectionConfig.getClient(gitLabConnection) : null;
@ -38,7 +39,7 @@ public class GitLabConnectionProperty extends JobProperty<Job<?, ?>> {
return null; return null;
} }
public static GitLabApi getClient(Run<?, ?> build) { public static GitLabClient getClient(Run<?, ?> build) {
final GitLabConnectionProperty connectionProperty = build.getParent().getProperty(GitLabConnectionProperty.class); final GitLabConnectionProperty connectionProperty = build.getParent().getProperty(GitLabConnectionProperty.class);
if (connectionProperty != null) { if (connectionProperty != null) {
return connectionProperty.getClient(); return connectionProperty.getClient();

View File

@ -1,134 +0,0 @@
package com.dabsquared.gitlabjenkins.gitlab;
import java.util.List;
import com.dabsquared.gitlabjenkins.gitlab.api.GitLabApi;
import com.dabsquared.gitlabjenkins.gitlab.api.GitLabApiClient;
import com.dabsquared.gitlabjenkins.gitlab.api.model.Branch;
import com.dabsquared.gitlabjenkins.gitlab.api.model.BuildState;
import com.dabsquared.gitlabjenkins.gitlab.api.model.Label;
import com.dabsquared.gitlabjenkins.gitlab.api.model.MergeRequest;
import com.dabsquared.gitlabjenkins.gitlab.api.model.Project;
import com.dabsquared.gitlabjenkins.gitlab.api.model.User;
import com.dabsquared.gitlabjenkins.gitlab.hook.model.State;
/**
* Implements {@link GitLabApi} by delegating REST-client calls to RESTEasy proxy.
*
* @author Alexander Leshkin
*
*/
class GitLabApiExtension implements GitLabApi {
private GitLabApiClient client;
private String gitlabHostUrl;
public GitLabApiExtension(GitLabApiClient client, String gitlabHostUrl) {
this.client = client;
this.gitlabHostUrl = gitlabHostUrl;
}
@Override
public String getGitLabHostUrl() {
return gitlabHostUrl;
}
@Override
public Project createProject(String projectName) {
return client.createProject(projectName);
}
@Override
public MergeRequest createMergeRequest(Integer projectId, String sourceBranch, String targetBranch, String title) {
return client.createMergeRequest(projectId, sourceBranch, targetBranch, title);
}
@Override
public Project getProject(String projectName) {
return client.getProject(projectName);
}
@Override
public Project updateProject(String projectId, String name, String path) {
return client.updateProject(projectId, name, path);
}
@Override
public void deleteProject(String projectId) {
client.deleteProject(projectId);
}
@Override
public void addProjectHook(String projectId, String url, Boolean pushEvents, Boolean mergeRequestEvents,
Boolean noteEvents) {
client.addProjectHook(projectId, url, pushEvents, mergeRequestEvents, noteEvents);
}
@Override
public void changeBuildStatus(String projectId, String sha, BuildState state, String ref, String context,
String targetUrl, String description) {
client.changeBuildStatus(projectId, sha, state, ref, context, targetUrl, description);
}
@Override
public void changeBuildStatus(Integer projectId, String sha, BuildState state, String ref, String context,
String targetUrl, String description) {
client.changeBuildStatus(projectId, sha, state, ref, context, targetUrl, description);
}
@Override
public void getCommit(String projectId, String sha) {
client.getCommit(projectId, sha);
}
@Override
public void acceptMergeRequest(Integer projectId, Integer mergeRequestId, String mergeCommitMessage,
boolean shouldRemoveSourceBranch) {
client.acceptMergeRequest(projectId, mergeRequestId, mergeCommitMessage, shouldRemoveSourceBranch);
}
@Override
public void createMergeRequestNote(Integer projectId, Integer mergeRequestId, String body) {
client.createMergeRequestNote(projectId, mergeRequestId, body);
}
@Override
public List<MergeRequest> getMergeRequests(String projectId, State state, int page, int perPage) {
return client.getMergeRequests(projectId, state, page, perPage);
}
@Override
public List<Branch> getBranches(String projectId) {
return client.getBranches(projectId);
}
@Override
public Branch getBranch(String projectId, String branch) {
return client.getBranch(projectId, branch);
}
@Override
public void headCurrentUser() {
client.headCurrentUser();
}
@Override
public User getCurrentUser() {
return client.getCurrentUser();
}
@Override
public User addUser(String email, String username, String name, String password) {
return client.addUser(email, username, name, password);
}
@Override
public User updateUser(String userId, String email, String username, String name, String password) {
return client.updateUser(userId, email, username, name, password);
}
@Override
public List<Label> getLabels(String projectId) {
return client.getLabels(projectId);
}
}

View File

@ -1,14 +1,54 @@
package com.dabsquared.gitlabjenkins.gitlab.api; package com.dabsquared.gitlabjenkins.gitlab.api;
/**
* Extends REST-client interface to provide additional methods for plugin's code. import com.dabsquared.gitlabjenkins.gitlab.api.model.*;
* import com.dabsquared.gitlabjenkins.gitlab.hook.model.State;
* @author Alexander Leshkin import org.kohsuke.accmod.Restricted;
* import org.kohsuke.accmod.restrictions.NoExternalUse;
*/
public interface GitLabApi extends GitLabApiClient { import javax.ws.rs.PathParam;
/** import java.util.List;
* Returns GitLab host base url from plugin confugruation.
*/
String getGitLabHostUrl(); @Restricted(NoExternalUse.class)
public interface GitLabApi {
Project createProject(String projectName);
MergeRequest createMergeRequest(Integer projectId, String sourceBranch, String targetBranch, String title);
Project getProject(String projectName);
Project updateProject(String projectId, String name, String path);
void deleteProject(String projectId);
void addProjectHook(String projectId, String url, Boolean pushEvents, Boolean mergeRequestEvents, Boolean noteEvents);
void changeBuildStatus(String projectId, String sha, BuildState state, String ref, String context, String targetUrl, String description);
void changeBuildStatus(Integer projectId, String sha, BuildState state, String ref, String context, String targetUrl, String description);
void getCommit(String projectId, String sha);
void acceptMergeRequest(Integer projectId, Integer mergeRequestId, String mergeCommitMessage, boolean shouldRemoveSourceBranch);
void createMergeRequestNote(Integer projectId, Integer mergeRequestId, String body);
List<MergeRequest> getMergeRequests(String projectId, State state, int page, int perPage);
List<Branch> getBranches(String projectId);
Branch getBranch(String projectId, String branch);
void headCurrentUser();
User getCurrentUser();
User addUser(String email, String username, String name, String password);
User updateUser(String userId, String email, String username, String name, String password);
List<Label> getLabels(String projectId);
List<Pipeline> getPipelines(@PathParam("projectId") String projectName);
} }

View File

@ -0,0 +1,125 @@
package com.dabsquared.gitlabjenkins.gitlab.api;
import com.dabsquared.gitlabjenkins.gitlab.api.model.*;
import com.dabsquared.gitlabjenkins.gitlab.hook.model.State;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.NoExternalUse;
import java.util.List;
public final class GitLabClient implements GitLabApi {
private final String hostUrl;
private final GitLabApi api;
@Restricted(NoExternalUse.class)
public GitLabClient(String hostUrl, GitLabApi api) {
this.hostUrl = hostUrl;
this.api = api;
}
public final String getHostUrl() {
return hostUrl;
}
@Override
public Project createProject(String projectName) {
return api.createProject(projectName);
}
@Override
public MergeRequest createMergeRequest(Integer projectId, String sourceBranch, String targetBranch, String title) {
return api.createMergeRequest(projectId, sourceBranch, targetBranch, title);
}
@Override
public Project getProject(String projectName) {
return api.getProject(projectName);
}
@Override
public Project updateProject(String projectId, String name, String path) {
return api.updateProject(projectId, name, path);
}
@Override
public void deleteProject(String projectId) {
api.deleteProject(projectId);
}
@Override
public void addProjectHook(String projectId, String url, Boolean pushEvents, Boolean mergeRequestEvents, Boolean noteEvents) {
api.addProjectHook(projectId, url, pushEvents, mergeRequestEvents, noteEvents);
}
@Override
public void changeBuildStatus(String projectId, String sha, BuildState state, String ref, String context, String targetUrl, String description) {
api.changeBuildStatus(projectId, sha, state, ref, context, targetUrl, description);
}
@Override
public void changeBuildStatus(Integer projectId, String sha, BuildState state, String ref, String context, String targetUrl, String description) {
api.changeBuildStatus(projectId, sha, state, ref, context, targetUrl, description);
}
@Override
public void getCommit(String projectId, String sha) {
api.getCommit(projectId, sha);
}
@Override
public void acceptMergeRequest(Integer projectId, Integer mergeRequestId, String mergeCommitMessage, boolean shouldRemoveSourceBranch) {
api.acceptMergeRequest(projectId, mergeRequestId, mergeCommitMessage, shouldRemoveSourceBranch);
}
@Override
public void createMergeRequestNote(Integer projectId, Integer mergeRequestId, String body) {
api.createMergeRequestNote(projectId, mergeRequestId, body);
}
@Override
public List<MergeRequest> getMergeRequests(String projectId, State state, int page, int perPage) {
return api.getMergeRequests(projectId, state, page, perPage);
}
@Override
public List<Branch> getBranches(String projectId) {
return api.getBranches(projectId);
}
@Override
public Branch getBranch(String projectId, String branch) {
return api.getBranch(projectId, branch);
}
@Override
public void headCurrentUser() {
api.headCurrentUser();
}
@Override
public User getCurrentUser() {
return api.getCurrentUser();
}
@Override
public User addUser(String email, String username, String name, String password) {
return api.addUser(email, username, name, password);
}
@Override
public User updateUser(String userId, String email, String username, String name, String password) {
return api.updateUser(userId, email, username, name, password);
}
@Override
public List<Label> getLabels(String projectId) {
return api.getLabels(projectId);
}
@Override
public List<Pipeline> getPipelines(String projectName) {
return api.getPipelines(projectName);
}
}

View File

@ -0,0 +1,53 @@
package com.dabsquared.gitlabjenkins.gitlab.api;
import hudson.ExtensionPoint;
import jenkins.model.Jenkins;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.NoExternalUse;
import javax.annotation.Nonnull;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.NoSuchElementException;
import static java.util.Collections.sort;
@Restricted(NoExternalUse.class)
public abstract class GitLabClientBuilder implements Comparable<GitLabClientBuilder>, ExtensionPoint, Serializable {
public static GitLabClientBuilder getGitLabClientBuilderById(String id) {
for (GitLabClientBuilder provider : getAllGitLabClientBuilders()) {
if (provider.id().equals(id)) {
return provider;
}
}
throw new NoSuchElementException("unknown client-builder-id: " + id);
}
public static List<GitLabClientBuilder> getAllGitLabClientBuilders() {
List<GitLabClientBuilder> builders = new ArrayList<>(Jenkins.getInstance().getExtensionList(GitLabClientBuilder.class));
sort(builders);
return builders;
}
private final String id;
protected GitLabClientBuilder(String id) {
this.id = id;
}
@Nonnull
public final String id() {
return id;
}
@Nonnull
public abstract GitLabClient buildClient(String url, String token, boolean ignoreCertificateErrors, int connectionTimeout, int readTimeout);
@Override
public final int compareTo(@Nonnull GitLabClientBuilder other) {
return id().compareTo(other.id());
}
}

View File

@ -0,0 +1,53 @@
package com.dabsquared.gitlabjenkins.gitlab.api.impl;
import com.dabsquared.gitlabjenkins.gitlab.api.GitLabClient;
import com.dabsquared.gitlabjenkins.gitlab.api.GitLabClientBuilder;
import hudson.Extension;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.NoExternalUse;
import javax.annotation.Nonnull;
import javax.ws.rs.NotFoundException;
import java.util.NoSuchElementException;
@Extension
@Restricted(NoExternalUse.class)
public final class AutodetectGitLabClientBuilder extends GitLabClientBuilder {
public AutodetectGitLabClientBuilder() {
super("autodetect");
}
@Override
@Nonnull
public GitLabClient buildClient(String url, String token, boolean ignoreCertificateErrors, int connectionTimeout, int readTimeout) {
return autodetectOrDie(url, token, ignoreCertificateErrors, connectionTimeout, readTimeout);
}
private GitLabClient autodetectOrDie(String url, String token, boolean ignoreCertificateErrors, int connectionTimeout, int readTimeout) {
GitLabClient client = autodetect(url, token, ignoreCertificateErrors, connectionTimeout, readTimeout);
if (client != null) {
return client;
}
throw new NoSuchElementException("no client-builder found that supports server at " + url);
}
private GitLabClient autodetect(String url, String token, boolean ignoreCertificateErrors, int connectionTimeout, int readTimeout) {
for (GitLabClientBuilder candidate : getAllGitLabClientBuilders()) {
if (candidate == this) {
continue; // ignore ourself...
}
GitLabClient client = candidate.buildClient(url, token, ignoreCertificateErrors, connectionTimeout, readTimeout);
try {
client.headCurrentUser();
return client;
} catch (NotFoundException ignored) {
// api-endpoint not found (== api-level not supported by this client)
}
}
return null;
}
}

View File

@ -1,12 +1,10 @@
package com.dabsquared.gitlabjenkins.gitlab; package com.dabsquared.gitlabjenkins.gitlab.api.impl;
import com.cloudbees.plugins.credentials.CredentialsMatchers;
import com.cloudbees.plugins.credentials.common.StandardCredentials; import com.dabsquared.gitlabjenkins.gitlab.JacksonConfig;
import com.cloudbees.plugins.credentials.domains.DomainRequirement;
import com.dabsquared.gitlabjenkins.connection.GitLabApiToken;
import com.dabsquared.gitlabjenkins.connection.GitLabConnection;
import com.dabsquared.gitlabjenkins.gitlab.api.GitLabApi; import com.dabsquared.gitlabjenkins.gitlab.api.GitLabApi;
import com.dabsquared.gitlabjenkins.gitlab.api.GitLabApiClient; import com.dabsquared.gitlabjenkins.gitlab.api.GitLabClient;
import com.dabsquared.gitlabjenkins.gitlab.api.GitLabClientBuilder;
import com.dabsquared.gitlabjenkins.util.JsonUtil; import com.dabsquared.gitlabjenkins.util.JsonUtil;
import com.dabsquared.gitlabjenkins.util.LoggerUtil; import com.dabsquared.gitlabjenkins.util.LoggerUtil;
import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider; import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider;
@ -16,8 +14,6 @@ import com.google.common.collect.FluentIterable;
import hudson.ProxyConfiguration; import hudson.ProxyConfiguration;
import hudson.init.InitMilestone; import hudson.init.InitMilestone;
import hudson.init.Initializer; import hudson.init.Initializer;
import hudson.model.Item;
import hudson.security.ACL;
import jenkins.model.Jenkins; import jenkins.model.Jenkins;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
@ -30,8 +26,10 @@ import org.jboss.resteasy.client.jaxrs.ClientHttpEngine;
import org.jboss.resteasy.client.jaxrs.engines.ApacheHttpClient4Engine; import org.jboss.resteasy.client.jaxrs.engines.ApacheHttpClient4Engine;
import org.jboss.resteasy.plugins.providers.JaxrsFormProvider; import org.jboss.resteasy.plugins.providers.JaxrsFormProvider;
import org.jboss.resteasy.spi.ResteasyProviderFactory; import org.jboss.resteasy.spi.ResteasyProviderFactory;
import org.jenkinsci.plugins.plaincredentials.StringCredentials; import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.NoExternalUse;
import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.annotation.Priority; import javax.annotation.Priority;
import javax.ws.rs.Priorities; import javax.ws.rs.Priorities;
@ -47,96 +45,92 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.net.MalformedURLException; import java.net.MalformedURLException;
import java.net.Proxy;
import java.net.URL; import java.net.URL;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import static com.cloudbees.plugins.credentials.CredentialsProvider.lookupCredentials; @Restricted(NoExternalUse.class)
public class ResteasyGitLabClientBuilder extends GitLabClientBuilder {
/** private static final Logger LOGGER = Logger.getLogger(ResteasyGitLabClientBuilder.class.getName());
* @author Robin Müller
*/
public class GitLabClientBuilder {
private final static Logger LOGGER = Logger.getLogger(GitLabClientBuilder.class.getName());
private static final String PRIVATE_TOKEN = "PRIVATE-TOKEN"; private static final String PRIVATE_TOKEN = "PRIVATE-TOKEN";
public static GitLabApi buildClient(String gitlabHostUrl, final String gitlabApiTokenId, boolean ignoreCertificateErrors, int connectionTimeout, int readTimeout) {
ResteasyClientBuilder builder = new ResteasyClientBuilder();
if (ignoreCertificateErrors) {
builder.hostnameVerification(ResteasyClientBuilder.HostnameVerificationPolicy.ANY);
builder.disableTrustManager();
}
ProxyConfiguration proxyConfiguration = Jenkins.getActiveInstance().proxy;
Proxy proxy = proxyConfiguration == null ? Proxy.NO_PROXY : proxyConfiguration.createProxy(getHost(gitlabHostUrl));
if (!proxy.equals(Proxy.NO_PROXY)) {
InetSocketAddress address = (InetSocketAddress) proxy.address();
builder.defaultProxy(address.getHostName().replaceFirst("^.*://", ""),
address.getPort(),
address.getHostName().startsWith("https") ? "https" : "http",
proxyConfiguration.getUserName(),
proxyConfiguration.getPassword());
}
GitLabApiClient apiClient = builder
.connectionPoolSize(60)
.maxPooledPerRoute(30)
.establishConnectionTimeout(connectionTimeout, TimeUnit.SECONDS)
.socketTimeout(readTimeout, TimeUnit.SECONDS)
.register(new JacksonJsonProvider())
.register(new JacksonConfig())
.register(new ApiHeaderTokenFilter(getApiToken(gitlabApiTokenId)))
.register(new LoggingFilter())
.register(new RemoveAcceptEncodingFilter())
.register(new JaxrsFormProvider())
.build().target(gitlabHostUrl)
.proxyBuilder(GitLabApiClient.class)
.classloader(GitLabApiClient.class.getClassLoader())
.build();
return new GitLabApiExtension(apiClient, gitlabHostUrl);
}
public static GitLabApi buildClient(GitLabConnection connection) {
return buildClient(connection.getUrl(),
connection.getApiTokenId(),
connection.isIgnoreCertificateErrors(),
connection.getConnectionTimeout(),
connection.getReadTimeout());
}
@Initializer(before = InitMilestone.PLUGINS_STARTED) @Initializer(before = InitMilestone.PLUGINS_STARTED)
public static void setRuntimeDelegate() { public static void setRuntimeDelegate() {
RuntimeDelegate.setInstance(new ResteasyProviderFactory()); RuntimeDelegate.setInstance(new ResteasyProviderFactory());
} }
private static String getHost(String gitlabUrl) { private final Class<? extends GitLabApi> apiProxyClass;
ResteasyGitLabClientBuilder(String id, Class<? extends GitLabApi> apiProxyClass) {
super(id);
this.apiProxyClass = apiProxyClass;
}
@Nonnull
@Override
public final GitLabClient buildClient(String url, String token, boolean ignoreCertificateErrors, int connectionTimeout, int readTimeout) {
return buildClient(url, token, apiProxyClass, ignoreCertificateErrors, connectionTimeout, readTimeout);
}
private GitLabClient buildClient(String url, String apiToken, Class<? extends GitLabApi> proxyClass, boolean ignoreCertificateErrors, int connectionTimeout, int readTimeout) {
Jenkins jenkins = Jenkins.getInstance();
ProxyConfiguration proxy = (jenkins != null) ? jenkins.proxy : null;
return buildClient(
url,
apiToken,
proxyClass,
proxy,
ignoreCertificateErrors,
connectionTimeout,
readTimeout
);
}
private GitLabClient buildClient(String url, String apiToken, Class<? extends GitLabApi> proxyClass, ProxyConfiguration httpProxyConfig, boolean ignoreCertificateErrors, int connectionTimeout, int readTimeout) {
ResteasyClientBuilder builder = new ResteasyClientBuilder();
if (ignoreCertificateErrors) {
builder.hostnameVerification(ResteasyClientBuilder.HostnameVerificationPolicy.ANY);
builder.disableTrustManager();
}
if (httpProxyConfig != null) {
InetSocketAddress address = (InetSocketAddress) httpProxyConfig.createProxy(getHost(url)).address();
builder.defaultProxy(address.getHostName().replaceFirst("^.*://", ""),
address.getPort(),
address.getHostName().startsWith("https") ? "https" : "http",
httpProxyConfig.getUserName(),
httpProxyConfig.getPassword());
}
GitLabApi apiProxy = builder
.connectionPoolSize(60)
.maxPooledPerRoute(30)
.establishConnectionTimeout(connectionTimeout, TimeUnit.SECONDS)
.socketTimeout(readTimeout, TimeUnit.SECONDS)
.register(new JacksonJsonProvider())
.register(new JacksonConfig())
.register(new ApiHeaderTokenFilter(apiToken))
.register(new LoggingFilter())
.register(new RemoveAcceptEncodingFilter())
.register(new JaxrsFormProvider())
.build().target(url)
.proxyBuilder(proxyClass)
.classloader(proxyClass.getClassLoader())
.build();
return new GitLabClient(url, apiProxy);
}
private String getHost(String url) {
try { try {
return new URL(gitlabUrl).getHost(); return new URL(url).getHost();
} catch (MalformedURLException e) { } catch (MalformedURLException e) {
return null; return null;
} }
} }
private static String getApiToken(String apiTokenId) {
StandardCredentials credentials = CredentialsMatchers.firstOrNull(
lookupCredentials(StandardCredentials.class, (Item) null, ACL.SYSTEM, new ArrayList<DomainRequirement>()),
CredentialsMatchers.withId(apiTokenId));
if (credentials != null) {
if (credentials instanceof GitLabApiToken) {
return ((GitLabApiToken) credentials).getApiToken().getPlainText();
}
if (credentials instanceof StringCredentials) {
return ((StringCredentials) credentials).getSecret().getPlainText();
}
}
throw new IllegalStateException("No credentials found for credentialsId: " + apiTokenId);
}
@Priority(Priorities.HEADER_DECORATOR) @Priority(Priorities.HEADER_DECORATOR)
private static class ApiHeaderTokenFilter implements ClientRequestFilter { private static class ApiHeaderTokenFilter implements ClientRequestFilter {
private final String gitlabApiToken; private final String gitlabApiToken;
@ -232,7 +226,6 @@ public class GitLabClientBuilder {
} }
private static class ResteasyClientBuilder extends org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder { private static class ResteasyClientBuilder extends org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder {
private CredentialsProvider proxyCredentials; private CredentialsProvider proxyCredentials;
ResteasyClientBuilder defaultProxy(String hostname, int port, final String scheme, String username, String password) { ResteasyClientBuilder defaultProxy(String hostname, int port, final String scheme, String username, String password) {
@ -244,6 +237,7 @@ public class GitLabClientBuilder {
return this; return this;
} }
@SuppressWarnings("deprecation")
@Override @Override
protected ClientHttpEngine initDefaultEngine() { protected ClientHttpEngine initDefaultEngine() {
ApacheHttpClient4Engine httpEngine = (ApacheHttpClient4Engine) super.initDefaultEngine(); ApacheHttpClient4Engine httpEngine = (ApacheHttpClient4Engine) super.initDefaultEngine();

View File

@ -1,11 +1,8 @@
package com.dabsquared.gitlabjenkins.gitlab.api; package com.dabsquared.gitlabjenkins.gitlab.api.impl;
import com.dabsquared.gitlabjenkins.gitlab.api.model.Branch;
import com.dabsquared.gitlabjenkins.gitlab.api.model.BuildState; import com.dabsquared.gitlabjenkins.gitlab.api.GitLabApi;
import com.dabsquared.gitlabjenkins.gitlab.api.model.Label; import com.dabsquared.gitlabjenkins.gitlab.api.model.*;
import com.dabsquared.gitlabjenkins.gitlab.api.model.MergeRequest;
import com.dabsquared.gitlabjenkins.gitlab.api.model.Project;
import com.dabsquared.gitlabjenkins.gitlab.api.model.User;
import com.dabsquared.gitlabjenkins.gitlab.hook.model.State; import com.dabsquared.gitlabjenkins.gitlab.hook.model.State;
import javax.ws.rs.Consumes; import javax.ws.rs.Consumes;
@ -22,22 +19,26 @@ import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MediaType;
import java.util.List; import java.util.List;
import static com.dabsquared.gitlabjenkins.gitlab.api.impl.V3GitLabClientBuilder.ID;
/** /**
* @author Robin Müller * @author Robin Müller
*/ */
@Path("/api/v3") @Path("/api/" + ID)
public interface GitLabApiClient { interface V3GitLabApiProxy extends GitLabApi {
@POST @POST
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_FORM_URLENCODED) @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@Path("/projects") @Path("/projects")
@Override
Project createProject(@FormParam("name") String projectName); Project createProject(@FormParam("name") String projectName);
@POST @POST
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_FORM_URLENCODED) @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@Path("/projects/{projectId}/merge_requests") @Path("/projects/{projectId}/merge_requests")
@Override
MergeRequest createMergeRequest( MergeRequest createMergeRequest(
@PathParam("projectId") Integer projectId, @PathParam("projectId") Integer projectId,
@FormParam("source_branch") String sourceBranch, @FormParam("source_branch") String sourceBranch,
@ -47,24 +48,28 @@ public interface GitLabApiClient {
@GET @GET
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
@Path("/projects/{projectName}") @Path("/projects/{projectName}")
@Override
Project getProject(@PathParam("projectName") String projectName); Project getProject(@PathParam("projectName") String projectName);
@PUT @PUT
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_FORM_URLENCODED) @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@Path("/projects/{projectId}") @Path("/projects/{projectId}")
@Override
Project updateProject(@PathParam("projectId") String projectId, Project updateProject(@PathParam("projectId") String projectId,
@FormParam("name") String name, @FormParam("name") String name,
@FormParam("path") String path); @FormParam("path") String path);
@DELETE @DELETE
@Path("/projects/{projectId}") @Path("/projects/{projectId}")
@Override
void deleteProject(@PathParam("projectId") String projectId); void deleteProject(@PathParam("projectId") String projectId);
@POST @POST
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_FORM_URLENCODED) @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@Path("/projects/{projectId}/hooks") @Path("/projects/{projectId}/hooks")
@Override
void addProjectHook(@PathParam("projectId") String projectId, void addProjectHook(@PathParam("projectId") String projectId,
@FormParam("url") String url, @FormParam("url") String url,
@FormParam("push_events") Boolean pushEvents, @FormParam("push_events") Boolean pushEvents,
@ -75,6 +80,7 @@ public interface GitLabApiClient {
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_FORM_URLENCODED) @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@Path("/projects/{projectId}/statuses/{sha}") @Path("/projects/{projectId}/statuses/{sha}")
@Override
void changeBuildStatus(@PathParam("projectId") String projectId, void changeBuildStatus(@PathParam("projectId") String projectId,
@PathParam("sha") String sha, @PathParam("sha") String sha,
@FormParam("state") BuildState state, @FormParam("state") BuildState state,
@ -87,6 +93,7 @@ public interface GitLabApiClient {
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_FORM_URLENCODED) @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@Path("/projects/{projectId}/statuses/{sha}") @Path("/projects/{projectId}/statuses/{sha}")
@Override
void changeBuildStatus(@PathParam("projectId") Integer projectId, void changeBuildStatus(@PathParam("projectId") Integer projectId,
@PathParam("sha") String sha, @PathParam("sha") String sha,
@FormParam("state") BuildState state, @FormParam("state") BuildState state,
@ -98,6 +105,7 @@ public interface GitLabApiClient {
@GET @GET
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
@Path("/projects/{projectId}/repository/commits/{sha}") @Path("/projects/{projectId}/repository/commits/{sha}")
@Override
void getCommit(@PathParam("projectId") String projectId, @PathParam("sha") String sha); void getCommit(@PathParam("projectId") String projectId, @PathParam("sha") String sha);
@ -105,6 +113,7 @@ public interface GitLabApiClient {
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_FORM_URLENCODED) @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@Path("/projects/{projectId}/merge_requests/{mergeRequestId}/merge") @Path("/projects/{projectId}/merge_requests/{mergeRequestId}/merge")
@Override
void acceptMergeRequest(@PathParam("projectId") Integer projectId, void acceptMergeRequest(@PathParam("projectId") Integer projectId,
@PathParam("mergeRequestId") Integer mergeRequestId, @PathParam("mergeRequestId") Integer mergeRequestId,
@FormParam("merge_commit_message") String mergeCommitMessage, @FormParam("merge_commit_message") String mergeCommitMessage,
@ -114,6 +123,7 @@ public interface GitLabApiClient {
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_FORM_URLENCODED) @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@Path("/projects/{projectId}/merge_requests/{mergeRequestId}/notes") @Path("/projects/{projectId}/merge_requests/{mergeRequestId}/notes")
@Override
void createMergeRequestNote(@PathParam("projectId") Integer projectId, void createMergeRequestNote(@PathParam("projectId") Integer projectId,
@PathParam("mergeRequestId") Integer mergeRequestId, @PathParam("mergeRequestId") Integer mergeRequestId,
@FormParam("body") String body); @FormParam("body") String body);
@ -121,6 +131,7 @@ public interface GitLabApiClient {
@GET @GET
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
@Path("/projects/{projectId}/merge_requests") @Path("/projects/{projectId}/merge_requests")
@Override
List<MergeRequest> getMergeRequests(@PathParam("projectId") String projectId, List<MergeRequest> getMergeRequests(@PathParam("projectId") String projectId,
@QueryParam("state") State state, @QueryParam("state") State state,
@QueryParam("page") int page, @QueryParam("page") int page,
@ -129,28 +140,33 @@ public interface GitLabApiClient {
@GET @GET
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
@Path("/projects/{projectId}/repository/branches") @Path("/projects/{projectId}/repository/branches")
@Override
List<Branch> getBranches(@PathParam("projectId") String projectId); List<Branch> getBranches(@PathParam("projectId") String projectId);
@GET @GET
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
@Path("/projects/{projectId}/repository/branches/{branch}") @Path("/projects/{projectId}/repository/branches/{branch}")
@Override
Branch getBranch(@PathParam("projectId") String projectId, Branch getBranch(@PathParam("projectId") String projectId,
@PathParam("branch") String branch); @PathParam("branch") String branch);
@HEAD @HEAD
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
@Path("/user") @Path("/user")
@Override
void headCurrentUser(); void headCurrentUser();
@GET @GET
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
@Path("/user") @Path("/user")
@Override
User getCurrentUser(); User getCurrentUser();
@POST @POST
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_FORM_URLENCODED) @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@Path("/users") @Path("/users")
@Override
User addUser(@FormParam("email") String email, User addUser(@FormParam("email") String email,
@FormParam("username") String username, @FormParam("username") String username,
@FormParam("name") String name, @FormParam("name") String name,
@ -160,6 +176,7 @@ public interface GitLabApiClient {
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_FORM_URLENCODED) @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@Path("/users/{userId}") @Path("/users/{userId}")
@Override
User updateUser(@PathParam("userId") String userId, User updateUser(@PathParam("userId") String userId,
@FormParam("email") String email, @FormParam("email") String email,
@FormParam("username") String username, @FormParam("username") String username,
@ -169,5 +186,12 @@ public interface GitLabApiClient {
@GET @GET
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
@Path("/projects/{projectId}/labels") @Path("/projects/{projectId}/labels")
@Override
List<Label> getLabels(@PathParam("projectId") String projectId); List<Label> getLabels(@PathParam("projectId") String projectId);
@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("/projects/{projectId}/pipelines")
@Override
List<Pipeline> getPipelines(@PathParam("projectId") String projectId);
} }

View File

@ -0,0 +1,17 @@
package com.dabsquared.gitlabjenkins.gitlab.api.impl;
import hudson.Extension;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.NoExternalUse;
@Extension
@Restricted(NoExternalUse.class)
public final class V3GitLabClientBuilder extends ResteasyGitLabClientBuilder {
static final String ID = "v3";
public V3GitLabClientBuilder() {
super(ID, V3GitLabApiProxy.class);
}
}

View File

@ -0,0 +1,197 @@
package com.dabsquared.gitlabjenkins.gitlab.api.impl;
import com.dabsquared.gitlabjenkins.gitlab.api.GitLabApi;
import com.dabsquared.gitlabjenkins.gitlab.api.model.*;
import com.dabsquared.gitlabjenkins.gitlab.hook.model.State;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.FormParam;
import javax.ws.rs.GET;
import javax.ws.rs.HEAD;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import java.util.List;
import static com.dabsquared.gitlabjenkins.gitlab.api.impl.V4GitLabClientBuilder.ID;
/**
* @author Robin Müller
*/
@Path("/api/" + ID)
interface V4GitLabApiProxy extends GitLabApi {
@POST
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@Path("/projects")
@Override
Project createProject(@FormParam("name") String projectName);
@POST
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@Path("/projects/{projectId}/merge_requests")
@Override
MergeRequest createMergeRequest(
@PathParam("projectId") Integer projectId,
@FormParam("source_branch") String sourceBranch,
@FormParam("target_branch") String targetBranch,
@FormParam("title") String title);
@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("/projects/{projectName}")
@Override
Project getProject(@PathParam("projectName") String projectName);
@PUT
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@Path("/projects/{projectId}")
@Override
Project updateProject(@PathParam("projectId") String projectId,
@FormParam("name") String name,
@FormParam("path") String path);
@DELETE
@Path("/projects/{projectId}")
@Override
void deleteProject(@PathParam("projectId") String projectId);
@POST
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@Path("/projects/{projectId}/hooks")
@Override
void addProjectHook(@PathParam("projectId") String projectId,
@FormParam("url") String url,
@FormParam("push_events") Boolean pushEvents,
@FormParam("merge_requests_events") Boolean mergeRequestEvents,
@FormParam("note_events") Boolean noteEvents);
@POST
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@Path("/projects/{projectId}/statuses/{sha}")
@Override
void changeBuildStatus(@PathParam("projectId") String projectId,
@PathParam("sha") String sha,
@FormParam("state") BuildState state,
@FormParam("ref") String ref,
@FormParam("context") String context,
@FormParam("target_url") String targetUrl,
@FormParam("description") String description);
@POST
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@Path("/projects/{projectId}/statuses/{sha}")
@Override
void changeBuildStatus(@PathParam("projectId") Integer projectId,
@PathParam("sha") String sha,
@FormParam("state") BuildState state,
@FormParam("ref") String ref,
@FormParam("context") String context,
@FormParam("target_url") String targetUrl,
@FormParam("description") String description);
@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("/projects/{projectId}/repository/commits/{sha}")
@Override
void getCommit(@PathParam("projectId") String projectId, @PathParam("sha") String sha);
@PUT
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@Path("/projects/{projectId}/merge_requests/{mergeRequestIid}/merge")
@Override
void acceptMergeRequest(@PathParam("projectId") Integer projectId,
@PathParam("mergeRequestIid") Integer mergeRequestIid,
@FormParam("merge_commit_message") String mergeCommitMessage,
@FormParam("should_remove_source_branch") boolean shouldRemoveSourceBranch);
@POST
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@Path("/projects/{projectId}/merge_requests/{mergeRequestIid}/notes")
@Override
void createMergeRequestNote(@PathParam("projectId") Integer projectId,
@PathParam("mergeRequestIid") Integer mergeRequestIid,
@FormParam("body") String body);
@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("/projects/{projectId}/merge_requests")
@Override
List<MergeRequest> getMergeRequests(@PathParam("projectId") String projectId,
@QueryParam("state") State state,
@QueryParam("page") int page,
@QueryParam("per_page") int perPage);
@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("/projects/{projectId}/repository/branches")
@Override
List<Branch> getBranches(@PathParam("projectId") String projectId);
@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("/projects/{projectId}/repository/branches/{branch}")
@Override
Branch getBranch(@PathParam("projectId") String projectId,
@PathParam("branch") String branch);
@HEAD
@Produces(MediaType.APPLICATION_JSON)
@Path("/user")
@Override
void headCurrentUser();
@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("/user")
@Override
User getCurrentUser();
@POST
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@Path("/users")
@Override
User addUser(@FormParam("email") String email,
@FormParam("username") String username,
@FormParam("name") String name,
@FormParam("password") String password);
@PUT
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@Path("/users/{userId}")
@Override
User updateUser(@PathParam("userId") String userId,
@FormParam("email") String email,
@FormParam("username") String username,
@FormParam("name") String name,
@FormParam("password") String password);
@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("/projects/{projectId}/labels")
@Override
List<Label> getLabels(@PathParam("projectId") String projectId);
@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("/projects/{projectId}/pipelines")
@Override
List<Pipeline> getPipelines(@PathParam("projectId") String projectId);
}

View File

@ -0,0 +1,17 @@
package com.dabsquared.gitlabjenkins.gitlab.api.impl;
import hudson.Extension;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.NoExternalUse;
@Extension
@Restricted(NoExternalUse.class)
public final class V4GitLabClientBuilder extends ResteasyGitLabClientBuilder {
static final String ID = "v4";
public V4GitLabClientBuilder() {
super(ID, V4GitLabApiProxy.class);
}
}

View File

@ -0,0 +1,34 @@
package com.dabsquared.gitlabjenkins.gitlab.api.model;
import net.karneim.pojobuilder.GeneratePojoBuilder;
@GeneratePojoBuilder(intoPackage = "*.builder.generated", withFactoryMethod = "*")
public class Pipeline {
private Integer id;
private String sha;
private String status;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getSha() {
return sha;
}
public void setSha(String sha) {
this.sha = sha;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
}

View File

@ -1,6 +1,7 @@
package com.dabsquared.gitlabjenkins.publisher; package com.dabsquared.gitlabjenkins.publisher;
import com.dabsquared.gitlabjenkins.gitlab.api.GitLabApi;
import com.dabsquared.gitlabjenkins.gitlab.api.GitLabClient;
import hudson.Extension; import hudson.Extension;
import hudson.model.AbstractProject; import hudson.model.AbstractProject;
import hudson.model.Result; import hudson.model.Result;
@ -44,7 +45,7 @@ public class GitLabAcceptMergeRequestPublisher extends MergeRequestNotifier {
} }
@Override @Override
protected void perform(Run<?, ?> build, TaskListener listener, GitLabApi client, Integer projectId, Integer mergeRequestId) { protected void perform(Run<?, ?> build, TaskListener listener, GitLabClient client, Integer projectId, Integer mergeRequestId) {
try { try {
if (build.getResult() == Result.SUCCESS) { if (build.getResult() == Result.SUCCESS) {
client.acceptMergeRequest(projectId, mergeRequestId, "Merge Request accepted by jenkins build success", false); client.acceptMergeRequest(projectId, mergeRequestId, "Merge Request accepted by jenkins build success", false);

View File

@ -1,6 +1,7 @@
package com.dabsquared.gitlabjenkins.publisher; package com.dabsquared.gitlabjenkins.publisher;
import com.dabsquared.gitlabjenkins.gitlab.api.GitLabApi;
import com.dabsquared.gitlabjenkins.gitlab.api.GitLabClient;
import hudson.Extension; import hudson.Extension;
import hudson.Util; import hudson.Util;
import hudson.model.AbstractProject; import hudson.model.AbstractProject;
@ -107,7 +108,7 @@ public class GitLabMessagePublisher extends MergeRequestNotifier {
} }
@Override @Override
protected void perform(Run<?, ?> build, TaskListener listener, GitLabApi client, Integer projectId, Integer mergeRequestId) { protected void perform(Run<?, ?> build, TaskListener listener, GitLabClient client, Integer projectId, Integer mergeRequestId) {
try { try {
if (!onlyForFailure || build.getResult() == Result.FAILURE || build.getResult() == Result.UNSTABLE) { if (!onlyForFailure || build.getResult() == Result.FAILURE || build.getResult() == Result.UNSTABLE) {
client.createMergeRequestNote(projectId, mergeRequestId, getNote(build, listener)); client.createMergeRequestNote(projectId, mergeRequestId, getNote(build, listener));

View File

@ -1,6 +1,7 @@
package com.dabsquared.gitlabjenkins.publisher; package com.dabsquared.gitlabjenkins.publisher;
import com.dabsquared.gitlabjenkins.gitlab.api.GitLabApi;
import com.dabsquared.gitlabjenkins.gitlab.api.GitLabClient;
import hudson.Extension; import hudson.Extension;
import hudson.model.AbstractProject; import hudson.model.AbstractProject;
import hudson.model.Result; import hudson.model.Result;
@ -44,7 +45,7 @@ public class GitLabVotePublisher extends MergeRequestNotifier {
} }
@Override @Override
protected void perform(Run<?, ?> build, TaskListener listener, GitLabApi client, Integer projectId, Integer mergeRequestId) { protected void perform(Run<?, ?> build, TaskListener listener, GitLabClient client, Integer projectId, Integer mergeRequestId) {
try { try {
client.createMergeRequestNote(projectId, mergeRequestId, getResultIcon(build.getResult())); client.createMergeRequestNote(projectId, mergeRequestId, getResultIcon(build.getResult()));
} catch (WebApplicationException | ProcessingException e) { } catch (WebApplicationException | ProcessingException e) {

View File

@ -1,7 +1,7 @@
package com.dabsquared.gitlabjenkins.publisher; package com.dabsquared.gitlabjenkins.publisher;
import com.dabsquared.gitlabjenkins.cause.GitLabWebHookCause; import com.dabsquared.gitlabjenkins.cause.GitLabWebHookCause;
import com.dabsquared.gitlabjenkins.gitlab.api.GitLabApi; import com.dabsquared.gitlabjenkins.gitlab.api.GitLabClient;
import hudson.Launcher; import hudson.Launcher;
import hudson.matrix.MatrixAggregatable; import hudson.matrix.MatrixAggregatable;
import hudson.matrix.MatrixAggregator; import hudson.matrix.MatrixAggregator;
@ -27,7 +27,7 @@ public abstract class MergeRequestNotifier extends Notifier implements MatrixAgg
@Override @Override
public boolean perform(AbstractBuild<?, ?> build, Launcher launcher, BuildListener listener) throws InterruptedException, IOException { public boolean perform(AbstractBuild<?, ?> build, Launcher launcher, BuildListener listener) throws InterruptedException, IOException {
GitLabApi client = getClient(build); GitLabClient client = getClient(build);
if (client == null) { if (client == null) {
listener.getLogger().println("No GitLab connection configured"); listener.getLogger().println("No GitLab connection configured");
return true; return true;
@ -50,7 +50,7 @@ public abstract class MergeRequestNotifier extends Notifier implements MatrixAgg
}; };
} }
protected abstract void perform(Run<?, ?> build, TaskListener listener, GitLabApi client, Integer projectId, Integer mergeRequestId); protected abstract void perform(Run<?, ?> build, TaskListener listener, GitLabClient client, Integer projectId, Integer mergeRequestId);
Integer getProjectId(Run<?, ?> build) { Integer getProjectId(Run<?, ?> build) {
GitLabWebHookCause cause = build.getCause(GitLabWebHookCause.class); GitLabWebHookCause cause = build.getCause(GitLabWebHookCause.class);

View File

@ -1,6 +1,7 @@
package com.dabsquared.gitlabjenkins.service; package com.dabsquared.gitlabjenkins.service;
import com.dabsquared.gitlabjenkins.gitlab.api.GitLabApi;
import com.dabsquared.gitlabjenkins.gitlab.api.GitLabClient;
import com.dabsquared.gitlabjenkins.gitlab.api.model.Branch; import com.dabsquared.gitlabjenkins.gitlab.api.model.Branch;
import com.dabsquared.gitlabjenkins.util.LoggerUtil; import com.dabsquared.gitlabjenkins.util.LoggerUtil;
import com.dabsquared.gitlabjenkins.util.ProjectIdUtil; import com.dabsquared.gitlabjenkins.util.ProjectIdUtil;
@ -37,7 +38,7 @@ public class GitLabProjectBranchesService {
return gitLabProjectBranchesService; return gitLabProjectBranchesService;
} }
public List<String> getBranches(GitLabApi client, String sourceRepositoryString) { public List<String> getBranches(GitLabClient client, String sourceRepositoryString) {
synchronized (projectBranchCache) { synchronized (projectBranchCache) {
try { try {
return projectBranchCache.get(sourceRepositoryString, new BranchNamesLoader(client, sourceRepositoryString)); return projectBranchCache.get(sourceRepositoryString, new BranchNamesLoader(client, sourceRepositoryString));
@ -54,10 +55,10 @@ public class GitLabProjectBranchesService {
} }
private static class BranchNamesLoader implements Callable<List<String>> { private static class BranchNamesLoader implements Callable<List<String>> {
private final GitLabApi client; private final GitLabClient client;
private final String sourceRepository; private final String sourceRepository;
private BranchNamesLoader(GitLabApi client, String sourceRepository) { private BranchNamesLoader(GitLabClient client, String sourceRepository) {
this.client = client; this.client = client;
this.sourceRepository = sourceRepository; this.sourceRepository = sourceRepository;
} }

View File

@ -1,6 +1,7 @@
package com.dabsquared.gitlabjenkins.service; package com.dabsquared.gitlabjenkins.service;
import com.dabsquared.gitlabjenkins.gitlab.api.GitLabApi;
import com.dabsquared.gitlabjenkins.gitlab.api.GitLabClient;
import com.dabsquared.gitlabjenkins.gitlab.api.model.Label; import com.dabsquared.gitlabjenkins.gitlab.api.model.Label;
import com.dabsquared.gitlabjenkins.util.LoggerUtil; import com.dabsquared.gitlabjenkins.util.LoggerUtil;
import com.dabsquared.gitlabjenkins.util.ProjectIdUtil; import com.dabsquared.gitlabjenkins.util.ProjectIdUtil;
@ -37,7 +38,7 @@ public class GitLabProjectLabelsService {
return instance; return instance;
} }
public List<String> getLabels(GitLabApi client, String sourceRepositoryString) { public List<String> getLabels(GitLabClient client, String sourceRepositoryString) {
synchronized (projectLabelsCache) { synchronized (projectLabelsCache) {
try { try {
return projectLabelsCache.get(sourceRepositoryString, new LabelNamesLoader(client, sourceRepositoryString)); return projectLabelsCache.get(sourceRepositoryString, new LabelNamesLoader(client, sourceRepositoryString));
@ -54,10 +55,10 @@ public class GitLabProjectLabelsService {
} }
private static class LabelNamesLoader implements Callable<List<String>> { private static class LabelNamesLoader implements Callable<List<String>> {
private final GitLabApi client; private final GitLabClient client;
private final String sourceRepository; private final String sourceRepository;
private LabelNamesLoader(GitLabApi client, String sourceRepository) { private LabelNamesLoader(GitLabClient client, String sourceRepository) {
this.client = client; this.client = client;
this.sourceRepository = sourceRepository; this.sourceRepository = sourceRepository;
} }

View File

@ -3,7 +3,7 @@ package com.dabsquared.gitlabjenkins.trigger.handler;
import com.dabsquared.gitlabjenkins.cause.CauseData; import com.dabsquared.gitlabjenkins.cause.CauseData;
import com.dabsquared.gitlabjenkins.cause.GitLabWebHookCause; import com.dabsquared.gitlabjenkins.cause.GitLabWebHookCause;
import com.dabsquared.gitlabjenkins.connection.GitLabConnectionProperty; import com.dabsquared.gitlabjenkins.connection.GitLabConnectionProperty;
import com.dabsquared.gitlabjenkins.gitlab.api.GitLabApi; import com.dabsquared.gitlabjenkins.gitlab.api.GitLabClient;
import com.dabsquared.gitlabjenkins.gitlab.api.model.BuildState; import com.dabsquared.gitlabjenkins.gitlab.api.model.BuildState;
import com.dabsquared.gitlabjenkins.gitlab.hook.model.WebHook; import com.dabsquared.gitlabjenkins.gitlab.hook.model.WebHook;
import com.dabsquared.gitlabjenkins.publisher.GitLabCommitStatusPublisher; import com.dabsquared.gitlabjenkins.publisher.GitLabCommitStatusPublisher;
@ -63,7 +63,7 @@ public abstract class AbstractWebHookTriggerHandler<H extends WebHook> implement
if (job instanceof AbstractProject && ((AbstractProject) job).getPublishersList().get(GitLabCommitStatusPublisher.class) != null) { if (job instanceof AbstractProject && ((AbstractProject) job).getPublishersList().get(GitLabCommitStatusPublisher.class) != null) {
GitLabCommitStatusPublisher publisher = GitLabCommitStatusPublisher publisher =
(GitLabCommitStatusPublisher) ((AbstractProject) job).getPublishersList().get(GitLabCommitStatusPublisher.class); (GitLabCommitStatusPublisher) ((AbstractProject) job).getPublishersList().get(GitLabCommitStatusPublisher.class);
GitLabApi client = job.getProperty(GitLabConnectionProperty.class).getClient(); GitLabClient client = job.getProperty(GitLabConnectionProperty.class).getClient();
BuildStatusUpdate buildStatusUpdate = retrieveBuildStatusUpdate(hook); BuildStatusUpdate buildStatusUpdate = retrieveBuildStatusUpdate(hook);
try { try {
if (client == null) { if (client == null) {

View File

@ -1,10 +1,11 @@
package com.dabsquared.gitlabjenkins.trigger.handler.pipeline; package com.dabsquared.gitlabjenkins.trigger.handler.pipeline;
import com.dabsquared.gitlabjenkins.cause.CauseData; import com.dabsquared.gitlabjenkins.cause.CauseData;
import com.dabsquared.gitlabjenkins.cause.GitLabWebHookCause;
import com.dabsquared.gitlabjenkins.connection.GitLabConnectionProperty; import com.dabsquared.gitlabjenkins.connection.GitLabConnectionProperty;
import com.dabsquared.gitlabjenkins.gitlab.api.GitLabApi; import com.dabsquared.gitlabjenkins.gitlab.api.GitLabClient;
import com.dabsquared.gitlabjenkins.gitlab.hook.model.*; import com.dabsquared.gitlabjenkins.gitlab.hook.model.PipelineEventObjectAttributes;
import com.dabsquared.gitlabjenkins.gitlab.hook.model.PipelineHook;
import com.dabsquared.gitlabjenkins.trigger.exception.NoRevisionToBuildException; import com.dabsquared.gitlabjenkins.trigger.exception.NoRevisionToBuildException;
import com.dabsquared.gitlabjenkins.trigger.filter.BranchFilter; import com.dabsquared.gitlabjenkins.trigger.filter.BranchFilter;
import com.dabsquared.gitlabjenkins.trigger.filter.MergeRequestLabelFilter; import com.dabsquared.gitlabjenkins.trigger.filter.MergeRequestLabelFilter;
@ -46,7 +47,7 @@ class PipelineHookTriggerHandlerImpl extends AbstractWebHookTriggerHandler<Pipel
GitLabConnectionProperty property = job.getProperty(GitLabConnectionProperty.class); GitLabConnectionProperty property = job.getProperty(GitLabConnectionProperty.class);
if (property != null && property.getClient() != null) { if (property != null && property.getClient() != null) {
GitLabApi client = property.getClient(); GitLabClient client = property.getClient();
com.dabsquared.gitlabjenkins.gitlab.api.model.Project projectForName = client.getProject(hook.getProject().getPathWithNamespace()); com.dabsquared.gitlabjenkins.gitlab.api.model.Project projectForName = client.getProject(hook.getProject().getPathWithNamespace());
hook.setProjectId(projectForName.getId()); hook.setProjectId(projectForName.getId());
} }

View File

@ -1,10 +1,11 @@
package com.dabsquared.gitlabjenkins.trigger.handler.push; package com.dabsquared.gitlabjenkins.trigger.handler.push;
import com.dabsquared.gitlabjenkins.GitLabPushTrigger; import com.dabsquared.gitlabjenkins.GitLabPushTrigger;
import com.dabsquared.gitlabjenkins.cause.CauseData; import com.dabsquared.gitlabjenkins.cause.CauseData;
import com.dabsquared.gitlabjenkins.cause.GitLabWebHookCause; import com.dabsquared.gitlabjenkins.cause.GitLabWebHookCause;
import com.dabsquared.gitlabjenkins.connection.GitLabConnectionProperty; import com.dabsquared.gitlabjenkins.connection.GitLabConnectionProperty;
import com.dabsquared.gitlabjenkins.gitlab.api.GitLabApi; import com.dabsquared.gitlabjenkins.gitlab.api.GitLabClient;
import com.dabsquared.gitlabjenkins.gitlab.api.model.Branch; import com.dabsquared.gitlabjenkins.gitlab.api.model.Branch;
import com.dabsquared.gitlabjenkins.gitlab.api.model.BuildState; import com.dabsquared.gitlabjenkins.gitlab.api.model.BuildState;
import com.dabsquared.gitlabjenkins.gitlab.api.model.MergeRequest; import com.dabsquared.gitlabjenkins.gitlab.api.model.MergeRequest;
@ -21,11 +22,9 @@ import hudson.model.CauseAction;
import hudson.model.Job; import hudson.model.Job;
import hudson.plugins.git.RevisionParameterAction; import hudson.plugins.git.RevisionParameterAction;
import hudson.triggers.Trigger; import hudson.triggers.Trigger;
import hudson.triggers.TriggerDescriptor;
import jenkins.model.Jenkins; import jenkins.model.Jenkins;
import jenkins.model.ParameterizedJobMixIn; import jenkins.model.ParameterizedJobMixIn;
import jenkins.model.ParameterizedJobMixIn.ParameterizedJob; import jenkins.model.ParameterizedJobMixIn.ParameterizedJob;
import org.eclipse.jgit.transport.URIish; import org.eclipse.jgit.transport.URIish;
import javax.ws.rs.ProcessingException; import javax.ws.rs.ProcessingException;
@ -34,7 +33,6 @@ import java.net.URISyntaxException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
@ -65,7 +63,7 @@ class OpenMergeRequestPushHookTriggerHandler implements PushHookTriggerHandler {
final GitLabPushTrigger trigger = (GitLabPushTrigger) t; final GitLabPushTrigger trigger = (GitLabPushTrigger) t;
Integer projectId = hook.getProjectId(); Integer projectId = hook.getProjectId();
if (property != null && property.getClient() != null && projectId != null && trigger != null) { if (property != null && property.getClient() != null && projectId != null && trigger != null) {
GitLabApi client = property.getClient(); GitLabClient client = property.getClient();
for (MergeRequest mergeRequest : getOpenMergeRequests(client, projectId.toString())) { for (MergeRequest mergeRequest : getOpenMergeRequests(client, projectId.toString())) {
if (mergeRequestLabelFilter.isMergeRequestAllowed(mergeRequest.getLabels())) { if (mergeRequestLabelFilter.isMergeRequestAllowed(mergeRequest.getLabels())) {
handleMergeRequest(job, hook, ciSkip, branchFilter, client, mergeRequest); handleMergeRequest(job, hook, ciSkip, branchFilter, client, mergeRequest);
@ -83,7 +81,7 @@ class OpenMergeRequestPushHookTriggerHandler implements PushHookTriggerHandler {
} }
} }
private List<MergeRequest> getOpenMergeRequests(GitLabApi client, String projectId) { private List<MergeRequest> getOpenMergeRequests(GitLabClient client, String projectId) {
List<MergeRequest> result = new ArrayList<>(); List<MergeRequest> result = new ArrayList<>();
Integer page = 1; Integer page = 1;
do { do {
@ -94,8 +92,8 @@ class OpenMergeRequestPushHookTriggerHandler implements PushHookTriggerHandler {
return result; return result;
} }
private void handleMergeRequest(Job<?, ?> job, PushHook hook, boolean ciSkip, BranchFilter branchFilter, GitLabApi client, MergeRequest mergeRequest) { private void handleMergeRequest(Job<?, ?> job, PushHook hook, boolean ciSkip, BranchFilter branchFilter, GitLabClient client, MergeRequest mergeRequest) {
if (ciSkip && mergeRequest.getDescription() != null && mergeRequest.getDescription().contains("[ci-skip]")) { if (ciSkip && mergeRequest.getDescription() != null && mergeRequest.getDescription().contains("[ci-skip]")) {
LOGGER.log(Level.INFO, "Skipping MR " + mergeRequest.getTitle() + " due to ci-skip."); LOGGER.log(Level.INFO, "Skipping MR " + mergeRequest.getTitle() + " due to ci-skip.");
return; return;
} }
@ -158,7 +156,7 @@ class OpenMergeRequestPushHookTriggerHandler implements PushHookTriggerHandler {
if (job instanceof AbstractProject && ((AbstractProject) job).getPublishersList().get(GitLabCommitStatusPublisher.class) != null) { if (job instanceof AbstractProject && ((AbstractProject) job).getPublishersList().get(GitLabCommitStatusPublisher.class) != null) {
GitLabCommitStatusPublisher publisher = GitLabCommitStatusPublisher publisher =
(GitLabCommitStatusPublisher) ((AbstractProject) job).getPublishersList().get(GitLabCommitStatusPublisher.class); (GitLabCommitStatusPublisher) ((AbstractProject) job).getPublishersList().get(GitLabCommitStatusPublisher.class);
GitLabApi client = job.getProperty(GitLabConnectionProperty.class).getClient(); GitLabClient client = job.getProperty(GitLabConnectionProperty.class).getClient();
try { try {
String targetUrl = Jenkins.getInstance().getRootUrl() + job.getUrl() + job.getNextBuildNumber() + "/"; String targetUrl = Jenkins.getInstance().getRootUrl() + job.getUrl() + job.getNextBuildNumber() + "/";
client.changeBuildStatus(projectId, commit, BuildState.pending, ref, publisher.getName(), targetUrl, null); client.changeBuildStatus(projectId, commit, BuildState.pending, ref, publisher.getName(), targetUrl, null);

View File

@ -1,28 +1,9 @@
package com.dabsquared.gitlabjenkins.util; package com.dabsquared.gitlabjenkins.util;
import static com.dabsquared.gitlabjenkins.connection.GitLabConnectionProperty.getClient;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.ws.rs.NotFoundException;
import javax.ws.rs.ProcessingException;
import javax.ws.rs.WebApplicationException;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.eclipse.jgit.lib.ObjectId;
import com.dabsquared.gitlabjenkins.cause.GitLabWebHookCause; import com.dabsquared.gitlabjenkins.cause.GitLabWebHookCause;
import com.dabsquared.gitlabjenkins.gitlab.api.GitLabApi; import com.dabsquared.gitlabjenkins.gitlab.api.GitLabClient;
import com.dabsquared.gitlabjenkins.gitlab.api.model.BuildState; import com.dabsquared.gitlabjenkins.gitlab.api.model.BuildState;
import hudson.EnvVars; import hudson.EnvVars;
import hudson.model.Run; import hudson.model.Run;
import hudson.model.TaskListener; import hudson.model.TaskListener;
@ -33,6 +14,23 @@ import jenkins.model.Jenkins;
import jenkins.plugins.git.AbstractGitSCMSource; import jenkins.plugins.git.AbstractGitSCMSource;
import jenkins.scm.api.SCMRevision; import jenkins.scm.api.SCMRevision;
import jenkins.scm.api.SCMRevisionAction; import jenkins.scm.api.SCMRevisionAction;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.eclipse.jgit.lib.ObjectId;
import javax.ws.rs.NotFoundException;
import javax.ws.rs.ProcessingException;
import javax.ws.rs.WebApplicationException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import static com.dabsquared.gitlabjenkins.connection.GitLabConnectionProperty.getClient;
/** /**
* @author Robin Müller * @author Robin Müller
@ -42,7 +40,7 @@ public class CommitStatusUpdater {
private final static Logger LOGGER = Logger.getLogger(CommitStatusUpdater.class.getName()); private final static Logger LOGGER = Logger.getLogger(CommitStatusUpdater.class.getName());
public static void updateCommitStatus(Run<?, ?> build, TaskListener listener, BuildState state, String name) { public static void updateCommitStatus(Run<?, ?> build, TaskListener listener, BuildState state, String name) {
GitLabApi client = getClient(build); GitLabClient client = getClient(build);
if (client == null) { if (client == null) {
println(listener, "No GitLab connection configured"); println(listener, "No GitLab connection configured");
return; return;
@ -82,7 +80,7 @@ public class CommitStatusUpdater {
} }
} }
private static boolean existsCommit(GitLabApi client, String gitlabProjectId, String commitHash) { private static boolean existsCommit(GitLabClient client, String gitlabProjectId, String commitHash) {
try { try {
client.getCommit(gitlabProjectId, commitHash); client.getCommit(gitlabProjectId, commitHash);
return true; return true;
@ -111,7 +109,7 @@ public class CommitStatusUpdater {
cause.getData().getLastCommit())); cause.getData().getLastCommit()));
} }
final GitLabApi gitLabClient = getClient(build); final GitLabClient gitLabClient = getClient(build);
if (gitLabClient == null) { if (gitLabClient == null) {
LOGGER.log(Level.WARNING, "No gitlab client found."); LOGGER.log(Level.WARNING, "No gitlab client found.");
return result; return result;
@ -171,8 +169,8 @@ public class CommitStatusUpdater {
return action.getLastBuild(lastBuiltRevision.getSha1()).getMarked().getSha1String(); return action.getLastBuild(lastBuiltRevision.getSha1()).getMarked().getSha1String();
} }
private static void addGitLabBranchBuild(List<GitLabBranchBuild> result, String scmRevisionHash, private static void addGitLabBranchBuild(List<GitLabBranchBuild> result, String scmRevisionHash,
Set<String> remoteUrls, EnvVars environment, GitLabApi gitLabClient) { Set<String> remoteUrls, EnvVars environment, GitLabClient gitLabClient) {
for (String remoteUrl : remoteUrls) { for (String remoteUrl : remoteUrls) {
try { try {
LOGGER.log(Level.INFO, "Retrieving the gitlab project id from remote url {0}", remoteUrl); LOGGER.log(Level.INFO, "Retrieving the gitlab project id from remote url {0}", remoteUrl);
@ -184,7 +182,7 @@ public class CommitStatusUpdater {
projectId = gitLabClient.getProject(projectNameWithNameSpace).getId().toString(); projectId = gitLabClient.getProject(projectNameWithNameSpace).getId().toString();
} catch (WebApplicationException | ProcessingException e) { } catch (WebApplicationException | ProcessingException e) {
LOGGER.log(Level.SEVERE, String.format("Failed to retrieve projectId for project '%s'", LOGGER.log(Level.SEVERE, String.format("Failed to retrieve projectId for project '%s'",
projectNameWithNameSpace), e); projectNameWithNameSpace), e);
} }
} }
result.add(new GitLabBranchBuild(projectId, scmRevisionHash)); result.add(new GitLabBranchBuild(projectId, scmRevisionHash));

View File

@ -1,8 +1,8 @@
package com.dabsquared.gitlabjenkins.util; package com.dabsquared.gitlabjenkins.util;
import org.eclipse.jgit.transport.URIish;
import com.dabsquared.gitlabjenkins.gitlab.api.GitLabApi; import com.dabsquared.gitlabjenkins.gitlab.api.GitLabClient;
import org.eclipse.jgit.transport.URIish;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.util.regex.Matcher; import java.util.regex.Matcher;
@ -17,10 +17,10 @@ public final class ProjectIdUtil {
private ProjectIdUtil() { } private ProjectIdUtil() { }
public static String retrieveProjectId(GitLabApi client, String remoteUrl) throws ProjectIdResolutionException { public static String retrieveProjectId(GitLabClient client, String remoteUrl) throws ProjectIdResolutionException {
try { try {
String projectId = null; String baseUri = client.getHostUrl();
String baseUri = client.getGitLabHostUrl(); String projectId;
if (baseUri != null && remoteUrl.startsWith(baseUri)) { if (baseUri != null && remoteUrl.startsWith(baseUri)) {
projectId = new URIish(remoteUrl.substring(baseUri.length(), remoteUrl.length())).getPath(); projectId = new URIish(remoteUrl.substring(baseUri.length(), remoteUrl.length())).getPath();
} else { } else {

View File

@ -1,7 +1,7 @@
package com.dabsquared.gitlabjenkins.workflow; package com.dabsquared.gitlabjenkins.workflow;
import com.dabsquared.gitlabjenkins.cause.GitLabWebHookCause; import com.dabsquared.gitlabjenkins.cause.GitLabWebHookCause;
import com.dabsquared.gitlabjenkins.gitlab.api.GitLabApi; import com.dabsquared.gitlabjenkins.gitlab.api.GitLabClient;
import hudson.Extension; import hudson.Extension;
import hudson.model.Run; import hudson.model.Run;
import hudson.model.TaskListener; import hudson.model.TaskListener;
@ -63,7 +63,7 @@ public class AcceptGitLabMergeRequestStep extends AbstractStepImpl {
Integer projectId = cause.getData().getTargetProjectId(); Integer projectId = cause.getData().getTargetProjectId();
Integer mergeRequestId = cause.getData().getMergeRequestId(); Integer mergeRequestId = cause.getData().getMergeRequestId();
if (projectId != null && mergeRequestId != null) { if (projectId != null && mergeRequestId != null) {
GitLabApi client = getClient(run); GitLabClient client = getClient(run);
if (client == null) { if (client == null) {
println("No GitLab connection configured"); println("No GitLab connection configured");
} else { } else {

View File

@ -1,7 +1,7 @@
package com.dabsquared.gitlabjenkins.workflow; package com.dabsquared.gitlabjenkins.workflow;
import com.dabsquared.gitlabjenkins.cause.GitLabWebHookCause; import com.dabsquared.gitlabjenkins.cause.GitLabWebHookCause;
import com.dabsquared.gitlabjenkins.gitlab.api.GitLabApi; import com.dabsquared.gitlabjenkins.gitlab.api.GitLabClient;
import hudson.Extension; import hudson.Extension;
import hudson.model.Run; import hudson.model.Run;
import hudson.model.TaskListener; import hudson.model.TaskListener;
@ -63,7 +63,7 @@ public class AddGitLabMergeRequestCommentStep extends AbstractStepImpl {
Integer projectId = cause.getData().getTargetProjectId(); Integer projectId = cause.getData().getTargetProjectId();
Integer mergeRequestId = cause.getData().getMergeRequestId(); Integer mergeRequestId = cause.getData().getMergeRequestId();
if (projectId != null && mergeRequestId != null) { if (projectId != null && mergeRequestId != null) {
GitLabApi client = getClient(run); GitLabClient client = getClient(run);
if (client == null) { if (client == null) {
println("No GitLab connection configured"); println("No GitLab connection configured");
} else { } else {

View File

@ -18,6 +18,9 @@
<c:select/> <c:select/>
</f:entry> </f:entry>
<f:advanced> <f:advanced>
<f:entry title="${%API-Level}" field="clientBuilderId" description="${%API Level for accessing Gitlab}">
<f:select value="${connection.clientBuilderId}" default="autodetect"/>
</f:entry>
<f:entry title="${%Ignore SSL Certificate Errors}" field="ignoreCertificateErrors"> <f:entry title="${%Ignore SSL Certificate Errors}" field="ignoreCertificateErrors">
<f:checkbox checked="${connection.ignoreCertificateErrors}"/> <f:checkbox checked="${connection.ignoreCertificateErrors}"/>
</f:entry> </f:entry>
@ -30,7 +33,7 @@
<st:include page="configure-advanced.jelly" optional="true" /> <st:include page="configure-advanced.jelly" optional="true" />
</f:advanced> </f:advanced>
<f:validateButton title="${%Test Connection}" progress="${%Testing...}" method="testConnection" <f:validateButton title="${%Test Connection}" progress="${%Testing...}" method="testConnection"
with="apiTokenId,url,ignoreCertificateErrors"/> with="apiTokenId,clientBuilderId,url,ignoreCertificateErrors"/>
<f:entry title=""> <f:entry title="">
<div align="right"> <div align="right">
<f:repeatableDeleteButton/> <f:repeatableDeleteButton/>

View File

@ -1,5 +1,6 @@
package com.dabsquared.gitlabjenkins.connection; package com.dabsquared.gitlabjenkins.connection;
import com.cloudbees.plugins.credentials.CredentialsProvider; import com.cloudbees.plugins.credentials.CredentialsProvider;
import com.cloudbees.plugins.credentials.CredentialsScope; import com.cloudbees.plugins.credentials.CredentialsScope;
import com.cloudbees.plugins.credentials.CredentialsStore; import com.cloudbees.plugins.credentials.CredentialsStore;
@ -87,7 +88,7 @@ public class GitLabConnectionConfigSSLTest {
public void doCheckConnection_ignoreCertificateErrors() { public void doCheckConnection_ignoreCertificateErrors() {
GitLabConnectionConfig connectionConfig = jenkins.get(GitLabConnectionConfig.class); GitLabConnectionConfig connectionConfig = jenkins.get(GitLabConnectionConfig.class);
FormValidation formValidation = connectionConfig.doTestConnection("https://localhost:" + port + "/gitlab", API_TOKEN_ID, true, 10, 10); FormValidation formValidation = connectionConfig.doTestConnection("https://localhost:" + port + "/gitlab", API_TOKEN_ID, "v3", true, 10, 10);
assertThat(formValidation.getMessage(), is(Messages.connection_success())); assertThat(formValidation.getMessage(), is(Messages.connection_success()));
} }
@ -95,7 +96,7 @@ public class GitLabConnectionConfigSSLTest {
public void doCheckConnection_certificateError() throws IOException { public void doCheckConnection_certificateError() throws IOException {
GitLabConnectionConfig connectionConfig = jenkins.get(GitLabConnectionConfig.class); GitLabConnectionConfig connectionConfig = jenkins.get(GitLabConnectionConfig.class);
FormValidation formValidation = connectionConfig.doTestConnection("https://localhost:" + port + "/gitlab", API_TOKEN_ID, false, 10, 10); FormValidation formValidation = connectionConfig.doTestConnection("https://localhost:" + port + "/gitlab", API_TOKEN_ID, "v3", false, 10, 10);
assertThat(formValidation.getMessage(), containsString(Messages.connection_error(""))); assertThat(formValidation.getMessage(), containsString(Messages.connection_error("")));
} }
} }

View File

@ -1,11 +1,14 @@
package com.dabsquared.gitlabjenkins.connection; package com.dabsquared.gitlabjenkins.connection;
import com.cloudbees.plugins.credentials.CredentialsProvider; import com.cloudbees.plugins.credentials.CredentialsProvider;
import com.cloudbees.plugins.credentials.CredentialsScope; import com.cloudbees.plugins.credentials.CredentialsScope;
import com.cloudbees.plugins.credentials.CredentialsStore; import com.cloudbees.plugins.credentials.CredentialsStore;
import com.cloudbees.plugins.credentials.SystemCredentialsProvider; import com.cloudbees.plugins.credentials.SystemCredentialsProvider;
import com.cloudbees.plugins.credentials.domains.Domain; import com.cloudbees.plugins.credentials.domains.Domain;
import com.dabsquared.gitlabjenkins.GitLabPushTrigger; import com.dabsquared.gitlabjenkins.GitLabPushTrigger;
import com.dabsquared.gitlabjenkins.gitlab.api.GitLabClient;
import com.dabsquared.gitlabjenkins.gitlab.api.impl.V3GitLabClientBuilder;
import hudson.model.FreeStyleProject; import hudson.model.FreeStyleProject;
import hudson.model.Item; import hudson.model.Item;
import hudson.security.GlobalMatrixAuthorizationStrategy; import hudson.security.GlobalMatrixAuthorizationStrategy;
@ -33,9 +36,11 @@ import java.io.IOException;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.net.URL; import java.net.URL;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.util.List;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List;
import static junit.framework.TestCase.assertNotNull;
import static junit.framework.TestCase.assertSame;
import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat; import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
@ -72,28 +77,29 @@ public class GitLabConnectionConfigTest {
@Test @Test
public void doCheckConnection_success() { public void doCheckConnection_success() {
HttpRequest request = request().withPath("/gitlab/api/v3/.*").withHeader("PRIVATE-TOKEN", API_TOKEN); String expected = Messages.connection_success();
mockServerClient.when(request).respond(response().withStatusCode(Response.Status.OK.getStatusCode())); assertThat(doCheckConnection("v3", Response.Status.OK), is(expected));
assertThat(doCheckConnection("v4", Response.Status.OK), is(expected));
GitLabConnectionConfig connectionConfig = jenkins.get(GitLabConnectionConfig.class);
FormValidation formValidation = connectionConfig.doTestConnection(gitLabUrl, API_TOKEN_ID, false, 10, 10);
assertThat(formValidation.getMessage(), is(Messages.connection_success()));
mockServerClient.verify(request);
} }
@Test @Test
public void doCheckConnection_forbidden() throws IOException { public void doCheckConnection_forbidden() throws IOException {
HttpRequest request = request().withPath("/gitlab/api/v3/.*").withHeader("PRIVATE-TOKEN", API_TOKEN); String expected = Messages.connection_error("HTTP 403 Forbidden");
mockServerClient.when(request).respond(response().withStatusCode(Response.Status.FORBIDDEN.getStatusCode())); assertThat(doCheckConnection("v3", Response.Status.FORBIDDEN), is(expected));
assertThat(doCheckConnection("v4", Response.Status.FORBIDDEN), is(expected));
}
private String doCheckConnection(String clientBuilderId, Response.Status status) {
HttpRequest request = request().withPath("/gitlab/api/" + clientBuilderId + "/.*").withHeader("PRIVATE-TOKEN", API_TOKEN);
mockServerClient.when(request).respond(response().withStatusCode(status.getStatusCode()));
GitLabConnectionConfig connectionConfig = jenkins.get(GitLabConnectionConfig.class); GitLabConnectionConfig connectionConfig = jenkins.get(GitLabConnectionConfig.class);
FormValidation formValidation = connectionConfig.doTestConnection(gitLabUrl, API_TOKEN_ID, false, 10, 10); FormValidation formValidation = connectionConfig.doTestConnection(gitLabUrl, API_TOKEN_ID, clientBuilderId, false, 10, 10);
assertThat(formValidation.getMessage(), is(Messages.connection_error("HTTP 403 Forbidden")));
mockServerClient.verify(request); mockServerClient.verify(request);
return formValidation.getMessage();
} }
@Test @Test
public void authenticationEnabled_anonymous_forbidden() throws IOException, URISyntaxException { public void authenticationEnabled_anonymous_forbidden() throws IOException, URISyntaxException {
Boolean defaultValue = jenkins.get(GitLabConnectionConfig.class).isUseAuthenticatedEndpoint(); Boolean defaultValue = jenkins.get(GitLabConnectionConfig.class).isUseAuthenticatedEndpoint();
@ -155,8 +161,8 @@ public class GitLabConnectionConfigTest {
@Test @Test
public void setConnectionsTest() { public void setConnectionsTest() {
GitLabConnection connection1 = new GitLabConnection("1", "http://localhost", null, false, 10, 10); GitLabConnection connection1 = new GitLabConnection("1", "http://localhost", null, new V3GitLabClientBuilder(), false, 10, 10);
GitLabConnection connection2 = new GitLabConnection("2", "http://localhost", null, false, 10, 10); GitLabConnection connection2 = new GitLabConnection("2", "http://localhost", null, new V3GitLabClientBuilder(), false, 10, 10);
GitLabConnectionConfig config = jenkins.get(GitLabConnectionConfig.class); GitLabConnectionConfig config = jenkins.get(GitLabConnectionConfig.class);
List<GitLabConnection> connectionList1 = new ArrayList<>(); List<GitLabConnection> connectionList1 = new ArrayList<>();
connectionList1.add(connection1); connectionList1.add(connection1);
@ -166,7 +172,7 @@ public class GitLabConnectionConfigTest {
List<GitLabConnection> connectionList2 = new ArrayList<>(); List<GitLabConnection> connectionList2 = new ArrayList<>();
connectionList2.add(connection1); connectionList2.add(connection1);
connectionList2.add(connection1); connectionList2.add(connection2);
config.setConnections(connectionList2); config.setConnections(connectionList2);
assertThat(config.getConnections(), is(connectionList2)); assertThat(config.getConnections(), is(connectionList2));
@ -174,4 +180,17 @@ public class GitLabConnectionConfigTest {
config.setConnections(connectionList1); config.setConnections(connectionList1);
assertThat(config.getConnections(), is(connectionList1)); assertThat(config.getConnections(), is(connectionList1));
} }
@Test
public void getClient_is_cached() {
GitLabConnection connection = new GitLabConnection("test", "http://localhost", API_TOKEN_ID, new V3GitLabClientBuilder(), false, 10, 10);
GitLabConnectionConfig config = jenkins.get(GitLabConnectionConfig.class);
List<GitLabConnection> connectionList1 = new ArrayList<>();
connectionList1.add(connection);
config.setConnections(connectionList1);
GitLabClient client = config.getClient(connection.getName());
assertNotNull(client);
assertSame(client, config.getClient(connection.getName()));
}
} }

View File

@ -0,0 +1,41 @@
package com.dabsquared.gitlabjenkins.gitlab.api;
import com.dabsquared.gitlabjenkins.gitlab.api.impl.AutodetectGitLabClientBuilder;
import com.dabsquared.gitlabjenkins.gitlab.api.impl.V3GitLabClientBuilder;
import com.dabsquared.gitlabjenkins.gitlab.api.impl.V4GitLabClientBuilder;
import org.junit.Rule;
import org.junit.Test;
import org.jvnet.hudson.test.JenkinsRule;
import java.util.List;
import java.util.NoSuchElementException;
import static com.dabsquared.gitlabjenkins.gitlab.api.GitLabClientBuilder.getAllGitLabClientBuilders;
import static com.dabsquared.gitlabjenkins.gitlab.api.GitLabClientBuilder.getGitLabClientBuilderById;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.IsInstanceOf.instanceOf;
public class GitLabClientBuilderTest {
@Rule
public JenkinsRule jenkins = new JenkinsRule();
@Test
public void getAllGitLabClientBuilders_list_is_sorted_by_id() {
List<GitLabClientBuilder> builders = getAllGitLabClientBuilders();
assertThat(builders.get(0), instanceOf(AutodetectGitLabClientBuilder.class));
assertThat(builders.get(1), instanceOf(V3GitLabClientBuilder.class));
assertThat(builders.get(2), instanceOf(V4GitLabClientBuilder.class));
}
@Test
public void getGitLabClientBuilderById_success() {
assertThat(getGitLabClientBuilderById(new V3GitLabClientBuilder().id()), instanceOf(V3GitLabClientBuilder.class));
}
@Test(expected = NoSuchElementException.class)
public void getGitLabClientBuilderById_no_match() {
getGitLabClientBuilderById("unknown");
}
}

View File

@ -0,0 +1,74 @@
package com.dabsquared.gitlabjenkins.gitlab.api.impl;
import com.dabsquared.gitlabjenkins.gitlab.api.GitLabClientBuilder;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.jvnet.hudson.test.JenkinsRule;
import org.mockserver.client.server.MockServerClient;
import org.mockserver.junit.MockServerRule;
import org.mockserver.model.HttpRequest;
import java.io.IOException;
import java.util.NoSuchElementException;
import static com.dabsquared.gitlabjenkins.gitlab.api.impl.TestUtility.API_TOKEN;
import static com.dabsquared.gitlabjenkins.gitlab.api.impl.TestUtility.addGitLabApiToken;
import static com.dabsquared.gitlabjenkins.gitlab.api.impl.TestUtility.buildClientWithDefaults;
import static com.dabsquared.gitlabjenkins.gitlab.api.impl.TestUtility.responseNotFound;
import static com.dabsquared.gitlabjenkins.gitlab.api.impl.TestUtility.responseOk;
import static com.dabsquared.gitlabjenkins.gitlab.api.impl.TestUtility.versionRequest;
import static org.junit.Assert.fail;
public class AutodetectGitLabClientBuilderTest {
@Rule
public MockServerRule mockServer = new MockServerRule(this);
@Rule
public JenkinsRule jenkins = new JenkinsRule();
private MockServerClient mockServerClient;
private String gitLabUrl;
private GitLabClientBuilder clientBuilder;
private HttpRequest v3Request;
private HttpRequest v4Request;
@Before
public void setup() throws IOException {
gitLabUrl = "http://localhost:" + mockServer.getPort() + "/gitlab";
addGitLabApiToken();
clientBuilder = new AutodetectGitLabClientBuilder();
v3Request = versionRequest(V3GitLabClientBuilder.ID);
v4Request = versionRequest(V4GitLabClientBuilder.ID);
}
@Test
public void buildClient_success_v3() throws Exception {
mockServerClient.when(v3Request).respond(responseOk());
TestUtility.assertApiImpl(buildClientWithDefaults(clientBuilder, gitLabUrl),V3GitLabApiProxy.class);
mockServerClient.verify(v3Request);
}
@Test
public void buildClient_success_v4() throws Exception {
mockServerClient.when(v3Request).respond(responseNotFound());
mockServerClient.when(v4Request).respond(responseOk());
TestUtility.assertApiImpl(buildClientWithDefaults(clientBuilder, gitLabUrl),V4GitLabApiProxy.class);
mockServerClient.verify(v3Request, v4Request);
}
@Test
public void buildClient_no_match() {
mockServerClient.when(v3Request).respond(responseNotFound());
mockServerClient.when(v4Request).respond(responseNotFound());
try {
clientBuilder.buildClient(gitLabUrl, API_TOKEN, true, 10, 10);
fail("buildClient should throw exception when no matching candidate is found");
} catch (NoSuchElementException e) {
mockServerClient.verify(v3Request, v4Request);
}
}
}

View File

@ -0,0 +1,25 @@
package com.dabsquared.gitlabjenkins.gitlab.api.impl;
import com.dabsquared.gitlabjenkins.gitlab.api.GitLabClientBuilder;
import org.junit.Rule;
import org.junit.Test;
import org.jvnet.hudson.test.JenkinsRule;
import org.mockserver.junit.MockServerRule;
import static com.dabsquared.gitlabjenkins.gitlab.api.impl.TestUtility.assertApiImpl;
import static com.dabsquared.gitlabjenkins.gitlab.api.impl.TestUtility.buildClientWithDefaults;
public class ResteasyGitLabClientBuilderTest {
@Rule
public MockServerRule mockServer = new MockServerRule(this);
@Rule
public JenkinsRule jenkins = new JenkinsRule();
@Test
public void buildClient() throws Exception {
GitLabClientBuilder clientBuilder = new ResteasyGitLabClientBuilder("test", V3GitLabApiProxy.class);
assertApiImpl(buildClientWithDefaults(clientBuilder, "http://localhost/"), V3GitLabApiProxy.class);
}
}

View File

@ -0,0 +1,81 @@
package com.dabsquared.gitlabjenkins.gitlab.api.impl;
import com.cloudbees.plugins.credentials.CredentialsProvider;
import com.cloudbees.plugins.credentials.CredentialsScope;
import com.cloudbees.plugins.credentials.CredentialsStore;
import com.cloudbees.plugins.credentials.SystemCredentialsProvider;
import com.cloudbees.plugins.credentials.domains.Domain;
import com.dabsquared.gitlabjenkins.gitlab.api.GitLabApi;
import com.dabsquared.gitlabjenkins.gitlab.api.GitLabClient;
import com.dabsquared.gitlabjenkins.gitlab.api.GitLabClientBuilder;
import hudson.util.Secret;
import jenkins.model.Jenkins;
import org.jenkinsci.plugins.plaincredentials.impl.StringCredentialsImpl;
import org.mockserver.model.HttpRequest;
import org.mockserver.model.HttpResponse;
import javax.ws.rs.core.Response.Status;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.List;
import static javax.ws.rs.HttpMethod.HEAD;
import static javax.ws.rs.core.Response.Status.NOT_FOUND;
import static javax.ws.rs.core.Response.Status.OK;
import static javax.ws.rs.core.Response.Status.UNAUTHORIZED;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.instanceOf;
import static org.mockserver.model.HttpRequest.request;
import static org.mockserver.model.HttpResponse.response;
class TestUtility {
static final String API_TOKEN = "secret";
static final String API_TOKEN_ID = "apiTokenId";
static final boolean IGNORE_CERTIFICATE_ERRORS = true;
static final int CONNECTION_TIMEOUT = 10;
static final int READ_TIMEOUT = 10;
static void addGitLabApiToken() throws IOException {
for (CredentialsStore credentialsStore : CredentialsProvider.lookupStores(Jenkins.getInstance())) {
if (credentialsStore instanceof SystemCredentialsProvider.StoreImpl) {
List<Domain> domains = credentialsStore.getDomains();
credentialsStore.addCredentials(domains.get(0),
new StringCredentialsImpl(CredentialsScope.SYSTEM, API_TOKEN_ID, "GitLab API Token", Secret.fromString(API_TOKEN)));
}
}
}
static HttpRequest versionRequest(String id) {
return request().withMethod(HEAD).withPath("/gitlab/api/" + id + "/.*").withHeader("PRIVATE-TOKEN", API_TOKEN);
}
static HttpResponse responseOk() {
return responseWithStatus(OK);
}
static HttpResponse responseNotFound() {
return responseWithStatus(NOT_FOUND);
}
static HttpResponse responseUnauthorized() {
return responseWithStatus(UNAUTHORIZED);
}
static HttpResponse responseWithStatus(Status status) {
return response().withStatusCode(status.getStatusCode());
}
static GitLabClient buildClientWithDefaults(GitLabClientBuilder clientBuilder, String url) {
return clientBuilder.buildClient(url, API_TOKEN, IGNORE_CERTIFICATE_ERRORS, CONNECTION_TIMEOUT, READ_TIMEOUT);
}
static void assertApiImpl(GitLabClient client, Class<? extends GitLabApi> apiImplClass) throws Exception {
Field apiField = client.getClass().getDeclaredField("api");
apiField.setAccessible(true);
assertThat(apiField.get(client), instanceOf(apiImplClass));
}
private TestUtility() { /* utility class */ }
}

View File

@ -1,26 +1,10 @@
package com.dabsquared.gitlabjenkins.publisher; package com.dabsquared.gitlabjenkins.publisher;
import com.cloudbees.plugins.credentials.CredentialsProvider;
import com.cloudbees.plugins.credentials.CredentialsScope;
import com.cloudbees.plugins.credentials.CredentialsStore;
import com.cloudbees.plugins.credentials.SystemCredentialsProvider;
import com.cloudbees.plugins.credentials.domains.Domain;
import com.dabsquared.gitlabjenkins.connection.GitLabConnection;
import com.dabsquared.gitlabjenkins.connection.GitLabConnectionConfig;
import com.dabsquared.gitlabjenkins.connection.GitLabConnectionProperty;
import hudson.Launcher;
import hudson.matrix.MatrixAggregator;
import hudson.matrix.MatrixConfiguration;
import hudson.matrix.MatrixBuild;
import hudson.model.AbstractBuild; import hudson.model.AbstractBuild;
import hudson.model.AbstractProject;
import hudson.model.BuildListener; import hudson.model.BuildListener;
import hudson.model.Result; import hudson.model.Result;
import hudson.model.StreamBuildListener; import hudson.model.StreamBuildListener;
import hudson.plugins.git.util.BuildData;
import hudson.util.Secret;
import jenkins.model.Jenkins;
import org.jenkinsci.plugins.plaincredentials.impl.StringCredentialsImpl;
import org.junit.After; import org.junit.After;
import org.junit.Before; import org.junit.Before;
import org.junit.BeforeClass; import org.junit.BeforeClass;
@ -34,16 +18,16 @@ import org.mockserver.model.HttpRequest;
import java.io.IOException; import java.io.IOException;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import static org.mockito.Matchers.any; import static com.dabsquared.gitlabjenkins.publisher.TestUtility.GITLAB_CONNECTION_V3;
import static com.dabsquared.gitlabjenkins.publisher.TestUtility.GITLAB_CONNECTION_V4;
import static com.dabsquared.gitlabjenkins.publisher.TestUtility.MERGE_REQUEST_ID;
import static com.dabsquared.gitlabjenkins.publisher.TestUtility.PROJECT_ID;
import static com.dabsquared.gitlabjenkins.publisher.TestUtility.mockSimpleBuild;
import static com.dabsquared.gitlabjenkins.publisher.TestUtility.setupGitLabConnections;
import static com.dabsquared.gitlabjenkins.publisher.TestUtility.verifyMatrixAggregatable;
import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy; import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.mockserver.model.HttpRequest.request; import static org.mockserver.model.HttpRequest.request;
import static org.mockserver.model.HttpResponse.response; import static org.mockserver.model.HttpResponse.response;
@ -51,10 +35,6 @@ import static org.mockserver.model.HttpResponse.response;
* @author Nikolay Ustinov * @author Nikolay Ustinov
*/ */
public class GitLabAcceptMergeRequestPublisherTest { public class GitLabAcceptMergeRequestPublisherTest {
private static final String GIT_LAB_CONNECTION = "GitLab";
private static final String API_TOKEN = "secret";
@ClassRule @ClassRule
public static MockServerRule mockServer = new MockServerRule(new Object()); public static MockServerRule mockServer = new MockServerRule(new Object());
@ -65,17 +45,8 @@ public class GitLabAcceptMergeRequestPublisherTest {
private BuildListener listener; private BuildListener listener;
@BeforeClass @BeforeClass
public static void setupConnection() throws IOException { public static void setupClass() throws IOException {
GitLabConnectionConfig connectionConfig = jenkins.get(GitLabConnectionConfig.class); setupGitLabConnections(jenkins, mockServer);
String apiTokenId = "apiTokenId";
for (CredentialsStore credentialsStore : CredentialsProvider.lookupStores(Jenkins.getInstance())) {
if (credentialsStore instanceof SystemCredentialsProvider.StoreImpl) {
List<Domain> domains = credentialsStore.getDomains();
credentialsStore.addCredentials(domains.get(0),
new StringCredentialsImpl(CredentialsScope.SYSTEM, apiTokenId, "GitLab API Token", Secret.fromString(API_TOKEN)));
}
}
connectionConfig.addConnection(new GitLabConnection(GIT_LAB_CONNECTION, "http://localhost:" + mockServer.getPort() + "/gitlab", apiTokenId, false, 10, 10));
} }
@Before @Before
@ -90,84 +61,46 @@ public class GitLabAcceptMergeRequestPublisherTest {
} }
@Test @Test
public void matrixAggregatable() throws UnsupportedEncodingException, InterruptedException, IOException { public void matrixAggregatable() throws InterruptedException, IOException {
AbstractBuild build = mock(AbstractBuild.class); verifyMatrixAggregatable(GitLabAcceptMergeRequestPublisher.class, listener);
AbstractProject project = mock(MatrixConfiguration.class);
GitLabCommitStatusPublisher publisher = mock(GitLabCommitStatusPublisher.class);
MatrixBuild parentBuild = mock(MatrixBuild.class);
when(build.getParent()).thenReturn(project);
when(publisher.createAggregator(any(MatrixBuild.class), any(Launcher.class), any(BuildListener.class))).thenCallRealMethod();
when(publisher.perform(any(AbstractBuild.class), any(Launcher.class), any(BuildListener.class))).thenReturn(true);
MatrixAggregator aggregator = publisher.createAggregator(parentBuild, null, listener);
aggregator.startBuild();
aggregator.endBuild();
verify(publisher).perform(parentBuild, null, listener);
} }
@Test @Test
public void success() throws IOException, InterruptedException { public void success() throws IOException, InterruptedException {
Integer buildNumber = 1; publish(mockSimpleBuild(GITLAB_CONNECTION_V3, Result.SUCCESS));
Integer projectId = 3; publish(mockSimpleBuild(GITLAB_CONNECTION_V4, Result.SUCCESS));
Integer mergeRequestId = 1;
AbstractBuild build = mockBuild("/build/123", GIT_LAB_CONNECTION, Result.SUCCESS, buildNumber);
HttpRequest[] requests = new HttpRequest[] { mockServerClient.verify(
prepareAcceptMergeRequestWithSuccessResponse(projectId, mergeRequestId) prepareAcceptMergeRequestWithSuccessResponse("v3"),
}; prepareAcceptMergeRequestWithSuccessResponse("v4"));
GitLabAcceptMergeRequestPublisher publisher = spy(new GitLabAcceptMergeRequestPublisher());
doReturn(projectId).when(publisher).getProjectId(build);
doReturn(mergeRequestId).when(publisher).getMergeRequestId(build);
publisher.perform(build, null, listener);
mockServerClient.verify(requests);
} }
@Test @Test
public void failed() throws IOException, InterruptedException { public void failed() throws IOException, InterruptedException {
Integer buildNumber = 1; publish(mockSimpleBuild(GITLAB_CONNECTION_V3, Result.FAILURE));
Integer projectId = 3; publish(mockSimpleBuild(GITLAB_CONNECTION_V4, Result.FAILURE));
Integer mergeRequestId = 1;
AbstractBuild build = mockBuild("/build/123", GIT_LAB_CONNECTION, Result.FAILURE, buildNumber);
GitLabAcceptMergeRequestPublisher publisher = spy(new GitLabAcceptMergeRequestPublisher());
doReturn(projectId).when(publisher).getProjectId(build);
doReturn(mergeRequestId).when(publisher).getMergeRequestId(build);
publisher.perform(build, null, listener);
mockServerClient.verifyZeroInteractions(); mockServerClient.verifyZeroInteractions();
} }
private HttpRequest prepareAcceptMergeRequestWithSuccessResponse(Integer projectId, Integer mergeRequestId) throws UnsupportedEncodingException { private void publish(AbstractBuild build) throws InterruptedException, IOException {
HttpRequest updateCommitStatus = prepareAcceptMergeRequest(projectId, mergeRequestId); GitLabAcceptMergeRequestPublisher publisher = spy(new GitLabAcceptMergeRequestPublisher());
doReturn(PROJECT_ID).when(publisher).getProjectId(build);
doReturn(MERGE_REQUEST_ID).when(publisher).getMergeRequestId(build);
publisher.perform(build, null, listener);
}
private HttpRequest prepareAcceptMergeRequestWithSuccessResponse(String apiLevel) throws UnsupportedEncodingException {
HttpRequest updateCommitStatus = prepareAcceptMergeRequest(apiLevel);
mockServerClient.when(updateCommitStatus).respond(response().withStatusCode(200)); mockServerClient.when(updateCommitStatus).respond(response().withStatusCode(200));
return updateCommitStatus; return updateCommitStatus;
} }
private HttpRequest prepareAcceptMergeRequest(Integer projectId, Integer mergeRequestId) throws UnsupportedEncodingException { private HttpRequest prepareAcceptMergeRequest(String apiLevel) throws UnsupportedEncodingException {
return request() return request()
.withPath("/gitlab/api/v3/projects/" + projectId + "/merge_requests/" + mergeRequestId + "/merge") .withPath("/gitlab/api/" + apiLevel + "/projects/" + PROJECT_ID + "/merge_requests/" + MERGE_REQUEST_ID + "/merge")
.withMethod("PUT") .withMethod("PUT")
.withHeader("PRIVATE-TOKEN", "secret") .withHeader("PRIVATE-TOKEN", "secret")
.withBody("merge_commit_message=Merge+Request+accepted+by+jenkins+build+success&should_remove_source_branch=false"); .withBody("merge_commit_message=Merge+Request+accepted+by+jenkins+build+success&should_remove_source_branch=false");
} }
private AbstractBuild mockBuild(String buildUrl, String gitLabConnection, Result result, Integer buildNumber, String... remoteUrls) {
AbstractBuild build = mock(AbstractBuild.class);
BuildData buildData = mock(BuildData.class);
when(buildData.getRemoteUrls()).thenReturn(new HashSet<>(Arrays.asList(remoteUrls)));
when(build.getAction(BuildData.class)).thenReturn(buildData);
when(build.getResult()).thenReturn(result);
when(build.getUrl()).thenReturn(buildUrl);
when(build.getResult()).thenReturn(result);
when(build.getUrl()).thenReturn(buildUrl);
when(build.getNumber()).thenReturn(buildNumber);
AbstractProject<?, ?> project = mock(AbstractProject.class);
when(project.getProperty(GitLabConnectionProperty.class)).thenReturn(new GitLabConnectionProperty(gitLabConnection));
when(build.getProject()).thenReturn(project);
return build;
}
} }

View File

@ -1,31 +1,23 @@
package com.dabsquared.gitlabjenkins.publisher; package com.dabsquared.gitlabjenkins.publisher;
import static org.junit.Assert.assertThat;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.mockserver.model.HttpRequest.request;
import static org.mockserver.model.HttpResponse.response;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import com.dabsquared.gitlabjenkins.connection.GitLabConnectionProperty;
import com.dabsquared.gitlabjenkins.gitlab.api.model.BuildState;
import hudson.EnvVars;
import hudson.model.AbstractBuild;
import hudson.model.AbstractProject;
import hudson.model.BuildListener;
import hudson.model.Result;
import hudson.model.StreamBuildListener;
import hudson.model.TaskListener;
import hudson.plugins.git.Revision;
import hudson.plugins.git.util.Build;
import hudson.plugins.git.util.BuildData;
import jenkins.plugins.git.AbstractGitSCMSource;
import jenkins.scm.api.SCMRevisionAction;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.ObjectId;
import org.hamcrest.CoreMatchers; import org.hamcrest.CoreMatchers;
import org.jenkinsci.plugins.plaincredentials.impl.StringCredentialsImpl;
import org.junit.After; import org.junit.After;
import org.junit.Before; import org.junit.Before;
import org.junit.BeforeClass; import org.junit.BeforeClass;
@ -40,42 +32,37 @@ import org.mockserver.model.HttpRequest;
import org.mockserver.model.HttpResponse; import org.mockserver.model.HttpResponse;
import org.mockserver.verify.VerificationTimes; import org.mockserver.verify.VerificationTimes;
import com.cloudbees.plugins.credentials.CredentialsProvider; import java.io.ByteArrayOutputStream;
import com.cloudbees.plugins.credentials.CredentialsScope; import java.io.IOException;
import com.cloudbees.plugins.credentials.CredentialsStore; import java.io.PrintStream;
import com.cloudbees.plugins.credentials.SystemCredentialsProvider; import java.io.UnsupportedEncodingException;
import com.cloudbees.plugins.credentials.domains.Domain; import java.net.URLEncoder;
import com.dabsquared.gitlabjenkins.connection.GitLabConnection; import java.nio.charset.Charset;
import com.dabsquared.gitlabjenkins.connection.GitLabConnectionConfig; import java.util.ArrayList;
import com.dabsquared.gitlabjenkins.connection.GitLabConnectionProperty; import java.util.Arrays;
import com.dabsquared.gitlabjenkins.gitlab.api.model.BuildState; import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import hudson.EnvVars; import static com.dabsquared.gitlabjenkins.publisher.TestUtility.BUILD_URL;
import hudson.Launcher; import static com.dabsquared.gitlabjenkins.publisher.TestUtility.GITLAB_CONNECTION_V3;
import hudson.matrix.MatrixAggregator; import static com.dabsquared.gitlabjenkins.publisher.TestUtility.GITLAB_CONNECTION_V4;
import hudson.matrix.MatrixConfiguration; import static com.dabsquared.gitlabjenkins.publisher.TestUtility.PROJECT_ID;
import hudson.matrix.MatrixBuild; import static com.dabsquared.gitlabjenkins.publisher.TestUtility.setupGitLabConnections;
import hudson.model.AbstractBuild; import static com.dabsquared.gitlabjenkins.publisher.TestUtility.verifyMatrixAggregatable;
import hudson.model.AbstractProject; import static org.junit.Assert.assertThat;
import hudson.model.BuildListener; import static org.mockito.Matchers.any;
import hudson.model.Result; import static org.mockito.Matchers.anyString;
import hudson.model.StreamBuildListener; import static org.mockito.Mockito.mock;
import hudson.model.TaskListener; import static org.mockito.Mockito.when;
import hudson.plugins.git.Revision; import static org.mockserver.model.HttpRequest.request;
import hudson.plugins.git.util.Build; import static org.mockserver.model.HttpResponse.response;
import hudson.plugins.git.util.BuildData;
import hudson.util.Secret;
import jenkins.model.Jenkins;
import jenkins.plugins.git.AbstractGitSCMSource;
import jenkins.scm.api.SCMRevisionAction;
/** /**
* @author Robin Müller * @author Robin Müller
*/ */
public class GitLabCommitStatusPublisherTest { public class GitLabCommitStatusPublisherTest {
private static final String GIT_LAB_CONNECTION = "GitLab";
private static final String API_TOKEN = "secret";
private static final String SHA1 = "0616d12a3a24068691027a1e113147e3c1cfa2f4"; private static final String SHA1 = "0616d12a3a24068691027a1e113147e3c1cfa2f4";
@ClassRule @ClassRule
@ -88,17 +75,8 @@ public class GitLabCommitStatusPublisherTest {
private BuildListener listener; private BuildListener listener;
@BeforeClass @BeforeClass
public static void setupConnection() throws IOException { public static void setupClass() throws IOException {
GitLabConnectionConfig connectionConfig = jenkins.get(GitLabConnectionConfig.class); setupGitLabConnections(jenkins, mockServer);
String apiTokenId = "apiTokenId";
for (CredentialsStore credentialsStore : CredentialsProvider.lookupStores(Jenkins.getInstance())) {
if (credentialsStore instanceof SystemCredentialsProvider.StoreImpl) {
List<Domain> domains = credentialsStore.getDomains();
credentialsStore.addCredentials(domains.get(0),
new StringCredentialsImpl(CredentialsScope.SYSTEM, apiTokenId, "GitLab API Token", Secret.fromString(API_TOKEN)));
}
}
connectionConfig.addConnection(new GitLabConnection(GIT_LAB_CONNECTION, "http://localhost:" + mockServer.getPort() + "/gitlab", apiTokenId, false, 10, 10));
} }
@Before @Before
@ -113,283 +91,247 @@ public class GitLabCommitStatusPublisherTest {
} }
@Test @Test
public void matrixAggregatable() throws UnsupportedEncodingException, InterruptedException, IOException { public void matrixAggregatable() throws InterruptedException, IOException {
AbstractBuild build = mock(AbstractBuild.class); verifyMatrixAggregatable(GitLabCommitStatusPublisher.class, listener);
AbstractProject project = mock(MatrixConfiguration.class);
GitLabCommitStatusPublisher publisher = mock(GitLabCommitStatusPublisher.class);
MatrixBuild parentBuild = mock(MatrixBuild.class);
when(build.getParent()).thenReturn(project);
when(publisher.createAggregator(any(MatrixBuild.class), any(Launcher.class), any(BuildListener.class))).thenCallRealMethod();
when(publisher.perform(any(AbstractBuild.class), any(Launcher.class), any(BuildListener.class))).thenReturn(true);
MatrixAggregator aggregator = publisher.createAggregator(parentBuild, null, listener);
aggregator.startBuild();
aggregator.endBuild();
verify(publisher).perform(parentBuild, null, listener);
} }
@Test @Test
public void running() throws UnsupportedEncodingException { public void running_v3() throws UnsupportedEncodingException {
HttpRequest[] requests = new HttpRequest[] { AbstractBuild build = mockBuild(GITLAB_CONNECTION_V3, null, "test/project.git");
prepareExistsCommitWithSuccessResponse("test/project", SHA1), HttpRequest[] requests = prepareCheckCommitAndUpdateStatusRequests("v3", BuildState.running);
prepareUpdateCommitStatusWithSuccessResponse("test/project", SHA1, jenkins.getInstance().getRootUrl() + "/build/123", BuildState.running)
};
AbstractBuild build = mockBuild(SHA1, "/build/123", GIT_LAB_CONNECTION, null, "test/project.git");
GitLabCommitStatusPublisher publisher = new GitLabCommitStatusPublisher("jenkins", false); prebuildAndVerify(build, listener, requests);
publisher.prebuild(build, listener);
mockServerClient.verify(requests);
} }
@Test
public void running_v4() throws UnsupportedEncodingException {
AbstractBuild build = mockBuild(GITLAB_CONNECTION_V4, null, "test/project.git");
HttpRequest[] requests = prepareCheckCommitAndUpdateStatusRequests("v4", BuildState.running);
prebuildAndVerify(build, listener, requests);
}
@Test @Test
public void runningWithLibrary() throws UnsupportedEncodingException { public void runningWithLibrary() throws UnsupportedEncodingException {
HttpRequest[] requests = new HttpRequest[] { AbstractBuild build = mockBuildWithLibrary(GITLAB_CONNECTION_V4, null, "test/project.git");
prepareExistsCommitWithSuccessResponse("test/project", SHA1), HttpRequest[] requests = prepareCheckCommitAndUpdateStatusRequests("v4", BuildState.running);
prepareUpdateCommitStatusWithSuccessResponse("test/project", SHA1, jenkins.getInstance().getRootUrl() + "/build/123", BuildState.running)
};
AbstractBuild build = mockBuildWithLibrary(SHA1, "/build/123", GIT_LAB_CONNECTION, null, "test/project.git");
GitLabCommitStatusPublisher publisher = new GitLabCommitStatusPublisher("jenkins", false); prebuildAndVerify(build, listener, requests);
publisher.prebuild(build, listener);
mockServerClient.verify(requests);
} }
@Test @Test
public void runningWithDotInProjectId() throws IOException { public void runningWithDotInProjectId() throws IOException {
AbstractBuild build = mockBuild(GITLAB_CONNECTION_V4, null, "test/project.test.git");
HttpRequest[] requests = new HttpRequest[] { HttpRequest[] requests = new HttpRequest[] {
prepareGetProjectResponse("test/project.test",1), prepareGetProjectResponse("test/project.test"),
prepareExistsCommitWithSuccessResponse("1", SHA1), prepareExistsCommitWithSuccessResponse("v4", String.valueOf(PROJECT_ID)),
prepareUpdateCommitStatusWithSuccessResponse("1", SHA1, jenkins.getInstance().getRootUrl() + "/build/123", BuildState.running) prepareUpdateCommitStatusWithSuccessResponse("v4", String.valueOf(PROJECT_ID), BuildState.running)
}; };
AbstractBuild build = mockBuild(SHA1, "/build/123", GIT_LAB_CONNECTION, null, "test/project.test.git");
GitLabCommitStatusPublisher publisher = new GitLabCommitStatusPublisher("jenkins", false); prebuildAndVerify(build, listener, requests);
publisher.prebuild(build, listener);
mockServerClient.verify(requests);
} }
@Test @Test
public void canceled() throws IOException, InterruptedException { public void canceled_v3() throws IOException, InterruptedException {
HttpRequest[] requests = new HttpRequest[] { AbstractBuild build = mockBuild(GITLAB_CONNECTION_V3, Result.ABORTED, "test/project.git");
prepareExistsCommitWithSuccessResponse("test/project", SHA1), HttpRequest[] requests = prepareCheckCommitAndUpdateStatusRequests("v3", BuildState.canceled);
prepareUpdateCommitStatusWithSuccessResponse("test/project", SHA1, jenkins.getInstance().getRootUrl() + "/build/123", BuildState.canceled)
};
AbstractBuild build = mockBuild(SHA1, "/build/123", GIT_LAB_CONNECTION, Result.ABORTED, "test/project.git");
GitLabCommitStatusPublisher publisher = new GitLabCommitStatusPublisher("jenkins", false); performAndVerify(build, false, requests);
publisher.perform(build, null, listener); }
mockServerClient.verify(requests); @Test
public void canceled_v4() throws IOException, InterruptedException {
AbstractBuild build = mockBuild(GITLAB_CONNECTION_V4, Result.ABORTED, "test/project.git");
HttpRequest[] requests = prepareCheckCommitAndUpdateStatusRequests("v4", BuildState.canceled);
performAndVerify(build, false, requests);
} }
@Test @Test
public void canceledWithLibrary() throws IOException, InterruptedException { public void canceledWithLibrary() throws IOException, InterruptedException {
HttpRequest[] requests = new HttpRequest[] { AbstractBuild build = mockBuildWithLibrary(GITLAB_CONNECTION_V4, Result.ABORTED, "test/project.git");
prepareExistsCommitWithSuccessResponse("test/project", SHA1), HttpRequest[] requests = prepareCheckCommitAndUpdateStatusRequests("v4", BuildState.canceled);
prepareUpdateCommitStatusWithSuccessResponse("test/project", SHA1, jenkins.getInstance().getRootUrl() + "/build/123", BuildState.canceled)
};
AbstractBuild build = mockBuildWithLibrary(SHA1, "/build/123", GIT_LAB_CONNECTION, Result.ABORTED, "test/project.git");
GitLabCommitStatusPublisher publisher = new GitLabCommitStatusPublisher("jenkins", false); performAndVerify(build, false, requests);
publisher.perform(build, null, listener);
mockServerClient.verify(requests);
} }
@Test @Test
public void success() throws IOException, InterruptedException { public void success_v3() throws IOException, InterruptedException {
HttpRequest[] requests = new HttpRequest[] { AbstractBuild build = mockBuild(GITLAB_CONNECTION_V3, Result.SUCCESS, "test/project.git");
prepareExistsCommitWithSuccessResponse("test/project", SHA1), HttpRequest[] requests = prepareCheckCommitAndUpdateStatusRequests("v3", BuildState.success);
prepareUpdateCommitStatusWithSuccessResponse("test/project", SHA1, jenkins.getInstance().getRootUrl() + "/build/123", BuildState.success)
};
AbstractBuild build = mockBuild(SHA1, "/build/123", GIT_LAB_CONNECTION, Result.SUCCESS, "test/project.git");
GitLabCommitStatusPublisher publisher = new GitLabCommitStatusPublisher("jenkins", false); performAndVerify(build, false, requests);
publisher.perform(build, null, listener); }
mockServerClient.verify(requests);
@Test
public void success_v4() throws IOException, InterruptedException {
AbstractBuild build = mockBuild(GITLAB_CONNECTION_V4, Result.SUCCESS, "test/project.git");
HttpRequest[] requests = prepareCheckCommitAndUpdateStatusRequests("v4", BuildState.success);
performAndVerify(build, false, requests);
} }
@Test @Test
public void successWithLibrary() throws IOException, InterruptedException { public void successWithLibrary() throws IOException, InterruptedException {
HttpRequest[] requests = new HttpRequest[] { AbstractBuild build = mockBuildWithLibrary(GITLAB_CONNECTION_V4, Result.SUCCESS, "test/project.git");
prepareExistsCommitWithSuccessResponse("test/project", SHA1), HttpRequest[] requests = prepareCheckCommitAndUpdateStatusRequests("v4", BuildState.success);
prepareUpdateCommitStatusWithSuccessResponse("test/project", SHA1, jenkins.getInstance().getRootUrl() + "/build/123", BuildState.success)
};
AbstractBuild build = mockBuildWithLibrary(SHA1, "/build/123", GIT_LAB_CONNECTION, Result.SUCCESS, "test/project.git");
GitLabCommitStatusPublisher publisher = new GitLabCommitStatusPublisher("jenkins", false); performAndVerify(build, false, requests);
publisher.perform(build, null, listener);
mockServerClient.verify(requests);
} }
@Test @Test
public void failed() throws IOException, InterruptedException { public void failed_v3() throws IOException, InterruptedException {
HttpRequest[] requests = new HttpRequest[] { AbstractBuild build = mockBuild(GITLAB_CONNECTION_V3, Result.FAILURE, "test/project.git");
prepareExistsCommitWithSuccessResponse("test/project", SHA1), HttpRequest[] requests = prepareCheckCommitAndUpdateStatusRequests("v3", BuildState.failed);
prepareUpdateCommitStatusWithSuccessResponse("test/project", SHA1, jenkins.getInstance().getRootUrl() + "/build/123", BuildState.failed)
};
AbstractBuild build = mockBuild(SHA1, "/build/123", GIT_LAB_CONNECTION, Result.FAILURE, "test/project.git");
GitLabCommitStatusPublisher publisher = new GitLabCommitStatusPublisher("jenkins", false); performAndVerify(build, false, requests);
publisher.perform(build, null, listener); }
mockServerClient.verify(requests); @Test
public void failed_v4() throws IOException, InterruptedException {
AbstractBuild build = mockBuild(GITLAB_CONNECTION_V3, Result.FAILURE, "test/project.git");
HttpRequest[] requests = prepareCheckCommitAndUpdateStatusRequests("v3", BuildState.failed);
performAndVerify(build, false, requests);
} }
@Test @Test
public void failedWithLibrary() throws IOException, InterruptedException { public void failedWithLibrary() throws IOException, InterruptedException {
HttpRequest[] requests = new HttpRequest[] { AbstractBuild build = mockBuildWithLibrary(GITLAB_CONNECTION_V4, Result.FAILURE, "test/project.git");
prepareExistsCommitWithSuccessResponse("test/project", SHA1), HttpRequest[] requests = prepareCheckCommitAndUpdateStatusRequests("v4", BuildState.failed);
prepareUpdateCommitStatusWithSuccessResponse("test/project", SHA1, jenkins.getInstance().getRootUrl() + "/build/123", BuildState.failed)
};
AbstractBuild build = mockBuildWithLibrary(SHA1, "/build/123", GIT_LAB_CONNECTION, Result.FAILURE, "test/project.git");
GitLabCommitStatusPublisher publisher = new GitLabCommitStatusPublisher("jenkins", false); performAndVerify(build, false, requests);
publisher.perform(build, null, listener);
mockServerClient.verify(requests);
} }
@Test @Test
public void unstable() throws IOException, InterruptedException { public void unstable() throws IOException, InterruptedException {
HttpRequest[] requests = new HttpRequest[] { AbstractBuild build = mockBuild(GITLAB_CONNECTION_V4, Result.UNSTABLE, "test/project.git");
prepareExistsCommitWithSuccessResponse("test/project", SHA1), HttpRequest[] requests = prepareCheckCommitAndUpdateStatusRequests("v4", BuildState.failed);
prepareUpdateCommitStatusWithSuccessResponse("test/project", SHA1, jenkins.getInstance().getRootUrl() + "/build/123", BuildState.failed)
};
AbstractBuild build = mockBuild(SHA1, "/build/123", GIT_LAB_CONNECTION, Result.UNSTABLE, "test/project.git");
GitLabCommitStatusPublisher publisher = new GitLabCommitStatusPublisher("jenkins", false); performAndVerify(build, false, requests);
publisher.perform(build, null, listener);
mockServerClient.verify(requests);
} }
@Test @Test
public void unstableWithLibrary() throws IOException, InterruptedException { public void unstableWithLibrary() throws IOException, InterruptedException {
HttpRequest[] requests = new HttpRequest[] { AbstractBuild build = mockBuildWithLibrary(GITLAB_CONNECTION_V4, Result.UNSTABLE, "test/project.git");
prepareExistsCommitWithSuccessResponse("test/project", SHA1), HttpRequest[] requests = prepareCheckCommitAndUpdateStatusRequests("v4", BuildState.failed);
prepareUpdateCommitStatusWithSuccessResponse("test/project", SHA1, jenkins.getInstance().getRootUrl() + "/build/123", BuildState.failed)
};
AbstractBuild build = mockBuildWithLibrary(SHA1, "/build/123", GIT_LAB_CONNECTION, Result.UNSTABLE, "test/project.git");
GitLabCommitStatusPublisher publisher = new GitLabCommitStatusPublisher("jenkins", false); performAndVerify(build, false, requests);
publisher.perform(build, null, listener);
mockServerClient.verify(requests);
} }
@Test @Test
public void unstableAsSuccess() throws IOException, InterruptedException { public void unstableAsSuccess() throws IOException, InterruptedException {
HttpRequest[] requests = new HttpRequest[] { AbstractBuild build = mockBuild(GITLAB_CONNECTION_V4, Result.UNSTABLE, "test/project.git");
prepareExistsCommitWithSuccessResponse("test/project", SHA1), HttpRequest[] requests = prepareCheckCommitAndUpdateStatusRequests("v4", BuildState.success);
prepareUpdateCommitStatusWithSuccessResponse("test/project", SHA1, jenkins.getInstance().getRootUrl() + "/build/123", BuildState.success)
};
AbstractBuild build = mockBuild(SHA1, "/build/123", GIT_LAB_CONNECTION, Result.UNSTABLE, "test/project.git");
GitLabCommitStatusPublisher publisher = new GitLabCommitStatusPublisher("jenkins", true); performAndVerify(build, true, requests);
publisher.perform(build, null, listener);
mockServerClient.verify(requests);
} }
@Test @Test
public void running_multipleRepos() throws UnsupportedEncodingException { public void running_multipleRepos() throws UnsupportedEncodingException {
AbstractBuild build = mockBuild(GITLAB_CONNECTION_V4, null, "test/project-1.git", "test/project-2.git");
HttpRequest[] requests = new HttpRequest[] { HttpRequest[] requests = new HttpRequest[] {
prepareExistsCommitWithSuccessResponse("test/project-1", SHA1), prepareExistsCommitWithSuccessResponse("v4", "test/project-1"),
prepareUpdateCommitStatusWithSuccessResponse("test/project-1", SHA1, jenkins.getInstance().getRootUrl() + "/build/123", BuildState.running), prepareUpdateCommitStatusWithSuccessResponse("v4", "test/project-1", BuildState.running),
prepareExistsCommitWithSuccessResponse("test/project-2", SHA1), prepareExistsCommitWithSuccessResponse("v4", "test/project-2"),
prepareUpdateCommitStatusWithSuccessResponse("test/project-2", SHA1, jenkins.getInstance().getRootUrl() + "/build/123", BuildState.running) prepareUpdateCommitStatusWithSuccessResponse("v4", "test/project-2", BuildState.running)
}; };
AbstractBuild build = mockBuild(SHA1, "/build/123", GIT_LAB_CONNECTION, null, "test/project-1.git", "test/project-2.git");
GitLabCommitStatusPublisher publisher = new GitLabCommitStatusPublisher("jenkins", false); prebuildAndVerify(build, listener, requests);
publisher.prebuild(build, listener);
mockServerClient.verify(requests);
} }
@Test @Test
public void running_commitNotExists() throws UnsupportedEncodingException { public void running_commitNotExists() throws UnsupportedEncodingException {
HttpRequest updateCommitStatus = prepareUpdateCommitStatusWithSuccessResponse("test/project", SHA1, jenkins.getInstance().getRootUrl() + "/build/123", BuildState.running); AbstractBuild build = mockBuild(GITLAB_CONNECTION_V4, null, "test/project.git");
AbstractBuild build = mockBuild(SHA1, "/build/123", GIT_LAB_CONNECTION, null, "test/project.git"); HttpRequest updateCommitStatus = prepareUpdateCommitStatusWithSuccessResponse("v4", "test/project", BuildState.running);
GitLabCommitStatusPublisher publisher = new GitLabCommitStatusPublisher("jenkins", false);
publisher.prebuild(build, listener);
new GitLabCommitStatusPublisher("jenkins", false).prebuild(build, listener);
mockServerClient.verify(updateCommitStatus, VerificationTimes.exactly(0)); mockServerClient.verify(updateCommitStatus, VerificationTimes.exactly(0));
} }
@Test @Test
public void running_failToUpdate() throws UnsupportedEncodingException { public void running_failToUpdate() throws UnsupportedEncodingException {
prepareExistsCommitWithSuccessResponse("test/project", SHA1); AbstractBuild build = mockBuild(GITLAB_CONNECTION_V4, null, "test/project.git");
HttpRequest updateCommitStatus = prepareUpdateCommitStatus("test/project", SHA1, jenkins.getInstance().getRootUrl() + "/build/123", BuildState.running);
mockServerClient.when(updateCommitStatus).respond(response().withStatusCode(403));
AbstractBuild build = mockBuild(SHA1, "/build/123", GIT_LAB_CONNECTION, null, "test/project.git");
BuildListener buildListener = mock(BuildListener.class); BuildListener buildListener = mock(BuildListener.class);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
when(buildListener.getLogger()).thenReturn(new PrintStream(outputStream)); when(buildListener.getLogger()).thenReturn(new PrintStream(outputStream));
GitLabCommitStatusPublisher publisher = new GitLabCommitStatusPublisher("jenkins", false); prepareExistsCommitWithSuccessResponse("v4", "test/project");
publisher.prebuild(build, buildListener); HttpRequest updateCommitStatus = prepareUpdateCommitStatus("v4", "test/project", BuildState.running);
mockServerClient.when(updateCommitStatus).respond(response().withStatusCode(403));
prebuildAndVerify(build, buildListener, updateCommitStatus);
assertThat(outputStream.toString(), CoreMatchers.containsString("Failed to update Gitlab commit status for project 'test/project': HTTP 403 Forbidden")); assertThat(outputStream.toString(), CoreMatchers.containsString("Failed to update Gitlab commit status for project 'test/project': HTTP 403 Forbidden"));
mockServerClient.verify(updateCommitStatus);
} }
private void prebuildAndVerify(AbstractBuild build, BuildListener listener, HttpRequest... requests) {
new GitLabCommitStatusPublisher("jenkins", false).prebuild(build, listener);
mockServerClient.verify(requests);
}
private HttpRequest prepareUpdateCommitStatusWithSuccessResponse(String projectId, String sha, String targetUrl, BuildState state) throws UnsupportedEncodingException { private void performAndVerify(AbstractBuild build, boolean markUnstableAsSuccess, HttpRequest... requests) throws InterruptedException, IOException {
HttpRequest updateCommitStatus = prepareUpdateCommitStatus(projectId, sha, targetUrl, state); new GitLabCommitStatusPublisher("jenkins", markUnstableAsSuccess).perform(build, null, listener);
mockServerClient.verify(requests);
}
private HttpRequest[] prepareCheckCommitAndUpdateStatusRequests(String apiLevel, BuildState buildState) throws UnsupportedEncodingException {
return new HttpRequest[] {
prepareExistsCommitWithSuccessResponse(apiLevel, "test/project"),
prepareUpdateCommitStatusWithSuccessResponse(apiLevel, "test/project", buildState)
};
}
private HttpRequest prepareUpdateCommitStatusWithSuccessResponse(String apiLevel, String projectName, BuildState state) throws UnsupportedEncodingException {
HttpRequest updateCommitStatus = prepareUpdateCommitStatus(apiLevel, projectName, state);
mockServerClient.when(updateCommitStatus).respond(response().withStatusCode(200)); mockServerClient.when(updateCommitStatus).respond(response().withStatusCode(200));
return updateCommitStatus; return updateCommitStatus;
} }
private HttpRequest prepareUpdateCommitStatus(String projectId, String sha, String targetUrl, BuildState state) throws UnsupportedEncodingException { private HttpRequest prepareUpdateCommitStatus(final String apiLevel, String projectName, BuildState state) throws UnsupportedEncodingException {
return request() return request()
.withPath("/gitlab/api/v3/projects/" + URLEncoder.encode(projectId, "UTF-8") + "/statuses/" + sha) .withPath("/gitlab/api/" + apiLevel + "/projects/" + URLEncoder.encode(projectName, "UTF-8") + "/statuses/" + SHA1)
.withMethod("POST") .withMethod("POST")
.withHeader("PRIVATE-TOKEN", "secret") .withHeader("PRIVATE-TOKEN", "secret")
.withBody("state=" + URLEncoder.encode(state.name(), "UTF-8") + "&context=jenkins&" + "target_url=" + URLEncoder.encode(targetUrl, "UTF-8")); .withBody("state=" + URLEncoder.encode(state.name(), "UTF-8") + "&context=jenkins&" + "target_url=" + URLEncoder.encode(jenkins.getInstance().getRootUrl() + BUILD_URL, "UTF-8"));
} }
private HttpRequest prepareExistsCommitWithSuccessResponse(String projectId, String sha) throws UnsupportedEncodingException { private HttpRequest prepareExistsCommitWithSuccessResponse(String apiLevel, String projectName) throws UnsupportedEncodingException {
HttpRequest existsCommit = prepareExistsCommit(projectId, sha); HttpRequest existsCommit = prepareExistsCommit(apiLevel, projectName);
mockServerClient.when(existsCommit).respond(response().withStatusCode(200)); mockServerClient.when(existsCommit).respond(response().withStatusCode(200));
return existsCommit; return existsCommit;
} }
private HttpRequest prepareExistsCommit(String projectId, String sha) throws UnsupportedEncodingException { private HttpRequest prepareExistsCommit(String apiLevel, String projectName) throws UnsupportedEncodingException {
return request() return request()
.withPath("/gitlab/api/v3/projects/" + URLEncoder.encode(projectId, "UTF-8") + "/repository/commits/" + sha) .withPath("/gitlab/api/" + apiLevel + "/projects/" + URLEncoder.encode(projectName, "UTF-8") + "/repository/commits/" + SHA1)
.withMethod("GET") .withMethod("GET")
.withHeader("PRIVATE-TOKEN", "secret"); .withHeader("PRIVATE-TOKEN", "secret");
} }
private HttpRequest prepareGetProjectResponse(String projectName, int projectId) throws IOException { private HttpRequest prepareGetProjectResponse(String projectName) throws IOException {
HttpRequest request= request() HttpRequest request= request()
.withPath("/gitlab/api/v3/projects/" + URLEncoder.encode(projectName, "UTF-8")) .withPath("/gitlab/api/v4/projects/" + URLEncoder.encode(projectName, "UTF-8"))
.withMethod("GET") .withMethod("GET")
. withHeader("PRIVATE-TOKEN", "secret"); . withHeader("PRIVATE-TOKEN", "secret");
HttpResponse response = response().withBody(getSingleProjectJson("GetSingleProject.json",projectName,projectId)); HttpResponse response = response().withBody(getSingleProjectJson("GetSingleProject.json", projectName, PROJECT_ID));
response.withHeader("Content-Type", "application/json"); response.withHeader("Content-Type", "application/json");
mockServerClient.when(request).respond(response.withStatusCode(200)); mockServerClient.when(request).respond(response.withStatusCode(200));
return request; return request;
} }
private AbstractBuild mockBuild(String sha, String buildUrl, String gitLabConnection, Result result, String... remoteUrls) { private AbstractBuild mockBuild(String gitLabConnection, Result result, String... remoteUrls) {
AbstractBuild build = mock(AbstractBuild.class); AbstractBuild build = mock(AbstractBuild.class);
List<BuildData> buildDatas = new ArrayList<BuildData>(); List<BuildData> buildDatas = new ArrayList<>();
BuildData buildData = mock(BuildData.class); BuildData buildData = mock(BuildData.class);
Revision revision = mock(Revision.class); Revision revision = mock(Revision.class);
when(revision.getSha1String()).thenReturn(sha); when(revision.getSha1String()).thenReturn(SHA1);
when(buildData.getLastBuiltRevision()).thenReturn(revision); when(buildData.getLastBuiltRevision()).thenReturn(revision);
when(buildData.getRemoteUrls()).thenReturn(new HashSet<>(Arrays.asList(remoteUrls))); when(buildData.getRemoteUrls()).thenReturn(new HashSet<>(Arrays.asList(remoteUrls)));
Build gitBuild = mock(Build.class); Build gitBuild = mock(Build.class);
@ -399,7 +341,7 @@ public class GitLabCommitStatusPublisherTest {
when(build.getActions(BuildData.class)).thenReturn(buildDatas); when(build.getActions(BuildData.class)).thenReturn(buildDatas);
when(build.getAction(BuildData.class)).thenReturn(buildData); when(build.getAction(BuildData.class)).thenReturn(buildData);
when(build.getResult()).thenReturn(result); when(build.getResult()).thenReturn(result);
when(build.getUrl()).thenReturn(buildUrl); when(build.getUrl()).thenReturn(BUILD_URL);
AbstractProject<?, ?> project = mock(AbstractProject.class); AbstractProject<?, ?> project = mock(AbstractProject.class);
when(project.getProperty(GitLabConnectionProperty.class)).thenReturn(new GitLabConnectionProperty(gitLabConnection)); when(project.getProperty(GitLabConnectionProperty.class)).thenReturn(new GitLabConnectionProperty(gitLabConnection));
when(build.getProject()).thenReturn(project); when(build.getProject()).thenReturn(project);
@ -418,26 +360,26 @@ public class GitLabCommitStatusPublisherTest {
return build; return build;
} }
private AbstractBuild mockBuildWithLibrary(String sha, String buildUrl, String gitLabConnection, Result result, String... remoteUrls) { private AbstractBuild mockBuildWithLibrary(String gitLabConnection, Result result, String... remoteUrls) {
AbstractBuild build = mock(AbstractBuild.class); AbstractBuild build = mock(AbstractBuild.class);
List<BuildData> buildDatas = new ArrayList<BuildData>(); List<BuildData> buildDatas = new ArrayList<>();
BuildData buildData = mock(BuildData.class); BuildData buildData = mock(BuildData.class);
SCMRevisionAction scmRevisionAction = mock(SCMRevisionAction.class); SCMRevisionAction scmRevisionAction = mock(SCMRevisionAction.class);
AbstractGitSCMSource.SCMRevisionImpl revisionImpl = mock(AbstractGitSCMSource.SCMRevisionImpl.class); AbstractGitSCMSource.SCMRevisionImpl revisionImpl = mock(AbstractGitSCMSource.SCMRevisionImpl.class);
when(build.getAction(SCMRevisionAction.class)).thenReturn(scmRevisionAction); when(build.getAction(SCMRevisionAction.class)).thenReturn(scmRevisionAction);
when(scmRevisionAction.getRevision()).thenReturn(revisionImpl); when(scmRevisionAction.getRevision()).thenReturn(revisionImpl);
when(revisionImpl.getHash()).thenReturn(sha); when(revisionImpl.getHash()).thenReturn(SHA1);
Revision revision = mock(Revision.class); Revision revision = mock(Revision.class);
when(revision.getSha1String()).thenReturn(sha); when(revision.getSha1String()).thenReturn(SHA1);
when(buildData.getLastBuiltRevision()).thenReturn(revision); when(buildData.getLastBuiltRevision()).thenReturn(revision);
when(buildData.getRemoteUrls()).thenReturn(new HashSet<>(Arrays.asList(remoteUrls))); when(buildData.getRemoteUrls()).thenReturn(new HashSet<>(Arrays.asList(remoteUrls)));
Build gitBuild = mock(Build.class); Build gitBuild = mock(Build.class);
when(gitBuild.getMarked()).thenReturn(revision); when(gitBuild.getMarked()).thenReturn(revision);
when(gitBuild.getSHA1()).thenReturn(ObjectId.fromString(sha)); when(gitBuild.getSHA1()).thenReturn(ObjectId.fromString(SHA1));
when(buildData.getLastBuild(any(ObjectId.class))).thenReturn(gitBuild); when(buildData.getLastBuild(any(ObjectId.class))).thenReturn(gitBuild);
Map<String, Build> buildsByBranchName = new HashMap<>(); Map<String, Build> buildsByBranchName = new HashMap<>();
buildsByBranchName.put("develop", gitBuild); buildsByBranchName.put("develop", gitBuild);
@ -456,7 +398,7 @@ public class GitLabCommitStatusPublisherTest {
when(build.getActions(BuildData.class)).thenReturn(buildDatas); when(build.getActions(BuildData.class)).thenReturn(buildDatas);
when(build.getResult()).thenReturn(result); when(build.getResult()).thenReturn(result);
when(build.getUrl()).thenReturn(buildUrl); when(build.getUrl()).thenReturn(BUILD_URL);
AbstractProject<?, ?> project = mock(AbstractProject.class); AbstractProject<?, ?> project = mock(AbstractProject.class);
when(project.getProperty(GitLabConnectionProperty.class)).thenReturn(new GitLabConnectionProperty(gitLabConnection)); when(project.getProperty(GitLabConnectionProperty.class)).thenReturn(new GitLabConnectionProperty(gitLabConnection));

View File

@ -1,18 +1,8 @@
package com.dabsquared.gitlabjenkins.publisher; package com.dabsquared.gitlabjenkins.publisher;
import com.cloudbees.plugins.credentials.CredentialsProvider;
import com.cloudbees.plugins.credentials.CredentialsScope;
import com.cloudbees.plugins.credentials.CredentialsStore;
import com.cloudbees.plugins.credentials.SystemCredentialsProvider;
import com.cloudbees.plugins.credentials.domains.Domain;
import com.dabsquared.gitlabjenkins.connection.GitLabConnection;
import com.dabsquared.gitlabjenkins.connection.GitLabConnectionConfig;
import com.dabsquared.gitlabjenkins.connection.GitLabConnectionProperty; import com.dabsquared.gitlabjenkins.connection.GitLabConnectionProperty;
import hudson.EnvVars; import hudson.EnvVars;
import hudson.Launcher;
import hudson.matrix.MatrixAggregator;
import hudson.matrix.MatrixConfiguration;
import hudson.matrix.MatrixBuild;
import hudson.model.AbstractBuild; import hudson.model.AbstractBuild;
import hudson.model.AbstractProject; import hudson.model.AbstractProject;
import hudson.model.BuildListener; import hudson.model.BuildListener;
@ -20,9 +10,6 @@ import hudson.model.Result;
import hudson.model.StreamBuildListener; import hudson.model.StreamBuildListener;
import hudson.model.TaskListener; import hudson.model.TaskListener;
import hudson.plugins.git.util.BuildData; import hudson.plugins.git.util.BuildData;
import hudson.util.Secret;
import jenkins.model.Jenkins;
import org.jenkinsci.plugins.plaincredentials.impl.StringCredentialsImpl;
import org.junit.After; import org.junit.After;
import org.junit.Before; import org.junit.Before;
import org.junit.BeforeClass; import org.junit.BeforeClass;
@ -39,17 +26,23 @@ import java.io.IOException;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.net.URLEncoder; import java.net.URLEncoder;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.text.MessageFormat;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashSet; import java.util.HashSet;
import java.util.List;
import static com.dabsquared.gitlabjenkins.publisher.TestUtility.BUILD_NUMBER;
import static com.dabsquared.gitlabjenkins.publisher.TestUtility.BUILD_URL;
import static com.dabsquared.gitlabjenkins.publisher.TestUtility.GITLAB_CONNECTION_V3;
import static com.dabsquared.gitlabjenkins.publisher.TestUtility.GITLAB_CONNECTION_V4;
import static com.dabsquared.gitlabjenkins.publisher.TestUtility.MERGE_REQUEST_ID;
import static com.dabsquared.gitlabjenkins.publisher.TestUtility.PROJECT_ID;
import static com.dabsquared.gitlabjenkins.publisher.TestUtility.formatNote;
import static com.dabsquared.gitlabjenkins.publisher.TestUtility.setupGitLabConnections;
import static com.dabsquared.gitlabjenkins.publisher.TestUtility.verifyMatrixAggregatable;
import static org.mockito.Matchers.any; import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy; import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import static org.mockserver.model.HttpRequest.request; import static org.mockserver.model.HttpRequest.request;
import static org.mockserver.model.HttpResponse.response; import static org.mockserver.model.HttpResponse.response;
@ -58,10 +51,6 @@ import static org.mockserver.model.HttpResponse.response;
* @author Nikolay Ustinov * @author Nikolay Ustinov
*/ */
public class GitLabMessagePublisherTest { public class GitLabMessagePublisherTest {
private static final String GIT_LAB_CONNECTION = "GitLab";
private static final String API_TOKEN = "secret";
@ClassRule @ClassRule
public static MockServerRule mockServer = new MockServerRule(new Object()); public static MockServerRule mockServer = new MockServerRule(new Object());
@ -72,17 +61,8 @@ public class GitLabMessagePublisherTest {
private BuildListener listener; private BuildListener listener;
@BeforeClass @BeforeClass
public static void setupConnection() throws IOException { public static void setupClass() throws IOException {
GitLabConnectionConfig connectionConfig = jenkins.get(GitLabConnectionConfig.class); setupGitLabConnections(jenkins, mockServer);
String apiTokenId = "apiTokenId";
for (CredentialsStore credentialsStore : CredentialsProvider.lookupStores(Jenkins.getInstance())) {
if (credentialsStore instanceof SystemCredentialsProvider.StoreImpl) {
List<Domain> domains = credentialsStore.getDomains();
credentialsStore.addCredentials(domains.get(0),
new StringCredentialsImpl(CredentialsScope.SYSTEM, apiTokenId, "GitLab API Token", Secret.fromString(API_TOKEN)));
}
}
connectionConfig.addConnection(new GitLabConnection(GIT_LAB_CONNECTION, "http://localhost:" + mockServer.getPort() + "/gitlab", apiTokenId, false, 10, 10));
} }
@Before @Before
@ -97,229 +77,170 @@ public class GitLabMessagePublisherTest {
} }
@Test @Test
public void canceled() throws IOException, InterruptedException { public void matrixAggregatable() throws InterruptedException, IOException {
Integer buildNumber = 1; verifyMatrixAggregatable(GitLabMessagePublisher.class, listener);
Integer projectId = 3;
Integer mergeRequestId = 1;
AbstractBuild build = mockBuild("/build/123", GIT_LAB_CONNECTION, Result.ABORTED, buildNumber);
String buildUrl = Jenkins.getInstance().getRootUrl() + build.getUrl();
String defaultNote = MessageFormat.format(":point_up: Jenkins Build {0}\n\nResults available at: [Jenkins [{1} #{2}]]({3})",
Result.ABORTED, build.getParent().getDisplayName(), buildNumber, buildUrl);
HttpRequest[] requests = new HttpRequest[] {
prepareSendMessageWithSuccessResponse(projectId, mergeRequestId, defaultNote)
};
GitLabMessagePublisher publisher = spy(new GitLabMessagePublisher(false, false, false, false, false, null, null, null, null));
doReturn(projectId).when(publisher).getProjectId(build);
doReturn(mergeRequestId).when(publisher).getMergeRequestId(build);
publisher.perform(build, null, listener);
mockServerClient.verify(requests);
} }
@Test @Test
public void matrixAggregatable() throws UnsupportedEncodingException, InterruptedException, IOException { public void canceled_v3() throws IOException, InterruptedException {
AbstractBuild build = mock(AbstractBuild.class); AbstractBuild build = mockBuild(GITLAB_CONNECTION_V3, Result.ABORTED);
AbstractProject project = mock(MatrixConfiguration.class); String defaultNote = formatNote(build, ":point_up: Jenkins Build {0}\n\nResults available at: [Jenkins [{1} #{2}]]({3})");
GitLabCommitStatusPublisher publisher = mock(GitLabCommitStatusPublisher.class);
MatrixBuild parentBuild = mock(MatrixBuild.class);
when(build.getParent()).thenReturn(project); performAndVerify(
when(publisher.createAggregator(any(MatrixBuild.class), any(Launcher.class), any(BuildListener.class))).thenCallRealMethod(); build, defaultNote, false, false, false, false, false,
when(publisher.perform(any(AbstractBuild.class), any(Launcher.class), any(BuildListener.class))).thenReturn(true); prepareSendMessageWithSuccessResponse("v3", defaultNote));
MatrixAggregator aggregator = publisher.createAggregator(parentBuild, null, listener);
aggregator.startBuild();
aggregator.endBuild();
verify(publisher).perform(parentBuild, null, listener);
} }
@Test
public void canceled_v4() throws IOException, InterruptedException {
AbstractBuild build = mockBuild(GITLAB_CONNECTION_V4, Result.ABORTED);
String defaultNote = formatNote(build, ":point_up: Jenkins Build {0}\n\nResults available at: [Jenkins [{1} #{2}]]({3})");
performAndVerify(
build, defaultNote, false, false, false, false, false,
prepareSendMessageWithSuccessResponse("v4", defaultNote));
}
@Test
public void success_v3() throws IOException, InterruptedException {
AbstractBuild build = mockBuild(GITLAB_CONNECTION_V3, Result.SUCCESS);
String defaultNote = formatNote(build, ":white_check_mark: Jenkins Build {0}\n\nResults available at: [Jenkins [{1} #{2}]]({3})");
performAndVerify(
build, defaultNote, false, false, false, false, false,
prepareSendMessageWithSuccessResponse("v3", defaultNote));
}
@Test @Test
public void success() throws IOException, InterruptedException { public void success() throws IOException, InterruptedException {
Integer buildNumber = 1; AbstractBuild build = mockBuild(GITLAB_CONNECTION_V4, Result.SUCCESS);
Integer projectId = 3; String defaultNote = formatNote(build, ":white_check_mark: Jenkins Build {0}\n\nResults available at: [Jenkins [{1} #{2}]]({3})");
Integer mergeRequestId = 1;
AbstractBuild build = mockBuild("/build/123", GIT_LAB_CONNECTION, Result.SUCCESS, buildNumber);
String buildUrl = Jenkins.getInstance().getRootUrl() + build.getUrl();
String defaultNote = MessageFormat.format(":white_check_mark: Jenkins Build {0}\n\nResults available at: [Jenkins [{1} #{2}]]({3})",
Result.SUCCESS, build.getParent().getDisplayName(), buildNumber, buildUrl);
HttpRequest[] requests = new HttpRequest[] { performAndVerify(
prepareSendMessageWithSuccessResponse(projectId, mergeRequestId, defaultNote) build, defaultNote, false, false, false, false, false,
}; prepareSendMessageWithSuccessResponse("v4", defaultNote));
GitLabMessagePublisher publisher = spy(new GitLabMessagePublisher(false, false, false, false, false, null, null, null, null));
doReturn(projectId).when(publisher).getProjectId(build);
doReturn(mergeRequestId).when(publisher).getMergeRequestId(build);
publisher.perform(build, null, listener);
mockServerClient.verify(requests);
} }
@Test @Test
public void success_withOnlyForFailure() throws IOException, InterruptedException { public void success_withOnlyForFailure() throws IOException, InterruptedException {
Integer buildNumber = 1; AbstractBuild build = mockBuild(GITLAB_CONNECTION_V4, Result.SUCCESS);
Integer projectId = 3;
Integer mergeRequestId = 1;
AbstractBuild build = mockBuild("/build/123", GIT_LAB_CONNECTION, Result.SUCCESS, buildNumber);
GitLabMessagePublisher publisher = spy(new GitLabMessagePublisher(true, false, false, false, false, null, null, null, null)); performAndVerify(build, "test", true, false, false, false, false);
doReturn(projectId).when(publisher).getProjectId(build);
doReturn(mergeRequestId).when(publisher).getMergeRequestId(build);
publisher.perform(build, null, listener);
mockServerClient.verifyZeroInteractions();
} }
@Test @Test
public void failed() throws IOException, InterruptedException { public void failed_v3() throws IOException, InterruptedException {
Integer buildNumber = 1; AbstractBuild build = mockBuild(GITLAB_CONNECTION_V3, Result.FAILURE);
Integer projectId = 3; String defaultNote = formatNote(build, ":negative_squared_cross_mark: Jenkins Build {0}\n\nResults available at: [Jenkins [{1} #{2}]]({3})");
Integer mergeRequestId = 1;
AbstractBuild build = mockBuild("/build/123", GIT_LAB_CONNECTION, Result.FAILURE, buildNumber);
String buildUrl = Jenkins.getInstance().getRootUrl() + build.getUrl();
String defaultNote = MessageFormat.format(":negative_squared_cross_mark: Jenkins Build {0}\n\nResults available at: [Jenkins [{1} #{2}]]({3})",
Result.FAILURE, build.getParent().getDisplayName(), buildNumber, buildUrl);
HttpRequest[] requests = new HttpRequest[] { performAndVerify(
prepareSendMessageWithSuccessResponse(projectId, mergeRequestId, defaultNote) build, defaultNote, false, false, false, false, false,
}; prepareSendMessageWithSuccessResponse("v3", defaultNote));
GitLabMessagePublisher publisher = spy(new GitLabMessagePublisher(false, false, false, false, false, null, null, null, null));
doReturn(projectId).when(publisher).getProjectId(build);
doReturn(mergeRequestId).when(publisher).getMergeRequestId(build);
publisher.perform(build, null, listener);
mockServerClient.verify(requests);
} }
@Test
public void failed_v4() throws IOException, InterruptedException {
AbstractBuild build = mockBuild(GITLAB_CONNECTION_V4, Result.FAILURE);
String defaultNote = formatNote(build, ":negative_squared_cross_mark: Jenkins Build {0}\n\nResults available at: [Jenkins [{1} #{2}]]({3})");
performAndVerify(
build, defaultNote, false, false, false, false, false,
prepareSendMessageWithSuccessResponse("v4", defaultNote));
}
@Test @Test
public void failed_withOnlyForFailed() throws IOException, InterruptedException { public void failed_withOnlyForFailed() throws IOException, InterruptedException {
Integer buildNumber = 1; AbstractBuild build = mockBuild(GITLAB_CONNECTION_V4, Result.FAILURE);
Integer projectId = 3; String defaultNote = formatNote(build, ":negative_squared_cross_mark: Jenkins Build {0}\n\nResults available at: [Jenkins [{1} #{2}]]({3})");
Integer mergeRequestId = 1;
AbstractBuild build = mockBuild("/build/123", GIT_LAB_CONNECTION, Result.FAILURE, buildNumber);
String buildUrl = Jenkins.getInstance().getRootUrl() + build.getUrl();
String defaultNote = MessageFormat.format(":negative_squared_cross_mark: Jenkins Build {0}\n\nResults available at: [Jenkins [{1} #{2}]]({3})",
Result.FAILURE, build.getParent().getDisplayName(), buildNumber, buildUrl);
HttpRequest[] requests = new HttpRequest[] { performAndVerify(
prepareSendMessageWithSuccessResponse(projectId, mergeRequestId, defaultNote) build, defaultNote, true, false, false, false, false,
}; prepareSendMessageWithSuccessResponse("v4", defaultNote));
GitLabMessagePublisher publisher = spy(new GitLabMessagePublisher(true, false, false, false, false, null, null, null, null));
doReturn(projectId).when(publisher).getProjectId(build);
doReturn(mergeRequestId).when(publisher).getMergeRequestId(build);
publisher.perform(build, null, listener);
mockServerClient.verify(requests);
} }
@Test @Test
public void canceledWithCustomNote() throws IOException, InterruptedException { public void canceledWithCustomNote() throws IOException, InterruptedException {
Integer buildNumber = 1; AbstractBuild build = mockBuild(GITLAB_CONNECTION_V4, Result.ABORTED);
Integer projectId = 3;
Integer mergeRequestId = 1;
AbstractBuild build = mockBuild("/build/123", GIT_LAB_CONNECTION, Result.ABORTED, buildNumber);
String defaultNote = "abort"; String defaultNote = "abort";
HttpRequest[] requests = new HttpRequest[] { performAndVerify(
prepareSendMessageWithSuccessResponse(projectId, mergeRequestId, defaultNote) build, defaultNote, false, false, false, true, false,
}; prepareSendMessageWithSuccessResponse("v4", defaultNote));
GitLabMessagePublisher publisher = spy(new GitLabMessagePublisher(false, false, false, true, false, null, null, defaultNote, null));
doReturn(projectId).when(publisher).getProjectId(build);
doReturn(mergeRequestId).when(publisher).getMergeRequestId(build);
publisher.perform(build, null, listener);
mockServerClient.verify(requests);
} }
@Test @Test
public void successWithCustomNote() throws IOException, InterruptedException { public void successWithCustomNote() throws IOException, InterruptedException {
Integer buildNumber = 1; AbstractBuild build = mockBuild(GITLAB_CONNECTION_V4, Result.SUCCESS);
Integer projectId = 3;
Integer mergeRequestId = 1;
AbstractBuild build = mockBuild("/build/123", GIT_LAB_CONNECTION, Result.SUCCESS, buildNumber);
String defaultNote = "success"; String defaultNote = "success";
HttpRequest[] requests = new HttpRequest[] { performAndVerify(
prepareSendMessageWithSuccessResponse(projectId, mergeRequestId, defaultNote) build, defaultNote, false, true, false, false, false,
}; prepareSendMessageWithSuccessResponse("v4", defaultNote));
GitLabMessagePublisher publisher = spy(new GitLabMessagePublisher(false, true, false, false, false, defaultNote, null, null, null));
doReturn(projectId).when(publisher).getProjectId(build);
doReturn(mergeRequestId).when(publisher).getMergeRequestId(build);
publisher.perform(build, null, listener);
mockServerClient.verify(requests);
} }
@Test @Test
public void failedWithCustomNote() throws IOException, InterruptedException { public void failedWithCustomNote() throws IOException, InterruptedException {
Integer buildNumber = 1; AbstractBuild build = mockBuild(GITLAB_CONNECTION_V4, Result.FAILURE);
Integer projectId = 3;
Integer mergeRequestId = 1;
AbstractBuild build = mockBuild("/build/123", GIT_LAB_CONNECTION, Result.FAILURE, buildNumber);
String defaultNote = "failure"; String defaultNote = "failure";
HttpRequest[] requests = new HttpRequest[] { performAndVerify(
prepareSendMessageWithSuccessResponse(projectId, mergeRequestId, defaultNote) build, defaultNote, false, false, true, false, false,
}; prepareSendMessageWithSuccessResponse("v4", defaultNote));
GitLabMessagePublisher publisher = spy(new GitLabMessagePublisher(false, false, true, false, false, null, defaultNote, null, null));
doReturn(projectId).when(publisher).getProjectId(build);
doReturn(mergeRequestId).when(publisher).getMergeRequestId(build);
publisher.perform(build, null, listener);
mockServerClient.verify(requests);
} }
@Test @Test
public void unstableWithCustomNote() throws IOException, InterruptedException { public void unstableWithCustomNote() throws IOException, InterruptedException {
Integer buildNumber = 1; AbstractBuild build = mockBuild(GITLAB_CONNECTION_V4, Result.UNSTABLE);
Integer projectId = 3;
Integer mergeRequestId = 1;
AbstractBuild build = mockBuild("/build/123", GIT_LAB_CONNECTION, Result.UNSTABLE, buildNumber);
String defaultNote = "unstable"; String defaultNote = "unstable";
HttpRequest[] requests = new HttpRequest[] { performAndVerify(
prepareSendMessageWithSuccessResponse(projectId, mergeRequestId, defaultNote) build, defaultNote, false, false, false, false, true,
}; prepareSendMessageWithSuccessResponse("v4", defaultNote));
GitLabMessagePublisher publisher = spy(new GitLabMessagePublisher(false, false, false, false, true, null, null, null, defaultNote));
doReturn(projectId).when(publisher).getProjectId(build);
doReturn(mergeRequestId).when(publisher).getMergeRequestId(build);
publisher.perform(build, null, listener);
mockServerClient.verify(requests);
} }
private HttpRequest prepareSendMessageWithSuccessResponse(Integer projectId, Integer mergeRequestId, String body) throws UnsupportedEncodingException { private void performAndVerify(AbstractBuild build, String note, boolean onlyForFailure, boolean replaceSuccessNote, boolean replaceFailureNote, boolean replaceAbortNote, boolean replaceUnstableNote, HttpRequest... requests) throws InterruptedException, IOException {
HttpRequest updateCommitStatus = prepareSendMessageStatus(projectId, mergeRequestId, body); String successNoteText = replaceSuccessNote ? note : null;
String failureNoteText = replaceFailureNote ? note : null;
String abortNoteText = replaceAbortNote ? note : null;
String unstableNoteText = replaceUnstableNote ? note : null;
GitLabMessagePublisher publisher = spy(new GitLabMessagePublisher(onlyForFailure, replaceSuccessNote, replaceFailureNote, replaceAbortNote, replaceUnstableNote, successNoteText, failureNoteText, abortNoteText, unstableNoteText));
doReturn(PROJECT_ID).when(publisher).getProjectId(build);
doReturn(MERGE_REQUEST_ID).when(publisher).getMergeRequestId(build);
publisher.perform(build, null, listener);
if (requests.length > 0) {
mockServerClient.verify(requests);
} else {
mockServerClient.verifyZeroInteractions();
}
}
private HttpRequest prepareSendMessageWithSuccessResponse(String apiLevel, String body) throws UnsupportedEncodingException {
HttpRequest updateCommitStatus = prepareSendMessageStatus(apiLevel, body);
mockServerClient.when(updateCommitStatus).respond(response().withStatusCode(200)); mockServerClient.when(updateCommitStatus).respond(response().withStatusCode(200));
return updateCommitStatus; return updateCommitStatus;
} }
private HttpRequest prepareSendMessageStatus(Integer projectId, Integer mergeRequestId, String body) throws UnsupportedEncodingException { private HttpRequest prepareSendMessageStatus(final String apiLevel, String body) throws UnsupportedEncodingException {
return request() return request()
.withPath("/gitlab/api/v3/projects/" + projectId + "/merge_requests/" + mergeRequestId + "/notes") .withPath("/gitlab/api/" + apiLevel + "/projects/" + PROJECT_ID + "/merge_requests/" + MERGE_REQUEST_ID + "/notes")
.withMethod("POST") .withMethod("POST")
.withHeader("PRIVATE-TOKEN", "secret") .withHeader("PRIVATE-TOKEN", "secret")
.withBody("body=" + URLEncoder.encode(body, "UTF-8")); .withBody("body=" + URLEncoder.encode(body, "UTF-8"));
} }
private AbstractBuild mockBuild(String buildUrl, String gitLabConnection, Result result, Integer buildNumber, String... remoteUrls) { private AbstractBuild mockBuild(String gitLabConnection, Result result, String... remoteUrls) {
AbstractBuild build = mock(AbstractBuild.class); AbstractBuild build = mock(AbstractBuild.class);
BuildData buildData = mock(BuildData.class); BuildData buildData = mock(BuildData.class);
when(buildData.getRemoteUrls()).thenReturn(new HashSet<>(Arrays.asList(remoteUrls))); when(buildData.getRemoteUrls()).thenReturn(new HashSet<>(Arrays.asList(remoteUrls)));
when(build.getAction(BuildData.class)).thenReturn(buildData); when(build.getAction(BuildData.class)).thenReturn(buildData);
when(build.getResult()).thenReturn(result); when(build.getResult()).thenReturn(result);
when(build.getUrl()).thenReturn(buildUrl); when(build.getUrl()).thenReturn(BUILD_URL);
when(build.getResult()).thenReturn(result); when(build.getResult()).thenReturn(result);
when(build.getUrl()).thenReturn(buildUrl); when(build.getNumber()).thenReturn(BUILD_NUMBER);
when(build.getNumber()).thenReturn(buildNumber);
AbstractProject<?, ?> project = mock(AbstractProject.class); AbstractProject<?, ?> project = mock(AbstractProject.class);
when(project.getProperty(GitLabConnectionProperty.class)).thenReturn(new GitLabConnectionProperty(gitLabConnection)); when(project.getProperty(GitLabConnectionProperty.class)).thenReturn(new GitLabConnectionProperty(gitLabConnection));

View File

@ -1,26 +1,10 @@
package com.dabsquared.gitlabjenkins.publisher; package com.dabsquared.gitlabjenkins.publisher;
import com.cloudbees.plugins.credentials.CredentialsProvider;
import com.cloudbees.plugins.credentials.CredentialsScope;
import com.cloudbees.plugins.credentials.CredentialsStore;
import com.cloudbees.plugins.credentials.SystemCredentialsProvider;
import com.cloudbees.plugins.credentials.domains.Domain;
import com.dabsquared.gitlabjenkins.connection.GitLabConnection;
import com.dabsquared.gitlabjenkins.connection.GitLabConnectionConfig;
import com.dabsquared.gitlabjenkins.connection.GitLabConnectionProperty;
import hudson.Launcher;
import hudson.matrix.MatrixAggregator;
import hudson.matrix.MatrixConfiguration;
import hudson.matrix.MatrixBuild;
import hudson.model.AbstractBuild; import hudson.model.AbstractBuild;
import hudson.model.AbstractProject;
import hudson.model.BuildListener; import hudson.model.BuildListener;
import hudson.model.Result; import hudson.model.Result;
import hudson.model.StreamBuildListener; import hudson.model.StreamBuildListener;
import hudson.plugins.git.util.BuildData;
import hudson.util.Secret;
import jenkins.model.Jenkins;
import org.jenkinsci.plugins.plaincredentials.impl.StringCredentialsImpl;
import org.junit.After; import org.junit.After;
import org.junit.Before; import org.junit.Before;
import org.junit.BeforeClass; import org.junit.BeforeClass;
@ -35,17 +19,16 @@ import java.io.IOException;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.net.URLEncoder; import java.net.URLEncoder;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.text.MessageFormat;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import static org.mockito.Matchers.any; import static com.dabsquared.gitlabjenkins.publisher.TestUtility.GITLAB_CONNECTION_V3;
import static com.dabsquared.gitlabjenkins.publisher.TestUtility.GITLAB_CONNECTION_V4;
import static com.dabsquared.gitlabjenkins.publisher.TestUtility.MERGE_REQUEST_ID;
import static com.dabsquared.gitlabjenkins.publisher.TestUtility.PROJECT_ID;
import static com.dabsquared.gitlabjenkins.publisher.TestUtility.formatNote;
import static com.dabsquared.gitlabjenkins.publisher.TestUtility.setupGitLabConnections;
import static com.dabsquared.gitlabjenkins.publisher.TestUtility.verifyMatrixAggregatable;
import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy; import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.mockserver.model.HttpRequest.request; import static org.mockserver.model.HttpRequest.request;
import static org.mockserver.model.HttpResponse.response; import static org.mockserver.model.HttpResponse.response;
@ -53,10 +36,6 @@ import static org.mockserver.model.HttpResponse.response;
* @author Nikolay Ustinov * @author Nikolay Ustinov
*/ */
public class GitLabVotePublisherTest { public class GitLabVotePublisherTest {
private static final String GIT_LAB_CONNECTION = "GitLab";
private static final String API_TOKEN = "secret";
@ClassRule @ClassRule
public static MockServerRule mockServer = new MockServerRule(new Object()); public static MockServerRule mockServer = new MockServerRule(new Object());
@ -67,17 +46,8 @@ public class GitLabVotePublisherTest {
private BuildListener listener; private BuildListener listener;
@BeforeClass @BeforeClass
public static void setupConnection() throws IOException { public static void setupClass() throws IOException {
GitLabConnectionConfig connectionConfig = jenkins.get(GitLabConnectionConfig.class); setupGitLabConnections(jenkins, mockServer);
String apiTokenId = "apiTokenId";
for (CredentialsStore credentialsStore : CredentialsProvider.lookupStores(Jenkins.getInstance())) {
if (credentialsStore instanceof SystemCredentialsProvider.StoreImpl) {
List<Domain> domains = credentialsStore.getDomains();
credentialsStore.addCredentials(domains.get(0),
new StringCredentialsImpl(CredentialsScope.SYSTEM, apiTokenId, "GitLab API Token", Secret.fromString(API_TOKEN)));
}
}
connectionConfig.addConnection(new GitLabConnection(GIT_LAB_CONNECTION, "http://localhost:" + mockServer.getPort() + "/gitlab", apiTokenId, false, 10, 10));
} }
@Before @Before
@ -92,94 +62,51 @@ public class GitLabVotePublisherTest {
} }
@Test @Test
public void matrixAggregatable() throws UnsupportedEncodingException, InterruptedException, IOException { public void matrixAggregatable() throws InterruptedException, IOException {
AbstractBuild build = mock(AbstractBuild.class); verifyMatrixAggregatable(GitLabVotePublisher.class, listener);
AbstractProject project = mock(MatrixConfiguration.class);
GitLabCommitStatusPublisher publisher = mock(GitLabCommitStatusPublisher.class);
MatrixBuild parentBuild = mock(MatrixBuild.class);
when(build.getParent()).thenReturn(project);
when(publisher.createAggregator(any(MatrixBuild.class), any(Launcher.class), any(BuildListener.class))).thenCallRealMethod();
when(publisher.perform(any(AbstractBuild.class), any(Launcher.class), any(BuildListener.class))).thenReturn(true);
MatrixAggregator aggregator = publisher.createAggregator(parentBuild, null, listener);
aggregator.startBuild();
aggregator.endBuild();
verify(publisher).perform(parentBuild, null, listener);
} }
@Test @Test
public void success() throws IOException, InterruptedException { public void success_v3() throws IOException, InterruptedException {
Integer buildNumber = 1; performAndVerify(TestUtility.mockSimpleBuild(GITLAB_CONNECTION_V3, Result.SUCCESS), "v3", ":+1:");
Integer projectId = 3;
Integer mergeRequestId = 1;
AbstractBuild build = mockBuild("/build/123", GIT_LAB_CONNECTION, Result.SUCCESS, buildNumber);
String buildUrl = Jenkins.getInstance().getRootUrl() + build.getUrl();
String defaultNote = MessageFormat.format(":+1:",
Result.SUCCESS, build.getParent().getDisplayName(), buildNumber, buildUrl);
HttpRequest[] requests = new HttpRequest[] {
prepareSendMessageWithSuccessResponse(projectId, mergeRequestId, defaultNote)
};
GitLabVotePublisher publisher = spy(new GitLabVotePublisher());
doReturn(projectId).when(publisher).getProjectId(build);
doReturn(mergeRequestId).when(publisher).getMergeRequestId(build);
publisher.perform(build, null, listener);
mockServerClient.verify(requests);
} }
@Test @Test
public void failed() throws IOException, InterruptedException { public void success_v4() throws IOException, InterruptedException {
Integer buildNumber = 1; performAndVerify(TestUtility.mockSimpleBuild(GITLAB_CONNECTION_V4, Result.SUCCESS), "v4", ":+1:");
Integer projectId = 3;
Integer mergeRequestId = 1;
AbstractBuild build = mockBuild("/build/123", GIT_LAB_CONNECTION, Result.FAILURE, buildNumber);
String buildUrl = Jenkins.getInstance().getRootUrl() + build.getUrl();
String defaultNote = MessageFormat.format(":-1:",
Result.FAILURE, build.getParent().getDisplayName(), buildNumber, buildUrl);
HttpRequest[] requests = new HttpRequest[] {
prepareSendMessageWithSuccessResponse(projectId, mergeRequestId, defaultNote)
};
GitLabVotePublisher publisher = spy(new GitLabVotePublisher());
doReturn(projectId).when(publisher).getProjectId(build);
doReturn(mergeRequestId).when(publisher).getMergeRequestId(build);
publisher.perform(build, null, listener);
mockServerClient.verify(requests);
} }
private HttpRequest prepareSendMessageWithSuccessResponse(Integer projectId, Integer mergeRequestId, String body) throws UnsupportedEncodingException { @Test
HttpRequest updateCommitStatus = prepareSendMessageStatus(projectId, mergeRequestId, body); public void failed_v3() throws IOException, InterruptedException {
performAndVerify(TestUtility.mockSimpleBuild(GITLAB_CONNECTION_V3, Result.FAILURE), "v3", ":-1:");
}
@Test
public void failed_v4() throws IOException, InterruptedException {
performAndVerify(TestUtility.mockSimpleBuild(GITLAB_CONNECTION_V4, Result.FAILURE), "v4", ":-1:");
}
private void performAndVerify(AbstractBuild build, String apiLevel, String defaultNote) throws InterruptedException, IOException {
GitLabVotePublisher publisher = spy(new GitLabVotePublisher());
doReturn(PROJECT_ID).when(publisher).getProjectId(build);
doReturn(MERGE_REQUEST_ID).when(publisher).getMergeRequestId(build);
publisher.perform(build, null, listener);
mockServerClient.verify(prepareSendMessageWithSuccessResponse(build, apiLevel, defaultNote));
}
private HttpRequest prepareSendMessageWithSuccessResponse(AbstractBuild build, String apiLevel, String body) throws UnsupportedEncodingException {
HttpRequest updateCommitStatus = prepareSendMessageStatus(apiLevel, formatNote(build, body));
mockServerClient.when(updateCommitStatus).respond(response().withStatusCode(200)); mockServerClient.when(updateCommitStatus).respond(response().withStatusCode(200));
return updateCommitStatus; return updateCommitStatus;
} }
private HttpRequest prepareSendMessageStatus(Integer projectId, Integer mergeRequestId, String body) throws UnsupportedEncodingException { private HttpRequest prepareSendMessageStatus(final String apiLevel, String body) throws UnsupportedEncodingException {
return request() return request()
.withPath("/gitlab/api/v3/projects/" + projectId + "/merge_requests/" + mergeRequestId + "/notes") .withPath("/gitlab/api/" + apiLevel + "/projects/" + PROJECT_ID + "/merge_requests/" + MERGE_REQUEST_ID + "/notes")
.withMethod("POST") .withMethod("POST")
.withHeader("PRIVATE-TOKEN", "secret") .withHeader("PRIVATE-TOKEN", "secret")
.withBody("body=" + URLEncoder.encode(body, "UTF-8")); .withBody("body=" + URLEncoder.encode(body, "UTF-8"));
} }
private AbstractBuild mockBuild(String buildUrl, String gitLabConnection, Result result, Integer buildNumber, String... remoteUrls) {
AbstractBuild build = mock(AbstractBuild.class);
BuildData buildData = mock(BuildData.class);
when(buildData.getRemoteUrls()).thenReturn(new HashSet<>(Arrays.asList(remoteUrls)));
when(build.getAction(BuildData.class)).thenReturn(buildData);
when(build.getResult()).thenReturn(result);
when(build.getUrl()).thenReturn(buildUrl);
when(build.getResult()).thenReturn(result);
when(build.getUrl()).thenReturn(buildUrl);
when(build.getNumber()).thenReturn(buildNumber);
AbstractProject<?, ?> project = mock(AbstractProject.class);
when(project.getProperty(GitLabConnectionProperty.class)).thenReturn(new GitLabConnectionProperty(gitLabConnection));
when(build.getProject()).thenReturn(project);
return build;
}
} }

View File

@ -0,0 +1,107 @@
package com.dabsquared.gitlabjenkins.publisher;
import com.cloudbees.plugins.credentials.CredentialsProvider;
import com.cloudbees.plugins.credentials.CredentialsScope;
import com.cloudbees.plugins.credentials.CredentialsStore;
import com.cloudbees.plugins.credentials.SystemCredentialsProvider;
import com.cloudbees.plugins.credentials.domains.Domain;
import com.dabsquared.gitlabjenkins.connection.GitLabConnection;
import com.dabsquared.gitlabjenkins.connection.GitLabConnectionConfig;
import com.dabsquared.gitlabjenkins.connection.GitLabConnectionProperty;
import com.dabsquared.gitlabjenkins.gitlab.api.impl.V3GitLabClientBuilder;
import com.dabsquared.gitlabjenkins.gitlab.api.impl.V4GitLabClientBuilder;
import hudson.Launcher;
import hudson.matrix.MatrixAggregatable;
import hudson.matrix.MatrixAggregator;
import hudson.matrix.MatrixBuild;
import hudson.matrix.MatrixConfiguration;
import hudson.model.AbstractBuild;
import hudson.model.AbstractProject;
import hudson.model.BuildListener;
import hudson.model.Result;
import hudson.plugins.git.util.BuildData;
import hudson.tasks.Notifier;
import hudson.util.Secret;
import jenkins.model.Jenkins;
import org.jenkinsci.plugins.plaincredentials.impl.StringCredentialsImpl;
import org.jvnet.hudson.test.JenkinsRule;
import org.mockserver.junit.MockServerRule;
import java.io.IOException;
import java.text.MessageFormat;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
final class TestUtility {
static final String GITLAB_CONNECTION_V3 = "GitLabV3";
static final String GITLAB_CONNECTION_V4 = "GitLabV4";
static final String BUILD_URL = "/build/123";
static final int BUILD_NUMBER = 1;
static final int PROJECT_ID = 3;
static final int MERGE_REQUEST_ID = 1;
private static final String API_TOKEN = "secret";
static void setupGitLabConnections(JenkinsRule jenkins, MockServerRule mockServer) throws IOException {
GitLabConnectionConfig connectionConfig = jenkins.get(GitLabConnectionConfig.class);
String apiTokenId = "apiTokenId";
for (CredentialsStore credentialsStore : CredentialsProvider.lookupStores(Jenkins.getInstance())) {
if (credentialsStore instanceof SystemCredentialsProvider.StoreImpl) {
List<Domain> domains = credentialsStore.getDomains();
credentialsStore.addCredentials(domains.get(0),
new StringCredentialsImpl(CredentialsScope.SYSTEM, apiTokenId, "GitLab API Token", Secret.fromString(TestUtility.API_TOKEN)));
}
}
connectionConfig.addConnection(new GitLabConnection(TestUtility.GITLAB_CONNECTION_V3, "http://localhost:" + mockServer.getPort() + "/gitlab", apiTokenId, new V3GitLabClientBuilder(), false, 10, 10));
connectionConfig.addConnection(new GitLabConnection(TestUtility.GITLAB_CONNECTION_V4, "http://localhost:" + mockServer.getPort() + "/gitlab", apiTokenId, new V4GitLabClientBuilder(), false, 10, 10));
}
static <T extends Notifier & MatrixAggregatable> void verifyMatrixAggregatable(Class<T> publisherClass, BuildListener listener) throws InterruptedException, IOException {
AbstractBuild build = mock(AbstractBuild.class);
AbstractProject project = mock(MatrixConfiguration.class);
Notifier publisher = mock(publisherClass);
MatrixBuild parentBuild = mock(MatrixBuild.class);
when(build.getParent()).thenReturn(project);
when(((MatrixAggregatable) publisher).createAggregator(any(MatrixBuild.class), any(Launcher.class), any(BuildListener.class))).thenCallRealMethod();
when(publisher.perform(any(AbstractBuild.class), any(Launcher.class), any(BuildListener.class))).thenReturn(true);
MatrixAggregator aggregator = ((MatrixAggregatable) publisher).createAggregator(parentBuild, null, listener);
aggregator.startBuild();
aggregator.endBuild();
verify(publisher).perform(parentBuild, null, listener);
}
static AbstractBuild mockSimpleBuild(String gitLabConnection, Result result, String... remoteUrls) {
AbstractBuild build = mock(AbstractBuild.class);
BuildData buildData = mock(BuildData.class);
when(buildData.getRemoteUrls()).thenReturn(new HashSet<>(Arrays.asList(remoteUrls)));
when(build.getAction(BuildData.class)).thenReturn(buildData);
when(build.getResult()).thenReturn(result);
when(build.getUrl()).thenReturn(BUILD_URL);
when(build.getResult()).thenReturn(result);
when(build.getNumber()).thenReturn(BUILD_NUMBER);
AbstractProject<?, ?> project = mock(AbstractProject.class);
when(project.getProperty(GitLabConnectionProperty.class)).thenReturn(new GitLabConnectionProperty(gitLabConnection));
when(build.getProject()).thenReturn(project);
return build;
}
@SuppressWarnings("ConstantConditions")
static String formatNote(AbstractBuild build, String note) {
String buildUrl = Jenkins.getInstance().getRootUrl() + build.getUrl();
return MessageFormat.format(note, build.getResult(), build.getParent().getDisplayName(), BUILD_NUMBER, buildUrl);
}
private TestUtility() { /* contains only static utility-methods */ }
}

View File

@ -0,0 +1,161 @@
package com.dabsquared.gitlabjenkins.service;
import com.dabsquared.gitlabjenkins.gitlab.api.GitLabApi;
import com.dabsquared.gitlabjenkins.gitlab.api.model.*;
import com.dabsquared.gitlabjenkins.gitlab.hook.model.State;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.Pair;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static java.util.Collections.emptyList;
class GitLabApiStub implements GitLabApi {
private final Map<Pair<String, Class>, List<?>> data;
private final Map<Pair<String, Class>, Integer> calls;
GitLabApiStub() {
data = new HashMap<>();
calls = new HashMap<>();
}
void addBranches(String project, List<Branch> branches) {
addData(project, Branch.class, branches);
}
void addLabels(String project, List<Label> labels) {
addData(project, Label.class, labels);
}
int calls(String projectId, Class dataClass) {
Pair<String, Class> key = createKey(projectId, dataClass);
return calls.containsKey(key) ? calls.get(key) : 0;
}
@Override
public List<Branch> getBranches(String projectId) {
return getData(projectId, Branch.class);
}
@Override
public List<Label> getLabels(String projectId) {
return getData(projectId, Label.class);
}
private void addData(String projectId, Class dataClass, List<?> datas) {
data.put(createKey(projectId, dataClass), datas);
}
@SuppressWarnings("unchecked")
private <T> List<T> getData(String projectId, Class dataClass) {
Pair<String, Class> key = createKey(projectId, dataClass);
if (!calls.containsKey(key)) {
calls.put(key, 0);
}
calls.put(key, calls.get(key) + 1);
return (List<T>) data.get(key);
}
private Pair<String, Class> createKey(String projectId, Class dataClass) {
return new ImmutablePair<>(projectId, dataClass);
}
/************** no implementation below ********************************/
@Override
public Project createProject(String projectName) {
return null;
}
@Override
public MergeRequest createMergeRequest(Integer projectId, String sourceBranch, String targetBranch, String title) {
return null;
}
@Override
public Project getProject(String projectName) {
return null;
}
@Override
public Project updateProject(String projectId, String name, String path) {
return null;
}
@Override
public void deleteProject(String projectId) {
}
@Override
public void addProjectHook(String projectId, String url, Boolean pushEvents, Boolean mergeRequestEvents, Boolean noteEvents) {
}
@Override
public void changeBuildStatus(String projectId, String sha, BuildState state, String ref, String context, String targetUrl, String description) {
}
@Override
public void changeBuildStatus(Integer projectId, String sha, BuildState state, String ref, String context, String targetUrl, String description) {
}
@Override
public void getCommit(String projectId, String sha) {
}
@Override
public void acceptMergeRequest(Integer projectId, Integer mergeRequestId, String mergeCommitMessage, boolean shouldRemoveSourceBranch) {
}
@Override
public void createMergeRequestNote(Integer projectId, Integer mergeRequestId, String body) {
}
@Override
public List<MergeRequest> getMergeRequests(String projectId, State state, int page, int perPage) {
return null;
}
@Override
public Branch getBranch(String projectId, String branch) {
return null;
}
@Override
public void headCurrentUser() {
}
@Override
public User getCurrentUser() {
return null;
}
@Override
public User addUser(String email, String username, String name, String password) {
return null;
}
@Override
public User updateUser(String userId, String email, String username, String name, String password) {
return null;
}
@Override
public List<Pipeline> getPipelines(String projectName) {
return emptyList();
}
}

View File

@ -1,12 +1,10 @@
package com.dabsquared.gitlabjenkins.service; package com.dabsquared.gitlabjenkins.service;
import com.dabsquared.gitlabjenkins.gitlab.api.GitLabApi;
import com.dabsquared.gitlabjenkins.gitlab.api.GitLabClient;
import com.dabsquared.gitlabjenkins.gitlab.api.model.Branch; import com.dabsquared.gitlabjenkins.gitlab.api.model.Branch;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
@ -14,29 +12,26 @@ import java.util.List;
import static com.dabsquared.gitlabjenkins.gitlab.api.model.builder.generated.BranchBuilder.branch; import static com.dabsquared.gitlabjenkins.gitlab.api.model.builder.generated.BranchBuilder.branch;
import static java.util.Arrays.asList; import static java.util.Arrays.asList;
import static junit.framework.TestCase.assertEquals;
import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat; import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@RunWith(MockitoJUnitRunner.class)
public class GitLabProjectBranchesServiceTest { public class GitLabProjectBranchesServiceTest {
private final static List<String> BRANCH_NAMES_PROJECT_B = asList("master", "B-branch-1", "B-branch-2"); private final static List<String> BRANCH_NAMES_PROJECT_B = asList("master", "B-branch-1", "B-branch-2");
private GitLabProjectBranchesService branchesService; private GitLabProjectBranchesService branchesService;
@Mock private GitLabApiStub apiStub;
private GitLabApi gitlabApi; private GitLabClient gitlabClient;
@Before @Before
public void setUp() throws IOException { public void setUp() throws IOException {
List<Branch> branchNamesProjectA = convert(asList("master", "A-branch-1")); apiStub = new GitLabApiStub();
apiStub.addBranches("groupOne/A", convert(asList("master", "A-branch-1")));
apiStub.addBranches("groupOne/B", convert(BRANCH_NAMES_PROJECT_B));
// mock the gitlab factory gitlabClient = new GitLabClient("", apiStub);
when(gitlabApi.getBranches("groupOne/A")).thenReturn(branchNamesProjectA);
when(gitlabApi.getBranches("groupOne/B")).thenReturn(convert(BRANCH_NAMES_PROJECT_B));
// never expire cache for tests // never expire cache for tests
branchesService = new GitLabProjectBranchesService(); branchesService = new GitLabProjectBranchesService();
@ -45,7 +40,7 @@ public class GitLabProjectBranchesServiceTest {
@Test @Test
public void shouldReturnBranchNamesFromGitlabApi() { public void shouldReturnBranchNamesFromGitlabApi() {
// when // when
List<String> actualBranchNames = branchesService.getBranches(gitlabApi, "git@git.example.com:groupOne/B.git"); List<String> actualBranchNames = branchesService.getBranches(gitlabClient, "git@git.example.com:groupOne/B.git");
// then // then
assertThat(actualBranchNames, is(BRANCH_NAMES_PROJECT_B)); assertThat(actualBranchNames, is(BRANCH_NAMES_PROJECT_B));
@ -54,11 +49,11 @@ public class GitLabProjectBranchesServiceTest {
@Test @Test
public void shouldNotMakeUnnecessaryCallsToGitlabApiGetBranches() { public void shouldNotMakeUnnecessaryCallsToGitlabApiGetBranches() {
// when // when
branchesService.getBranches(gitlabApi, "git@git.example.com:groupOne/A.git"); branchesService.getBranches(gitlabClient, "git@git.example.com:groupOne/A.git");
// then // then
verify(gitlabApi, times(1)).getBranches("groupOne/A"); assertEquals(1, apiStub.calls("groupOne/A", Branch.class));
verify(gitlabApi, times(0)).getBranches("groupOne/B"); assertEquals(0, apiStub.calls("groupOne/B", Branch.class));
} }
private List<Branch> convert(List<String> branchNames) { private List<Branch> convert(List<String> branchNames) {

View File

@ -1,12 +1,10 @@
package com.dabsquared.gitlabjenkins.service; package com.dabsquared.gitlabjenkins.service;
import com.dabsquared.gitlabjenkins.gitlab.api.GitLabApi;
import com.dabsquared.gitlabjenkins.gitlab.api.GitLabClient;
import com.dabsquared.gitlabjenkins.gitlab.api.model.Label; import com.dabsquared.gitlabjenkins.gitlab.api.model.Label;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
@ -14,29 +12,27 @@ import java.util.List;
import static com.dabsquared.gitlabjenkins.gitlab.api.model.builder.generated.LabelBuilder.label; import static com.dabsquared.gitlabjenkins.gitlab.api.model.builder.generated.LabelBuilder.label;
import static java.util.Arrays.asList; import static java.util.Arrays.asList;
import static junit.framework.TestCase.assertEquals;
import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat; import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@RunWith(MockitoJUnitRunner.class)
public class GitLabProjectLabelsServiceTest { public class GitLabProjectLabelsServiceTest {
private final static List<String> LABELS_PROJECT_B = asList("label1", "label2", "label3"); private final static List<String> LABELS_PROJECT_B = asList("label1", "label2", "label3");
private GitLabProjectLabelsService labelsService; private GitLabProjectLabelsService labelsService;
@Mock private GitLabApiStub apiStub;
private GitLabApi gitlabApi; private GitLabClient gitlabClient;
@Before @Before
public void setUp() throws IOException { public void setUp() throws IOException {
List<Label> labelsProjectA = convert(asList("label1", "label2")); apiStub = new GitLabApiStub();
apiStub.addLabels("groupOne/A", convert(asList("label1", "label2")));
apiStub.addLabels("groupOne/B", convert(LABELS_PROJECT_B));
// mock the gitlab factory gitlabClient = new GitLabClient("", apiStub);
when(gitlabApi.getLabels("groupOne/A")).thenReturn(labelsProjectA);
when(gitlabApi.getLabels("groupOne/B")).thenReturn(convert(LABELS_PROJECT_B));
// never expire cache for tests // never expire cache for tests
labelsService = new GitLabProjectLabelsService(); labelsService = new GitLabProjectLabelsService();
@ -45,7 +41,7 @@ public class GitLabProjectLabelsServiceTest {
@Test @Test
public void shouldReturnLabelsFromGitlabApi() { public void shouldReturnLabelsFromGitlabApi() {
// when // when
List<String> actualLabels = labelsService.getLabels(gitlabApi, "git@git.example.com:groupOne/B.git"); List<String> actualLabels = labelsService.getLabels(gitlabClient, "git@git.example.com:groupOne/B.git");
// then // then
assertThat(actualLabels, is(LABELS_PROJECT_B)); assertThat(actualLabels, is(LABELS_PROJECT_B));
@ -54,11 +50,11 @@ public class GitLabProjectLabelsServiceTest {
@Test @Test
public void shouldNotMakeUnnecessaryCallsToGitlabApiGetLabels() { public void shouldNotMakeUnnecessaryCallsToGitlabApiGetLabels() {
// when // when
labelsService.getLabels(gitlabApi, "git@git.example.com:groupOne/A.git"); labelsService.getLabels(gitlabClient, "git@git.example.com:groupOne/A.git");
// then // then
verify(gitlabApi, times(1)).getLabels("groupOne/A"); assertEquals(1, apiStub.calls("groupOne/A", Label.class));
verify(gitlabApi, times(0)).getLabels("groupOne/B"); assertEquals(0, apiStub.calls("groupOne/B", Label.class));
} }
private List<Label> convert(List<String> labels) { private List<Label> convert(List<String> labels) {

View File

@ -1,13 +1,23 @@
package com.dabsquared.gitlabjenkins.testing.gitlab.rule; package com.dabsquared.gitlabjenkins.testing.gitlab.rule;
import com.dabsquared.gitlabjenkins.gitlab.JacksonConfig;
import com.dabsquared.gitlabjenkins.gitlab.api.GitLabApi; import com.cloudbees.plugins.credentials.CredentialsProvider;
import com.cloudbees.plugins.credentials.CredentialsScope;
import com.cloudbees.plugins.credentials.CredentialsStore;
import com.cloudbees.plugins.credentials.SystemCredentialsProvider;
import com.cloudbees.plugins.credentials.domains.Domain;
import com.dabsquared.gitlabjenkins.connection.GitLabConnection;
import com.dabsquared.gitlabjenkins.connection.GitLabConnectionConfig;
import com.dabsquared.gitlabjenkins.connection.GitLabConnectionProperty;
import com.dabsquared.gitlabjenkins.gitlab.api.GitLabClient;
import com.dabsquared.gitlabjenkins.gitlab.api.impl.V3GitLabClientBuilder;
import com.dabsquared.gitlabjenkins.gitlab.api.model.MergeRequest; import com.dabsquared.gitlabjenkins.gitlab.api.model.MergeRequest;
import com.dabsquared.gitlabjenkins.gitlab.api.model.Pipeline;
import com.dabsquared.gitlabjenkins.gitlab.api.model.Project; import com.dabsquared.gitlabjenkins.gitlab.api.model.Project;
import com.dabsquared.gitlabjenkins.gitlab.api.model.User; import com.dabsquared.gitlabjenkins.gitlab.api.model.User;
import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider; import hudson.util.Secret;
import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder; import jenkins.model.Jenkins;
import org.jboss.resteasy.client.jaxrs.engines.ApacheHttpClient4Engine; import org.jenkinsci.plugins.plaincredentials.impl.StringCredentialsImpl;
import org.junit.rules.TestRule; import org.junit.rules.TestRule;
import org.junit.runner.Description; import org.junit.runner.Description;
import org.junit.runners.model.Statement; import org.junit.runners.model.Statement;
@ -27,21 +37,20 @@ import java.util.UUID;
* @author Robin Müller * @author Robin Müller
*/ */
public class GitLabRule implements TestRule { public class GitLabRule implements TestRule {
private static final String API_TOKEN_ID = "apiTokenId";
private final GitLabApi client; private final String url;
private final int postgresPort;
private final GitLabClient client;
private final String username; private final String username;
private final String password; private final String password;
private List<String> projectIds = new ArrayList<>(); private List<String> projectIds = new ArrayList<>();
public GitLabRule(String url, int postgresPort) { public GitLabRule(String url, int postgresPort) {
client = new ResteasyClientBuilder() this.url = url;
.httpEngine(new ApacheHttpClient4Engine()) this.postgresPort = postgresPort;
.register(new JacksonJsonProvider()) client = new V3GitLabClientBuilder().buildClient(url, getApiToken(), false, -1, -1);
.register(new JacksonConfig())
.register(new ApiHeaderTokenFilter(getApiToken(postgresPort))).build().target(url)
.proxyBuilder(GitLabApi.class)
.build();
User user = client.getCurrentUser(); User user = client.getCurrentUser();
username = user.getUsername(); username = user.getUsername();
password = "integration-test"; password = "integration-test";
@ -57,6 +66,10 @@ public class GitLabRule implements TestRule {
return client.getProject(projectName); return client.getProject(projectName);
} }
public List<Pipeline> getPipelines(int projectId) {
return client.getPipelines(String.valueOf(projectId));
}
public List<String> getProjectIds() { public List<String> getProjectIds() {
return projectIds; return projectIds;
} }
@ -70,6 +83,22 @@ public class GitLabRule implements TestRule {
return project.getHttpUrlToRepo(); return project.getHttpUrlToRepo();
} }
public GitLabConnectionProperty createGitLabConnectionProperty() throws IOException {
for (CredentialsStore credentialsStore : CredentialsProvider.lookupStores(Jenkins.getInstance())) {
if (credentialsStore instanceof SystemCredentialsProvider.StoreImpl) {
List<Domain> domains = credentialsStore.getDomains();
credentialsStore.addCredentials(domains.get(0),
new StringCredentialsImpl(CredentialsScope.SYSTEM, API_TOKEN_ID, "GitLab API Token", Secret.fromString(getApiToken())));
}
}
GitLabConnectionConfig config = Jenkins.getInstance().getDescriptorByType(GitLabConnectionConfig.class);
GitLabConnection connection = new GitLabConnection("test", url, API_TOKEN_ID, new V3GitLabClientBuilder(), true,10, 10);
config.addConnection(connection);
config.save();
return new GitLabConnectionProperty(connection.getName());
}
public MergeRequest createMergeRequest(final Integer projectId, public MergeRequest createMergeRequest(final Integer projectId,
final String sourceBranch, final String sourceBranch,
final String targetBranch, final String targetBranch,
@ -98,7 +127,7 @@ public class GitLabRule implements TestRule {
} }
} }
private String getApiToken(int postgresPort) { private String getApiToken() {
try { try {
Class.forName("org.postgresql.Driver"); Class.forName("org.postgresql.Driver");
try (Connection connection = DriverManager.getConnection("jdbc:postgresql://localhost:" + postgresPort + "/gitlabhq_production", "gitlab", "password")) { try (Connection connection = DriverManager.getConnection("jdbc:postgresql://localhost:" + postgresPort + "/gitlabhq_production", "gitlab", "password")) {

View File

@ -1,23 +1,34 @@
package com.dabsquared.gitlabjenkins.testing.integration; package com.dabsquared.gitlabjenkins.testing.integration;
import com.dabsquared.gitlabjenkins.GitLabPushTrigger; import com.dabsquared.gitlabjenkins.GitLabPushTrigger;
import com.dabsquared.gitlabjenkins.gitlab.api.model.MergeRequest; import com.dabsquared.gitlabjenkins.gitlab.api.model.MergeRequest;
import com.dabsquared.gitlabjenkins.gitlab.api.model.Pipeline;
import com.dabsquared.gitlabjenkins.publisher.GitLabCommitStatusPublisher;
import com.dabsquared.gitlabjenkins.testing.gitlab.rule.GitLabRule; import com.dabsquared.gitlabjenkins.testing.gitlab.rule.GitLabRule;
import com.dabsquared.gitlabjenkins.trigger.filter.BranchFilterType; import com.dabsquared.gitlabjenkins.trigger.filter.BranchFilterType;
import hudson.Launcher; import hudson.Launcher;
import hudson.model.AbstractBuild; import hudson.model.AbstractBuild;
import hudson.model.BuildListener; import hudson.model.BuildListener;
import hudson.model.Descriptor;
import hudson.model.FreeStyleProject; import hudson.model.FreeStyleProject;
import hudson.tasks.Publisher;
import hudson.util.DescribableList;
import hudson.util.OneShotEvent; import hudson.util.OneShotEvent;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.Pair;
import org.eclipse.jgit.api.Git; import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.errors.GitAPIException; import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.lib.StoredConfig; import org.eclipse.jgit.lib.StoredConfig;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider; import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider;
import org.junit.Rule; import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.junit.rules.TemporaryFolder; import org.junit.rules.TemporaryFolder;
import org.jvnet.hudson.test.JenkinsRule; import org.jvnet.hudson.test.JenkinsRule;
import org.jvnet.hudson.test.SleepBuilder;
import org.jvnet.hudson.test.TestBuilder; import org.jvnet.hudson.test.TestBuilder;
import org.jvnet.hudson.test.TestNotifier;
import java.io.IOException; import java.io.IOException;
import java.net.Inet4Address; import java.net.Inet4Address;
@ -30,6 +41,9 @@ import java.util.List;
import static com.dabsquared.gitlabjenkins.builder.generated.GitLabPushTriggerBuilder.gitLabPushTrigger; import static com.dabsquared.gitlabjenkins.builder.generated.GitLabPushTriggerBuilder.gitLabPushTrigger;
import static com.dabsquared.gitlabjenkins.testing.gitlab.rule.builder.generated.ProjectRequestBuilder.projectRequest; import static com.dabsquared.gitlabjenkins.testing.gitlab.rule.builder.generated.ProjectRequestBuilder.projectRequest;
import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.collection.IsCollectionWithSize.hasSize;
import static org.hamcrest.collection.IsEmptyCollection.empty;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertSame; import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertThat; import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
@ -38,9 +52,10 @@ import static org.junit.Assert.assertTrue;
* @author Robin Müller * @author Robin Müller
*/ */
public class GitLabIT { public class GitLabIT {
private static final String GITLAB_URL = "http://localhost:" + System.getProperty("gitlab.http.port", "10080");
@Rule @Rule
public GitLabRule gitlab = new GitLabRule("http://localhost:" + System.getProperty("gitlab.http.port", "10080"), public GitLabRule gitlab = new GitLabRule(GITLAB_URL,
Integer.parseInt(System.getProperty("postgres.port", "5432"))); Integer.parseInt(System.getProperty("postgres.port", "5432")));
@Rule @Rule
@ -51,12 +66,6 @@ public class GitLabIT {
@Test @Test
public void buildOnPush() throws IOException, InterruptedException, GitAPIException { public void buildOnPush() throws IOException, InterruptedException, GitAPIException {
final String httpUrl = gitlab.createProject(projectRequest()
.withName("test")
.withWebHookUrl("http://" + getDocker0Ip() + ":" + jenkins.getURL().getPort() + "/jenkins/project/test")
.withPushHook(true)
.build());
final OneShotEvent buildTriggered = new OneShotEvent(); final OneShotEvent buildTriggered = new OneShotEvent();
FreeStyleProject project = jenkins.createFreeStyleProject("test"); FreeStyleProject project = jenkins.createFreeStyleProject("test");
GitLabPushTrigger trigger = gitLabPushTrigger().withTriggerOnPush(true).withBranchFilterType(BranchFilterType.All).build(); GitLabPushTrigger trigger = gitLabPushTrigger().withTriggerOnPush(true).withBranchFilterType(BranchFilterType.All).build();
@ -71,45 +80,15 @@ public class GitLabIT {
}); });
project.setQuietPeriod(0); project.setQuietPeriod(0);
Git.init().setDirectory(tmp.getRoot()).call(); createGitLabProject(false,true, true, false);
tmp.newFile("test");
Git git = Git.open(tmp.getRoot());
git.add().addFilepattern("test");
git.commit().setMessage("test").call();
StoredConfig config = git.getRepository().getConfig();
config.setString("remote", "origin", "url", httpUrl);
config.save();
git.push()
.setRemote("origin").add("master")
.setCredentialsProvider(new UsernamePasswordCredentialsProvider(gitlab.getUsername(), gitlab.getPassword()))
.call();
buildTriggered.block(10000); buildTriggered.block(10000);
assertThat(buildTriggered.isSignaled(), is(true)); assertThat(buildTriggered.isSignaled(), is(true));
} }
@Test @Test
public void buildOnMergeRequest() throws IOException, InterruptedException, GitAPIException { public void buildOnMergeRequest() throws IOException, InterruptedException, GitAPIException {
// check for clean slate
assertTrue(gitlab.getProjectIds().isEmpty());
final String httpUrl = gitlab.createProject(projectRequest()
.withName("test")
.withWebHookUrl("http://" + getDocker0Ip() + ":" + jenkins.getURL().getPort() + "/jenkins/project/test")
.withMergeRequestHook(true)
.build());
// Fix: Hack to get the project id
// A preferable approach would be here to get the target project by name using getProject function of the
// GitLabRule and to use the id from the project instance. However, due to a bug in GitLab (tested on 8.6.1)
// retrieving the project by name is not properly working.
// (see issue https://github.com/gitlabhq/gitlabhq/issues/4921).
// Once the issue is resolved, replace this implementation.
final List<String> projectIds = gitlab.getProjectIds();
assertSame(projectIds.size(), 1);
final Integer projectId = Integer.parseInt(projectIds.get(0));
final OneShotEvent buildTriggered = new OneShotEvent(); final OneShotEvent buildTriggered = new OneShotEvent();
FreeStyleProject project = jenkins.createFreeStyleProject("test"); FreeStyleProject project = jenkins.createFreeStyleProject("test");
GitLabPushTrigger trigger = gitLabPushTrigger().withTriggerOnMergeRequest(true).withBranchFilterType(BranchFilterType.All).build(); GitLabPushTrigger trigger = gitLabPushTrigger().withTriggerOnMergeRequest(true).withBranchFilterType(BranchFilterType.All).build();
@ -124,30 +103,9 @@ public class GitLabIT {
}); });
project.setQuietPeriod(0); project.setQuietPeriod(0);
// Setup git repository Pair<Integer, String> gitlabData = createGitLabProject(true,false, false, true);
Git.init().setDirectory(tmp.getRoot()).call();
Git git = Git.open(tmp.getRoot());
StoredConfig config = git.getRepository().getConfig();
config.setString("remote", "origin", "url", httpUrl);
config.save();
// Setup remote master branch gitlab.createMergeRequest(gitlabData.getLeft(), "feature", "master", "Merge feature branch to master.");
tmp.newFile("test");
git.add().addFilepattern("test");
git.commit().setMessage("test").call();
git.push()
.setRemote("origin").add("master")
.setCredentialsProvider(new UsernamePasswordCredentialsProvider(gitlab.getUsername(), gitlab.getPassword()))
.call();
// Setup remote feature branch
git.checkout().setName("feature").setCreateBranch(true).call();
tmp.newFile("feature");
git.commit().setMessage("feature").call();
git.push().setRemote("origin").add("feature").setCredentialsProvider(new UsernamePasswordCredentialsProvider(gitlab.getUsername(), gitlab.getPassword()))
.call();
gitlab.createMergeRequest(projectId, "feature", "master", "Merge feature branch to master.");
buildTriggered.block(10000); buildTriggered.block(10000);
assertThat(buildTriggered.isSignaled(), is(true)); assertThat(buildTriggered.isSignaled(), is(true));
@ -155,26 +113,6 @@ public class GitLabIT {
@Test @Test
public void buildOnNote() throws IOException, InterruptedException, GitAPIException { public void buildOnNote() throws IOException, InterruptedException, GitAPIException {
// check for clean slate
assertTrue(gitlab.getProjectIds().isEmpty());
final String httpUrl = gitlab.createProject(projectRequest()
.withName("test")
.withWebHookUrl("http://" + getDocker0Ip() + ":" + jenkins.getURL().getPort() + "/jenkins/project/test")
.withNoteHook(true)
.build());
// Fix: Hack to get the project id
// A preferable approach would be here to get the target project by name using getProject function of the
// GitLabRule and to use the id from the project instance. However, due to a bug in GitLab (tested on 8.6.1)
// retrieving the project by name is not properly working.
// (see issue https://github.com/gitlabhq/gitlabhq/issues/4921).
// Once the issue is resolved, replace this implementation.
final List<String> projectIds = gitlab.getProjectIds();
assertSame(projectIds.size(), 1);
final Integer projectId = Integer.parseInt(projectIds.get(0));
final OneShotEvent buildTriggered = new OneShotEvent(); final OneShotEvent buildTriggered = new OneShotEvent();
FreeStyleProject project = jenkins.createFreeStyleProject("test"); FreeStyleProject project = jenkins.createFreeStyleProject("test");
GitLabPushTrigger trigger = gitLabPushTrigger() GitLabPushTrigger trigger = gitLabPushTrigger()
@ -190,42 +128,134 @@ public class GitLabIT {
}); });
project.setQuietPeriod(0); project.setQuietPeriod(0);
// Setup git repository Pair<Integer, String> gitlabData = createGitLabProject(true, false, true, false);
Git.init().setDirectory(tmp.getRoot()).call();
Git git = Git.open(tmp.getRoot());
StoredConfig config = git.getRepository().getConfig();
config.setString("remote", "origin", "url", httpUrl);
config.save();
// Setup remote master branch
tmp.newFile("test");
git.add().addFilepattern("test");
git.commit().setMessage("test").call();
git.push()
.setRemote("origin").add("master")
.setCredentialsProvider(new UsernamePasswordCredentialsProvider(gitlab.getUsername(), gitlab.getPassword()))
.call();
// Setup remote feature branch
git.checkout().setName("feature").setCreateBranch(true).call();
tmp.newFile("feature");
git.commit().setMessage("feature").call();
git.push().setRemote("origin").add("feature").setCredentialsProvider(new UsernamePasswordCredentialsProvider(gitlab.getUsername(), gitlab.getPassword()))
.call();
// create merge-request // create merge-request
MergeRequest mr = gitlab.createMergeRequest(projectId, "feature", "master", "Merge feature branch to master."); MergeRequest mr = gitlab.createMergeRequest(gitlabData.getLeft(), "feature", "master", "Merge feature branch to master.");
// add trigger after push/merge-request so it may only receive the note-hook // add trigger after push/merge-request so it may only receive the note-hook
project.addTrigger(trigger); project.addTrigger(trigger);
trigger.start(project, true); trigger.start(project, true);
gitlab.createMergeRequestNote(projectId, mr.getId(), "this is a test note"); gitlab.createMergeRequestNote(gitlabData.getLeft(), mr.getId(), "this is a test note");
buildTriggered.block(20000); buildTriggered.block(20000);
assertThat(buildTriggered.isSignaled(), is(true)); assertThat(buildTriggered.isSignaled(), is(true));
} }
@Test
public void reportBuildStatus() throws IOException, InterruptedException, GitAPIException {
final OneShotEvent buildTriggered = new OneShotEvent();
final OneShotEvent buildReported = new OneShotEvent();
GitLabPushTrigger trigger = gitLabPushTrigger()
.withTriggerOnPush(true)
.withBranchFilterType(BranchFilterType.All)
.build();
FreeStyleProject project = jenkins.createFreeStyleProject("test");
project.addProperty(gitlab.createGitLabConnectionProperty());
project.addTrigger(trigger);
project.getBuildersList().add(new TestBuilder() {
@Override
public boolean perform(AbstractBuild<?, ?> build, Launcher launcher, BuildListener listener) throws InterruptedException, IOException {
buildTriggered.signal();
return true;
}
});
project.getBuildersList().add(new SleepBuilder(20000));
DescribableList<Publisher, Descriptor<Publisher>> publishers = project.getPublishersList();
publishers.add(new GitLabCommitStatusPublisher("integration-test", false));
publishers.add(new TestNotifier() {
@Override
public boolean perform(AbstractBuild<?, ?> build, Launcher launcher, BuildListener listener) throws InterruptedException, IOException {
buildReported.signal();
return true;
}
});
trigger.start(project, true);
project.setQuietPeriod(0);
Pair<Integer, String> gitlabData = createGitLabProject(false,true, true, false);
assertThat(gitlab.getPipelines(gitlabData.getLeft()), empty());
buildTriggered.block(20000);
assertThat(buildTriggered.isSignaled(), is(true));
assertPipelineStatus(gitlabData, "running");
buildReported.block(40000);
assertThat(buildReported.isSignaled(), is(true));
Thread.sleep(5000); // wait for gitlab to update
assertPipelineStatus(gitlabData, "success");
}
private void assertPipelineStatus(Pair<Integer, String> gitlabData, String status) {
List<Pipeline> pipelines = gitlab.getPipelines(gitlabData.getLeft());
assertThat(pipelines, hasSize(1));
Pipeline pipeline = pipelines.get(0);
assertEquals(gitlabData.getRight(), pipeline.getSha());
assertEquals(status, pipeline.getStatus());
}
private Pair<Integer, String> createGitLabProject(boolean addFeatureBranch, boolean withPushHook, boolean withNoteHook, boolean withMergeRequestHook) throws IOException, GitAPIException {
// check for clean slate
assertTrue(gitlab.getProjectIds().isEmpty());
String url = gitlab.createProject(projectRequest()
.withName("test")
.withWebHookUrl("http://" + getDocker0Ip() + ":" + jenkins.getURL().getPort() + "/jenkins/project/test")
.withPushHook(withPushHook)
.withNoteHook(withNoteHook)
.withMergeRequestHook(withMergeRequestHook)
.build());
String sha = initGitLabProject(url, addFeatureBranch);
// Fix: Hack to get the project id
// A preferable approach would be here to get the target project by name using getProject function of the
// GitLabRule and to use the id from the project instance. However, due to a bug in GitLab (tested on 8.6.1)
// retrieving the project by name is not properly working.
// (see issue https://github.com/gitlabhq/gitlabhq/issues/4921).
// Once the issue is resolved, replace this implementation.
List<String> projectIds = gitlab.getProjectIds();
assertSame(projectIds.size(), 1);
return new ImmutablePair<>(Integer.parseInt(projectIds.get(0)), sha);
}
private String initGitLabProject(String url, boolean addFeatureBranch) throws GitAPIException, IOException {
// Setup git repository
Git.init().setDirectory(tmp.getRoot()).call();
Git git = Git.open(tmp.getRoot());
StoredConfig config = git.getRepository().getConfig();
config.setString("remote", "origin", "url", url);
config.save();
// Setup remote master branch
tmp.newFile("test");
git.add().addFilepattern("test");
RevCommit commit = git.commit().setMessage("test").call();
git.push()
.setRemote("origin").add("master")
.setCredentialsProvider(new UsernamePasswordCredentialsProvider(gitlab.getUsername(), gitlab.getPassword()))
.call();
if (addFeatureBranch) {
// Setup remote feature branch
git.checkout().setName("feature").setCreateBranch(true).call();
tmp.newFile("feature");
commit = git.commit().setMessage("feature").call();
git.push().setRemote("origin").add("feature").setCredentialsProvider(new UsernamePasswordCredentialsProvider(gitlab.getUsername(), gitlab.getPassword()))
.call();
}
return commit.getName();
}
private String getDocker0Ip() { private String getDocker0Ip() {
try { try {
Enumeration<InetAddress> docker0Addresses = NetworkInterface.getByName("docker0").getInetAddresses(); Enumeration<InetAddress> docker0Addresses = NetworkInterface.getByName("docker0").getInetAddresses();

View File

@ -1,17 +1,16 @@
package com.dabsquared.gitlabjenkins.util; package com.dabsquared.gitlabjenkins.util;
import static com.dabsquared.gitlabjenkins.util.ProjectIdUtilTest.TestData.forRemoteUrl;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import com.dabsquared.gitlabjenkins.gitlab.api.GitLabClient;
import org.junit.experimental.theories.DataPoints; import org.junit.experimental.theories.DataPoints;
import org.junit.experimental.theories.Theories; import org.junit.experimental.theories.Theories;
import org.junit.experimental.theories.Theory; import org.junit.experimental.theories.Theory;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import com.dabsquared.gitlabjenkins.gitlab.api.GitLabApi; import static com.dabsquared.gitlabjenkins.util.ProjectIdUtilTest.TestData.forRemoteUrl;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
/** /**
* @author Robin Müller * @author Robin Müller
@ -33,8 +32,7 @@ public class ProjectIdUtilTest {
@Theory @Theory
public void retrieveProjectId(TestData testData) throws ProjectIdUtil.ProjectIdResolutionException { public void retrieveProjectId(TestData testData) throws ProjectIdUtil.ProjectIdResolutionException {
GitLabApi client = mock(GitLabApi.class); GitLabClient client = new GitLabClient(testData.hostUrl, null);
when(client.getGitLabHostUrl()).thenReturn(testData.baseUrl);
String projectId = ProjectIdUtil.retrieveProjectId(client, testData.remoteUrl); String projectId = ProjectIdUtil.retrieveProjectId(client, testData.remoteUrl);
@ -44,12 +42,12 @@ public class ProjectIdUtilTest {
static final class TestData { static final class TestData {
private final String baseUrl; private final String hostUrl;
private final String remoteUrl; private final String remoteUrl;
private String expectedProjectId; private String expectedProjectId;
private TestData(String baseUrl, String remoteUrl) { private TestData(String hostUrl, String remoteUrl) {
this.baseUrl = baseUrl; this.hostUrl = hostUrl;
this.remoteUrl = remoteUrl; this.remoteUrl = remoteUrl;
} }

View File

@ -59,7 +59,7 @@ public abstract class BuildPageRedirectActionTest {
testProject.setScm(new GitSCM(gitRepoUrl)); testProject.setScm(new GitSCM(gitRepoUrl));
testProject.setQuietPeriod(0); testProject.setQuietPeriod(0);
QueueTaskFuture<FreeStyleBuild> future = testProject.scheduleBuild2(0); QueueTaskFuture<FreeStyleBuild> future = testProject.scheduleBuild2(0);
FreeStyleBuild build = future.get(5, TimeUnit.SECONDS); FreeStyleBuild build = future.get(15, TimeUnit.SECONDS);
getBuildPageRedirectAction(testProject).execute(response); getBuildPageRedirectAction(testProject).execute(response);

5
travis/before_cache.sh Normal file
View File

@ -0,0 +1,5 @@
#!/usr/bin/env bash
rm -rf $HOME/.m2/repository/com/dabsquared/gitlabjenkins
mkdir -p $DOCKER_CACHE_DIR
docker images -a --filter='dangling=false' --format '{{.Repository}}:{{.Tag}} {{.ID}}' | xargs -n 2 -t sh -c 'test -e $DOCKER_CACHE_DIR/$1.tar.gz || docker save $0 | gzip -2 > $DOCKER_CACHE_DIR/$1.tar.gz'

View File

@ -0,0 +1,7 @@
#!/usr/bin/env bash
if [[ -d $DOCKER_CACHE_DIR ]]; then
echo "Loading cached images into docker..."
ls $DOCKER_CACHE_DIR/*.tar.gz | xargs -I {file} sh -c "zcat {file} | docker load";
fi
./mvnw integration-test -P integration-test -Dgitlab.version=$GITLAB_VERSION -Dfindbugs.skip=true -Dmaven.javadoc.skip=true

2
travis/test.sh Normal file
View File

@ -0,0 +1,2 @@
#!/usr/bin/env bash
./mvnw verify -B -Pall-tests,skip-javadoc-with-tests -Dmaven.javadoc.skip=true