Merge remote-tracking branch 'upstream/master'
diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md
new file mode 100644
index 0000000..44440f6
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE.md
@@ -0,0 +1,23 @@
+# Issues and Contributing
+
+Please note that only issues related to this Docker image will be addressed here.
+
+* If you have Docker related issues, please ask in the [Docker user mailing list](https://groups.google.com/forum/#!forum/docker-user).
+* If you have Jenkins related issues, please ask in the [Jenkins mailing lists](https://jenkins-ci.org/content/mailing-lists).
+* If you are not sure, then this is probably not the place to create an issue and you should use any of the previously mentioned mailing lists.
+
+If after going through the previous checklist you still think you should create an issue here please provide:
+
+
+### Docker commands that you execute
+
+### Actual result
+
+### Expected outcome
+
+### Have you tried a non-dockerized Jenkins and get the expected outcome?
+
+### Output of `docker version`
+
+### Other relevant information
+
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 49a61f5..92aafd7 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -11,5 +11,6 @@
 * Docker commands that you execute
 * Actual result
 * Expected outcome
+* Have you tried a non-dockerized Jenkins and get the expected outcome?
 * Output of `docker version`
 * Other relevant information
diff --git a/Dockerfile b/Dockerfile
index 3731795..86b8a07 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,6 +1,6 @@
 FROM openjdk:8-jdk
 
-RUN apt-get update && apt-get install -y git gettext-base curl zip apt-transport-https ca-certificates && rm -rf /var/lib/apt/lists/*
+RUN apt-get update && apt-get install -y git curl && rm -rf /var/lib/apt/lists/*
 
 ENV JENKINS_HOME /var/jenkins_home
 ENV JENKINS_SLAVE_AGENT_PORT 50000
@@ -25,29 +25,29 @@
 # or config file with your custom jenkins Docker image.
 RUN mkdir -p /usr/share/jenkins/ref/init.groovy.d; chown ${uid}:${gid} /usr/share/jenkins/ref/init.groovy.d
 
-ENV TINI_VERSION 0.9.0
-ENV TINI_SHA fa23d1e20732501c3bb8eeeca423c89ac80ed452
+ENV TINI_VERSION 0.13.2
+ENV TINI_SHA afbf8de8a63ce8e4f18cb3f34dfdbbd354af68a1
 
 # Use tini as subreaper in Docker container to adopt zombie processes 
-RUN curl -fsSL https://github.com/krallin/tini/releases/download/v${TINI_VERSION}/tini-static -o /bin/tini && chmod +x /bin/tini \
+RUN curl -fsSL https://github.com/krallin/tini/releases/download/v${TINI_VERSION}/tini-static-amd64 -o /bin/tini && chmod +x /bin/tini \
   && echo "$TINI_SHA  /bin/tini" | sha1sum -c -
 
 COPY init.groovy /usr/share/jenkins/ref/init.groovy.d/tcp-slave-agent-port.groovy
 
 # jenkins version being bundled in this docker image
 ARG JENKINS_VERSION
-ENV JENKINS_VERSION ${JENKINS_VERSION:-2.7.2}
+ENV JENKINS_VERSION ${JENKINS_VERSION:-2.32.3}
 
 # jenkins.war checksum, download will be validated using it
-ARG JENKINS_SHA=4c05175677825a0c311ef3001bbb0a767dad0e8d
+ARG JENKINS_SHA=3eb599dd78ecf00e5f177ec5c4b1ba4274be4e5f63236da6ac92401a66fa91e8
 
 # Can be used to customize where jenkins.war get downloaded from
-ARG JENKINS_URL=http://repo.jenkins-ci.org/public/org/jenkins-ci/main/jenkins-war/${JENKINS_VERSION}/jenkins-war-${JENKINS_VERSION}.war
+ARG JENKINS_URL=https://repo.jenkins-ci.org/public/org/jenkins-ci/main/jenkins-war/${JENKINS_VERSION}/jenkins-war-${JENKINS_VERSION}.war
 
 # could use ADD but this one does not check Last-Modified header neither does it allow to control checksum 
 # see https://github.com/docker/docker/issues/8331
 RUN curl -fsSL ${JENKINS_URL} -o /usr/share/jenkins/jenkins.war \
-  && echo "${JENKINS_SHA}  /usr/share/jenkins/jenkins.war" | sha1sum -c -
+  && echo "${JENKINS_SHA}  /usr/share/jenkins/jenkins.war" | sha256sum -c -
 
 ENV JENKINS_UC https://updates.jenkins.io
 RUN chown -R ${user} "$JENKINS_HOME" /usr/share/jenkins/ref
diff --git a/Jenkinsfile b/Jenkinsfile
index 51fe5f1..7cbb3d2 100644
--- a/Jenkinsfile
+++ b/Jenkinsfile
@@ -1,11 +1,38 @@
+#!/usr/bin/env groovy
+
+properties([
+    buildDiscarder(logRotator(numToKeepStr: '5', artifactNumToKeepStr: '5')),
+    pipelineTriggers([cron('@daily')]),
+])
+
 node('docker') {
-  stage 'Checkout'
-  checkout scm
+    deleteDir()
 
-  stage 'Build'
-  docker.build('jenkins')
+    stage('Checkout') {
+        checkout scm
+    }
 
-//  stage 'Test'
-//  sh "git clone https://github.com/sstephenson/bats.git"
-//  sh "bats/bin/bats tests/tests.bats"
+    if (!infra.isTrusted()) {
+        /* Outside of the trusted.ci environment, we're building and testing
+         * the Dockerful in this repository, but not publishing to docker hub
+         */
+        stage('Build') {
+            docker.build('jenkins')
+        }
+
+        stage('Test') {
+            sh """
+            git submodule update --init --recursive
+            git clone https://github.com/sstephenson/bats.git
+            bats/bin/bats tests
+            """
+        }
+    } else {
+        /* In our trusted.ci environment we only want to be publishing our
+         * containers from artifacts
+         */
+        stage('Publish') {
+            sh './publish.sh'
+        }
+    }
 }
