Created
April 14, 2017 08:54
-
-
Save sun-mir/847985cb2dd283c9fc54ce3f1338c191 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash -eu | |
# Resolve dependencies and download plugins given on the command line | |
# | |
# install-jenkins-plugins.sh docker-slaves ssh-agent:1.8 | |
set -o pipefail | |
JENKINS_HOME=${JENKINS_HOME:-/var/lib/jenkins} | |
JENKINS_WAR=${JENKINS_WAR:-/usr/lib/jenkins/jenkins.war} | |
JENKINS_UC=https://updates.jenkins.io | |
REF_DIR=/usr/share/jenkins/ref/plugins | |
FAILED="$REF_DIR/failed-plugins.txt" | |
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 $? | |
} | |
# compare if version1 < version2 | |
versionLT() { | |
local v1; v1=$(echo "$1" | cut -d '-' -f 1 ) | |
local q1; q1=$(echo "$1" | cut -s -d '-' -f 2- ) | |
local v2; v2=$(echo "$2" | cut -d '-' -f 1 ) | |
local q2; q2=$(echo "$2" | cut -s -d '-' -f 2- ) | |
if [ "$v1" = "$v2" ]; then | |
if [ "$q1" = "$q2" ]; then | |
return 1 | |
else | |
if [ -z "$q1" ]; then | |
return 1 | |
else | |
if [ -z "$q2" ]; then | |
return 0 | |
else | |
[ "$q1" = "$(echo -e "$q1\n$q2" | sort -V | head -n1)" ] | |
fi | |
fi | |
fi | |
else | |
[ "$v1" = "$(echo -e "$v1\n$v2" | sort -V | head -n1)" ] | |
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 | |
if pluginInstalled="$(echo "${bundledPlugins}" | 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 bundled dependency $d ($minVersion <= $versionInstalled)" | |
fi | |
else | |
download "$plugin" & | |
fi | |
fi | |
done | |
wait | |
} | |
bundledPlugins() { | |
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 | |
} | |
# returns a plugin version from a plugin archive | |
get_plugin_version() { | |
local archive; archive=$1 | |
local version; version=$(unzip -p "$archive" META-INF/MANIFEST.MF | grep "^Plugin-Version: " | sed -e 's#^Plugin-Version: ##') | |
version=${version%%[[:space:]]} | |
echo "$version" | |
} | |
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 $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!" | |
echo "$(<"$FAILED")" >&2 | |
exit 1 | |
fi | |
echo "Cleaning up locks" | |
rm -r "$REF_DIR"/*.lock | |
} | |
main "$@" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment