diff --git a/Jenkinsfile b/Jenkinsfile
new file mode 100644
index 00000000..3dda1381
--- /dev/null
+++ b/Jenkinsfile
@@ -0,0 +1,128 @@
+def p = [:]
+node {
+ checkout scm
+ p = readProperties interpolate: true, file: 'ci/pipeline.properties'
+}
+
+pipeline {
+ agent none
+
+ triggers {
+ pollSCM 'H/10 * * * *'
+ }
+
+ options {
+ disableConcurrentBuilds()
+ buildDiscarder(logRotator(numToKeepStr: '14'))
+ }
+
+ stages {
+ stage("Docker images") {
+ parallel {
+ stage('Publish JDK 8 + Vault Docker image') {
+ when {
+ anyOf {
+ changeset "ci/openjdk8-vault/Dockerfile"
+ changeset "src/test/bash/install_vault.sh"
+ changeset "ci/pipeline.properties"
+ }
+ }
+ agent { label 'data' }
+ options { timeout(time: 20, unit: 'MINUTES') }
+
+ steps {
+ script {
+ def image = docker.build("${p['docker.build.image.name']}", "--build-arg BASE=${p['docker.java.main.image']} --build-arg VAULT=${p['docker.vault.version']} -f ci/openjdk8-vault/Dockerfile .")
+ docker.withRegistry(p['docker.registry'], p['docker.credentials']) {
+ image.push()
+ }
+ }
+ }
+ }
+ }
+ }
+
+ stage("test: baseline (Java 8)") {
+ when {
+ beforeAgent(true)
+ anyOf {
+ branch(pattern: "main|(\\d\\.\\d\\.x)", comparator: "REGEXP")
+ not { triggeredBy 'UpstreamCause' }
+ }
+ }
+ agent {
+ label 'data'
+ }
+ options { timeout(time: 30, unit: 'MINUTES') }
+ environment {
+ ARTIFACTORY = credentials("${p['artifactory.credentials']}")
+ }
+ steps {
+ script {
+ docker.image("${p['docker.image']}").inside(p['docker.java.inside.basic']) {
+ sh 'src/test/bash/create_certificates.sh'
+ sh '/opt/vault/vault server -config=$(pwd)/src/test/bash/vault.conf &'
+ sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw -s settings.xml clean dependency:list verify -Dsort -U -B'
+ }
+ }
+ }
+ }
+
+ stage('Deploy') {
+ when {
+ beforeAgent(true)
+ anyOf {
+ branch(pattern: "main|(\\d\\.\\d\\.x)|v(\\d\\.\\d\\.d)|", comparator: "REGEXP")
+ not { triggeredBy 'UpstreamCause' }
+ }
+ }
+ agent {
+ docker {
+ image "${p['docker.image']}"
+ args "${p['docker.java.inside.basic']}"
+ }
+ }
+ options { timeout(time: 20, unit: 'MINUTES') }
+
+ environment {
+ ARTIFACTORY = credentials("${p['artifactory.credentials']}")
+ SONATYPE = credentials('oss-login')
+ KEYRING = credentials('spring-signing-secring.gpg')
+ PASSPHRASE = credentials('spring-gpg-passphrase')
+ }
+
+ steps {
+ script {
+ PROJECT_VERSION = sh(
+ script: "ci/version.sh",
+ returnStdout: true
+ ).trim()
+
+ RELEASE_TYPE = 'snapshot'
+
+ if (PROJECT_VERSION.matches(/.*-RC[0-9]+$/) || PROJECT_VERSION.matches(/.*-M[0-9]+$/)) {
+ RELEASE_TYPE = "milestone"
+ } else if (PROJECT_VERSION.endsWith('SNAPSHOT')) {
+ RELEASE_TYPE = 'snapshot'
+ } else if (PROJECT_VERSION.matches(/.*\.[0-9]+$/)) {
+ RELEASE_TYPE = 'release'
+ }
+
+ sh "ci/deploy-${RELEASE_TYPE}.sh"
+ }
+ }
+ }
+ }
+
+ post {
+ changed {
+ script {
+ emailext(
+ subject: "[${currentBuild.fullDisplayName}] ${currentBuild.currentResult}",
+ mimeType: 'text/html',
+ recipientProviders: [[$class: 'CulpritsRecipientProvider'], [$class: 'RequesterRecipientProvider']],
+ body: "${currentBuild.fullDisplayName} is reported as ${currentBuild.currentResult}")
+ }
+ }
+ }
+}
diff --git a/ci/deploy-milestone.sh b/ci/deploy-milestone.sh
new file mode 100644
index 00000000..bd9da866
--- /dev/null
+++ b/ci/deploy-milestone.sh
@@ -0,0 +1,19 @@
+#!/bin/bash
+
+set -euo pipefail
+
+MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw -s settings.xml -Pci,milestone,artifactory,release \
+ -Dartifactory.server=https://repo.spring.io \
+ -Dartifactory.username=${ARTIFACTORY_USR} \
+ -Dartifactory.password=${ARTIFACTORY_PSW} \
+ -Dartifactory.staging-repository=libs-milestone-local \
+ -Dartifactory.build-name=spring-vault \
+ -Dartifactory.build-number=${BUILD_NUMBER} \
+ clean deploy -U -B
+
+MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw -s settings.xml -Pdistribute \
+ -Dartifactory.server=https://repo.spring.io \
+ -Dartifactory.username=${ARTIFACTORY_USR} \
+ -Dartifactory.password=${ARTIFACTORY_PSW} \
+ -Dartifactory.staging-repository=temp-private-local \
+ clean deploy -U -B
diff --git a/ci/deploy-release.sh b/ci/deploy-release.sh
new file mode 100644
index 00000000..dc07ef70
--- /dev/null
+++ b/ci/deploy-release.sh
@@ -0,0 +1,22 @@
+#!/bin/bash
+
+set -euo pipefail
+
+GNUPGHOME=/tmp/gpghome
+export GNUPGHOME
+
+mkdir $GNUPGHOME
+cp $KEYRING $GNUPGHOME
+
+MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw -s settings.xml -Pci,central,release \
+ -Dgpg.passphrase=${PASSPHRASE} \
+ -Dgpg.secretKeyring=${GNUPGHOME}/secring.gpg \
+ -DstagingDescription="Releasing Spring Vault" \
+ clean deploy -U -B
+
+MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw -s settings.xml -Pdistribute \
+ -Dartifactory.server=https://repo.spring.io \
+ -Dartifactory.username=${ARTIFACTORY_USR} \
+ -Dartifactory.password=${ARTIFACTORY_PSW} \
+ -Dartifactory.staging-repository=temp-private-local \
+ clean deploy -U -B
diff --git a/ci/deploy-snapshot.sh b/ci/deploy-snapshot.sh
new file mode 100644
index 00000000..5820fb94
--- /dev/null
+++ b/ci/deploy-snapshot.sh
@@ -0,0 +1,12 @@
+#!/bin/bash
+
+set -euo pipefail
+MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw -s settings.xml -Pci,snapshot,artifactory \
+ -Dartifactory.server=https://repo.spring.io \
+ -Dartifactory.username=${ARTIFACTORY_USR} \
+ -Dartifactory.password=${ARTIFACTORY_PSW} \
+ -Dartifactory.staging-repository=libs-snapshot-local \
+ -Dartifactory.build-name=spring-vault \
+ -Dartifactory.build-number=${BUILD_NUMBER} \
+ -Dmaven.test.skip=true \
+ clean deploy -U -B
diff --git a/ci/openjdk8-vault/Dockerfile b/ci/openjdk8-vault/Dockerfile
new file mode 100644
index 00000000..9b6cfaa8
--- /dev/null
+++ b/ci/openjdk8-vault/Dockerfile
@@ -0,0 +1,24 @@
+ARG BASE
+FROM ${BASE}
+# Any ARG statements before FROM are cleared.
+
+ENV TZ=Etc/UTC
+ENV DEBIAN_FRONTEND=noninteractive
+
+# Copy Vault's install file into the container
+
+COPY ./src/test/bash/install_vault.sh /opt
+
+ARG VAULT
+
+RUN set -eux && \
+ sed -i -e 's/archive.ubuntu.com/mirror.one.com/g' /etc/apt/sources.list && \
+ sed -i -e 's/security.ubuntu.com/mirror.one.com/g' /etc/apt/sources.list && \
+ sed -i -e 's/http/https/g' /etc/apt/sources.list && \
+ apt-get update && apt-get install -y apt-transport-https apt-utils gnupg2 wget unzip && \
+ echo ${TZ} > /etc/timezone && \
+ rm -rf /var/lib/apt/lists/*
+
+RUN set -eux && \
+ cd /opt && \
+ /opt/install_vault.sh -v ${VAULT}
diff --git a/ci/pipeline.properties b/ci/pipeline.properties
new file mode 100644
index 00000000..c8d23768
--- /dev/null
+++ b/ci/pipeline.properties
@@ -0,0 +1,21 @@
+# Java versions
+java.main.tag=8u362-b09-jdk
+
+# Docker container images - standard
+docker.java.main.image=eclipse-temurin:${java.main.tag}
+
+# Main docker image name using during the CI build
+docker.build.image.name=springci/spring-vault-openjdk8-vault:${java.main.tag}-${docker.vault.version}
+docker.image=harbor-repo.vmware.com/dockerhub-proxy-cache/${docker.build.image.name}
+
+# Supported versions of Vault
+docker.vault.version=1.6.1
+
+# Docker environment settings
+docker.java.inside.basic=-v $HOME:/tmp/jenkins-home
+docker.java.inside.docker=-u root -v /var/run/docker.sock:/var/run/docker.sock -v /usr/bin/docker:/usr/bin/docker -v $HOME:/tmp/jenkins-home
+
+# Credentials
+docker.registry=
+docker.credentials=hub.docker.com-springbuildmaster
+artifactory.credentials=02bd1690-b54f-4c9f-819d-a77cb7a9822c
diff --git a/ci/version.sh b/ci/version.sh
new file mode 100644
index 00000000..71b2f024
--- /dev/null
+++ b/ci/version.sh
@@ -0,0 +1,13 @@
+#!/bin/bash
+
+set -euo pipefail
+
+RAW_VERSION=`MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw \
+ org.apache.maven.plugins:maven-help-plugin:3.2.0:evaluate \
+ -Dexpression=project.version -q -DforceStdout`
+
+# Split things up
+VERSION_PARTS=($RAW_VERSION)
+
+# Grab the last part, which is the actual version number.
+echo ${VERSION_PARTS[${#VERSION_PARTS[@]}-1]}
diff --git a/settings.xml b/settings.xml
new file mode 100644
index 00000000..2700e663
--- /dev/null
+++ b/settings.xml
@@ -0,0 +1,34 @@
+
+
+
+
+ spring-plugins-release
+ ${env.ARTIFACTORY_USR}
+ ${env.ARTIFACTORY_PSW}
+
+
+ spring-libs-snapshot
+ ${env.ARTIFACTORY_USR}
+ ${env.ARTIFACTORY_PSW}
+
+
+ spring-libs-milestone
+ ${env.ARTIFACTORY_USR}
+ ${env.ARTIFACTORY_PSW}
+
+
+ spring-libs-release
+ ${env.ARTIFACTORY_USR}
+ ${env.ARTIFACTORY_PSW}
+
+
+ sonatype-nexus-staging
+ ${env.SONATYPE_USR}
+ ${env.SONATYPE_PSW}
+
+
+
+