diff --git a/README.md b/README.md
index 97f5285..78b37ff 100644
--- a/README.md
+++ b/README.md
@@ -20,23 +20,15 @@
 NOTE: read below the _build executors_ part for the role of the `50000` port mapping.
 
 This will store the workspace in /var/jenkins_home. All Jenkins data lives in there - including plugins and configuration.
-You will probably want to make that a persistent volume (recommended):
+You will probably want to make that an explicit volume so you can manage it and attach to another container for upgrades :
 
 ```
-docker run -p 8080:8080 -p 50000:50000 -v /your/home:/var/jenkins_home jenkins
+docker run -p 8080:8080 -p 50000:50000 -v jenkins_home:/var/jenkins_home jenkins
 ```
 
-This will store the jenkins data in `/your/home` on the host.
-Ensure that `/your/home` is accessible by the jenkins user in container (jenkins user - uid 1000) or use `-u some_other_user` parameter with `docker run`.
+this will automatically create a 'jenkins_home' volume on docker host, that will survive container stop/restart/deletion. 
 
-
-You can also use a volume container:
-
-```
-docker run --name myjenkins -p 8080:8080 -p 50000:50000 -v /var/jenkins_home jenkins
-```
-
-Then myjenkins container has the volume (please do read about docker volume handling to find out more).
+Avoid using a bind mount from a folder on host into `/var/jenkins_home`, as this might result in file permission issue. If you _really_ need to bind mount jenkins_home, ensure that directory on host is accessible by the jenkins user in container (jenkins user - uid 1000) or use `-u some_other_user` parameter with `docker run`.
 
 ## Backing up data
 
@@ -48,7 +40,7 @@
 If your volume is inside a container - you can use ```docker cp $ID:/var/jenkins_home``` command to extract the data, or other options to find where the volume data is.
 Note that some symlinks on some OSes may be converted to copies (this can confuse jenkins with lastStableBuild links etc)
 
