diff --git a/.gitignore b/.gitignore index 4ce9dbd..b66ca53 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,11 @@ Jenkins Config As Code Example.iml + +# Plugin installation manager dev tools +jenkins-plugin-manager-*.jar +tmp/ + +# IDE +.classpath +.project +.settings +.factorypath diff --git a/Dockerfile b/Dockerfile index 15ff4c7..35f2a41 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,11 +1,15 @@ -FROM jenkins/jenkins:2.164.1 +ARG JENKINS_VERSION=2.238 +FROM jenkins/jenkins:$JENKINS_VERSION MAINTAINER Oleg Nenashev LABEL Description="This demo shows how to setup Jenkins Config-as-Code with Docker, Pipeline, and Groovy Hook Scripts" Vendor="Oleg Nenashev" Version="0.2" +# Plugin Installation Manager +ARG PLUGIN_MANAGER_TOOL_VERSION=1.0.2 +RUN wget https://github.com/jenkinsci/plugin-installation-manager-tool/releases/download/plugin-management-parent-pom-$PLUGIN_MANAGER_TOOL_VERSION/jenkins-plugin-manager-$PLUGIN_MANAGER_TOOL_VERSION.jar -O /usr/share/jenkins/ref/jenkins-plugin-manager.jar + ENV JENKINS_UC_EXPERIMENTAL=https://updates.jenkins.io/experimental COPY plugins.txt /usr/share/jenkins/ref/plugins.txt -COPY download-and-check-updates.sh /usr/local/bin/download-and-check-updates.sh -RUN /usr/local/bin/download-and-check-updates.sh < /usr/share/jenkins/ref/plugins.txt +RUN /usr/local/bin/install-plugins.sh < /usr/share/jenkins/ref/plugins.txt COPY init_scripts/src/main/groovy/ /usr/share/jenkins/ref/init.groovy.d/ diff --git a/Makefile b/Makefile index 14d8e7d..07a1a6d 100644 --- a/Makefile +++ b/Makefile @@ -1,3 +1,6 @@ +PLUGIN_MANAGER_TOOL_VERSION=1.0.2 +JENKINS_VERSION=2.238 + build: docker build -t onenashev/demo-jenkins-config-as-code . @@ -6,3 +9,18 @@ run: debug: docker run --rm --name ci-jenkins-io-dev -e DEBUG=true -p 5005:5005 -v maven-repo:/root/.m2 -v ${MY_PIPELINE_LIBRARY_DIR}:/var/jenkins_home/pipeline-library -v ${MY_OTHER_PIPELINE_LIBS_DIRS}:/var/jenkins_home/pipeline-libs -e DEV_HOST=${CURRENT_HOST} -p 8080:8080 -p 50000:50000 onenashev/demo-jenkins-config-as-code + +clean: + rm -rf tmp + +tmp/jenkins.war: + wget https://repo.jenkins-ci.org/releases/org/jenkins-ci/main/jenkins-war/${JENKINS_VERSION}/jenkins-war-${JENKINS_VERSION}.war -O tmp/jenkins.war + touch tmp/jenkins.war + +tmp/jenkins-plugin-manager.jar: + wget https://github.com/jenkinsci/plugin-installation-manager-tool/releases/download/plugin-management-parent-pom-${PLUGIN_MANAGER_TOOL_VERSION}/jenkins-plugin-manager-${PLUGIN_MANAGER_TOOL_VERSION}.jar -O tmp/jenkins-plugin-manager.jar + touch tmp/jenkins-plugin-manager.jar + +show-updates: tmp/jenkins-plugin-manager.jar tmp/jenkins.war + mkdir -p tmp/plugins + java -jar tmp/jenkins-plugin-manager.jar --list -f plugins.txt --no-download -d tmp/plugins -w tmp/jenkins.war --available-updates diff --git a/download-and-check-updates.sh b/download-and-check-updates.sh deleted file mode 100755 index 0759050..0000000 --- a/download-and-check-updates.sh +++ /dev/null @@ -1,304 +0,0 @@ -#!/bin/bash -eu - -# Resolve dependencies and download plugins given on the command line -# -# FROM jenkins -# RUN install-plugins.sh docker-slaves github-branch-source -# TODO: Remove the file once check-for-updates is supported in the parent Dockerfile -set -o pipefail - -REF_DIR=${REF:-/usr/share/jenkins/ref/plugins} -FAILED="$REF_DIR/failed-plugins.txt" - -. /usr/local/bin/jenkins-support - -getLockFile() { - printf '%s' "$REF_DIR/${1}.lock" -} - -getArchiveFilename() { - printf '%s' "$REF_DIR/${1}.jpi" -} - -download() { - local plugin originalPlugin version lock ignoreLockFile - plugin="$1" - version="${2:-latest}" - ignoreLockFile="${3:-}" - lock="$(getLockFile "$plugin")" - - if [[ $ignoreLockFile ]] || mkdir "$lock" &>/dev/null; then - if ! doDownload "$plugin" "$version"; then - # some plugin don't follow the rules about artifact ID - # typically: docker-plugin - originalPlugin="$plugin" - plugin="${plugin}-plugin" - if ! doDownload "$plugin" "$version"; then - echo "Failed to download plugin: $originalPlugin or $plugin" >&2 - echo "Not downloaded: ${originalPlugin}" >> "$FAILED" - return 1 - fi - fi - - if ! checkIntegrity "$plugin"; then - echo "Downloaded file is not a valid ZIP: $(getArchiveFilename "$plugin")" >&2 - echo "Download integrity: ${plugin}" >> "$FAILED" - return 1 - fi - - resolveDependencies "$plugin" - fi -} - -doDownload() { - local plugin version url jpi - plugin="$1" - version="$2" - jpi="$(getArchiveFilename "$plugin")" - - # If plugin already exists and is the same version do not download - if test -f "$jpi" && unzip -p "$jpi" META-INF/MANIFEST.MF | tr -d '\r' | grep "^Plugin-Version: ${version}$" > /dev/null; then - echo "Using provided plugin: $plugin" - return 0 - fi - - if [[ "$version" == "latest" && -n "$JENKINS_UC_LATEST" ]]; then - # If version-specific Update Center is available, which is the case for LTS versions, - # use it to resolve latest versions. - url="$JENKINS_UC_LATEST/latest/${plugin}.hpi" - elif [[ "$version" == "experimental" && -n "$JENKINS_UC_EXPERIMENTAL" ]]; then - # Download from the experimental update center - url="$JENKINS_UC_EXPERIMENTAL/latest/${plugin}.hpi" - else - JENKINS_UC_DOWNLOAD=${JENKINS_UC_DOWNLOAD:-"$JENKINS_UC/download"} - url="$JENKINS_UC_DOWNLOAD/plugins/$plugin/$version/${plugin}.hpi" - fi - - echo "Downloading plugin: $plugin from $url" - curl --connect-timeout "${CURL_CONNECTION_TIMEOUT:-20}" --retry "${CURL_RETRY:-5}" --retry-delay "${CURL_RETRY_DELAY:-0}" --retry-max-time "${CURL_RETRY_MAX_TIME:-60}" -s -f -L "$url" -o "$jpi" - return $? -} - -checkIntegrity() { - local plugin jpi - plugin="$1" - jpi="$(getArchiveFilename "$plugin")" - - unzip -t -qq "$jpi" >/dev/null - return $? -} - -resolveDependencies() { - local plugin jpi dependencies - plugin="$1" - jpi="$(getArchiveFilename "$plugin")" - - dependencies="$(unzip -p "$jpi" META-INF/MANIFEST.MF | tr -d '\r' | tr '\n' '|' | sed -e 's#| ##g' | tr '|' '\n' | grep "^Plugin-Dependencies: " | sed -e 's#^Plugin-Dependencies: ##')" - - if [[ ! $dependencies ]]; then - echo " > $plugin has no dependencies" - return - fi - - echo " > $plugin depends on $dependencies" - - IFS=',' read -r -a array <<< "$dependencies" - - for d in "${array[@]}" - do - plugin="$(cut -d':' -f1 - <<< "$d")" - if [[ $d == *"resolution:=optional"* ]]; then - echo "Skipping optional dependency $plugin" - else - local pluginInstalled - if pluginInstalled="$(echo -e "${bundledPlugins}\n${installedPlugins}" | grep "^${plugin}:")"; then - pluginInstalled="${pluginInstalled//[$'\r']}" - local versionInstalled; versionInstalled=$(versionFromPlugin "${pluginInstalled}") - local minVersion; minVersion=$(versionFromPlugin "${d}") - if versionLT "${versionInstalled}" "${minVersion}"; then - echo "Upgrading bundled dependency $d ($minVersion > $versionInstalled)" - download "$plugin" & - else - echo "Skipping already installed dependency $d ($minVersion <= $versionInstalled)" - fi - else - download "$plugin" & - fi - fi - done - wait -} - -bundledPlugins() { - local JENKINS_WAR=/usr/share/jenkins/jenkins.war - if [ -f $JENKINS_WAR ] - then - TEMP_PLUGIN_DIR=/tmp/plugintemp.$$ - for i in $(jar tf $JENKINS_WAR | grep -E '[^detached-]plugins.*\..pi' | sort) - do - rm -fr $TEMP_PLUGIN_DIR - mkdir -p $TEMP_PLUGIN_DIR - PLUGIN=$(basename "$i"|cut -f1 -d'.') - (cd $TEMP_PLUGIN_DIR;jar xf "$JENKINS_WAR" "$i";jar xvf "$TEMP_PLUGIN_DIR/$i" META-INF/MANIFEST.MF >/dev/null 2>&1) - VER=$(grep -E -i Plugin-Version "$TEMP_PLUGIN_DIR/META-INF/MANIFEST.MF"|cut -d: -f2|sed 's/ //') - echo "$PLUGIN:$VER" - done - rm -fr $TEMP_PLUGIN_DIR - else - rm -f "$TEMP_ALREADY_INSTALLED" - echo "ERROR file not found: $JENKINS_WAR" - exit 1 - fi -} - -versionFromPlugin() { - local plugin=$1 - if [[ $plugin =~ .*:.* ]]; then - echo "${plugin##*:}" - else - echo "latest" - fi - -} - -installedPlugins() { - for f in "$REF_DIR"/*.jpi; do - echo "$(basename "$f" | sed -e 's/\.jpi//'):$(get_plugin_version "$f")" - done -} - -availableUpdates() { - local url - if [[ -n "$JENKINS_UC_LATEST" ]]; then - # If version-specific Update Center is available, which is the case for LTS versions, - # use it to resolve latest versions. - url="$JENKINS_UC_LATEST/update-center.actual.json" - else - JENKINS_UC_DOWNLOAD=${JENKINS_UC_DOWNLOAD:-"$JENKINS_UC/download"} - url="$JENKINS_UC_DOWNLOAD/update-center.actual.json" - fi - local jqExecutable="$REF_DIR/jq" - local ucMetadataFile="$REF_DIR/uc.json" - - local updatesFile="$REF_DIR/availableUpdates.txt" - - # TODO: do jq installation in Dockerfile so that it comes from cache when plugin list is refreshed - local failureReason="" - curl --connect-timeout "${CURL_CONNECTION_TIMEOUT:-20}" \ - --retry "${CURL_RETRY:-5}" --retry-delay "${CURL_RETRY_DELAY:-0}" --retry-max-time "${CURL_RETRY_MAX_TIME:-60}" \ - -s -f -L "https://github.com/stedolan/jq/releases/download/jq-1.5/jq-linux64" -o "$jqExecutable" \ - || failureReason="Cannot retrieve the jq executable, error code: $?" - chmod +x ${jqExecutable} || failureReason="Cannot chmod +x ${jqExecutable}, error code: $?" - - curl --connect-timeout "${CURL_CONNECTION_TIMEOUT:-20}" \ - --retry "${CURL_RETRY:-5}" --retry-delay "${CURL_RETRY_DELAY:-0}" --retry-max-time "${CURL_RETRY_MAX_TIME:-60}" \ - -s -f -L "$url" -o "$ucMetadataFile" \ - || failureReason="Cannot retrieve the UC metadata from ${url}, error code: $?" - - if [[ -n "$failureReason" ]] ; then - echo "Cannot check for updates: $failureReason" - else - for f in "$REF_DIR"/*.jpi; do - local pluginName=$(basename "$f" | sed -e 's/\.jpi//') - local versionInstalled=$(get_plugin_version "$f") - local latestVersion=$(cat "$ucMetadataFile" | ${jqExecutable} -r ".plugins[\"${pluginName}\"].version") - if versionLT "${versionInstalled}" "${latestVersion}"; then - echo "$pluginName:$versionInstalled:$latestVersion" >> $updatesFile - # Also report it in the build log - echo "$pluginName:$versionInstalled:$latestVersion" - fi - done - fi -} - -jenkinsMajorMinorVersion() { - local JENKINS_WAR - JENKINS_WAR=/usr/share/jenkins/jenkins.war - if [[ -f "$JENKINS_WAR" ]]; then - local version major minor - version="$(java -jar /usr/share/jenkins/jenkins.war --version)" - major="$(echo "$version" | cut -d '.' -f 1)" - minor="$(echo "$version" | cut -d '.' -f 2)" - echo "$major.$minor" - else - echo "ERROR file not found: $JENKINS_WAR" - return 1 - fi -} - -main() { - local plugin pluginVersion jenkinsVersion - local plugins=() - - mkdir -p "$REF_DIR" || exit 1 - - # Read plugins from stdin or from the command line arguments - if [[ ($# -eq 0) ]]; then - while read -r line || [ "$line" != "" ]; do - # Remove leading/trailing spaces, comments, and empty lines - plugin=$(echo "${line}" | tr -d '\r' | sed -e 's/^[ \t]*//g' -e 's/[ \t]*$//g' -e 's/[ \t]*#.*$//g' -e '/^[ \t]*$/d') - - # Avoid adding empty plugin into array - if [ ${#plugin} -ne 0 ]; then - plugins+=("${plugin}") - fi - done - else - plugins=("$@") - fi - - # Create lockfile manually before first run to make sure any explicit version set is used. - echo "Creating initial locks..." - for plugin in "${plugins[@]}"; do - mkdir "$(getLockFile "${plugin%%:*}")" - done - - echo "Analyzing war..." - bundledPlugins="$(bundledPlugins)" - - echo "Registering preinstalled plugins..." - installedPlugins="$(installedPlugins)" - - # Check if there's a version-specific update center, which is the case for LTS versions - jenkinsVersion="$(jenkinsMajorMinorVersion)" - if curl -fsL -o /dev/null "$JENKINS_UC/$jenkinsVersion"; then - JENKINS_UC_LATEST="$JENKINS_UC/$jenkinsVersion" - echo "Using version-specific update center: $JENKINS_UC_LATEST..." - else - JENKINS_UC_LATEST= - fi - - echo "Downloading plugins..." - for plugin in "${plugins[@]}"; do - pluginVersion="" - - if [[ $plugin =~ .*:.* ]]; then - pluginVersion=$(versionFromPlugin "${plugin}") - plugin="${plugin%%:*}" - fi - - download "$plugin" "$pluginVersion" "true" & - done - wait - - echo - echo "WAR bundled plugins:" - echo "${bundledPlugins}" - echo - echo "Installed plugins:" - installedPlugins - echo - echo "Available updates:" - availableUpdates - echo - - if [[ -f $FAILED ]]; then - echo "Some plugins failed to download!" "$(<"$FAILED")" >&2 - exit 1 - fi - - echo "Cleaning up locks" - rm -r "$REF_DIR"/*.lock -} - -main "$@" \ No newline at end of file diff --git a/plugins.txt b/plugins.txt index 576a428..2362878 100644 --- a/plugins.txt +++ b/plugins.txt @@ -1,27 +1,39 @@ -authorize-project:1.3.0 -blueocean:1.14.0 -checkstyle:4.0.0 -cloudbees-folder:6.7 -cobertura:1.13 -configuration-as-code-support:1.8 -configuration-as-code:1.8 -email-ext:2.65 -filesystem_scm:2.1 -findbugs:5.0.0 -git:3.9.3 -jacoco:3.0.4 -jdk-tool:1.2 -job-restrictions:0.8 -junit:1.27 -locale:1.4 -mailer:1.23 -matrix-auth:2.3 -monitoring:1.76.0 -ownership:0.12.1 -parallel-test-executor:1.12 -role-strategy:2.10 -security-inspector:0.5 -timestamper:1.9 -workflow-aggregator:2.6 -workflow-cps:2.64 -yet-another-docker-plugin:0.1.0-rc50 \ No newline at end of file +ace-editor:1.1 +blueocean:1.23.2 +cloud-stats:0.25 +code-coverage-api:1.1.6 +configuration-as-code:1.41 +docker-commons:1.16 +docker-workflow:1.23 +email-ext:2.69 +favorite:2.3.2 +git-client:3.2.1 +github:1.30.0 +github-api:1.111 +github-branch-source:2.7.1 +jackson2-api:2.11.0 +javadoc:1.5 +jdk-tool:1.4 +jsch:0.1.55.2 +lockable-resources:2.8 +mailer:1.32 +matrix-auth:2.6.1 +maven-plugin:3.6 +momentjs:1.1.1 +monitoring:1.83.0 +parameterized-trigger:2.36 +pipeline-build-step:2.12 +pipeline-model-definition:1.6.0 +pipeline-stage-tags-metadata:1.6.0 +pipeline-stage-view:2.13 +run-condition:1.3 +scm-api:2.6.3 +ssh-credentials:1.18.1 +token-macro:2.12 +workflow-basic-steps:2.20 +workflow-cps:2.80 +workflow-durable-task-step:2.35 +workflow-job:2.39 +workflow-multibranch:2.21 +workflow-scm-step:2.11 +yet-another-docker-plugin:0.2.0