| #!/bin/bash -eu |
| |
| # Resolve dependencies and download plugins given on the command line |
| # |
| # FROM jenkins |
| # RUN install-plugins.sh docker-slaves github-branch-source |
| |
| set -o pipefail |
| |
| REF_DIR=${REF:-/usr/share/jenkins/ref/plugins} |
| DEPS_PINNING_FILE=${DEPS_PINNING_FILE:-/usr/share/jenkins/ref/jenkins-plugins-deps} |
| 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 |
| |
| JENKINS_UC_DOWNLOAD=${JENKINS_UC_DOWNLOAD:-"$JENKINS_UC/download"} |
| |
| url="$JENKINS_UC_DOWNLOAD/plugins/$plugin/$version/${plugin}.hpi" |
| |
| 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 $? |
| } |
| |
| getPluginDepsPinnedVersion() { |
| local plugin |
| plugin="$1" |
| pinnedPlugin=$(cat $DEPS_PINNING_FILE | grep "^${plugin}:") |
| if [ -n "$pinnedPlugin" ]; then |
| pinnedPluginVersion="$(echo "$pinnedPlugin" | cut -d':' -f2 | tr -d ' ')" |
| printf '%s' "$pinnedPluginVersion" |
| fi |
| } |
| |
| 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 |
| local pluginVersion; pluginVersion=$(getPluginDepsPinnedVersion "${plugin}") |
| if pluginInstalled="$(echo "${bundledPlugins}" | grep "^${plugin}:")"; then |
| pluginInstalled="${pluginInstalled//[$'\r']}" |
| local versionInstalled; versionInstalled=$(versionFromPlugin "${pluginInstalled}") |
| local minVersion="$pluginVersion" |
| if [ -z "$minVersion" ]; then |
| minVersion=$(versionFromPlugin "${d}") |
| fi |
| if versionLT "${versionInstalled}" "${minVersion}"; then |
| echo "Upgrading bundled dependency $d ($minVersion > $versionInstalled)" |
| download "$plugin" "$pluginVersion" & |
| else |
| echo "Skipping already bundled dependency $d ($minVersion <= $versionInstalled)" |
| fi |
| else |
| download "$plugin" "$pluginVersion" & |
| 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 | egrep '[^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=$(egrep -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 |
| } |
| |
| main() { |
| local plugin version |
| |
| mkdir -p "$REF_DIR" || exit 1 |
| |
| # Create lockfile manually before first run to make sure any explicit version set is used. |
| echo "Creating initial locks..." |
| for plugin in "$@"; do |
| mkdir "$(getLockFile "${plugin%%:*}")" |
| done |
| |
| echo "Analyzing war..." |
| bundledPlugins="$(bundledPlugins)" |
| |
| echo "Downloading plugins..." |
| for plugin in "$@"; do |
| version="" |
| |
| if [[ $plugin =~ .*:.* ]]; then |
| version=$(versionFromPlugin "${plugin}") |
| plugin="${plugin%%:*}" |
| fi |
| |
| download "$plugin" "$version" "true" & |
| done |
| wait |
| |
| echo |
| echo "WAR bundled plugins:" |
| echo "${bundledPlugins}" |
| echo |
| echo "Installed plugins:" |
| installedPlugins |
| |
| 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 "$@" |