-For more info check Docker docs section on [Managing data in containers](https://docs.docker.com/userguide/dockervolumes/)
+For more info check Docker docs section on [Managing data in containers](https://docs.docker.com/engine/tutorials/dockervolumes/)
 
 # Setting the number of executors
 
@@ -100,6 +92,10 @@
 docker run --name myjenkins -p 8080:8080 -p 50000:50000 --env JAVA_OPTS="-Djava.util.logging.config.file=/var/jenkins_home/log.properties" -v `pwd`/data:/var/jenkins_home jenkins
 ```
 
+# Configuring reverse proxy
+If you want to install Jenkins behind a reverse proxy with prefix, example: mysite.com/jenkins, you need to add environnement variable `JENKINS_OPTS="--prefix=/jenkins"` and then follow the below procedures to configure your reverse proxy, which will depend if you have Apache ou Nginx:
+- [Apache](https://wiki.jenkins-ci.org/display/JENKINS/Running+Jenkins+behind+Apache)
+- [Nginx](https://wiki.jenkins-ci.org/display/JENKINS/Jenkins+behind+an+NGinX+reverse+proxy)
 
 # Passing Jenkins launcher parameters
 
@@ -142,7 +138,8 @@
 # if we want to install via apt
 USER root
 RUN apt-get update && apt-get install -y ruby make more-thing-here
-USER jenkins # drop back to the regular jenkins user - good practice
+# drop back to the regular jenkins user - good practice
+USER jenkins
 ```
 
 In such a derived image, you can customize your jenkins instance with hook scripts or additional plugins.
diff --git a/install-plugins.sh b/install-plugins.sh
index 8b8daee..233b739 100755
--- a/install-plugins.sh
+++ b/install-plugins.sh
@@ -12,15 +12,15 @@
 
 . /usr/local/bin/jenkins-support
 
-function getLockFile() {
-    echo -n "$REF_DIR/${1}.lock"
+getLockFile() {
+    printf '%s' "$REF_DIR/${1}.lock"
 }
 
-function getArchiveFilename() {
-    echo -n "$REF_DIR/${1}.jpi"
+getArchiveFilename() {
+    printf '%s' "$REF_DIR/${1}.jpi"
 }
 
-function download() {
+download() {
     local plugin originalPlugin version lock ignoreLockFile
     plugin="$1"
     version="${2:-latest}"
@@ -50,7 +50,7 @@
     fi
 }
 
-function doDownload() {
+doDownload() {
     local plugin version url jpi
     plugin="$1"
     version="$2"
@@ -67,20 +67,20 @@
     url="$JENKINS_UC_DOWNLOAD/plugins/$plugin/$version/${plugin}.hpi"
 
     echo "Downloading plugin: $plugin from $url"
-    curl --connect-timeout 5 --retry 5 --retry-delay 0 --retry-max-time 60 -s -f -L "$url" -o "$jpi"
+    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 $?
 }
 
-function checkIntegrity() {
+checkIntegrity() {
     local plugin jpi
     plugin="$1"
     jpi="$(getArchiveFilename "$plugin")"
 
-    zip -T "$jpi" >/dev/null
+    unzip -t -qq "$jpi" >/dev/null
     return $?
 }
 
-function resolveDependencies() {
+resolveDependencies() {
     local plugin jpi dependencies
     plugin="$1"
     jpi="$(getArchiveFilename "$plugin")"
@@ -94,7 +94,7 @@
 
     echo " > $plugin depends on $dependencies"
 
-    IFS=',' read -a array <<< "$dependencies"
+    IFS=',' read -r -a array <<< "$dependencies"
 
     for d in "${array[@]}"
     do
@@ -121,7 +121,7 @@
     wait
 }
 
-function bundledPlugins() {
+bundledPlugins() {
     local JENKINS_WAR=/usr/share/jenkins/jenkins.war
     if [ -f $JENKINS_WAR ]
     then
@@ -143,7 +143,7 @@
     fi
 }
 
-function versionFromPlugin() {
+versionFromPlugin() {
     local plugin=$1
     if [[ $plugin =~ .*:.* ]]; then
         echo "${plugin##*:}"
@@ -153,7 +153,7 @@
 
 }
 
-function installedPlugins() {
+installedPlugins() {
     for f in "$REF_DIR"/*.jpi; do
         echo "$(basename "$f" | sed -e 's/\.jpi//'):$(get_plugin_version "$f")"
     done
@@ -170,10 +170,10 @@
         mkdir "$(getLockFile "${plugin%%:*}")"
     done
 
-    echo -e "\nAnalyzing war..."
+    echo "Analyzing war..."
     bundledPlugins="$(bundledPlugins)"
 
-    echo -e "\nDownloading plugins..."
+    echo "Downloading plugins..."
     for plugin in "$@"; do
         version=""
 
@@ -194,11 +194,11 @@
     installedPlugins
 
     if [[ -f $FAILED ]]; then
-        echo -e "\nSome plugins failed to download!\n$(<"$FAILED")" >&2
+        echo "Some plugins failed to download!" "$(<"$FAILED")" >&2
         exit 1
     fi
 
-    echo -e "\nCleaning up locks"
+    echo "Cleaning up locks"
     rm -r "$REF_DIR"/*.lock
 }
 
diff --git a/jenkins.sh b/jenkins.sh
index 84d89be..74e39cd 100755
--- a/jenkins.sh
+++ b/jenkins.sh
@@ -1,9 +1,9 @@
 #! /bin/bash -e
 
-: ${JENKINS_HOME:="/var/jenkins_home"}
-touch "${COPY_REFERENCE_FILE_LOG}" || (echo "Can not write to ${COPY_REFERENCE_FILE_LOG}. Wrong volume permissions?" && exit 1)
+: "${JENKINS_HOME:="/var/jenkins_home"}"
+touch "${COPY_REFERENCE_FILE_LOG}" || { echo "Can not write to ${COPY_REFERENCE_FILE_LOG}. Wrong volume permissions?"; exit 1; }
 echo "--- Copying files at $(date)" >> "$COPY_REFERENCE_FILE_LOG"
-find /usr/share/jenkins/ref/ -type f -exec bash -c ". /usr/local/bin/jenkins-support; copy_reference_file '{}'" \;
+find /usr/share/jenkins/ref/ -type f -exec bash -c '. /usr/local/bin/jenkins-support; for arg; do copy_reference_file "$arg"; done' _ {} +
 
 cat /tmp/org.codefirst.SimpleThemeDecorator.xml | envsubst > $JENKINS_HOME/org.codefirst.SimpleThemeDecorator.xml
 
@@ -14,8 +14,20 @@
 
 # if `docker run` first argument start with `--` the user is passing jenkins launcher arguments
 if [[ $# -lt 1 ]] || [[ "$1" == "--"* ]]; then
-  eval "exec java $JAVA_OPTS -jar /usr/share/jenkins/jenkins.war $JENKINS_OPTS \"\$@\""
+
+  # read JAVA_OPTS and JENKINS_OPTS into arrays to avoid need for eval (and associated vulnerabilities)
+  java_opts_array=()
+  while IFS= read -r -d '' item; do
+    java_opts_array+=( "$item" )
+  done < <([[ $JAVA_OPTS ]] && xargs printf '%s\0' <<<"$JAVA_OPTS")
+
+  jenkins_opts_array=( )
+  while IFS= read -r -d '' item; do
+    jenkins_opts_array+=( "$item" )
+  done < <([[ $JENKINS_OPTS ]] && xargs printf '%s\0' <<<"$JENKINS_OPTS")
+
+  exec java "${java_opts_array[@]}" -jar /usr/share/jenkins/jenkins.war "${jenkins_opts_array[@]}" "$@"
 fi
 
-# As argument is not jenkins, assume user want to run his own process, for sample a `bash` shell to explore this image
+# As argument is not jenkins, assume user want to run his own process, for example a `bash` shell to explore this image
 exec "$@"
diff --git a/plugins.sh b/plugins.sh
index 027c382..9b08ddb 100755
--- a/plugins.sh
+++ b/plugins.sh
@@ -31,7 +31,7 @@
     exit 1
 else
     JENKINS_INPUT_JOB_LIST=$1
-    if [ ! -f $JENKINS_INPUT_JOB_LIST ]
+    if [ ! -f "$JENKINS_INPUT_JOB_LIST" ]
     then
         echo "ERROR File not found: $JENKINS_INPUT_JOB_LIST"
         exit 1
@@ -40,7 +40,7 @@
 
 # the war includes a # of plugins, to make the build efficient filter out
 # the plugins so we dont install 2x - there about 17!
-if [ -d $JENKINS_HOME ]
+if [ -d "$JENKINS_HOME" ]
 then
     TEMP_ALREADY_INSTALLED=$JENKINS_HOME/preinstalled.plugins.$$.txt
 else
@@ -49,33 +49,31 @@
 fi
 
 JENKINS_PLUGINS_DIR=/var/jenkins_home/plugins
-if [ -d $JENKINS_PLUGINS_DIR ]
+if [ -d "$JENKINS_PLUGINS_DIR" ]
 then
     echo "Analyzing: $JENKINS_PLUGINS_DIR"
-    for i in `ls -pd1 $JENKINS_PLUGINS_DIR/*|egrep '\/$'`
-    do
-        JENKINS_PLUGIN=`basename $i`
-        JENKINS_PLUGIN_VER=`egrep -i Plugin-Version "$i/META-INF/MANIFEST.MF"|cut -d\: -f2|sed 's/ //'`
+    for i in "$JENKINS_PLUGINS_DIR"/*/; do
+        JENKINS_PLUGIN=$(basename "$i")
+        JENKINS_PLUGIN_VER=$(egrep -i Plugin-Version "$i/META-INF/MANIFEST.MF"|cut -d: -f2|sed 's/ //')
         echo "$JENKINS_PLUGIN:$JENKINS_PLUGIN_VER"
-    done > $TEMP_ALREADY_INSTALLED
+    done >"$TEMP_ALREADY_INSTALLED"
 else
     JENKINS_WAR=/usr/share/jenkins/jenkins.war
-    if [ -f $JENKINS_WAR ]
+    if [ -f "$JENKINS_WAR" ]
     then
         echo "Analyzing war: $JENKINS_WAR"
         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/ //'`
+        while read -r i <&3; do
+            rm -fr "$TEMP_PLUGIN_DIR"
+            mkdir -p "$TEMP_PLUGIN_DIR"
+            PLUGIN=$(basename "$i"|cut -f1 -d'.')
+            (cd "$TEMP_PLUGIN_DIR" || exit; 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 > $TEMP_ALREADY_INSTALLED
-        rm -fr $TEMP_PLUGIN_DIR
+        done 3< <(jar tf "$JENKINS_WAR" | egrep '[^detached-]plugins.*\..pi' | sort) > "$TEMP_ALREADY_INSTALLED"
+        rm -fr "$TEMP_PLUGIN_DIR"
     else
-        rm -f $TEMP_ALREADY_INSTALLED
+        rm -f "$TEMP_ALREADY_INSTALLED"
         echo "ERROR file not found: $JENKINS_WAR"
         exit 1
     fi
@@ -84,30 +82,30 @@
 REF=/usr/share/jenkins/ref/plugins
 mkdir -p $REF
 COUNT_PLUGINS_INSTALLED=0
-while read spec || [ -n "$spec" ]; do
+while read -r spec || [ -n "$spec" ]; do
 
     plugin=(${spec//:/ });
     [[ ${plugin[0]} =~ ^# ]] && continue
-    [[ ${plugin[0]} =~ ^\s*$ ]] && continue
+    [[ ${plugin[0]} =~ ^[[:space:]]*$ ]] && continue
     [[ -z ${plugin[1]} ]] && plugin[1]="latest"
 
     if [ -z "$JENKINS_UC_DOWNLOAD" ]; then
       JENKINS_UC_DOWNLOAD=$JENKINS_UC/download
     fi
 
-    if ! grep -q "${plugin[0]}:${plugin[1]}" $TEMP_ALREADY_INSTALLED
+    if ! grep -q "${plugin[0]}:${plugin[1]}" "$TEMP_ALREADY_INSTALLED"
     then
         echo "Downloading ${plugin[0]}:${plugin[1]}"
-        curl --retry 3 --retry-delay 5 -sSL -f ${JENKINS_UC_DOWNLOAD}/plugins/${plugin[0]}/${plugin[1]}/${plugin[0]}.hpi -o $REF/${plugin[0]}.jpi
-        unzip -qqt $REF/${plugin[0]}.jpi
-        COUNT_PLUGINS_INSTALLED=`expr $COUNT_PLUGINS_INSTALLED + 1`
+        curl --retry 3 --retry-delay 5 -sSL -f "${JENKINS_UC_DOWNLOAD}/plugins/${plugin[0]}/${plugin[1]}/${plugin[0]}.hpi" -o "$REF/${plugin[0]}.jpi"
+        unzip -qqt "$REF/${plugin[0]}.jpi"
+        (( COUNT_PLUGINS_INSTALLED += 1 ))
     else
         echo "  ... skipping already installed:  ${plugin[0]}:${plugin[1]}"
     fi
-done  < $JENKINS_INPUT_JOB_LIST
+done  < "$JENKINS_INPUT_JOB_LIST"
 
 echo "---------------------------------------------------"
-if [ $COUNT_PLUGINS_INSTALLED -gt 0 ]
+if (( "$COUNT_PLUGINS_INSTALLED" > 0 ))
 then
     echo "INFO: Successfully installed $COUNT_PLUGINS_INSTALLED plugins."
 
@@ -122,5 +120,5 @@
 echo "---------------------------------------------------"
 
 #cleanup
-rm $TEMP_ALREADY_INSTALLED
+rm "$TEMP_ALREADY_INSTALLED"
 exit 0
diff --git a/publish.sh b/publish.sh
new file mode 100755
index 0000000..d435b17
--- /dev/null
+++ b/publish.sh
@@ -0,0 +1,211 @@
+#!/bin/bash -eu
+
+# Publish any versions of the docker image not yet pushed to jenkinsci/jenkins
+# Arguments:
+#   -n dry run, do not build or publish images
+#   -d debug
+
+set -o pipefail
+
+sort-versions() {
+    if [ "$(uname)" == 'Darwin' ]; then
+        gsort --version-sort
+    else
+        sort --version-sort
+    fi
+}
+
+# Try tagging with and without -f to support all versions of docker
+docker-tag() {
+    local from="jenkinsci/jenkins:$1"
+    local to="jenkinsci/jenkins:$2"
+    local out
+
+    docker pull "$from"
+    if out=$(docker tag -f "$from" "$to" 2>&1); then
+        echo "$out"
+    else
+        docker tag "$from" "$to"
+    fi
+}
+
+get-variant() {
+    local branch
+    branch=$(git show-ref | grep $(git rev-list -n 1 HEAD) | tail -1 | rev | cut -d/ -f 1 | rev)
+    if [ -z "$branch" ]; then
+        >&2 echo "Could not get the current branch name for commit, not in a branch?: $(git rev-list -n 1 HEAD)"
+        return 1
+    fi
+    case "$branch" in
+        master) echo "" ;;
+        *) echo "-${branch}" ;;
+    esac
+}
+
+login-token() {
+    # could use jq .token
+    curl -q -sSL https://auth.docker.io/token\?service\=registry.docker.io\&scope\=repository:jenkinsci/jenkins:pull | grep -o '"token":"[^"]*"' | cut -d':' -f 2 | xargs echo
+}
+
+is-published() {
+    get-manifest "$1" &> /dev/null
+}
+
+get-manifest() {
+    local tag=$1
+    local opts=""
+    if [ "$debug" = true ]; then
+        opts="-v"
+    fi
+    curl $opts -q -fsSL -H "Accept: application/vnd.docker.distribution.manifest.v2+json" -H "Authorization: Bearer $TOKEN" "https://index.docker.io/v2/jenkinsci/jenkins/manifests/$tag"
+}
+
+get-digest() {
+    local manifest
+    manifest=$(get-manifest "$1")
+    #get-manifest "$1" | jq .config.digest
+    if [ "$debug" = true ]; then
+        >&2 echo "DEBUG: Manifest for $1: $manifest"
+    fi
+    echo "$manifest" | grep -A 10 -o '"config".*' | grep digest | head -1 | cut -d':' -f 2,3 | xargs echo
+}
+
+get-latest-versions() {
+    curl -q -fsSL https://api.github.com/repos/jenkinsci/jenkins/tags?per_page=20 | grep '"name": "jenkins-' | egrep -o '[0-9]+(\.[0-9]+)+' | sort-versions | uniq
+}
+
+publish() {
+    local version=$1
+    local variant=$2
+    local tag="${version}${variant}"
+    local sha
+    local build_opts="--no-cache --pull"
+
+    if [ "$dry_run" = true ]; then
+        build_opts=""
+    else
+        build_opts="--no-cache --pull"
+    fi
+
+    local dir=war
+    # lts is in a different dir
+    if [[ "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
+        dir=war-stable
+    fi
+    sha=$(curl -q -fsSL "http://mirrors.jenkins.io/${dir}/${version}/jenkins.war.sha256" | cut -d' ' -f 1)
+
+    docker build --build-arg "JENKINS_VERSION=$version" \
+                 --build-arg "JENKINS_SHA=$sha" \
+                 --tag "jenkinsci/jenkins:${tag}" ${build_opts} .
+
+    if [ "$dry_run" = true ]; then
+        docker push "jenkinsci/jenkins:${tag}"
+    fi
+}
+
+tag-and-push() {
+    local source=$1
+    local target=$2
+    local digest_source
+    local digest_target
+
+    if [ "$debug" = true ]; then
+        >&2 echo "DEBUG: Getting digest for ${source}"
+    fi
+    # if tag doesn't exist yet, ie. dry run
+    if ! digest_source=$(get-digest "${source}"); then
+        echo "Unable to get digest for ${source} ${digest_source}"
+        digest_source=""
+    fi
+
+    if [ "$debug" = true ]; then
+        >&2 echo "DEBUG: Getting digest for ${target}"
+    fi
+    if ! digest_target=$(get-digest "${target}"); then
+        echo "Unable to get digest for ${target} ${digest_target}"
+        digest_target=""
+    fi
+
+    if [ "$digest_source" == "$digest_target" ] && [ -n "${digest_target}" ]; then
+        echo "Images ${source} [$digest_source] and ${target} [$digest_target] are already the same, not updating tags"
+    else
+        echo "Creating tag ${target} pointing to ${source}"
+        docker-tag "${source}" "${target}"
+        if [ ! "$dry_run" = true ]; then
+            echo "Pushing jenkinsci/jenkins:${target}"
+            docker push "jenkinsci/jenkins:${target}"
+        else
+            echo "Would push jenkinsci/jenkins:${target}"
+        fi
+    fi
+}
+
+publish-latest() {
+    local version=$1
+    local variant=$2
+
+    # push latest (for master) or the name of the branch (for other branches)
+    if [ -z "${variant}" ]; then
+        tag-and-push "${version}${variant}" "latest"
+    else
+        tag-and-push "${version}${variant}" "${variant#-}"
+    fi
+}
+
+publish-lts() {
+    local version=$1
+    local variant=$2
+    tag-and-push "${version}" "lts${variant}"
+}
+
+# Process arguments
+
+dry_run=false
+debug=false
+
+while [[ $# -gt 0 ]]; do
+    key="$1"
+    case $key in
+        -n)
+        dry_run=true
+        ;;
+        -d)
+        debug=true
+        ;;
+        *)
+        echo "Unknown option: $key"
+        return 1
+        ;;
+    esac
+    shift
+done
+
+
+if [ "$dry_run" = true ]; then
+    echo "Dry run, will not publish images"
+fi
+
+TOKEN=$(login-token)
+
+variant=$(get-variant)
+
+lts_version=""
+version=""
+for version in $(get-latest-versions); do
+    if is-published "$version$variant"; then
+        echo "Tag is already published: $version$variant"
+    else
+        echo "Publishing version: $version$variant"
+        publish "$version" "$variant"
+    fi
+
+    # Update lts tag
+    if [[ "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
+        lts_version="${version}"
+    fi
+done
+
+publish-latest "${version}" "${variant}"
+if [ -n "${lts_version}" ]; then
+    publish-lts "${lts_version}" "${variant}"
+fi
diff --git a/tests/functions.bats b/tests/functions.bats
index b89de8c..7a849eb 100644
--- a/tests/functions.bats
+++ b/tests/functions.bats
@@ -10,22 +10,22 @@
 
 @test "build image" {
   cd $BATS_TEST_DIRNAME/..
-  docker build -t $SUT_IMAGE .
+  docker_build -t $SUT_IMAGE .
 }
 
 @test "versionLT" {
-  run docker run -ti --rm $SUT_IMAGE bash -c "source /usr/local/bin/jenkins-support && versionLT 1.0 1.0"
+  run docker run --rm $SUT_IMAGE bash -c "source /usr/local/bin/jenkins-support && versionLT 1.0 1.0"
   assert_failure
-  run docker run -ti --rm $SUT_IMAGE bash -c "source /usr/local/bin/jenkins-support && versionLT 1.0 1.1"
+  run docker run --rm $SUT_IMAGE bash -c "source /usr/local/bin/jenkins-support && versionLT 1.0 1.1"
   assert_success
-  run docker run -ti --rm $SUT_IMAGE bash -c "source /usr/local/bin/jenkins-support && versionLT 1.1 1.0"
+  run docker run --rm $SUT_IMAGE bash -c "source /usr/local/bin/jenkins-support && versionLT 1.1 1.0"
   assert_failure
-  run docker run -ti --rm $SUT_IMAGE bash -c "source /usr/local/bin/jenkins-support && versionLT 1.0-beta-1 1.0"
+  run docker run --rm $SUT_IMAGE bash -c "source /usr/local/bin/jenkins-support && versionLT 1.0-beta-1 1.0"
   assert_success
-  run docker run -ti --rm $SUT_IMAGE bash -c "source /usr/local/bin/jenkins-support && versionLT 1.0 1.0-beta-1"
+  run docker run --rm $SUT_IMAGE bash -c "source /usr/local/bin/jenkins-support && versionLT 1.0 1.0-beta-1"
   assert_failure
-  run docker run -ti --rm $SUT_IMAGE bash -c "source /usr/local/bin/jenkins-support && versionLT 1.0-alpha-1 1.0-beta-1"
+  run docker run --rm $SUT_IMAGE bash -c "source /usr/local/bin/jenkins-support && versionLT 1.0-alpha-1 1.0-beta-1"
   assert_success
-  run docker run -ti --rm $SUT_IMAGE bash -c "source /usr/local/bin/jenkins-support && versionLT 1.0-beta-1 1.0-alpha-1"
+  run docker run --rm $SUT_IMAGE bash -c "source /usr/local/bin/jenkins-support && versionLT 1.0-beta-1 1.0-alpha-1"
   assert_failure
 }
diff --git a/tests/install-plugins.bats b/tests/install-plugins.bats
index e89758c..d795f23 100644
--- a/tests/install-plugins.bats
+++ b/tests/install-plugins.bats
@@ -8,14 +8,14 @@
 
 @test "build image" {
   cd $BATS_TEST_DIRNAME/..
-  docker build -t $SUT_IMAGE .
+  docker_build -t $SUT_IMAGE .
 }
 
 @test "plugins are installed with plugins.sh" {
   run docker build -t $SUT_IMAGE-plugins $BATS_TEST_DIRNAME/plugins
   assert_success
   # replace DOS line endings \r\n
-  run bash -c "docker run -ti --rm $SUT_IMAGE-plugins ls --color=never -1 /var/jenkins_home/plugins | tr -d '\r'"
+  run bash -c "docker run --rm $SUT_IMAGE-plugins ls --color=never -1 /var/jenkins_home/plugins | tr -d '\r'"
   assert_success
   assert_line 'maven-plugin.jpi'
   assert_line 'maven-plugin.jpi.pinned'
@@ -28,7 +28,7 @@
   assert_success
   refute_line --partial 'Skipping already bundled dependency'
   # replace DOS line endings \r\n
-  run bash -c "docker run -ti --rm $SUT_IMAGE-install-plugins ls --color=never -1 /var/jenkins_home/plugins | tr -d '\r'"
+  run bash -c "docker run --rm $SUT_IMAGE-install-plugins ls --color=never -1 /var/jenkins_home/plugins | tr -d '\r'"
   assert_success
   assert_line 'maven-plugin.jpi'
   assert_line 'maven-plugin.jpi.pinned'
@@ -54,7 +54,7 @@
   assert_line "Using provided plugin: ant"
   refute_line --partial 'Skipping already bundled dependency'
   # replace DOS line endings \r\n
-  run bash -c "docker run -ti --rm $SUT_IMAGE-install-plugins-update unzip -p /var/jenkins_home/plugins/maven-plugin.jpi META-INF/MANIFEST.MF | tr -d '\r'"
+  run bash -c "docker run --rm $SUT_IMAGE-install-plugins-update unzip -p /var/jenkins_home/plugins/maven-plugin.jpi META-INF/MANIFEST.MF | tr -d '\r'"
   assert_success
   assert_line 'Plugin-Version: 2.13'
 }
@@ -64,25 +64,26 @@
   run docker build -t $SUT_IMAGE-install-plugins $BATS_TEST_DIRNAME/install-plugins
   assert_success
   local work; work="$BATS_TEST_DIRNAME/upgrade-plugins/work"
+  mkdir -p $work
   # Image contains maven-plugin 2.7.1 and ant-plugin 1.3
-  run bash -c "docker run -ti -v $work:/var/jenkins_home --rm $SUT_IMAGE-install-plugins true"
+  run bash -c "docker run -u $UID -v $work:/var/jenkins_home --rm $SUT_IMAGE-install-plugins true"
   assert_success
-  run bash -c "unzip -p $work/plugins/maven-plugin.jpi META-INF/MANIFEST.MF | tr -d '\r'"
+  run unzip_manifest maven-plugin.jpi $work
   assert_line 'Plugin-Version: 2.7.1'
-  run bash -c "unzip -p $work/plugins/ant.jpi META-INF/MANIFEST.MF | tr -d '\r'"
+  run unzip_manifest ant.jpi $work
   assert_line 'Plugin-Version: 1.3'
 
   # Upgrade to new image with different plugins
   run docker build -t $SUT_IMAGE-upgrade-plugins $BATS_TEST_DIRNAME/upgrade-plugins
   assert_success
   # Images contains maven-plugin 2.13 and ant-plugin 1.2
-  run bash -c "docker run -ti -v $work:/var/jenkins_home --rm $SUT_IMAGE-upgrade-plugins true"
+  run bash -c "docker run -u $UID -v $work:/var/jenkins_home --rm $SUT_IMAGE-upgrade-plugins true"
   assert_success
-  run bash -c "unzip -p $work/plugins/maven-plugin.jpi META-INF/MANIFEST.MF | tr -d '\r'"
+  run unzip_manifest maven-plugin.jpi $work
   assert_success
   # Should be updated
   assert_line 'Plugin-Version: 2.13'
-  run bash -c "unzip -p $work/plugins/ant.jpi META-INF/MANIFEST.MF | tr -d '\r'"
+  run unzip_manifest ant.jpi $work
   # 1.2 is older than the existing 1.3, so keep 1.3
   assert_line 'Plugin-Version: 1.3'
 }
@@ -95,17 +96,18 @@
   run docker build -t $SUT_IMAGE-install-plugins $BATS_TEST_DIRNAME/install-plugins
   assert_success
   local work; work="$BATS_TEST_DIRNAME/upgrade-plugins/work"
+  mkdir -p $work
   # Image contains maven-plugin 2.7.1 and ant-plugin 1.3
-  run bash -c "docker run -ti -v $work:/var/jenkins_home --rm $SUT_IMAGE-install-plugins curl --connect-timeout 5 --retry 5 --retry-delay 0 --retry-max-time 60 -s -f -L https://updates.jenkins.io/download/plugins/maven-plugin/2.12.1/maven-plugin.hpi -o /var/jenkins_home/plugins/maven-plugin.jpi"
+  run bash -c "docker run -u $UID -v $work:/var/jenkins_home --rm $SUT_IMAGE-install-plugins curl --connect-timeout 20 --retry 5 --retry-delay 0 --retry-max-time 60 -s -f -L https://updates.jenkins.io/download/plugins/maven-plugin/2.12.1/maven-plugin.hpi -o /var/jenkins_home/plugins/maven-plugin.jpi"
   assert_success
-  run bash -c "unzip -p $work/plugins/maven-plugin.jpi META-INF/MANIFEST.MF | tr -d '\r'"
+  run unzip_manifest maven-plugin.jpi $work
   assert_line 'Plugin-Version: 2.12.1'
   run docker build -t $SUT_IMAGE-upgrade-plugins $BATS_TEST_DIRNAME/upgrade-plugins
   assert_success
   # Images contains maven-plugin 2.13 and ant-plugin 1.2
-  run bash -c "docker run -ti -v $work:/var/jenkins_home --rm $SUT_IMAGE-upgrade-plugins true"
+  run bash -c "docker run -u $UID -v $work:/var/jenkins_home --rm $SUT_IMAGE-upgrade-plugins true"
   assert_success
-  run bash -c "unzip -p $work/plugins/maven-plugin.jpi META-INF/MANIFEST.MF | tr -d '\r'"
+  run unzip_manifest maven-plugin.jpi $work
   assert_success
   # Shouldn't be updated
   refute_line 'Plugin-Version: 2.13'
diff --git a/tests/runtime.bats b/tests/runtime.bats
index 6b3da88..fe6763e 100644
--- a/tests/runtime.bats
+++ b/tests/runtime.bats
@@ -9,7 +9,7 @@
 
 @test "build image" {
   cd $BATS_TEST_DIRNAME/..
-  docker build -t $SUT_IMAGE .
+  docker_build -t $SUT_IMAGE .
 }
 
 @test "clean test containers" {
@@ -20,14 +20,14 @@
   # running --help --version should return the version, not the help
   local version=$(grep 'ENV JENKINS_VERSION' Dockerfile | sed -e 's/.*:-\(.*\)}/\1/')
   # need the last line of output
-  assert "${version}" docker run --rm -ti -e JENKINS_OPTS="--help --version" --name $SUT_CONTAINER -P $SUT_IMAGE | tail -n 1
+  assert "${version}" docker run --rm -e JENKINS_OPTS="--help --version" --name $SUT_CONTAINER -P $SUT_IMAGE | tail -n 1
 }
 
 @test "test jenkins arguments" {
   # running --help --version should return the version, not the help
   local version=$(grep 'ENV JENKINS_VERSION' Dockerfile | sed -e 's/.*:-\(.*\)}/\1/')
   # need the last line of output
-  assert "${version}" docker run --rm -ti --name $SUT_CONTAINER -P $SUT_IMAGE --help --version | tail -n 1
+  assert "${version}" docker run --rm --name $SUT_CONTAINER -P $SUT_IMAGE --help --version | tail -n 1
 }
 
 @test "create test container" {
diff --git a/tests/test_helpers.bash b/tests/test_helpers.bash
index ed1462c..eb67f45 100644
--- a/tests/test_helpers.bash
+++ b/tests/test_helpers.bash
@@ -3,7 +3,6 @@
 # check dependencies
 (
     type docker &>/dev/null || ( echo "docker is not available"; exit 1 )
-    type unzip &>/dev/null || ( echo "unzip is not available"; exit 1 )
     type curl &>/dev/null || ( echo "curl is not available"; exit 1 )
 )>&2
 
@@ -41,6 +40,14 @@
     false
 }
 
+function docker_build {
+    if [ -n "$JENKINS_VERSION" ]; then
+        docker build --build-arg JENKINS_VERSION=$JENKINS_VERSION --build-arg JENKINS_SHA=$JENKINS_SHA "$@"
+    else
+        docker build "$@"
+    fi
+}
+
 function get_jenkins_url {
     if [ -z "${DOCKER_HOST}" ]; then
         DOCKER_IP=localhost
@@ -69,3 +76,9 @@
     docker kill "$1" &>/dev/null ||:
     docker rm -fv "$1" &>/dev/null ||:
 }
+
+function unzip_manifest {
+    local plugin=$1
+    local work=$2
+    bash -c "docker run --rm -v $work:/var/jenkins_home --entrypoint unzip $SUT_IMAGE -p /var/jenkins_home/plugins/$plugin META-INF/MANIFEST.MF | tr -d '\r'"
+}
diff --git a/update-official-library.sh b/update-official-library.sh
new file mode 100755
index 0000000..07e3b1f
--- /dev/null
+++ b/update-official-library.sh
@@ -0,0 +1,36 @@
+#!/bin/bash -eu
+
+# Generate the Docker official-images file
+
+sha() {
+    local branch=$1
+    git rev-parse $branch
+}
+
+version_from_dockerfile() {
+    local branch=$1
+    git show $branch:Dockerfile | grep JENKINS_VERSION: | sed -e 's/.*:-\(.*\)}/\1/'
+}
+
+master_sha=$(sha master)
+alpine_sha=$(sha alpine)
+
+master_version=$(version_from_dockerfile master)
+alpine_version=$(version_from_dockerfile alpine)
+
+if ! [ "$master_version" == "$alpine_version" ]; then
+    echo "Master version '$master_version' does not match alpine version '$alpine_version'"
+    exit 1
+fi
+
+cat << EOF > ../official-images/library/jenkins
+# maintainer: Nicolas De Loof <nicolas.deloof@gmail.com> (@ndeloof)
+# maintainer: Michael Neale <mneale@cloudbees.com> (@michaelneale)
+# maintainer: Carlos Sanchez <csanchez@cloudbees.com> (@carlossg)
+
+latest: git://github.com/jenkinsci/jenkins-ci.org-docker@$master_sha
+$master_version: git://github.com/jenkinsci/jenkins-ci.org-docker@$master_sha
+
+alpine: git://github.com/jenkinsci/jenkins-ci.org-docker@$alpine_sha
+$alpine_version-alpine: git://github.com/jenkinsci/jenkins-ci.org-docker@$alpine_sha
+EOF
diff --git a/weekly.sh b/weekly.sh
deleted file mode 100755
index 155a7d5..0000000
--- a/weekly.sh
+++ /dev/null
@@ -1,22 +0,0 @@
-#!/bin/bash
-
-set -e
-set -x
-
-JENKINS_VERSION=`curl -sq https://api.github.com/repos/jenkinsci/jenkins/tags | grep '"name":' | grep -o '[0-9]\.[0-9]*'  | uniq | sort --version-sort | tail -1`
-echo $JENKINS_VERSION
-
-JENKINS_SHA=`curl http://repo.jenkins-ci.org/simple/releases/org/jenkins-ci/main/jenkins-war/${JENKINS_VERSION}/jenkins-war-${JENKINS_VERSION}.war.sha1`
-echo $JENKINS_SHA
-
-docker build --build-arg JENKINS_VERSION=$JENKINS_VERSION \
-             --build-arg JENKINS_SHA=$JENKINS_SHA \
-             --no-cache --pull \
-             --tag jenkinsci/jenkins:$JENKINS_VERSION .
-
-docker tag -f jenkinsci/jenkins:$JENKINS_VERSION jenkinsci/jenkins:latest
-
-docker push jenkinsci/jenkins:$JENKINS_VERSION
-docker push jenkinsci/jenkins:latest
-
-