├── gradle.properties ├── NOTICE ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── .gitignore ├── tests ├── docker │ └── run ├── random_port.sh ├── api_test.py ├── rpm │ ├── validate.sh │ ├── run │ └── goss.yaml ├── deb │ ├── validate.sh │ ├── run │ └── goss.yaml ├── test.sh └── job_test.py ├── .travis.yml ├── dependencies.gradle ├── docker-compose.yml ├── shared.gradle ├── packaging ├── jenkins.logrotate.in ├── preInstall.sh.in ├── daemon │ ├── jenkins.service.in │ ├── jenkins.config.in │ └── run.sh.in ├── jenkins.cron.sh.in ├── share │ ├── dailycommit.sh.in │ └── gitignore.in ├── README.md ├── postUninstall.sh.in ├── preUninstall.sh.in ├── postInstall.sh.in └── rpm │ └── jenkins.init.in ├── scripts ├── upgrade │ ├── plugins_gav.sh │ ├── wait_for_upgrade_to_complete.sh │ ├── env.sh │ ├── jenkins_needs_restart.sh │ ├── listShortNameVersion.groovy │ ├── setup_environment.sh │ ├── needsRestart.groovy │ ├── getPinnedPluginList.groovy │ ├── show_plugin_artifacts.sh │ ├── isUpgradeInProgress.groovy │ ├── installMinimalPlugins.groovy │ ├── generateSedExpr.groovy │ ├── upgradeJenkinsOnly.groovy │ ├── minimal-upgrade.sh │ ├── upgradeJenkinsAndPlugins.groovy │ ├── refresh_plugins.sh │ ├── plugin_refresh.md │ ├── upgrade_build_gradle.sh │ ├── README.md │ └── remote_dependencies.sh ├── docker-env.sh ├── vagrant-up.sh ├── security-disable-agent-controller.groovy ├── jenkins_call.sh ├── jenkins_wait_job.sh ├── configure-markup-formatter.groovy ├── gradle-getplugins.groovy ├── vagrant-env.sh ├── configure-disable-usage-stats.groovy ├── configure-csrf-protection.groovy ├── configure-warning-disable-controller-agent-alert.groovy ├── buildRelease.sh ├── configure-jnlp-agent-protocols.groovy ├── configure-job-dsl-security.groovy ├── configure-flow-durability.groovy ├── console-skip-2.0-wizard.groovy ├── configure-slack.groovy ├── console-needs-restart.groovy ├── configure-lockable-resources.groovy ├── init.groovy.d │ ├── disable-all-update-sites.groovy │ └── disable-jenkins-cli.groovy ├── configure-github-branch-source-plugin.groovy ├── set-content-security-policy.groovy ├── configure-github-oauth.groovy ├── create-job.groovy ├── configure-global-jenkinsfile.groovy ├── configure-primary-view.groovy ├── configure-script-security.groovy ├── credentials-jenkins-agent.groovy ├── configure-job-restrictions-controller.groovy ├── configure-ldap-settings.groovy ├── admin-script-approval.groovy ├── create-view.groovy ├── configure-grape-ivy-xml.groovy ├── common.sh ├── bootstrap.groovy ├── configure-jenkins-settings.groovy ├── uploadRelease.sh ├── configure-github-plugin.groovy └── configure-matrix-authorization-strategy.groovy ├── env.sh ├── Dockerfile ├── Vagrantfile ├── init.sh ├── 3rd_party └── packaging-scripts ├── USAGE.md ├── RELEASE.md ├── jenkins.gradle ├── Makefile ├── variables.gradle ├── gradlew.bat ├── MIGRATION.md ├── jenkins_bootstrap.sh └── gradlew /gradle.properties: -------------------------------------------------------------------------------- 1 | bootstrapHome=. 2 | version=2.462.3.1 3 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015-2023 Sam Gleske - https://github.com/samrocketman/jenkins-bootstrap-shared 2 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/samrocketman/jenkins-bootstrap-shared/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /*.bak 2 | /.gradle 3 | /.vagrant 4 | /build 5 | /console.log 6 | /jenkins-cli.jar 7 | /jenkins.pid 8 | /jenkins.war* 9 | /my_jenkins_home 10 | /plugins 11 | /ssh_config 12 | -------------------------------------------------------------------------------- /tests/docker/run: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Created by Sam Gleske - https://github.com/samrocketman 3 | 4 | # DESCRIPTION 5 | # A simple script which tests building the docker container. 6 | 7 | docker build -t jenkins . 8 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | sudo: required 3 | services: 4 | - docker 5 | branches: 6 | only: 7 | - main 8 | env: 9 | - PACKAGE=deb 10 | - PACKAGE=rpm 11 | - PACKAGE=docker 12 | script: 13 | - ./gradlew packages 14 | - ./tests/"${PACKAGE}"/run 15 | -------------------------------------------------------------------------------- /dependencies.gradle: -------------------------------------------------------------------------------- 1 | dependencies { 2 | //get Jenkins 3 | getjenkins 'org.jenkins-ci.main:jenkins-war:2.462.3@war' 4 | 5 | //custom plugins (if any) provided by custom-plugins.txt; format: G:A:V@hpi or G:A:V@jpi 6 | 7 | //getplugins configurations go here... 8 | } 9 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '2.2' 2 | volumes: 3 | jenkins-data: 4 | services: 5 | jenkins: 6 | init: true 7 | build: 8 | context: . 9 | dockerfile: Dockerfile 10 | ports: 11 | - 8080:8080 12 | volumes: 13 | - 'jenkins-data:/var/lib/jenkins' 14 | -------------------------------------------------------------------------------- /tests/random_port.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #Created by Sam Gleske 3 | #Chooses a random open port from 10000-20000 4 | 5 | while true; do 6 | PORT="$(( ($RANDOM % 10000) + 10000 ))" 7 | if ! timeout 5 nc -z localhost $PORT &> /dev/null; then 8 | echo $PORT 9 | break 10 | fi 11 | done 12 | -------------------------------------------------------------------------------- /shared.gradle: -------------------------------------------------------------------------------- 1 | //gradle plugins 2 | apply plugin: 'base' 3 | apply plugin: 'maven-publish' 4 | 5 | //default 6 | defaultTasks 'getjenkins', 'getplugins' 7 | 8 | apply from: 'variables.gradle' 9 | apply from: "${bootstrapHome}/jenkins.gradle" 10 | apply from: "${bootstrapHome}/packaging/packaging.gradle" 11 | -------------------------------------------------------------------------------- /packaging/jenkins.logrotate.in: -------------------------------------------------------------------------------- 1 | /var/log/@@ARTIFACTNAME@@/@@ARTIFACTNAME@@.log /var/log/@@ARTIFACTNAME@@/access_log { 2 | weekly 3 | rotate 52 4 | maxage 365 5 | compress 6 | delaycompress 7 | dateext 8 | size=+4096k 9 | notifempty 10 | missingok 11 | create 644 12 | copytruncate 13 | } 14 | -------------------------------------------------------------------------------- /scripts/upgrade/plugins_gav.sh: -------------------------------------------------------------------------------- 1 | echo 'Downloading plugin GAV parameters from Jenkins community...' >&2 2 | curl --progress-bar -L "${JENKINS_UPDATE_CENTER:-http://updates.jenkins-ci.org/update-center.json}" | \ 3 | awk '$1 ~ /^{/' | \ 4 | python -c ' 5 | import sys,json 6 | j=json.load(sys.stdin) 7 | for key in j["plugins"]: 8 | print(j["plugins"][key]["gav"]) 9 | ' 10 | -------------------------------------------------------------------------------- /packaging/preInstall.sh.in: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | #Add potentially missing user groups 5 | if ! grep -- '@@USER@@' /etc/group > /dev/null; then 6 | echo '`jenkins` group missing; creating...' 7 | groupadd -r @@USER@@ 8 | fi 9 | if ! grep -- '@@USER@@' /etc/passwd > /dev/null; then 10 | echo '`jenkins` user missing; creating...' 11 | useradd -r -g @@USER@@ -s /sbin/nologin -d "@@HOME@@" -c "@@SUMMARY@@" @@USER@@ 12 | fi 13 | 14 | exit 0 15 | -------------------------------------------------------------------------------- /tests/api_test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | #Created by Sam Gleske 3 | #Meant to read from stdin a JSON blob from Jenkins master root 4 | import json 5 | import sys 6 | response = json.load(sys.stdin) 7 | jobs = map(lambda x: x['name'], response['jobs']) 8 | views = map(lambda x: x['name'], response['views']) 9 | assert '_jervis_generator' in jobs 10 | assert 'GitHub Organizations' in views 11 | assert 'Welcome' in views 12 | assert 'Welcome' == response['primaryView']['name'] 13 | -------------------------------------------------------------------------------- /packaging/daemon/jenkins.service.in: -------------------------------------------------------------------------------- 1 | # /lib/systemd/system/jenkins.service 2 | [Unit] 3 | Description=@@SUMMARY@@ 4 | After=network.service 5 | Requires=network.service 6 | [Service] 7 | TimeoutStartSec=0 8 | Restart=on-failure 9 | Environment="JENKINS_HOME=@@HOME@@" 10 | EnvironmentFile=-/etc/default/jenkins 11 | EnvironmentFile=-/etc/sysconfig/jenkins 12 | WorkingDirectory=${JENKINS_HOME} 13 | User=@@USER@@ 14 | ExecStart=@@PREFIX@@/lib/@@ARTIFACTNAME@@/distrib/daemon/run.sh 15 | [Install] 16 | WantedBy=multi-user.target 17 | -------------------------------------------------------------------------------- /scripts/upgrade/wait_for_upgrade_to_complete.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #Created by Sam Gleske 3 | #Sat 19 Feb 2022 03:11:54 PM EST 4 | #Ubuntu 20.04.3 LTS 5 | #Linux 5.13.0-30-generic x86_64 6 | #GNU bash, version 5.0.17(1)-release (x86_64-pc-linux-gnu) 7 | 8 | # Waits for an upgrade to complete 9 | echo -n 'Waiting for Jenkins upgrade to complete.' 10 | while [ "$("${SCRIPT_LIBRARY_PATH}"/jenkins_call.sh "${SCRIPT_LIBRARY_PATH}"/upgrade/isUpgradeInProgress.groovy)" = 'true' ]; do 11 | sleep 3 12 | echo -n '.' 13 | done 14 | echo 15 | -------------------------------------------------------------------------------- /env.sh: -------------------------------------------------------------------------------- 1 | #sane defaults 2 | export JENKINS_WAR="${JENKINS_WAR:-jenkins.war}" 3 | 4 | export JENKINS_HOME="${JENKINS_HOME:-../my_jenkins_home}" 5 | export JENKINS_START="${JENKINS_START:-java -Xms4g -Xmx4g -jar ${JENKINS_WAR}}" 6 | export JENKINS_WEB="${JENKINS_WEB:-http://localhost:8080}" 7 | export jenkins_url="${jenkins_url:-http://mirrors.jenkins-ci.org/war/latest/jenkins.war}" 8 | 9 | if [ -d 'jenkins-bootstrap-shared' ]; then 10 | export SCRIPT_LIBRARY_PATH="${SCRIPT_LIBRARY_PATH:-./jenkins-bootstrap-shared/scripts}" 11 | else 12 | export SCRIPT_LIBRARY_PATH="${SCRIPT_LIBRARY_PATH:-./scripts}" 13 | fi 14 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | ARG base=alpine 2 | FROM ${base} 3 | 4 | ADD build/distributions/*.tar /usr/ 5 | 6 | ARG JENKINS_HOME=/var/lib/jenkins 7 | 8 | RUN set -ex; \ 9 | adduser -u 100 -G nogroup -h ${JENKINS_HOME} -S jenkins; \ 10 | apk add --no-cache --update bash curl font-dejavu-sans-mono-nerd fontconfig git openjdk17-jdk openssh python3 rsync; \ 11 | mkdir -p /var/cache/jenkins ${JENKINS_HOME}; \ 12 | chown -R jenkins: /usr/lib/jenkins /var/cache/jenkins ${JENKINS_HOME}; \ 13 | ln -s /usr/lib/jenkins/distrib/daemon/run.sh /run.sh 14 | 15 | EXPOSE 8080/tcp 16 | 17 | USER jenkins 18 | WORKDIR ${JENKINS_HOME} 19 | ENV JAVA_HOME="/usr/lib/jvm/java-17-openjdk" 20 | CMD /run.sh 21 | -------------------------------------------------------------------------------- /scripts/docker-env.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if ! type -P docker-compose || [ -z "$(docker-compose ps -q)" ]; then 4 | echo "ERROR: docker-compose does not exist or is not running" >&2 5 | exit 1 6 | fi 7 | 8 | function password_ready() { 9 | docker-compose exec jenkins test -r "${DOCKER_JENKINS_HOME:-/var/lib/jenkins}"/secrets/initialAdminPassword 10 | } 11 | 12 | 13 | if [ -z "${JENKINS_PASSWORD}" ]; then 14 | until password_ready; do 15 | echo "${DOCKER_JENKINS_HOME:-/var/lib/jenkins}/secrets/initialAdminPassword not available, yet..." 16 | sleep 5 17 | done 18 | JENKINS_PASSWORD="$(docker-compose exec -T jenkins cat "${DOCKER_JENKINS_HOME:-/var/lib/jenkins}"/secrets/initialAdminPassword)" 19 | export JENKINS_PASSWORD 20 | fi 21 | -------------------------------------------------------------------------------- /packaging/jenkins.cron.sh.in: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | if [ ! -r '/etc/sysconfig/@@ARTIFACTNAME@@' -a ! -r '/etc/default/@@ARTIFACTNAME@@' ]; then 3 | echo "Failed to source Jenkins daemon settings." 4 | echo "On RPM based distros: '/etc/sysconfig/@@ARTIFACTNAME@@'" 5 | echo "On DEB based distros: '/etc/default/@@ARTIFACTNAME@@'" 6 | exit 1 7 | fi 8 | 9 | [ -r '/etc/sysconfig/@@ARTIFACTNAME@@' ] && . '/etc/sysconfig/@@ARTIFACTNAME@@' 10 | [ -r '/etc/default/@@ARTIFACTNAME@@' ] && . '/etc/default/@@ARTIFACTNAME@@' 11 | 12 | JENKINS_HOME="${JENKINS_HOME:-@@HOME@@}" 13 | JENKINS_USER="${JENKINS_USER:-@@USER@@}" 14 | 15 | if [ -e "${JENKINS_HOME}/cron.disabled" ]; then 16 | exit 17 | fi 18 | 19 | su -s /bin/bash -c "${JENKINS_HOME}/dailycommit.sh" - ${JENKINS_USER} 20 | -------------------------------------------------------------------------------- /scripts/vagrant-up.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Created by Sam Gleske 3 | # This script is meant to run before "vagrant up". It will check if there's an 4 | # RPM already built and, if not, build it. 5 | 6 | # Please keep in mind that this script runs on _every_ vagrant command. 7 | # Therefore, idempotency is very important. Only output to stderr or stdout 8 | # when there's an error or when real commands are being executed. No output 9 | # means the script successfully and silently skipped everything. 10 | 11 | if [ ! -f 'build.gradle' -a ! -d '.git' ]; then 12 | echo "Error: not run from root of repository." >&2 13 | exit 1 14 | fi 15 | 16 | # If the RPM doesn't exist then build it. 17 | if [ ! -f build/distributions/*.rpm ]; then 18 | ./gradlew buildRpm 19 | fi 20 | -------------------------------------------------------------------------------- /scripts/upgrade/env.sh: -------------------------------------------------------------------------------- 1 | function canonicalize() ( 2 | cd "${1%/*}" 3 | echo "${PWD}/${1##*/}" 4 | ) 5 | 6 | if [ -f build.gradle ]; then 7 | if [ ! x"${JENKINS_WEB:-}" = x ]; then 8 | export JENKINS_WEB="${JENKINS_WEB%/}" 9 | fi 10 | if [ -z "${FORCE_UPGRADE:-}" ]; then 11 | export JENKINS_USER="admin" 12 | else 13 | export JENKINS_USER="${JENKINS_USER:-admin}" 14 | fi 15 | export JENKINS_PASSWORD="${JENKINS_PASSWORD:-$(<"${JENKINS_HOME}"/secrets/initialAdminPassword)}" 16 | unset JENKINS_CALL_ARGS 17 | "${SCRIPT_LIBRARY_PATH}"/jenkins_call.sh -a -v -v "${JENKINS_WEB}"/api/json -o /dev/null 18 | export JENKINS_CALL_ARGS="-m POST ${JENKINS_WEB}/scriptText --data-string script= -d" 19 | else 20 | echo "Not in repository root." 1>&2 21 | fi 22 | -------------------------------------------------------------------------------- /tests/rpm/validate.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #This script is meant to be run from within a centos:7 docker container 3 | 4 | set -euo pipefail 5 | 6 | GOSS_SHASUM=5669df08e406abf594de0e7a7718ef389e5dc7cc76905e7f6f64711e6aad7fa3 7 | GOSS_URL=https://github.com/aelsabbahy/goss/releases/download/v0.3.5/goss-linux-amd64 8 | 9 | #install packages 10 | #PACKAGES=(java-1.8.0-openjdk-devel git) 11 | #rpm -q "${PACKAGES[@]}" || yum install -y "${PACKAGES[@]}" 12 | #export JAVA_HOME=/usr/lib/jvm/java-1.8.0 13 | 14 | # build rpm 15 | [ -f build/distributions/*.rpm ] || ./gradlew buildRpm 16 | ! rpm -q jenkins-bootstrap 17 | yum localinstall -y build/distributions/*.rpm 18 | 19 | #install goss 20 | [ -x '/sbin/goss' ] || ( 21 | curl -Lo /sbin/goss "${GOSS_URL}" && 22 | sha256sum -c - <<<"${GOSS_SHASUM} /sbin/goss" && 23 | chmod 755 /sbin/goss 24 | ) 25 | 26 | cd tests/rpm 27 | goss validate 28 | -------------------------------------------------------------------------------- /scripts/security-disable-agent-controller.groovy: -------------------------------------------------------------------------------- 1 | /** 2 | WARNING SCRIPT IS DEPRECATED. 3 | 4 | Does not work on newer versions of Jenkins. Recommendation is to stop 5 | calling it. 6 | */ 7 | // https://www.jenkins.io/doc/book/security/controller-isolation/#agent-controller-access-control 8 | 9 | def rule = Jenkins.instance.getExtensionList(jenkins.security.s2m.MasterKillSwitchConfiguration.class)[0].rule 10 | if(!rule.getMasterKillSwitch()) { 11 | rule.setMasterKillSwitch(true) 12 | //dismiss the warning because we don't care (cobertura reporting is broken otherwise) 13 | Jenkins.instance.getExtensionList(jenkins.security.s2m.MasterKillSwitchWarning.class)[0].disable(true) 14 | Jenkins.instance.save() 15 | println 'Disabled agent -> built-in controller security for cobertura.' 16 | } 17 | else { 18 | println 'Nothing changed. Agent -> built-in controller security already disabled.' 19 | } 20 | -------------------------------------------------------------------------------- /scripts/upgrade/jenkins_needs_restart.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #Created by Sam Gleske 3 | #Sat 19 Feb 2022 03:14:39 PM EST 4 | #Ubuntu 20.04.3 LTS 5 | #Linux 5.13.0-30-generic x86_64 6 | #GNU bash, version 5.0.17(1)-release (x86_64-pc-linux-gnu) 7 | 8 | # DESCRIPTION 9 | # If Jenkins needs a restart, then restart 10 | # 11 | # EXIT CODES 12 | # Returns 13 | # 0 - If Jenkins restarts. Zero exit code. 14 | # 1 - If Jenkins does not need to restart. Non-zero exit code. 15 | 16 | EXIT_CODE=1 17 | if [ "$("${SCRIPT_LIBRARY_PATH}"/jenkins_call.sh "${SCRIPT_LIBRARY_PATH}"/upgrade/needsRestart.groovy)" = 'true' ]; then 18 | #restart Jenkins 19 | "${SCRIPT_LIBRARY_PATH}"/jenkins_call.sh <(echo "Jenkins.instance.restart()") 20 | #wait for jenkins to become available 21 | "${SCRIPT_LIBRARY_PATH}"/provision_jenkins.sh url-ready "${JENKINS_WEB}/jnlpJars/jenkins-cli.jar" 22 | EXIT_CODE=0 23 | fi 24 | 25 | exit "${EXIT_CODE}" 26 | -------------------------------------------------------------------------------- /tests/deb/validate.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #This script is meant to be run from within a centos:7 docker container 3 | 4 | set -euo pipefail 5 | 6 | GOSS_SHASUM=5669df08e406abf594de0e7a7718ef389e5dc7cc76905e7f6f64711e6aad7fa3 7 | GOSS_URL=https://github.com/aelsabbahy/goss/releases/download/v0.3.5/goss-linux-amd64 8 | 9 | #install packages 10 | #PACKAGES=(java-1.8.0-openjdk-devel git) 11 | #rpm -q "${PACKAGES[@]}" || yum install -y "${PACKAGES[@]}" 12 | #export JAVA_HOME=/usr/lib/jvm/java-1.8.0 13 | 14 | # build rpm 15 | [ -f build/distributions/*.deb ] || ./gradlew buildDeb 16 | ! ( dpkg -l | grep jenkins-bootstrap ) 17 | apt-get update 18 | apt install -y ./build/distributions/*.deb 19 | 20 | #install goss 21 | [ -x '/sbin/goss' ] || ( 22 | type -P curl || apt install -y curl 23 | curl -Lo /sbin/goss "${GOSS_URL}" && 24 | sha256sum -c - <<<"${GOSS_SHASUM} /sbin/goss" && 25 | chmod 755 /sbin/goss 26 | ) 27 | 28 | cd tests/deb 29 | goss validate 30 | -------------------------------------------------------------------------------- /scripts/upgrade/listShortNameVersion.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015-2023 Sam Gleske - https://github.com/samrocketman/jenkins-bootstrap-slack 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | /* 17 | Generates a list of plugins to search build.gradle for missing plugins. 18 | */ 19 | 20 | import jenkins.model.Jenkins 21 | 22 | Jenkins.instance.pluginManager.plugins.each { p -> println "${p.shortName}:${p.version}" } 23 | null 24 | -------------------------------------------------------------------------------- /tests/rpm/run: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Created by Sam Gleske - https://github.com/samrocketman 3 | 4 | # DESCRIPTION 5 | # A simple script to test installing the RPM and run goss infrastructure 6 | # tests. Run tests or boot a docker image for experimentation. This script 7 | # is meant to be run from the root of this repository. 8 | 9 | # USAGE: 10 | # Generic usage where `options` are arguments for "docker run" and `command` is executed in the docker image. 11 | # ./tests/rpm/run [options ...] [command] 12 | # 13 | # Run tests 14 | # ./tests/rpm/run 15 | # 16 | # Interactively start bash. 17 | # ./tests/rpm/run -i /bin/bash 18 | 19 | # collect docker arguments 20 | args=() 21 | while [ $# -gt 1 ]; do 22 | args+=( "${1}" ) 23 | shift 24 | done 25 | # $1 is left intact to be run as the shell command in the docker image 26 | 27 | DOCKER_IMAGE="centos:7" 28 | docker pull "${DOCKER_IMAGE}" 29 | docker run "${args[@]}" -w /mnt -t --rm -v "$PWD":/mnt "${DOCKER_IMAGE}" "${1:-/mnt/tests/rpm/validate.sh}" 30 | -------------------------------------------------------------------------------- /tests/deb/run: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Created by Sam Gleske - https://github.com/samrocketman 3 | 4 | # DESCRIPTION 5 | # A simple script to test installing the DEB and run goss infrastructure 6 | # tests. Run tests or boot a docker image for experimentation. This script 7 | # is meant to be run from the root of this repository. 8 | 9 | # USAGE: 10 | # Generic usage where `options` are arguments for "docker run" and `command` is executed in the docker image. 11 | # ./tests/deb/run [options ...] [command] 12 | # 13 | # Run tests 14 | # ./tests/deb/run 15 | # 16 | # Interactively start bash. 17 | # ./tests/deb/run -i /bin/bash 18 | 19 | # collect docker arguments 20 | args=() 21 | while [ $# -gt 1 ]; do 22 | args+=( "${1}" ) 23 | shift 24 | done 25 | # $1 is left intact to be run as the shell command in the docker image 26 | 27 | DOCKER_IMAGE="ubuntu:16.04" 28 | docker pull "${DOCKER_IMAGE}" 29 | docker run "${args[@]}" -w /mnt -t --rm -v "$PWD":/mnt "${DOCKER_IMAGE}" "${1:-/mnt/tests/deb/validate.sh}" 30 | -------------------------------------------------------------------------------- /scripts/upgrade/setup_environment.sh: -------------------------------------------------------------------------------- 1 | #Created by Sam Gleske 2 | #Sat 19 Feb 2022 03:28:16 PM EST 3 | #Ubuntu 20.04.3 LTS 4 | #Linux 5.13.0-30-generic x86_64 5 | #GNU bash, version 5.0.17(1)-release (x86_64-pc-linux-gnu) 6 | 7 | # DESCRIPTION 8 | # This script is meant to be sourced. It runs some pre-upgrade checks. 9 | 10 | #set password if using vagrant 11 | [ -n "${VAGRANT_JENKINS}" ] && source "${SCRIPT_LIBRARY_PATH}/vagrant-env.sh" 12 | [ -n "${DOCKER_JENKINS}" ] && source "${SCRIPT_LIBRARY_PATH}/docker-env.sh" 13 | 14 | 15 | #protect user from accidentally upgrading a remote Jenkins 16 | #this should always be localhost 17 | if [ -z "${NO_UPGRADE:-}" ] && [ ! "${JENKINS_WEB}" = 'http://localhost:8080' ]; then 18 | echo 'ERROR: JENKINS_WEB is not equal to localhost' >&2 19 | echo "JENKINS_WEB = ${JENKINS_WEB}" >&2 20 | exit 1 21 | fi 22 | 23 | if [ ! -f build.gradle ]; then 24 | echo "Not being run from repository root." 1>&2 25 | exit 1 26 | fi 27 | 28 | #set up Jenkins env vars 29 | export JENKINS_HEADERS_FILE=$(mktemp) 30 | source "${SCRIPT_LIBRARY_PATH}"/upgrade/env.sh 31 | -------------------------------------------------------------------------------- /packaging/share/dailycommit.sh.in: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #Sam Gleske (sag47) 3 | #Tue Mar 4 14:35:59 EST 2014 4 | #Red Hat Enterprise Linux Server release 6.5 (Santiago) 5 | #Linux 2.6.32-431.3.1.el6.x86_64 x86_64 6 | #GNU bash, version 4.1.2(1)-release (x86_64-redhat-linux-gnu) 7 | 8 | set -e 9 | 10 | [ -r '/etc/sysconfig/@@ARTIFACTNAME@@' ] && . '/etc/sysconfig/@@ARTIFACTNAME@@' 11 | [ -r '/etc/default/@@ARTIFACTNAME@@' ] && . '/etc/default/@@ARTIFACTNAME@@' 12 | 13 | if [ -z "${JENKINS_HOME}" ];then 14 | echo "JENKINS_HOME not set." 1>&2 15 | exit 1 16 | fi 17 | if [ ! "${USER}" = "@@USER@@" ];then 18 | echo "Must be @@USER@@ user to run daily commit." 1>&2 19 | exit 1 20 | fi 21 | 22 | export PATH="/usr/local/bin:${PATH}" 23 | 24 | cd "${JENKINS_HOME}" 25 | if [ ! -d '.git' ]; then 26 | git init 27 | fi 28 | git add . 29 | git add -u 30 | if [ -n "$1" ]; then 31 | git commit -m "package upgrade commit $1" 32 | else 33 | git commit -m "daily commit $(date '+%a %m/%d/%Y')" 34 | fi 35 | 36 | 37 | if git remote -v 2> /dev/null | grep origin > /dev/null; then 38 | timeout 300 git push origin master 39 | fi 40 | -------------------------------------------------------------------------------- /scripts/jenkins_call.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # This is a wrapper script for jenkins-call-url and jenkins-call-url-2.7. This 3 | # script will detect the python version from the current system. It selects an 4 | # appropriate way to call jenkins-call-url. This enables a better user 5 | # experience by not having to worry about python version. 6 | 7 | 8 | function minimum() ( 9 | exec &> /dev/null 10 | local major="${2%.*}" 11 | local minor="${2#*.}" 12 | if ! type -P "$1"; then 13 | return false 14 | fi 15 | "$1" -c 'import platform,sys; check=lambda x,y,z: x.startswith(y) and int(x.split(".")[0:2][-1]) >= z; sys.exit(0) if check(platform.python_version(), sys.argv[1], int(sys.argv[2])) else sys.exit(1)' \ 16 | "${major}" "${minor}" 17 | ) 18 | 19 | if minimum python3 '3.8'; then 20 | "${SCRIPT_LIBRARY_PATH}"/jenkins-call-url "$@" 21 | elif minimum python '3.8'; then 22 | python "${SCRIPT_LIBRARY_PATH}"/jenkins-call-url "$@" 23 | elif minimum python '2.7'; then 24 | "${SCRIPT_LIBRARY_PATH}"/jenkins-call-url-2.7 "$@" 25 | else 26 | echo 'Python 2 or 3.8+ could not be detected.' >&2 27 | exit 1 28 | fi 29 | -------------------------------------------------------------------------------- /Vagrantfile: -------------------------------------------------------------------------------- 1 | # -*- mode: ruby -*- 2 | # vi: set ft=ruby : 3 | 4 | # Execute shell script before running vagrant. This script will run on every 5 | # vagrant command. 6 | system('./scripts/vagrant-up.sh') 7 | 8 | Vagrant.configure("2") do |config| 9 | # https://docs.vagrantup.com. 10 | config.vm.box = "centos/7" 11 | config.vm.network "forwarded_port", guest: 8080, host: 8080, host_ip: "127.0.0.1" 12 | config.vm.provider "virtualbox" do |vb| 13 | vb.memory = "4096" 14 | end 15 | if Vagrant.has_plugin?("vagrant-vbguest") 16 | config.vbguest.auto_update = false 17 | end 18 | config.vm.provision "shell", inline: <<-SHELL 19 | set -ex 20 | 21 | # temporarily disable SELinux 22 | sudo sed -i 's/^SELINUX=.*/SELINUX=disabled/' /etc/selinux/config 23 | sudo setenforce 0 24 | 25 | # install Java 26 | yum install -y java-1.8.0-openjdk-devel.x86_64 git 27 | #install Jenkins 28 | rpm -i /vagrant/build/distributions/*.rpm 29 | echo 'JAVA_HOME=/etc/alternatives/java_sdk' >> /etc/sysconfig/jenkins 30 | #start the Jenkins daemon 31 | systemctl start jenkins.service 32 | SHELL 33 | end 34 | -------------------------------------------------------------------------------- /scripts/upgrade/needsRestart.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015-2023 Sam Gleske - https://github.com/samrocketman/jenkins-bootstrap-slack 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | /* 17 | Detects if Jenkins needs a restart due to Jenkins core or plugin downloads. 18 | Returns true if a restart is necessary or false if not. 19 | */ 20 | import hudson.model.UpdateCenter 21 | import hudson.model.UpdateCenter.DownloadJob 22 | 23 | UpdateCenter center = Jenkins.instance.updateCenter 24 | 25 | println (center.jobs.findAll { it instanceof DownloadJob }.size() as Boolean).toString() 26 | null 27 | -------------------------------------------------------------------------------- /packaging/README.md: -------------------------------------------------------------------------------- 1 | # Distribution Packages 2 | 3 | Parts or all scripts were taken from https://github.com/jenkinsci/packaging 4 | 5 | This provides OS-dependent packages which include the exact versions of Jenkins 6 | and exact versions of plugins. This provides more repeatable means of upgrading 7 | Jenkins within an immutable infrastructure environment. 8 | 9 | ### Packaging RPM 10 | 11 | To create an RPM package. 12 | 13 | ./gradlew clean buildRpm 14 | 15 | To inspect the package. 16 | 17 | rpm -qip --dump --scripts build/distributions/*.rpm | less 18 | 19 | List package dependencies. 20 | 21 | rpm -qRp build/distributions/*.rpm 22 | 23 | ### Packaging DEB 24 | 25 | To create a DEB package. 26 | 27 | ./gradlew clean buildDeb 28 | 29 | To inpsect the package. 30 | 31 | dpkg -I build/distributions/*.deb 32 | 33 | Dry run to see what dpkg would do. 34 | 35 | dpkg --dry-run -i build/distributions/*.deb 36 | 37 | Extract only the control information files from the package. 38 | 39 | dpkg -e build/distributions/*.deb 40 | 41 | ### Build all 42 | 43 | Build all available package formats. 44 | 45 | ./gradlew clean packages 46 | -------------------------------------------------------------------------------- /packaging/postUninstall.sh.in: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | RPM_BASED=false 5 | DEB_BASED=false 6 | 7 | #detect using RedHat based package 8 | #if not RPM then it's DEB 9 | if [ -n "${RPM_PACKAGE_NAME}" ]; then 10 | RPM_BASED=true 11 | else 12 | DEB_BASED=true 13 | fi 14 | 15 | if ${RPM_BASED}; then 16 | [ -r '/etc/sysconfig/@@ARTIFACTNAME@@' ] && . '/etc/sysconfig/@@ARTIFACTNAME@@' 17 | JENKINS_HOME="${JENKINS_HOME:-@@HOME@@}" 18 | if [ "$1" -ge 1 ]; then 19 | #upgrading package 20 | service @@ARTIFACTNAME@@ try-restart > /dev/null 2>&1 21 | fi 22 | else 23 | #DEB_BASED == true 24 | case "$1" in 25 | purge) 26 | [ -r '/etc/default/@@ARTIFACTNAME@@' ] && . '/etc/default/@@ARTIFACTNAME@@' 27 | userdel @@ARTIFACTNAME@@ || true 28 | groupdel @@ARTIFACTNAME@@ || true 29 | rm -rf "${JENKINS_HOME}" /var/log/@@ARTIFACTNAME@@ /var/run/@@ARTIFACTNAME@@ /var/cache/@@ARTIFACTNAME@@ 30 | ;; 31 | remove|upgrade|failed-upgrade|abort-install|abort-upgrade|disappear) 32 | ;; 33 | *) 34 | echo "postrm called with unknown argument '$1'" >&2 35 | exit 1 36 | ;; 37 | esac 38 | fi 39 | 40 | exit 0 41 | -------------------------------------------------------------------------------- /scripts/upgrade/getPinnedPluginList.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015-2023 Sam Gleske - https://github.com/samrocketman/jenkins-bootstrap-slack 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | /* 17 | Gets a list of pinned plugins and only prints the plugins which have been 18 | pinned as files. This is useful for determining a minimal set of plugins 19 | installed vs plugins installed provided as dependencies. 20 | */ 21 | import jenkins.model.Jenkins 22 | println Jenkins.instance.pluginManager.plugins.findAll { 23 | new File("${Jenkins.instance.root}/plugins/${it.shortName}.jpi.pinned").exists() 24 | }*.shortName.sort().unique().join('\n') 25 | -------------------------------------------------------------------------------- /scripts/jenkins_wait_job.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #Created by Sam Gleske (https://github.com/samrocketman/home) 3 | #Ubuntu 16.04.2 LTS 4 | #Linux 4.4.0-72-generic x86_64 5 | #Python 2.7.12 6 | 7 | function json() { 8 | python -c "import sys,json;print str(json.load(sys.stdin)[\"${1}\"]).lower()" 9 | } 10 | 11 | MESSAGE='Jobb success.' 12 | PIPELINE_INPUT=false 13 | count=0 14 | while true; do 15 | [ "$("${SCRIPT_LIBRARY_PATH}"/jenkins-call-url ${1%/}/api/json | json building)" = 'false' ] && break 16 | if [ "$count" -eq "0" ]; then 17 | if ( "${SCRIPT_LIBRARY_PATH}"/jenkins-call-url ${1%/}/consoleText | tail | grep 'Input requested' ); then 18 | PIPELINE_INPUT=true 19 | break 20 | fi 21 | fi 22 | echo "building..." 23 | #every 15 seconds check consoleText 24 | ((count++, count = count%3)) || true 25 | sleep 5 26 | done 27 | 28 | if ${PIPELINE_INPUT}; then 29 | RESULT=SUCCESS 30 | MESSAGE='Pipeline input requested.' 31 | else 32 | RESULT=$("${SCRIPT_LIBRARY_PATH}"/jenkins-call-url ${1%/}/api/json | json result | tr 'a-z' 'A-Z') 33 | fi 34 | 35 | "${SCRIPT_LIBRARY_PATH}"/jenkins-call-url ${1%/}/consoleText 36 | 37 | #script exit code is last command 38 | [ "${RESULT}" = 'SUCCESS' ] 39 | -------------------------------------------------------------------------------- /scripts/configure-markup-formatter.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015-2023 Sam Gleske - https://github.com/samrocketman/jenkins-bootstrap-shared 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | /* 17 | Configures the markup formatter in global security settings to be simple 18 | HTML. 19 | */ 20 | 21 | import hudson.markup.RawHtmlMarkupFormatter 22 | 23 | Jenkins j = Jenkins.instance 24 | 25 | if(j.markupFormatter.class != RawHtmlMarkupFormatter) { 26 | j.markupFormatter = new RawHtmlMarkupFormatter(false) 27 | j.save() 28 | println 'Markup Formatter configuration has changed. Configured Markup Formatter.' 29 | } 30 | else { 31 | println 'Nothing changed. Markup Formatter already configured.' 32 | } 33 | -------------------------------------------------------------------------------- /scripts/upgrade/show_plugin_artifacts.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Created by Sam Gleske 3 | # Sun Oct 23 05:48:03 EDT 2022 4 | # Pop!_OS 18.04 LTS 5 | # Linux 5.4.0-113-generic x86_64 6 | # GNU bash, version 4.4.20(1)-release (x86_64-pc-linux-gnu) 7 | # Gradle 6.2.2 8 | # Build time: 2020-03-04 08:49:31 UTC 9 | # Revision: 7d0bf6dcb46c143bcc3b7a0fa40a8e5ca28e5856 10 | # Kotlin: 1.3.61 11 | # Groovy: 2.5.8 12 | # Ant: Apache Ant(TM) version 1.10.7 compiled on September 1 2019 13 | # JVM: 1.8.0_342 (Private Build 25.342-b07) 14 | # OS: Linux 5.4.0-113-generic amd64 15 | 16 | # DESCRIPTION: 17 | # Reads dependencies.gradle for only plugin artifacts and prints a sorted 18 | # list of the artifacts found. This is useful for migrations when 19 | # occasionally you may need to start from scratch and capture the same 20 | # plugins but as their latest versions. 21 | # 22 | # Running this script is likely only required for validating upgrades to 23 | # ensure anything is missing. It is rare a Jenkins instance would need to 24 | # completely start from scratch. 25 | 26 | set -exo pipefail 27 | 28 | if [ ! -f dependencies.gradle ]; then 29 | echo 'Missing dependencies.gradle' >&2 30 | exit 1 31 | fi 32 | 33 | grep getplugins dependencies.gradle | cut -d: -f2 | sort -u 34 | -------------------------------------------------------------------------------- /scripts/gradle-getplugins.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015-2023 Sam Gleske - https://github.com/samrocketman/jenkins-bootstrap-jervis 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | /* 18 | Prints out plugins that need to be downloaded from the current instance. 19 | */ 20 | 21 | import groovy.json.JsonSlurper 22 | def json = new JsonSlurper() 23 | def addr="http://jenkins-updates.cloudbees.com/update-center.json" 24 | def updateCenterPlugins=json.parseText((new URL(addr).newReader().readLines())[1..-1].join(''))["plugins"] 25 | Jenkins.instance.pluginManager.plugins.each { 26 | def plugin=it.getShortName() 27 | println "getplugins 'org.jenkins-ci.plugins:${updateCenterPlugins[plugin]["name"]}:${updateCenterPlugins[plugin]["version"]}'" 28 | } 29 | -------------------------------------------------------------------------------- /scripts/vagrant-env.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | VAGRANT_SSH_CONFIG="$(mktemp)" 3 | export VAGRANT_SSH_CONFIG 4 | vagrant ssh-config > "$VAGRANT_SSH_CONFIG" 5 | function get_jenkins_home() { 6 | ssh -F "${VAGRANT_SSH_CONFIG}" default /bin/bash <<'EOF' 7 | CONFIG_FILE="" 8 | if sudo test -f /etc/sysconfig/jenkins && sudo grep '^JENKINS_HOME' /etc/sysconfig/jenkins &> /dev/null; then 9 | CONFIG_FILE='/etc/sysconfig/jenkins' 10 | elif sudo test -f /etc/default/jenkins && sudo grep '^JENKINS_HOME' /etc/default/jenkins &> /dev/null; then 11 | CONFIG_FILE='/etc/default/jenkins' 12 | fi 13 | if test -n "${CONFIG_FILE}"; then 14 | eval "$(sudo grep '^JENKINS_HOME' "${CONFIG_FILE}")" 15 | fi 16 | echo "${JENKINS_HOME:-/var/lib/jenkins}" 17 | EOF 18 | } 19 | 20 | VAGRANT_JENKINS_HOME="$(get_jenkins_home)" 21 | 22 | function password_ready() { 23 | ssh -nF "${VAGRANT_SSH_CONFIG}" default "sudo test -f \"${VAGRANT_JENKINS_HOME}\"/secrets/initialAdminPassword" 24 | } 25 | 26 | 27 | if [ -z "${JENKINS_PASSWORD}" ]; then 28 | until password_ready; do 29 | echo "${VAGRANT_JENKINS_HOME}/secrets/initialAdminPassword not available, yet..." 30 | sleep 5 31 | done 32 | JENKINS_PASSWORD="$(ssh -F "${VAGRANT_SSH_CONFIG}" default "sudo cat \"${VAGRANT_JENKINS_HOME}\"/secrets/initialAdminPassword")" 33 | export JENKINS_PASSWORD 34 | fi 35 | -------------------------------------------------------------------------------- /scripts/upgrade/isUpgradeInProgress.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015-2023 Sam Gleske - https://github.com/samrocketman/jenkins-bootstrap-slack 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | /* 17 | Returns true if there's a Jenkins upgrade in progress (e.g. Jenkins core or 18 | plugins downloads are not finished i.e. in progress). If there's no in 19 | progress downloads the the script returns false. 20 | */ 21 | import hudson.model.UpdateCenter 22 | import hudson.model.UpdateCenter.DownloadJob 23 | import hudson.model.UpdateCenter.DownloadJob.Success 24 | 25 | UpdateCenter center = Jenkins.instance.updateCenter 26 | 27 | println (center.jobs.findAll { it instanceof DownloadJob }.findAll { !(it.status instanceof Success) }.size() as Boolean).toString() 28 | null 29 | -------------------------------------------------------------------------------- /init.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ ! -d jenkins-bootstrap-shared -o ! -d .git ] || ! grep jenkins-bootstrap-shared .gitmodules &> /dev/null; then 4 | echo "ERROR: no submodule jenkins-bootstrap-shared" >&2 5 | exit 1 6 | fi 7 | 8 | if [ -f build.gradle ]; then 9 | echo "ERROR: build.gradle exists; already initialized bootstrap." >&2 10 | exit 1 11 | fi 12 | 13 | #GNU sed is required. Homebrew on Mac installs GNU sed as gsed. 14 | #Try to detect gsed; otherwise, fall back to just using sed. 15 | [ -x "$(type -P gsed)" ] && SED=gsed || SED=sed 16 | export SED 17 | 18 | ( 19 | cd jenkins-bootstrap-shared/ 20 | cp -r RELEASE.md Vagrantfile .gitignore gradle* build.gradle variables.gradle dependencies.gradle env.sh ../ 21 | mkdir -p ../scripts 22 | cp scripts/vagrant-up.sh ../scripts/ 23 | $SED 's#^\( \+\.\)\(/scripts/upgrade/.*\)$#\1/jenkins-bootstrap-shared\2#' README.md > ../README.md 24 | $SED 's#\([^:]*:\) \(Dockerfile\)#\1 jenkins-bootstrap-shared/\2#' docker-compose.yml > ../docker-compose.yml 25 | ) 26 | 27 | echo 'bootstrapHome=jenkins-bootstrap-shared' > gradle.properties 28 | grep -- '^version' jenkins-bootstrap-shared/gradle.properties >> gradle.properties 29 | cat > jenkins_bootstrap.sh <<'EOF' 30 | #!/bin/bash 31 | source jenkins-bootstrap-shared/jenkins_bootstrap.sh 32 | EOF 33 | chmod 755 jenkins_bootstrap.sh 34 | -------------------------------------------------------------------------------- /3rd_party/packaging-scripts: -------------------------------------------------------------------------------- 1 | For scripts copied from https://github.com/jenkinsci/packaging the following 2 | license applies. 3 | --- 4 | The MIT License 5 | 6 | Copyright (c) 2004-, Kohsuke Kawaguchi, Sun Microsystems, Inc., and a number of 7 | other of contributers 8 | 9 | Permission is hereby granted, free of charge, to any person obtaining a copy of 10 | this software and associated documentation files (the "Software"), to deal in 11 | the Software without restriction, including without limitation the rights to 12 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 13 | of the Software, and to permit persons to whom the Software is furnished to do 14 | so, subject to the following conditions: 15 | 16 | The above copyright notice and this permission notice shall be included in all 17 | copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 25 | SOFTWARE. 26 | -------------------------------------------------------------------------------- /scripts/configure-disable-usage-stats.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015-2023 Sam Gleske - https://github.com/samrocketman/jenkins-bootstrap-jervis 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | /* 18 | Disable submitting anonymous usage statistics to the Jenkins project. 19 | An option like this shouldn't be enabled by default. 20 | */ 21 | 22 | import jenkins.model.Jenkins 23 | 24 | def j = Jenkins.instance 25 | if(!j.isQuietingDown()) { 26 | if(j.isUsageStatisticsCollected()){ 27 | j.setNoUsageStatistics(true) 28 | j.save() 29 | println 'Disabled submitting usage stats to Jenkins project.' 30 | } 31 | else { 32 | println 'Nothing changed. Usage stats are not submitted to the Jenkins project.' 33 | } 34 | } 35 | else { 36 | println 'Shutdown mode enabled. Disable usage stats SKIPPED.' 37 | } 38 | -------------------------------------------------------------------------------- /scripts/configure-csrf-protection.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015-2023 Sam Gleske - https://github.com/samrocketman/jenkins-bootstrap-jervis 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | /* 17 | Configures CSRF protection in global security settings. 18 | */ 19 | 20 | import hudson.security.csrf.DefaultCrumbIssuer 21 | import jenkins.model.Jenkins 22 | 23 | if(!Jenkins.instance.isQuietingDown()) { 24 | def j = Jenkins.instance 25 | if(j.getCrumbIssuer() == null) { 26 | j.setCrumbIssuer(new DefaultCrumbIssuer(true)) 27 | j.save() 28 | println 'CSRF Protection configuration has changed. Enabled CSRF Protection.' 29 | } 30 | else { 31 | println 'Nothing changed. CSRF Protection already configured.' 32 | } 33 | } 34 | else { 35 | println "Shutdown mode enabled. Configure CSRF protection SKIPPED." 36 | } 37 | -------------------------------------------------------------------------------- /USAGE.md: -------------------------------------------------------------------------------- 1 | # Usage Instructions 2 | 3 | ### Provision Jenkins 4 | 5 | To provision Jenkins locally outside of a vagrant VM. 6 | 7 | ./jenkins_bootstrap.sh 8 | 9 | Visit `http://localhost:8080/` to see Jenkins running. 10 | 11 | ### Control Jenkins 12 | 13 | The [`provision_jenkins.sh`](scripts/provision_jenkins.sh) script is what 14 | controls Jenkins running on localhost. It has a few helper commands associated 15 | with it. Before running any commands it is recommended to add the `scripts` 16 | directory to your local `PATH` environment variable. 17 | 18 | PATH="./scripts:$PATH" 19 | unset JENKINS_HOME 20 | 21 | Now control the Jenkins service. 22 | 23 | provision_jenkins.sh stop 24 | provision_jenkins.sh start 25 | provision_jenkins.sh restart 26 | 27 | 28 | See other options. 29 | 30 | provision_jenkins.sh --help 31 | 32 | ### Delete Jenkins 33 | 34 | Clean up everything: 35 | 36 | ./gradlew clean 37 | 38 | # Upgrading Jenkins and plugins 39 | 40 | Jenkins is a fast moving development target. In order to ensure this repository 41 | stays up to date with development in Jenkins these instructions are provided. 42 | 43 | To upgrade the stable Jenkins and plugins versions used for testing PRs follow 44 | the [upgrade instructions](scripts/upgrade/README.md). 45 | 46 | # Upgrading Gradle 47 | 48 | ./gradlew wrapper --gradle-version=6.2.2 --distribution-type=bin 49 | 50 | Will upgrade the gradle wrapper used. 51 | -------------------------------------------------------------------------------- /scripts/configure-warning-disable-controller-agent-alert.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015-2023 Sam Gleske - https://github.com/samrocketman/jenkins-bootstrap-jervis 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | /** 17 | Disable warning when controller has executors. Controllers with executors 18 | are safe when the job restrictions plugin is configured. 19 | 20 | https://plugins.jenkins.io/job-restrictions/ 21 | */ 22 | import jenkins.model.Jenkins 23 | import jenkins.diagnostics.ControllerExecutorsAgents 24 | import hudson.model.AdministrativeMonitor 25 | 26 | def monitor = Jenkins.instance.getExtensionList(AdministrativeMonitor).get(ControllerExecutorsAgents) 27 | 28 | if(!monitor.isEnabled()) { 29 | println('Nothing changed. Executor on Controllers Warning already disabled') 30 | return 31 | } 32 | 33 | monitor.disable(true) 34 | println('Disabled warning for having executors on the controller.') 35 | -------------------------------------------------------------------------------- /scripts/upgrade/installMinimalPlugins.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015-2023 Sam Gleske - https://github.com/samrocketman/jenkins-bootstrap-jervis 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | /* 17 | Install plugins through the update center. 18 | */ 19 | if(!binding.hasVariable('plugins')) { 20 | throw new Exception('plugins is missing from the binding.') 21 | } 22 | 23 | if(!(plugins instanceof String)) { 24 | throw new Exception('plugins must defined as a String.') 25 | } 26 | 27 | List minimal_plugins = plugins.tokenize('\n')*.trim().sort().unique() 28 | 29 | 30 | Jenkins.instance.pluginManager.doCheckUpdatesServer() 31 | while(!Jenkins.instance.updateCenter.isSiteDataReady()) { 32 | sleep(500) 33 | } 34 | 35 | println "Installing pinned plugins: ${minimal_plugins.join(', ')}" 36 | 37 | // install minimal plugins with no dynamic loading (false) 38 | Jenkins.instance.pluginManager.install(minimal_plugins, false) 39 | 40 | null 41 | -------------------------------------------------------------------------------- /scripts/buildRelease.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #Created by Sam Gleske 3 | #Thu Feb 15 21:10:20 PST 2018 4 | #Linux 4.13.0-32-generic x86_64 5 | #GNU bash, version 4.3.48(1)-release (x86_64-pc-linux-gnu) 6 | 7 | #Build release binaries 8 | 9 | function read_err_on() { 10 | set +x 11 | exec >&2 12 | case $1 in 13 | 10) 14 | echo 'ERROR: must be in root of repository to build a release.' 15 | ;; 16 | 11) 17 | echo 'ERROR: sha256sum command is missing.' 18 | ;; 19 | 20) 20 | echo 'ERROR: Build "succeeded" but did not produce proper output.' 21 | ;; 22 | 0) 23 | echo 'SUCCESS: all release files located in build/distributions/.' 24 | ;; 25 | *) 26 | echo 'ERROR: an error occured.' 27 | ;; 28 | esac 29 | if [ -n "${TMP_DIR}" -a -d "${TMP_DIR}" ]; then 30 | rm -rf "${TMP_DIR}" 31 | fi 32 | } 33 | trap 'read_err_on $? "${1}"' EXIT 34 | 35 | function determine_shasum_prog() { 36 | set +x 37 | if type -P sha256sum; then 38 | SHASUM+=( 'sha256sum' '--' ) 39 | elif type -P shasum; then 40 | SHASUM+=( 'shasum' '-a' '256' '--' ) 41 | else 42 | return 1 43 | fi 44 | set -x 45 | } 46 | 47 | SHASUM=() 48 | 49 | set -ex 50 | 51 | #pre-flight tests (any failures will exit non-zero) 52 | [ -f build.gradle ] || exit 10 53 | determine_shasum_prog || exit 11 54 | 55 | ./gradlew clean packages 56 | [ -d build/distributions ] || exit 20 57 | cd build/distributions/ 58 | "${SHASUM[@]}" * > sha256sum.txt 59 | cp ../jenkins-versions.manifest ./ 60 | -------------------------------------------------------------------------------- /scripts/configure-jnlp-agent-protocols.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015-2023 Sam Gleske - https://github.com/samrocketman/jenkins-bootstrap-shared 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | /* 17 | Disable all JNLP protocols except for JNLP4. JNLP4 is the most secure agent 18 | protocol because it is using standard TLS. 19 | */ 20 | import jenkins.model.Jenkins 21 | 22 | Jenkins j = Jenkins.instance 23 | 24 | if(!j.isQuietingDown()) { 25 | Set agentProtocolsList = ['JNLP4-connect', 'Ping'] 26 | if(!j.getAgentProtocols().equals(agentProtocolsList)) { 27 | j.setAgentProtocols(agentProtocolsList) 28 | println "Agent Protocols have changed. Setting: ${agentProtocolsList}" 29 | j.save() 30 | } 31 | else { 32 | println "Nothing changed. Agent Protocols already configured: ${j.getAgentProtocols()}" 33 | } 34 | } 35 | else { 36 | println 'Shutdown mode enabled. Configure Agent Protocols SKIPPED.' 37 | } 38 | -------------------------------------------------------------------------------- /scripts/configure-job-dsl-security.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015-2023 Sam Gleske - https://github.com/samrocketman/jenkins-bootstrap-shared 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | /* 17 | Disable Job DSL plugin script Security. This is disabled because we assume 18 | users aren't able to create jobs. 19 | */ 20 | 21 | import jenkins.model.Jenkins 22 | Jenkins j = Jenkins.instance 23 | 24 | if(!j.isQuietingDown()) { 25 | def job_dsl_security = j.getExtensionList('javaposse.jobdsl.plugin.GlobalJobDslSecurityConfiguration')[0] 26 | if(job_dsl_security.useScriptSecurity) { 27 | job_dsl_security.useScriptSecurity = false 28 | println 'Job DSL script security has changed. It is now disabled.' 29 | job_dsl_security.save() 30 | j.save() 31 | } 32 | else { 33 | println 'Nothing changed. Job DSL script security already disabled.' 34 | } 35 | } 36 | else { 37 | println 'Shutdown mode enabled. Configure Job DSL script security SKIPPED.' 38 | } 39 | -------------------------------------------------------------------------------- /scripts/upgrade/generateSedExpr.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015-2023 Sam Gleske - https://github.com/samrocketman/jenkins-bootstrap-slack 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | /* 17 | Generates sed expressions list to be used for updating the build.gradle file. See also man page sed(1) for -r and -f options. 18 | 19 | Example sed command: 20 | 21 | sed -i.bak -rf sed_expression_file build.gradle 22 | */ 23 | 24 | import jenkins.model.Jenkins 25 | 26 | //update build.gradle package version string 27 | println "s/^(version ?= ?)'[^']+'(.*)\$/\\1'${Jenkins.instance.version}.1'\\2/" 28 | println "s/^(version)=.*\$/\\1=${Jenkins.instance.version}.1/" 29 | //update jenkins.war version in dependencies.gradle 30 | println "s/(.*getjenkins.*:jenkins-war):[^@]+@(war')/\\1:${Jenkins.instance.version}@\\2/" 31 | 32 | //update all plugins in dependencies.gradle 33 | Jenkins.instance.pluginManager.plugins.each { p -> 34 | println "s/(.*getplugins.*:${p.shortName}):[^@]+@(hpi')/\\1:${p.version}@\\2/" 35 | } 36 | null 37 | -------------------------------------------------------------------------------- /scripts/configure-flow-durability.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015-2023 Sam Gleske - https://github.com/samrocketman/jenkins-bootstrap-jervis 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | /* 17 | Configures pipeline flow durability to speed up the time it takes in which 18 | pipelines execute. This is a recommended performance optimization by the 19 | Jenkins community. 20 | */ 21 | import jenkins.model.Jenkins 22 | import org.jenkinsci.plugins.workflow.flow.FlowDurabilityHint 23 | import org.jenkinsci.plugins.workflow.flow.GlobalDefaultFlowDurabilityLevel 24 | 25 | Jenkins.instance.getExtensionList(GlobalDefaultFlowDurabilityLevel.DescriptorImpl).first().with { 26 | if(it.durabilityHint != FlowDurabilityHint.PERFORMANCE_OPTIMIZED) { 27 | it.durabilityHint = FlowDurabilityHint.PERFORMANCE_OPTIMIZED 28 | it.save() 29 | println 'Configured flow durability to be performance optimized.' 30 | } else { 31 | println 'Nothing changed. Flow durability already performance optimized.' 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /RELEASE.md: -------------------------------------------------------------------------------- 1 | # Releasing binaries to GitHub 2 | 3 | This repository provides helper scripts which aid releasing to GitHub. This 4 | document outlines how to release in a consistent manner. 5 | 6 | Before you begin you must generate a [GitHub personal access token][pat]. It 7 | must have at least `repo` scope if releasing to a private repository or 8 | `public_repo` scope to release to a public repository. Learn more [about GitHub 9 | OAuth scopes][os]. Then set up your environment with the following settings. 10 | 11 | export GITHUB_USER= 12 | export GITHUB_TOKEN= 13 | export GITHUB_REPO= 14 | 15 | The following is a recommended release process. 16 | 17 | ```bash 18 | git tag 19 | git push origin --tags 20 | 21 | # build binaries, copy manifest, and checksum files 22 | ./jenkins-bootstrap-shared/scripts/buildRelease.sh 23 | 24 | # upload binaries to GitHub (parallelized) 25 | ./jenkins-bootstrap-shared/scripts/uploadRelease.sh 26 | ``` 27 | 28 | # Troubleshooting 29 | 30 | If you get a 404 not found error, then you might not be using `repo` scope on a 31 | private repository. It's also possible you incorrectly set the `GITHUB_REPO` 32 | environment variable. 33 | 34 | If using GitHub Enterprise, then you'll need to set the following additional 35 | environment variable. 36 | 37 | export GITHUB_API=http://github.example.com/api/v3 38 | 39 | [os]: https://developer.github.com/apps/building-oauth-apps/scopes-for-oauth-apps/ 40 | [pat]: https://help.github.com/articles/creating-a-personal-access-token-for-the-command-line/ 41 | -------------------------------------------------------------------------------- /jenkins.gradle: -------------------------------------------------------------------------------- 1 | task cleanJenkins(type: Delete) { 2 | doFirst { 3 | //stop jenkins just in case it's running 4 | def stdout = new StringBuilder() 5 | def stderr = new StringBuilder() 6 | def process = ["${bootstrapHome}/scripts/provision_jenkins.sh", 'purge'].execute() 7 | process.waitForProcessOutput(stdout, stderr) 8 | println stdout.toString() 9 | println stderr.toString() 10 | } 11 | delete '.gradle' 12 | delete 'plugins' 13 | delete 'jenkins.war.tmp' 14 | delete 'jenkins.war.bak' 15 | } 16 | 17 | clean.dependsOn cleanJenkins 18 | 19 | configurations { 20 | getplugins { 21 | description = 'Download Jenkins plugins .jpi files.' 22 | transitive = false 23 | } 24 | getjenkins { 25 | description = 'Download Jenkins WAR file.' 26 | transitive = false 27 | } 28 | } 29 | repositories { 30 | maven { 31 | name 'jenkins' 32 | delegate.url('https://repo.jenkins-ci.org/public/') 33 | } 34 | mavenLocal() 35 | } 36 | 37 | //download and copy specific versions of plugins 38 | task getplugins(type: Copy) { 39 | into "${pluginsDest}/plugins" 40 | from configurations.getplugins 41 | include '*.hpi' 42 | include '*.jpi' 43 | rename '(.*)-[0-9]+[^-].*.hpi$', '$1.jpi' 44 | rename '(.*)-[0-9]+[^-].*.jpi$', '$1.jpi' 45 | } 46 | 47 | //download and copy a specific version of Jenkins 48 | task getjenkins(type: Copy) { 49 | //println configurations.getjenkins 50 | into jenkinsDest 51 | from configurations.getjenkins 52 | include '*.war' 53 | rename '.*', 'jenkins.war' 54 | } 55 | -------------------------------------------------------------------------------- /scripts/console-skip-2.0-wizard.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015-2023 Sam Gleske - https://github.com/samrocketman/jenkins-bootstrap-jervis 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | /* 17 | Skip the Jenkins 2.0 wizard. We already configure Jenkins to our liking during 18 | bootstrap. 19 | */ 20 | import hudson.util.PluginServletFilter 21 | def j=Jenkins.instance 22 | 23 | legacySetupWizard = ('getSetupWizard' in j.metaClass.methods*.name) 24 | newSetupWizard = (('getInstallState' in j.metaClass.methods*.name) && ('isSetupComplete' in j.installState.metaClass.methods*.name)) 25 | 26 | 27 | if((!newSetupWizard && legacySetupWizard) || (newSetupWizard && !j.installState.isSetupComplete())) { 28 | def w=j.setupWizard 29 | if(w != null) { 30 | try { 31 | //pre Jenkins 2.6 32 | w.completeSetup(j) 33 | PluginServletFilter.removeFilter(w.FORCE_SETUP_WIZARD_FILTER) 34 | } 35 | catch(Exception e) { 36 | w.completeSetup() 37 | } 38 | j.save() 39 | println 'Jenkins 2.0 wizard skipped.' 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /scripts/upgrade/upgradeJenkinsOnly.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015-2023 Sam Gleske - https://github.com/samrocketman/jenkins-bootstrap-shared 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | /* 17 | Will upgrade only Jenkins core assuming it has no plugins installed. This 18 | script supports the plugin refresh method. See plugin_refresh.md 19 | documentation. 20 | */ 21 | import hudson.model.UpdateCenter 22 | import jenkins.model.Jenkins 23 | 24 | //Check for the latest update center updates from jenkins.io 25 | Jenkins.instance.pluginManager.doCheckUpdatesServer() 26 | 27 | //get the current update center 28 | UpdateCenter center = Jenkins.instance.updateCenter 29 | 30 | //upgrade Jenkins core 31 | //fake emulate a stapler request 32 | def req = [ 33 | sendRedirect2: { String redirect -> null } 34 | ] as org.kohsuke.stapler.StaplerResponse 35 | //detect if Jenkins core needs an upgrade 36 | if(center.getCoreSource().data && center.getCoreSource().data.hasCoreUpdates() && !center.getHudsonJob()) { 37 | println "Upgrading Jenkins Core." 38 | center.doUpgrade(req) 39 | } 40 | null 41 | -------------------------------------------------------------------------------- /scripts/upgrade/minimal-upgrade.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | if [ ! -f plugins.txt ]; then 6 | echo 'ERROR: Missing plugins.txt at the root of your repository with plugin IDs to be installed.' >&2 7 | exit 1 8 | fi 9 | 10 | if ! type -P jq &> /dev/null; then 11 | echo 'ERROR: missing jq utility in PATH.' >&2 12 | exit 1 13 | fi 14 | 15 | latest_lts() { 16 | curl -sSfL https://updates.jenkins-ci.org/stable/update-center.json | \ 17 | awk 'NR == 1 {next}; {print; exit}' | \ 18 | jq -r '.core.version' 19 | } 20 | 21 | # update dependencies.gradle with latest release 22 | sed -i.bak 's/^\( *getjenkins *.*\):[^@]*@\(.*\)/\1:'"$(latest_lts)"'@\2/' \ 23 | dependencies.gradle 24 | rm dependencies.gradle.bak 25 | 26 | # Get the boot Jenkins and install plugins using Jenkins CLI 27 | ( 28 | # start Jenkins without plugins 29 | source env.sh 30 | "${SCRIPT_LIBRARY_PATH}"/../jenkins_bootstrap.sh --skip-plugins 31 | source env.sh 32 | source "${SCRIPT_LIBRARY_PATH}"/upgrade/setup_environment.sh 33 | 34 | # download jenkins cli, install plugins, remove jenkins cli 35 | curl -sSfLO "${JENKINS_WEB}/jnlpJars/jenkins-cli.jar" 36 | grep -v '^$\|^#.*' plugins.txt | \ 37 | xargs java -jar jenkins-cli.jar \ 38 | -auth "${JENKINS_USER}:${JENKINS_PASSWORD}" \ 39 | -s "${JENKINS_WEB}" \ 40 | -webSocket install-plugin 41 | rm jenkins-cli.jar 42 | 43 | "${SCRIPT_LIBRARY_PATH}"/upgrade/wait_for_upgrade_to_complete.sh 44 | if "${SCRIPT_LIBRARY_PATH}"/upgrade/jenkins_needs_restart.sh; then 45 | true # do nothing 46 | fi 47 | ) 48 | 49 | # update Gradle build files 50 | export NO_UPGRADE=1 51 | source env.sh 52 | "${SCRIPT_LIBRARY_PATH}"/upgrade/upgrade_build_gradle.sh 53 | -------------------------------------------------------------------------------- /packaging/preUninstall.sh.in: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | RPM_BASED=false 5 | DEB_BASED=false 6 | 7 | #detect using RedHat based package 8 | #if not RPM then it's DEB 9 | if [ -n "${RPM_PACKAGE_NAME}" ]; then 10 | RPM_BASED=true 11 | else 12 | DEB_BASED=true 13 | fi 14 | 15 | ${RPM_BASED} && [ -r '/etc/sysconfig/@@ARTIFACTNAME@@' ] && . '/etc/sysconfig/@@ARTIFACTNAME@@' 16 | ${DEB_BASED} && [ -r '/etc/default/@@ARTIFACTNAME@@' ] && . '/etc/default/@@ARTIFACTNAME@@' 17 | JENKINS_HOME="${JENKINS_HOME:-@@HOME@@}" 18 | JENKINS_USER="${JENKINS_USER:-@@USER@@}" 19 | RPM_INSTALL_PREFIX="${RPM_INSTALL_PREFIX:-@@PREFIX@@}" 20 | 21 | if ${RPM_BASED}; then 22 | case $1 in 23 | 0) 24 | # if this is uninstallation as opposed to upgrade, delete the service 25 | service @@ARTIFACTNAME@@ stop > /dev/null 2>&1 26 | chkconfig --del @@ARTIFACTNAME@@ 27 | #remove a symlink based on upstream RPM spec (see postInstall.sh.in for creation) 28 | rm -f "${RPM_INSTALL_PREFIX}/sbin/rc@@ARTIFACTNAME@@" 29 | ;; 30 | 1) 31 | #this is an upgrade; best to commit before upgrading package 32 | su -s /bin/bash -c "${JENKINS_HOME}/dailycommit.sh" - ${JENKINS_USER} 33 | ;; 34 | esac 35 | else 36 | #DEB_BASED == true 37 | case "$1" in 38 | remove) 39 | service @@ARTIFACTNAME@@ stop > /dev/null 2>&1 40 | ;; 41 | upgrade) 42 | #this is an upgrade; best to commit before upgrading package 43 | su -s /bin/bash -c "${JENKINS_HOME}/dailycommit.sh" - ${JENKINS_USER} 44 | ;; 45 | failed-upgrade|abort-install|abort-upgrade|disappear) 46 | ;; 47 | *) 48 | echo "postrm called with unknown argument '$1'" >&2 49 | exit 1 50 | ;; 51 | esac 52 | fi 53 | 54 | exit 0 55 | -------------------------------------------------------------------------------- /tests/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | export LC_ALL=C 3 | export PS4='$ ' 4 | export DEBIAN_FRONTEND=noninteractive 5 | if [ ! -d ".git" ]; then 6 | echo 'ERROR: must be run from the root of the repository. e.g.' 7 | echo './tests/test.sh' 8 | exit 1 9 | fi 10 | if [ -z "${GITHUB_TOKEN}" ]; then 11 | echo 'WARNING: Tests may fail without GITHUB_TOKEN environment variable set.' 12 | fi 13 | echo 'Last command to exit has the non-zero exit status.' 14 | 15 | #configure error exit trap 16 | function on_err() { 17 | set +x 18 | echo "^^ command above has non-zero exit code." 19 | echo 20 | echo "Cleaning up test environment: ./gradlew clean" 21 | ./gradlew clean &> /dev/null 22 | } 23 | trap on_err ERR 24 | 25 | set -x 26 | 27 | bash -n ./tests/random_port.sh 28 | test -x ./tests/random_port.sh 29 | export RANDOM_PORT="$(./tests/random_port.sh)" 30 | bash -n ./jenkins_bootstrap.sh 31 | test -x ./jenkins_bootstrap.sh 32 | export JENKINS_START="java -jar jenkins.war --httpPort=${RANDOM_PORT} --httpListenAddress=127.0.0.1" 33 | export JENKINS_WEB="http://127.0.0.1:${RANDOM_PORT}" 34 | export JENKINS_CLI="java -jar ./jenkins-cli.jar -s http://127.0.0.1:${RANDOM_PORT} -noKeyAuth" 35 | export JENKINS_HOME="$(mktemp -d /tmp/my_jenkins_homeXXX)" 36 | set +x 37 | ./jenkins_bootstrap.sh 38 | test -e jenkins.pid 39 | ./scripts/provision_jenkins.sh url-ready "${JENKINS_WEB}/jnlpJars/jenkins-cli.jar" 40 | bash -x ./scripts/provision_jenkins.sh stop 41 | test ! -e jenkins.pid 42 | test -e console.log 43 | test -e jenkins.war 44 | test -e "$JENKINS_HOME" 45 | test -e plugins 46 | ./gradlew clean 47 | test ! -e console.log 48 | test ! -e jenkins.war 49 | test ! -e "$JENKINS_HOME" 50 | test ! -e plugins 51 | 52 | # check for bash syntax 53 | find . -type f -name '*.sh' | xargs -n1 bash -n 54 | -------------------------------------------------------------------------------- /scripts/configure-slack.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015-2023 Sam Gleske - https://github.com/samrocketman/jenkins-bootstrap-shared 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | /* 17 | Configure slack plugin global settings. 18 | */ 19 | import jenkins.model.Jenkins 20 | 21 | if(!binding.hasVariable('slack_settings')) { 22 | slack_settings = [:] 23 | } 24 | if(!(slack_settings instanceof Map)) { 25 | throw new Exception('slack_settings must be a Map.') 26 | } 27 | 28 | def slack = Jenkins.instance.getExtensionList('jenkins.plugins.slack.SlackNotifier$DescriptorImpl')[0] 29 | 30 | boolean save = false 31 | String teamDomain = (slack_settings['teamDomain'])?:'' 32 | String tokenCredentialId = (slack_settings['tokenCredentialId'])?:'' 33 | String room = (slack_settings['room'])?:'' 34 | 35 | if(teamDomain != slack.teamDomain) { 36 | slack.teamDomain = teamDomain 37 | save = true 38 | } 39 | if(tokenCredentialId != slack.tokenCredentialId) { 40 | slack.tokenCredentialId = tokenCredentialId 41 | save = true 42 | } 43 | if(room != slack.room) { 44 | slack.room = room 45 | save = true 46 | } 47 | if(save) { 48 | println 'Slack configured.' 49 | slack.save() 50 | } 51 | else { 52 | println 'Nothing changed. Slack already configured.' 53 | } 54 | -------------------------------------------------------------------------------- /scripts/upgrade/upgradeJenkinsAndPlugins.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015-2023 Sam Gleske - https://github.com/samrocketman/jenkins-bootstrap-slack 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | /* 17 | Initiate an asynchronous upgrade of Jenkins core and plugins. This script 18 | will only upgrade if an upgrade is necessary. This script will not attempt 19 | to upgrade plugins which have already been upgraded. 20 | */ 21 | import hudson.model.UpdateCenter 22 | 23 | //Check for the latest update center updates from jenkins.io 24 | Jenkins.instance.pluginManager.doCheckUpdatesServer() 25 | 26 | //get the current update center 27 | UpdateCenter center = Jenkins.instance.updateCenter 28 | while(!center.isSiteDataReady()) { 29 | sleep(500) 30 | } 31 | 32 | //upgrade Jenkins core 33 | //fake emulate a stapler request 34 | def req = [ 35 | sendRedirect2: { String redirect -> null } 36 | ] as org.kohsuke.stapler.StaplerResponse 37 | //detect if Jenkins core needs an upgrade 38 | if(center.getCoreSource().data && center.getCoreSource().data.hasCoreUpdates() && !center.getHudsonJob()) { 39 | println "Upgrading Jenkins Core." 40 | center.doUpgrade(req) 41 | } 42 | 43 | //schedule an upgrade of all plugins 44 | print "Upgrading Plugins: " 45 | println center.updates.findAll { center.getJob(it) == null }.each { it.deploy(false) }*.name 46 | null 47 | -------------------------------------------------------------------------------- /tests/job_test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | #Created by Sam Gleske 3 | #Reads Jenkins lastBuild API input until a build is finished. 4 | #Prints out console log when build is finished. 5 | #Exits non-zero if failed build or zero if success. 6 | 7 | import base64 8 | import json 9 | import os.path 10 | import sys 11 | import time 12 | import urllib2 13 | 14 | headers={ 15 | 'Host': 'localhost' 16 | } 17 | 18 | #Check for Jenkins 2.0 authentication 19 | jenkins_home=os.environ['JENKINS_HOME'] 20 | if os.path.isfile(jenkins_home + '/secrets/initialAdminPassword'): 21 | with open(jenkins_home + '/secrets/initialAdminPassword', 'r') as f: 22 | password = f.read().strip() 23 | headers['Authorization'] = "Basic %s" % base64.b64encode("admin:%s" % password) 24 | 25 | #CSRF protection support 26 | csrf_crumb=os.environ['CSRF_CRUMB'] 27 | if len(csrf_crumb) > 0: 28 | headers[csrf_crumb.split('=')[0]] = csrf_crumb.split('=')[1] 29 | 30 | #assume always first argument and first argument is a proper URL to Jenkins 31 | response = {} 32 | STATUS = 0 33 | while True: 34 | req = urllib2.Request(url=sys.argv[1], headers=headers) 35 | url = urllib2.urlopen(req) 36 | response = json.load(url) 37 | url.close() 38 | if not response['building']: 39 | break 40 | time.sleep(1) 41 | 42 | console_url = response['url'] + 'consoleText' 43 | if len(os.environ['JENKINS_WEB']) > 0: 44 | jenkins_host = os.environ['JENKINS_WEB'].split('/')[2] 45 | console_url = console_url.split('/') 46 | console_url[2] = jenkins_host 47 | console_url = '/'.join(console_url) 48 | print "Retrieving %s" % console_url 49 | req = urllib2.Request(url=console_url, headers=headers) 50 | url = urllib2.urlopen(req) 51 | console = url.read() 52 | url.close() 53 | 54 | if 'WARNING' in console: 55 | STATUS = 1 56 | 57 | if 'SUCCESS' != response['result']: 58 | STATUS = 1 59 | 60 | print console 61 | sys.exit(STATUS) 62 | -------------------------------------------------------------------------------- /packaging/share/gitignore.in: -------------------------------------------------------------------------------- 1 | # Learn more about Jenkins and JENKINS_HOME directory for which this file is 2 | # intended. 3 | # 4 | # http://jenkins-ci.org/ 5 | # https://wiki.jenkins-ci.org/display/JENKINS/Administering+Jenkins 6 | # 7 | # Note: secret.key is purposefully not tracked by git. This should be backed up 8 | # separately because configs may contain secrets which were encrypted using the 9 | # secret.key. To back up secrets use 'tar -czf /tmp/secrets.tgz secret*' and 10 | # save the file separate from your repository. 11 | 12 | # ignore all JENKINS_HOME except jobs directory, root xml config, and .gitignore file 13 | /* 14 | !/jobs 15 | !/.gitignore 16 | !/*.xml 17 | !/@@MANIFEST@@ 18 | !/dailycommit.sh 19 | !/@@PACKAGENAME@@-commit 20 | !/cron.disabled 21 | 22 | # ignore all files in jobs subdirectories except for folders 23 | # note: git doesn't track folders, only file content 24 | jobs/** 25 | !jobs/**/ 26 | 27 | # save next build numbers with config 28 | !jobs/**/nextBuildNumber 29 | 30 | # For performance reasons, we want to ignore builds in Jenkins jobs because it 31 | # contains many tiny files on large installations. This can impact git 32 | # performance when running even basic commands like 'git status'. 33 | builds 34 | indexing 35 | 36 | # exclude only config.xml files in repository subdirectories 37 | !config.xml 38 | 39 | # don't track workspaces (when users build on the master) 40 | jobs/**/*workspace 41 | 42 | # Security warning: If secrets are included with your configuration, then an 43 | # adversary be able to decrypt all encrypted secrets within Jenkins config. 44 | # Including secrets is a bad practice, but the example is included in case 45 | # someone still wants it for convenience. Uncomment the following line to 46 | # include secrets for decryption with repository configuration in Git. 47 | 48 | #!/secret* 49 | 50 | # As a result only Jenkins settings and job config.xml files in JENKINS_HOME 51 | # will be tracked by git 52 | -------------------------------------------------------------------------------- /scripts/console-needs-restart.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015-2023 Sam Gleske - https://github.com/samrocketman/jenkins-bootstrap-jervis 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | /* 18 | Checks if Jenkins needs a restart after plugins were installed. 19 | 20 | Example Usage: 21 | curl -s --data-urlencode "script=$( plugins_to_install = [ 26 | "git", "github", "github-oauth", "token-macro", 27 | "cloudbees-folder", "job-dsl", "view-job-filters", 28 | "embeddable-build-status", "groovy", "dashboard-view", "rich-text-publisher-plugin", "console-column-plugin", "docker-plugin" 29 | ] 30 | 31 | List plugins = Jenkins.instance.pluginManager.getPlugins() 32 | 33 | //get a list of installed plugins 34 | Set installed_plugins = [] 35 | plugins.each { 36 | installed_plugins << it.getShortName() 37 | } 38 | 39 | //get a list of plugins needing an update 40 | Set plugins_to_update = [] 41 | plugins.each { 42 | if(it.hasUpdate()) { 43 | plugins_to_update << it.getShortName() 44 | } 45 | } 46 | 47 | //print out a statement for bash 48 | if(plugins_to_update.size() > 0 || (plugins_to_install-installed_plugins).size() > 0) { 49 | println "true" 50 | } else { 51 | println "false" 52 | } 53 | -------------------------------------------------------------------------------- /scripts/configure-lockable-resources.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015-2023 Sam Gleske - https://github.com/samrocketman/jenkins-bootstrap-shared 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | /* 17 | Configures global lockable resources. 18 | 19 | lockable-resources 2.2 20 | */ 21 | import jenkins.model.Jenkins 22 | import org.jenkins.plugins.lockableresources.LockableResource 23 | import org.jenkins.plugins.lockableresources.LockableResourcesManager 24 | 25 | if(!binding.hasVariable('lockable_resources')) { 26 | lockable_resources = [] 27 | } 28 | if(!(lockable_resources in List)) { 29 | throw new Exception('lockable_resources must be a List of Maps.') 30 | } 31 | lockable_resources.each { m -> 32 | if(!(m in Map)) { 33 | throw new Exception('lockable_resources must be a List of Maps.') 34 | } 35 | } 36 | 37 | List resources = [] 38 | 39 | lockable_resources.each { Map r -> 40 | if(r['name']) { 41 | resources << new LockableResource(r['name'], r['description']?:'', r['labels']?:'', r['reservedBy']?:'') 42 | } 43 | } 44 | 45 | if(resources) { 46 | Jenkins j = Jenkins.instance 47 | LockableResourcesManager resourceManager = j.getExtensionList(LockableResourcesManager.class)[0] 48 | if(resourceManager.resources != resources) { 49 | resourceManager.resources = resources 50 | resourceManager.save() 51 | println "Configured lockable resources: ${resources*.name.join(', ')}" 52 | } 53 | else { 54 | println 'Nothing changed. Lockable resources already configured.' 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /scripts/init.groovy.d/disable-all-update-sites.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015-2023 Sam Gleske - https://github.com/samrocketman/jenkins-bootstrap-jervis 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | this software and associated documentation files (the "Software"), to deal in 6 | the Software without restriction, including without limitation the rights to 7 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 8 | the Software, and to permit persons to whom the Software is furnished to do so, 9 | subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 16 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 17 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 18 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 19 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | /* 23 | This script completely disables all update sites in Jenkins, invalidates plugin 24 | upgrade data, and deletes the cached update data. 25 | 26 | Additionally, it sets a system property to disable all update sites. 27 | */ 28 | 29 | import hudson.model.UpdateSite 30 | import jenkins.model.Jenkins 31 | 32 | def j = Jenkins.instance 33 | for(UpdateSite site : j.getUpdateCenter().getSiteList()) { 34 | site.neverUpdate = true 35 | site.data = null 36 | if('setDataLastReadFromFile' in site.metaClass.methods*.name.sort().unique()) { 37 | site.dataLastReadFromFile = -1 38 | } 39 | site.dataTimestamp = 0 40 | new File(j.getRootDir(), "updates/${site.id}.json").delete() 41 | } 42 | 43 | //https://wiki.jenkins-ci.org/display/JENKINS/Features+controlled+by+system+properties 44 | System.setProperty('hudson.model.UpdateCenter.never', 'true') 45 | null 46 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | IMAGE_VERSION := $(shell awk 'BEGIN {FS="="}; $$1 == "version" { print $$2; exit }' gradle.properties ) 2 | PACKAGENAME := $(shell awk $$'$$1 == "PACKAGENAME:" { gsub("[\',]", "", $$2); print $$2 }' variables.gradle ) 3 | NEXUS_ENDPOINT := 4 | NEXUS_URL := https://$(NEXUS_ENDPOINT)/ 5 | DOCKER_PATH := samrocketman/jenkins 6 | NEXUS_MANIFESTS := $(NEXUS_URL)repository/hosted-docker/v2/$(DOCKER_PATH)/manifests 7 | .PHONE: help docker publish tag-release 8 | 9 | help: 10 | @echo 'Run "make docker" to build docker images.' 11 | @echo 'Run "make tag-release" to idempotently git tag and publish to GitHub.' 12 | @echo 'Run "make publish" to publish docker image to Nexus. (includes "make docker" and "make tag-release")' 13 | 14 | build/distributions/jenkins-ng-$(IMAGE_VERSION).tar: dependencies.gradle custom-plugins.txt gradle.properties 15 | ./gradlew clean buildTar 16 | 17 | docker: build/distributions/$(PACKAGENAME)-$(IMAGE_VERSION).tar 18 | docker build -t $(NEXUS_ENDPOINT)/$(DOCKER_PATH):$(IMAGE_VERSION) -f ./jenkins-bootstrap-shared/Dockerfile . 19 | 20 | clean: 21 | docker rmi $(NEXUS_ENDPOINT)/$(DOCKER_PATH):$(IMAGE_VERSION) 22 | docker image prune -f 23 | 24 | tag-release: 25 | set -ex; \ 26 | if ! git tag | grep '\b$(IMAGE_VERSION)\b'; then \ 27 | git tag $(IMAGE_VERSION); \ 28 | git remote | xargs -n1 -I{} git push {} 'refs/tags/$(IMAGE_VERSION):refs/tags/$(IMAGE_VERSION)'; \ 29 | elif git tag --points-at HEAD | grep '\b$(IMAGE_VERSION)\b'; then \ 30 | echo 'SUCCESS: tag $(IMAGE_VERSION) already exists; nothing to do...'; \ 31 | else \ 32 | echo 'ERROR: tag $(IMAGE_VERSION) exists but does not point to this commit. Cancelling release...'; \ 33 | exit 1; \ 34 | fi 35 | 36 | nexus-health-check: 37 | curl --show-error -fsLIo /dev/null -- $(NEXUS_URL) 38 | 39 | publish: tag-release docker nexus-health-check 40 | set -ex; \ 41 | if curl --show-error -fsLo /dev/null -- $(NEXUS_MANIFESTS)/$(IMAGE_VERSION); then \ 42 | echo "Docker image already exists in Nexus... not publishing again."; \ 43 | else \ 44 | echo "Docker image does not exist on Nexus so proceeding to publish to Nexus."; \ 45 | docker push $(NEXUS_ENDPOINT)/$(DOCKER_PATH):$(IMAGE_VERSION); \ 46 | fi 47 | -------------------------------------------------------------------------------- /scripts/configure-github-branch-source-plugin.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015-2023 Sam Gleske - https://github.com/samrocketman/jenkins-bootstrap-jervis 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | /* 17 | Configure API endpoints in the GitHub Branch Source plugin global 18 | configuration. 19 | 20 | github-branch-source 2.2.3 21 | */ 22 | import org.jenkinsci.plugins.github_branch_source.GitHubConfiguration 23 | import org.jenkinsci.plugins.github_branch_source.Endpoint 24 | 25 | boolean isEndpointsEqual(List e1, List e2) { 26 | e1.size() == e2.size() && 27 | !( 28 | false in [e1, e2].transpose().collect { a, b -> 29 | a.name == b.name && 30 | a.apiUri == b.apiUri 31 | } 32 | ) 33 | } 34 | 35 | /* Example configuration 36 | github_branch_source = [ 37 | 'https://github.example.com/api/v3': 'Public GitHub API' 38 | ] 39 | */ 40 | 41 | if(!binding.hasVariable('github_branch_source')) { 42 | github_branch_source = [:] 43 | } 44 | if(!github_branch_source in Map) { 45 | throw new Exception("github_branch_source must be an instance of Map but instead is instance of: ${github_branch_source.getClass()}") 46 | } 47 | 48 | List endpoints = github_branch_source.collect { k, v -> new Endpoint(k, v) } 49 | def global_settings = Jenkins.instance.getExtensionList(GitHubConfiguration.class)[0] 50 | 51 | if(!isEndpointsEqual(global_settings.endpoints, endpoints)) { 52 | global_settings.endpoints = endpoints 53 | println 'Configured GitHub Branch Source plugin.' 54 | } 55 | else { 56 | println 'Nothing changed. GitHub Branch Source plugin already configured.' 57 | } 58 | -------------------------------------------------------------------------------- /scripts/upgrade/refresh_plugins.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #Created by Sam Gleske 3 | #Sat 19 Feb 2022 02:28:59 PM EST 4 | #Ubuntu 20.04.3 LTS 5 | #Linux 5.13.0-30-generic x86_64 6 | #GNU bash, version 5.0.17(1)-release (x86_64-pc-linux-gnu) 7 | 8 | set -e 9 | 10 | function cleanup_on() { 11 | #clean up jenkins headers file 12 | [ -n "${JENKINS_HEADERS_FILE}" -a -f "${JENKINS_HEADERS_FILE}" ] && rm -f "${JENKINS_HEADERS_FILE}" 13 | [ -n "${VAGRANT_SSH_CONFIG}" -a -f "${VAGRANT_SSH_CONFIG}" ] && rm -f "${VAGRANT_SSH_CONFIG}" 14 | if [ "$1" = '0' ]; then 15 | echo "Jenkins is ready. Visit ${JENKINS_WEB}/" 16 | echo "User: ${JENKINS_USER}" 17 | echo "Password: ${JENKINS_PASSWORD}" 18 | fi 19 | } 20 | trap 'cleanup_on $?' EXIT 21 | 22 | source env.sh 23 | source "${SCRIPT_LIBRARY_PATH}"/upgrade/setup_environment.sh 24 | 25 | #wait for jenkins to become available 26 | "${SCRIPT_LIBRARY_PATH}"/provision_jenkins.sh url-ready "${JENKINS_WEB}/jnlpJars/jenkins-cli.jar" 27 | 28 | if [ ! -f pinned-plugins.txt ]; then 29 | echo 'ERROR: No pinned plugins found so cannot proceed.' >&2 30 | exit 1 31 | fi 32 | 33 | "${SCRIPT_LIBRARY_PATH}"/jenkins_call.sh "${SCRIPT_LIBRARY_PATH}"/upgrade/upgradeJenkinsOnly.groovy || ( 34 | echo "Upgrading Jenkins and plugins failed. Likely, jenkins.war or plugins" 35 | echo "are not writeable. This is typical if upgrading jenkins.war via" 36 | echo "vagrant because jenkins.war is owned by root" 37 | exit 1 38 | ) >&2 39 | 40 | # upgrade and restart if necessary 41 | "${SCRIPT_LIBRARY_PATH}"/upgrade/wait_for_upgrade_to_complete.sh 42 | if "${SCRIPT_LIBRARY_PATH}"/upgrade/jenkins_needs_restart.sh; then 43 | #set up Jenkins env vars post-restart 44 | source "${SCRIPT_LIBRARY_PATH}"/upgrade/env.sh 45 | fi 46 | 47 | "${SCRIPT_LIBRARY_PATH}"/jenkins_call.sh "${SCRIPT_LIBRARY_PATH}"/upgrade/installMinimalPlugins.groovy --data-string "plugins='''$(< pinned-plugins.txt)'''.trim();" 48 | "${SCRIPT_LIBRARY_PATH}"/upgrade/wait_for_upgrade_to_complete.sh 49 | if "${SCRIPT_LIBRARY_PATH}"/upgrade/jenkins_needs_restart.sh; then 50 | #set up Jenkins env vars post-restart 51 | source "${SCRIPT_LIBRARY_PATH}"/upgrade/env.sh 52 | fi 53 | export NO_UPGRADE=1 54 | "${SCRIPT_LIBRARY_PATH}"/upgrade/upgrade_build_gradle.sh* 55 | -------------------------------------------------------------------------------- /scripts/set-content-security-policy.groovy: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | Copyright (c) 2015-2023 Sam Gleske - https://github.com/samrocketman/jenkins-bootstrap-shared 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | /* 24 | Configure the content security policy to be less strict so reports can be 25 | loaded. This is necessary for reporting tools which embed unsafe inline 26 | JavaScript and CSS. 27 | 28 | Note: if this is undesirable, then you can override this script to disable 29 | it in a local bootstrapper. Example: 30 | 31 | mkdir scripts/init.groovy.d 32 | touch scripts/init.groovy.d/set-content-security-policy.groovy 33 | 34 | By having an empty file in your local bootstrapper, this script will get 35 | overwritten by it and never execute. Alternatively, you could copy this 36 | script to your local bootstrapper and customize the content security policy 37 | yourself. 38 | 39 | See also https://wiki.jenkins.io/display/JENKINS/Configuring+Content+Security+Policy 40 | */ 41 | 42 | import jenkins.model.Jenkins 43 | 44 | System.setProperty("hudson.model.DirectoryBrowserSupport.CSP", "default-src 'self' ${(Jenkins.instance.rootUrl ?: 'http://localhost:8080') -~ '/$'} 'unsafe-inline'") 45 | -------------------------------------------------------------------------------- /scripts/configure-github-oauth.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015-2023 Sam Gleske - https://github.com/samrocketman/jenkins-bootstrap-jervis 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | /* 18 | Configures GitHub as the security realm from the GitHub Authentication 19 | Plugin (github-oauth). 20 | 21 | github-oauth 0.29 22 | */ 23 | 24 | import hudson.security.SecurityRealm 25 | import org.jenkinsci.plugins.GithubSecurityRealm 26 | import net.sf.json.JSONObject 27 | 28 | if(!binding.hasVariable('github_realm')) { 29 | github_realm = [:] 30 | } 31 | 32 | if(!(github_realm instanceof Map)) { 33 | throw new Exception('github_realm must be a Map.') 34 | } 35 | 36 | github_realm = github_realm as JSONObject 37 | 38 | String githubWebUri = github_realm.optString('web_uri', GithubSecurityRealm.DEFAULT_WEB_URI) 39 | String githubApiUri = github_realm.optString('api_uri', GithubSecurityRealm.DEFAULT_API_URI) 40 | String oauthScopes = github_realm.optString('oauth_scopes', GithubSecurityRealm.DEFAULT_OAUTH_SCOPES) 41 | String clientID = github_realm.optString('client_id') 42 | String clientSecret = github_realm.optString('client_secret') 43 | 44 | if(clientID && clientSecret) { 45 | SecurityRealm github_realm = new GithubSecurityRealm(githubWebUri, githubApiUri, clientID, clientSecret, oauthScopes) 46 | //check for equality, no need to modify the runtime if no settings changed 47 | if(!github_realm.equals(Jenkins.instance.getSecurityRealm())) { 48 | Jenkins.instance.setSecurityRealm(github_realm) 49 | Jenkins.instance.save() 50 | println 'Security realm configuration has changed. Configured GitHub security realm.' 51 | } else { 52 | println 'Nothing changed. GitHub security realm already configured.' 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /variables.gradle: -------------------------------------------------------------------------------- 1 | //perhaps use JENKINS_HOME some day 2 | //avoiding JENKINS_HOME because I only want to copy if file doesn't exist 3 | //by default the copy function will overwrite plugins in JENKINS_HOME if the hash is different 4 | ext.jenkinsHome = System.getenv('JENKINS_HOME')?:'../my_jenkins_home' 5 | ext.pluginsDest = '.' 6 | ext.jenkinsDest = '.' 7 | //tokens for Jenkins packaging 8 | //note: tokens do not auto-correct when rpm prefix is customized during install 9 | ext.tokens = [ 10 | ARTIFACTNAME: 'jenkins', 11 | AUTHOR: 'Sam Gleske ', 12 | CAMELARTIFACTNAME: 'Jenkins', 13 | CHANGELOG_PAGE: 'http://jenkins.io/changelog', 14 | COMMIT: ['git', 'rev-parse', 'HEAD'].execute().text.toString().trim(), 15 | HOMEPAGE: 'https://github.com/samrocketman/jenkins-bootstrap-shared', 16 | LICENSE: 'MIT/X License, GPL/CDDL, ASL2', 17 | LICENSE_FILE: 'LICENSE', 18 | MANIFEST: 'jenkins-versions.manifest', 19 | PACKAGENAME: 'jenkins-bootstrap', 20 | PRODUCTNAME: 'Jenkins', 21 | SUMMARY: 'Jenkins Continuous Integration Server', 22 | VENDOR: 'jenkins-bootstrap by Sam Gleske', 23 | //additional system configuration customization 24 | PREFIX: '/usr', 25 | USER: 'jenkins', 26 | HOME: '/var/lib/jenkins', //JENKINS_HOME 27 | JENKINS_JAVA_CMD: '/usr/bin/java', 28 | JENKINS_JAVA_OPTIONS: '-Djava.awt.headless=true', 29 | HTTP_PORT: '8080', 30 | //RPM only 31 | JENKINS_DEBUG_LEVEL: '5', //5 is INFO; higher means more verbose 32 | JENKINS_ENABLE_ACCESS_LOG: 'no', //yes or no 33 | JENKINS_ENABLE_SYSV_CONFIG: 'no', //yes or no; should always be no for systemd operating systems 34 | HTTP_LISTEN_ADDRESS: '', 35 | HTTPS_PORT: '', //disabled by default 36 | HTTPS_KEYSTORE: '', //disabled by default 37 | HTTPS_KEYSTORE_PASSWORD: '', //disabled by default; security note: instead of hardcoding a password in the package set something that is easy to replace with sed 38 | HTTPS_LISTEN_ADDRESS: '', //disabled by default 39 | HTTP_MAX_WORKER_THREADS: '100', 40 | HTTP_MAX_IDLE_THREADS: '20', 41 | ADDITIONAL_JENKINS_ARGS: '', 42 | //DEB only 43 | MAXOPENFILES: '8192', //max open file handlers 44 | JENKINS_ARGS: '--webroot=/var/cache/$NAME/war --httpPort=$HTTP_PORT' 45 | ] 46 | ext.tokens['WAR'] = "${ext.tokens['PREFIX']}/lib/${ext.tokens['ARTIFACTNAME']}/${ext.tokens['ARTIFACTNAME']}.war".toString() 47 | -------------------------------------------------------------------------------- /scripts/create-job.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015-2023 Sam Gleske - https://github.com/samrocketman/jenkins-bootstrap-jervis 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | /* 17 | Create Jobs using the script console 18 | Script should be prepended with the following properties. 19 | - String itemName 20 | - String xmlData 21 | 22 | Example Usage: 23 | curl --data-urlencode "script=String itemName='_jervis_generator';String xmlData='''$(<./configs/job_jervis_config.xml)''';$(<./scripts/create-job.groovy)" http://localhost:8080/scriptText 24 | */ 25 | 26 | import javax.xml.transform.stream.StreamSource 27 | import jenkins.model.Jenkins 28 | 29 | void createOrUpdateJob(String name, String xml) { 30 | def j = Jenkins.instance 31 | String fullName = name 32 | if(name.contains('/')) { 33 | j = j.getItemByFullName(name.tokenize('/')[0..-2]) 34 | name = name.tokenize('/')[-1] 35 | } 36 | Jenkins.checkGoodName(name) 37 | if(j.getItem(name) == null) { 38 | println "Created job \"${fullName}\"." 39 | j.createProjectFromXML(name, new ByteArrayInputStream(xml.getBytes())) 40 | j.save() 41 | } 42 | else if(j.getItem(name).configFile.asString().trim() != xml.trim()) { 43 | j.getItem(name).updateByXml(new StreamSource(new ByteArrayInputStream(xml.getBytes()))) 44 | j.getItem(name).save() 45 | println "Job \"${fullName}\" already exists. Updated using XML." 46 | } 47 | else { 48 | println "Nothing changed. Job \"${fullName}\" already exists." 49 | } 50 | } 51 | 52 | try { 53 | //just by trying to access properties should throw an exception 54 | itemName == null 55 | xmlData == null 56 | isPropertiesSet = true 57 | } catch(MissingPropertyException e) { 58 | println 'ERROR Can\'t create job.' 59 | println 'ERROR Missing properties: itemName, xmlData' 60 | return 61 | } 62 | 63 | createOrUpdateJob(itemName, xmlData) 64 | -------------------------------------------------------------------------------- /scripts/configure-global-jenkinsfile.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015-2023 Sam Gleske - https://github.com/samrocketman/jenkins-bootstrap-jervis 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | /* 18 | Configure a global Jenkinsfile within the Config File Provider plugin. This 19 | is meant to be used by the Pipeline Multibranch Defaults Plugin. 20 | */ 21 | 22 | import jenkins.model.Jenkins 23 | import org.jenkinsci.plugins.configfiles.GlobalConfigFiles 24 | import org.jenkinsci.plugins.configfiles.groovy.GroovyScript 25 | import org.jenkinsci.plugins.scriptsecurity.scripts.languages.GroovyLanguage 26 | 27 | //bindings 28 | script_approval = Jenkins.instance.getExtensionList('org.jenkinsci.plugins.scriptsecurity.scripts.ScriptApproval')[0] 29 | config_files = Jenkins.instance.getExtensionList(GlobalConfigFiles)[0] 30 | 31 | if(!binding.hasVariable('jenkinsfile_script')) { 32 | jenkinsfile_script = '' 33 | } 34 | if(!(jenkinsfile_script instanceof String)) { 35 | throw new Exception('jenkinsfile_script must be a String.') 36 | } 37 | 38 | jenkinsfile_script = jenkinsfile_script.trim() 39 | 40 | if(!jenkinsfile_script) { 41 | return 42 | } 43 | 44 | if(!config_files.getById('Jenkinsfile') || (config_files.getById('Jenkinsfile').content != jenkinsfile_script)) { 45 | //create global config 46 | GroovyScript new_config = new GroovyScript('Jenkinsfile', 'Global Jenkinsfile', '', jenkinsfile_script) 47 | config_files.save(new_config) 48 | println 'Configured global Jenkinsfile.' 49 | } 50 | else { 51 | println 'Nothing changed. Global Jenkinsfile already configured.' 52 | } 53 | 54 | //approve Jenkinsfile script for execution 55 | String hash = script_approval.DEFAULT_HASHER.hash(jenkinsfile_script, GroovyLanguage.get().getName()) 56 | if(hash in script_approval.approvedScriptHashes) { 57 | println 'Nothing changed. Global Jenkinsfile script already approved.' 58 | } 59 | else { 60 | script_approval.approveScript(hash) 61 | script_approval.save() 62 | println 'Global Jenkinsfile script has been approved in script security.' 63 | } 64 | -------------------------------------------------------------------------------- /scripts/configure-primary-view.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015-2023 Sam Gleske - https://github.com/samrocketman/jenkins-bootstrap-jervis 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | /* 17 | Sets the primary view of Jenkins. 18 | 19 | Example Usage: 20 | curl --data-urlencode "script=$(<./scripts/configure-primary-view.groovy)" http://localhost:8080/scriptText 21 | */ 22 | import hudson.model.View 23 | import jenkins.model.Jenkins 24 | 25 | List plugins = Jenkins.instance.pluginManager.getPlugins() 26 | //get a list of installed plugins 27 | Set installed_plugins = [] 28 | plugins.each { 29 | installed_plugins << it.getShortName() 30 | } 31 | 32 | Set required_plugins = ['dashboard-view', 'view-job-filters'] 33 | 34 | Boolean hasConfigBeenUpdated = false 35 | 36 | //only execute of all required plugins are installed 37 | if((required_plugins-installed_plugins).size() == 0) { 38 | Jenkins instance = Jenkins.getInstance() 39 | View welcome_view = instance.getView('Welcome') 40 | View all_view = instance.getView('All') 41 | View primary_view = instance.getPrimaryView() 42 | if(welcome_view != null) { 43 | if(!primary_view.name.equals(welcome_view.name)) { 44 | println 'Set primary view to "Welcome".' 45 | instance.setPrimaryView(welcome_view) 46 | hasConfigBeenUpdated = true 47 | } else { 48 | println 'Nothing changed. Primary view already set to "Welcome".' 49 | } 50 | if(all_view != null) { 51 | println 'Deleting "All" view.' 52 | instance.deleteView(all_view) 53 | hasConfigBeenUpdated = true 54 | } 55 | //save configuration to disk 56 | if(hasConfigBeenUpdated) { 57 | instance.save() 58 | } 59 | } else { 60 | println '"Welcome" view not found. Nothing changed.' 61 | } 62 | } else { 63 | println 'Unable to configure primary view.' 64 | println "Missing required plugins: ${required_plugins-installed_plugins}" 65 | } 66 | -------------------------------------------------------------------------------- /scripts/configure-script-security.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015-2023 Sam Gleske - https://github.com/samrocketman/jenkins-bootstrap-jervis 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | /* 17 | Configure script-security plugin settings. 18 | 19 | Requirements: 20 | * Add (newly) approved script signatures to the list in configs/script-security-settings.groovy. 21 | * Compatible with script-security v1.73 22 | 23 | Reference APIs: 24 | 25 | * https://github.com/jenkinsci/script-security-plugin/blob/master/src/main/java/org/jenkinsci/plugins/scriptsecurity/scripts/ScriptApproval.java 26 | 27 | */ 28 | 29 | import jenkins.model.Jenkins 30 | import org.jenkinsci.plugins.scriptsecurity.scripts.ScriptApproval 31 | 32 | // CONFIGURATION AS CODE 33 | if(!binding.hasVariable('script_security_settings')) { 34 | println 'Configure script-security plugin by modifying configs/script-security-settings.groovy.' 35 | script_security_settings = [:] 36 | } 37 | 38 | if(!(script_security_settings instanceof Map)) { 39 | throw new Exception('script_security_settings must be a Map.') 40 | } 41 | ScriptApproval scriptApprovalConfig = Jenkins.instance.getExtensionList(ScriptApproval)[0] 42 | 43 | List newlyApprovedSignatures = ((script_security_settings['approvedSignatures'])?:[])*.trim().sort() 44 | List existingApprovedSignatures = scriptApprovalConfig.getApprovedSignatures().toList()*.trim().sort() 45 | 46 | // build a full list of signatures 47 | newlyApprovedSignatures = (newlyApprovedSignatures - existingApprovedSignatures).sort().unique() 48 | 49 | List signaturesToApprove = newlyApprovedSignatures + existingApprovedSignatures 50 | 51 | if (signaturesToApprove.sort().unique() != scriptApprovalConfig.approvedSignatures.toList().sort().unique()) { 52 | println "Configuring script-security plugin with approved signatures:\n ${signaturesToApprove.join('\n ')}" 53 | scriptApprovalConfig.setApprovedSignatures(newlyApprovedSignatures as String[]) 54 | println "Successfully configured script-security plugin." 55 | } else { 56 | println "Nothing changed. config/script-security-settings.groovy already configured." 57 | } 58 | 59 | -------------------------------------------------------------------------------- /scripts/credentials-jenkins-agent.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015-2023 Sam Gleske - https://github.com/samrocketman/jenkins-bootstrap-shared 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | /* 17 | Configure some dummy username and password credentials. 18 | */ 19 | import com.cloudbees.plugins.credentials.SystemCredentialsProvider 20 | import com.cloudbees.plugins.credentials.impl.UsernamePasswordCredentialsImpl 21 | import com.cloudbees.plugins.credentials.domains.Domain 22 | import com.cloudbees.plugins.credentials.CredentialsScope 23 | 24 | String credentials_id = 'jenkins-docker-cloud-credentials' 25 | String jenkins_agent_user = 'jenkins' 26 | String jenkins_agent_pass = 'jenkins' 27 | 28 | boolean modified_creds = false 29 | Domain domain 30 | SystemCredentialsProvider system_creds = SystemCredentialsProvider.getInstance() 31 | system_creds.metaClass.methods*.name.sort().unique() 32 | Map system_creds_map = system_creds.getDomainCredentialsMap() 33 | (system_creds_map.keySet() as List).each { 34 | //name of the global domain is null 35 | if(it.getName() == null) { 36 | domain = it 37 | } 38 | } 39 | if(!system_creds_map[domain] || (system_creds_map[domain].findAll {credentials_id.equals(it.id)}).size() < 1) { 40 | UsernamePasswordCredentialsImpl jenkins_docker_creds = new UsernamePasswordCredentialsImpl( 41 | CredentialsScope.GLOBAL, 42 | credentials_id, 43 | "${credentials_id} credentials", 44 | jenkins_agent_user, 45 | jenkins_agent_pass) 46 | if(system_creds_map[domain] && system_creds_map[domain].size() > 0) { 47 | //other credentials exist so should only append 48 | system_creds_map[domain] << jenkins_docker_creds 49 | } 50 | else { 51 | system_creds_map[domain] = [jenkins_docker_creds] 52 | } 53 | modified_creds = true 54 | } 55 | //save any modified credentials 56 | if(modified_creds) { 57 | println 'Credentials for Docker agent configured.' 58 | system_creds.setDomainCredentialsMap(system_creds_map) 59 | system_creds.save() 60 | } 61 | else { 62 | println 'Nothing changed. Docker agent credentials already configured.' 63 | } 64 | -------------------------------------------------------------------------------- /scripts/init.groovy.d/disable-jenkins-cli.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License 3 | 4 | Copyright (c) 2015, Kohsuke Kawaguchi 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | */ 24 | /* 25 | Disable Jenkins CLI. 26 | 27 | Copy script to $JENKINS_HOME/init.groovy.d/ 28 | 29 | This init script for Jenkins fixes a zero day vulnerability. 30 | http://jenkins-ci.org/content/mitigating-unauthenticated-remote-code-execution-0-day-jenkins-cli 31 | https://github.com/jenkinsci-cert/SECURITY-218 32 | https://github.com/samrocketman/jenkins-script-console-scripts/blob/master/disable-jenkins-cli.groovy 33 | */ 34 | 35 | //import jenkins.*; 36 | //import jenkins.model.*; 37 | //import hudson.model.*; 38 | import jenkins.AgentProtocol 39 | import jenkins.model.Jenkins 40 | import hudson.model.RootAction 41 | 42 | //determined if changes were made 43 | configChanged = false 44 | 45 | // disabled CLI access over TCP listener (separate port) 46 | def p = AgentProtocol.all() 47 | p.each { x -> 48 | if(x.name && x.name.contains("CLI")) { 49 | //println "remove ${x}" 50 | p.remove(x) 51 | configChanged = true 52 | } 53 | } 54 | 55 | // disable CLI access over /cli URL 56 | def removal = { lst -> 57 | lst.each { x -> 58 | if(x.getClass().name.contains("CLIAction")) { 59 | //println "remove ${x}" 60 | lst.remove(x) 61 | configChanged = true 62 | } 63 | } 64 | } 65 | def j = Jenkins.instance; 66 | removal(j.getExtensionList(RootAction.class)) 67 | removal(j.actions) 68 | 69 | if(configChanged) { 70 | println 'Jenkins CLI has been disabled.' 71 | } else { 72 | println 'Nothing changed. Jenkins CLI already disabled.' 73 | } 74 | -------------------------------------------------------------------------------- /scripts/configure-job-restrictions-controller.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015-2023 Sam Gleske - https://github.com/samrocketman/jenkins-bootstrap-shared 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | /* 18 | Configure job restrictions plugin. Configure the controller node to only allow 19 | the _jervis_generator job to execute. 20 | 21 | Pipeline jobs are not allowed to run on the built-in controller. However, 22 | pipelines use the built-in controller to orchestrate nodes, stages, and 23 | steps. The WorkflowJob class is a workaround for allowing pipeline jobs to 24 | orchestrate on the built-in controllver via a flyweight task. ref: 25 | https://issues.jenkins-ci.org/browse/JENKINS-31866 26 | 27 | job-restrictions ver 0.6 28 | */ 29 | 30 | import com.synopsys.arc.jenkinsci.plugins.jobrestrictions.nodes.JobRestrictionProperty 31 | import com.synopsys.arc.jenkinsci.plugins.jobrestrictions.restrictions.job.RegexNameRestriction 32 | import com.synopsys.arc.jenkinsci.plugins.jobrestrictions.restrictions.logic.OrJobRestriction 33 | import io.jenkins.plugins.jobrestrictions.restrictions.job.JobClassNameRestriction 34 | import io.jenkins.plugins.jobrestrictions.util.ClassSelector 35 | import jenkins.branch.OrganizationFolder 36 | import jenkins.model.Jenkins 37 | import org.jenkinsci.plugins.workflow.job.WorkflowJob 38 | import org.jenkinsci.plugins.workflow.multibranch.WorkflowMultiBranchProject 39 | 40 | List classList = [ 41 | OrganizationFolder, 42 | WorkflowMultiBranchProject, 43 | WorkflowJob 44 | ].collect { Class clazz -> 45 | new ClassSelector(clazz.name) 46 | } 47 | def classes = new JobClassNameRestriction(classList) 48 | def names = new RegexNameRestriction('_jervis_generator|^__.*', true) 49 | def restriction = new OrJobRestriction(names, classes) 50 | def prop = new JobRestrictionProperty(restriction) 51 | 52 | def j = Jenkins.instance 53 | if(!j.getNodeProperty(JobRestrictionProperty)) { 54 | j.nodeProperties.add(prop) 55 | j.save() 56 | println 'Restricted built-in controller to only allow execution from _jervis_generator job.' 57 | } 58 | else { 59 | println 'Nothing changed. Controller built-in already restricts jobs.' 60 | } 61 | -------------------------------------------------------------------------------- /scripts/upgrade/plugin_refresh.md: -------------------------------------------------------------------------------- 1 | # Plugin refresh procedure 2 | 3 | If you haven't already create a `pinned-plugins.txt` file at the root of your 4 | jenkins bootstrap repository. 5 | 6 | 1. Bootstrap Jenkins normally and capture a list of plugins. 7 | 2. Bootstrap Jenkins without plugins. 8 | 3. Run the refresh plugins script which will install the plugins and update your 9 | gradle files. 10 | 4. Capture list of plugins again to compare before-after. 11 | 12 | # Procedure 13 | 14 | ### 1. Capture list of plugins 15 | 16 | > **Please note:** The purpose of capturing a list of plugins before and after 17 | > upgrade is to compare what plugins were added and removed. Inspect the list 18 | > and ensure a plugin in which you depend upon is not removed. Any missing 19 | > plugins identified should be added to `pinned-plugins.txt` and restart the 20 | > procedure to refresh plugins. 21 | 22 | Bootstrap your Jenkins instance normally with the following command. 23 | 24 | ./jenkins_bootstrap.sh 25 | 26 | You can capture a full list of plugins before and after plugin refresh to ensure 27 | no important plugins are dropped. The following is a [script 28 | console][script-console] script. 29 | 30 | ```groovy 31 | println Jenkins.instance.pluginManager.plugins*.shortName.sort().unique().join('\n') 32 | ``` 33 | 34 | The above script is run from the script console. Save the result of this script 35 | to a file called `old_plugins.txt`. 36 | 37 | ### 2. Bootstrap Jenkins without plugins 38 | 39 | A new option is provided if you upgrade your jenkins-bootstrap-shared submodule. 40 | `--skip-plugins`. Run the following command to bootstrap your version of 41 | Jenkins without plugins. 42 | 43 | ./jenkins_boostrap.sh --skip-plugins 44 | 45 | ### 3. Run refresh plugins script 46 | 47 | From your repository initialized with jenkins-bootstrap-shared run the following 48 | command. 49 | 50 | ./jenkins-bootstrap-shared/scripts/upgrade/refresh_plugins.sh 51 | 52 | It will upgrade your Jenkins instance and reboot. After reboot it will install 53 | all plugins listed in `pinned-plugins.txt`. Finally, it will call 54 | [`upgrade_build_gradle.sh`](upgrade_build_gradle.sh) script which will update 55 | your `dependencies.gradle` and `gradle.properties` file with the new plugin 56 | versions. 57 | 58 | ### 4. Capture a list of plugins to compare 59 | 60 | Re-run the script console script listed in section 1 and save the list of 61 | plugins to `new_plugins.txt`. You can compare a unified diff between plugins 62 | which have been added or removed. 63 | 64 | git diff --color -u old_plugins.txt new_plugins.txt 65 | 66 | If you determine you're missing critical plugins, then add the missing plugin 67 | IDs to `pinned-plugins.txt` and restart the plugin refresh procedure. 68 | 69 | [script-console]: https://www.jenkins.io/doc/book/managing/script-console/ 70 | -------------------------------------------------------------------------------- /scripts/configure-ldap-settings.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015-2023 Sam Gleske - https://github.com/samrocketman/jenkins-bootstrap-shared 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | /* 17 | Configure a single LDAP server in the LDAP plugin security realm. 18 | 19 | ldap 1.16 20 | */ 21 | 22 | import hudson.security.LDAPSecurityRealm 23 | import hudson.util.Secret 24 | import jenkins.model.IdStrategy 25 | import jenkins.security.plugins.ldap.LDAPConfiguration 26 | import net.sf.json.JSONObject 27 | 28 | if(!binding.hasVariable('ldap_settings')) { 29 | ldap_settings = [:] 30 | } 31 | if(!(ldap_settings instanceof Map)) { 32 | throw new Exception('ldap_settings must be a Map.') 33 | } 34 | 35 | ldap_settings = ldap_settings as JSONObject 36 | 37 | if(!(Jenkins.instance.securityRealm instanceof LDAPSecurityRealm)) { 38 | LDAPConfiguration conf = new LDAPConfiguration( 39 | ldap_settings.optString('server'), 40 | ldap_settings.optString('rootDN'), 41 | ldap_settings.optBoolean('inhibitInferRootDN'), 42 | ldap_settings.optString('managerDN'), 43 | Secret.fromString(ldap_settings.optString('managerPasswordSecret'))) 44 | conf.userSearchBase = ldap_settings.optString('userSearchBase') 45 | conf.userSearch = ldap_settings.optString('userSearch', LDAPSecurityRealm.DescriptorImpl.DEFAULT_USER_SEARCH) 46 | conf.groupSearchBase = ldap_settings.optString('groupSearchBase') 47 | conf.groupSearchFilter = ldap_settings.optString('groupSearchFilter') 48 | conf.environmentProperties = (ldap_settings.opt('environmentProperties')?:[:]).collect { k, v -> 49 | new LDAPSecurityRealm.EnvironmentProperty(k.toString(), v.toString()) 50 | } as LDAPSecurityRealm.EnvironmentProperty[] 51 | conf.displayNameAttributeName = ldap_settings.optString('displayNameAttributeName', LDAPSecurityRealm.DescriptorImpl.DEFAULT_DISPLAYNAME_ATTRIBUTE_NAME) 52 | conf.mailAddressAttributeName = ldap_settings.optString('mailAddressAttributeName', LDAPSecurityRealm.DescriptorImpl.DEFAULT_MAILADDRESS_ATTRIBUTE_NAME) 53 | 54 | List configurations = [conf] 55 | Jenkins.instance.securityRealm = new LDAPSecurityRealm( 56 | configurations, 57 | ldap_settings.optBoolean('disableMailAddressResolver'), 58 | null, 59 | IdStrategy.CASE_INSENSITIVE, 60 | IdStrategy.CASE_INSENSITIVE) 61 | Jenkins.instance.save() 62 | println 'Security realm set to LDAP.' 63 | } 64 | else { 65 | println 'Nothing changed. LDAP security realm already configured.' 66 | } 67 | -------------------------------------------------------------------------------- /scripts/upgrade/upgrade_build_gradle.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #Created by Sam Gleske 3 | #Fri Apr 21 23:26:16 PDT 2017 4 | #Ubuntu 16.04.2 LTS 5 | #Linux 4.4.0-72-generic x86_64 6 | 7 | set -e 8 | 9 | function cleanup_on() { 10 | [ -n "${TMPFILE}" ] && rm -f "${TMPFILE}" 11 | [ -n "${GAV_TMPFILE}" ] && rm -f "${GAV_TMPFILE}" 12 | #clean up jenkins headers file 13 | [ -n "${JENKINS_HEADERS_FILE}" -a -f "${JENKINS_HEADERS_FILE}" ] && rm -f "${JENKINS_HEADERS_FILE}" 14 | [ -n "${VAGRANT_SSH_CONFIG}" -a -f "${VAGRANT_SSH_CONFIG}" ] && rm -f "${VAGRANT_SSH_CONFIG}" 15 | if [ "$1" = '0' ]; then 16 | echo "Jenkins is ready. Visit ${JENKINS_WEB}/" 17 | echo "User: ${JENKINS_USER}" 18 | if [ x"${REMOTE_JENKINS:-}" = x ]; then 19 | echo "Password: ${JENKINS_PASSWORD}" 20 | fi 21 | fi 22 | } 23 | trap 'cleanup_on $?' EXIT 24 | 25 | #allow users to import when using no upgrade 26 | if [ -n "${NO_UPGRADE}" ]; then 27 | export FORCE_UPGRADE=1 28 | fi 29 | 30 | source env.sh 31 | source "${SCRIPT_LIBRARY_PATH}"/upgrade/setup_environment.sh 32 | 33 | #wait for jenkins to become available 34 | "${SCRIPT_LIBRARY_PATH}"/provision_jenkins.sh url-ready "${JENKINS_WEB}/jnlpJars/jenkins-cli.jar" 35 | 36 | #upgrade jenkins.war and all plugins 37 | if [ -z "${NO_UPGRADE}" ]; then 38 | "${SCRIPT_LIBRARY_PATH}"/jenkins_call.sh "${SCRIPT_LIBRARY_PATH}"/upgrade/upgradeJenkinsAndPlugins.groovy || ( 39 | echo "Upgrading Jenkins and plugins failed. Likely, jenkins.war or plugins" 40 | echo "are not writeable. This is typical if upgrading jenkins.war via" 41 | echo "vagrant because jenkins.war is owned by root" 42 | exit 1 43 | ) >&2 44 | 45 | "${SCRIPT_LIBRARY_PATH}"/upgrade/wait_for_upgrade_to_complete.sh 46 | if "${SCRIPT_LIBRARY_PATH}"/upgrade/jenkins_needs_restart.sh; then 47 | #set up Jenkins env vars post-restart 48 | source "${SCRIPT_LIBRARY_PATH}"/upgrade/env.sh 49 | fi 50 | fi 51 | 52 | if grep -- '^version' gradle.properties; then 53 | export USE_GRADLE_PROPERTIES=1 54 | fi 55 | 56 | function get_version() { 57 | awk 'BEGIN { FS="=" }; $1 == "version" { print $2 }' < gradle.properties 58 | } 59 | 60 | if [ -n "${USE_GRADLE_PROPERTIES:-}" ]; then 61 | VERSION="$(get_version)" 62 | # increment the patch 63 | VERSION="${VERSION%.*}.$(( ${VERSION##*.} + 1 ))" 64 | fi 65 | 66 | #write jenkins controller and plugin versions to build.gradle and dependencies.gradle 67 | "${SCRIPT_LIBRARY_PATH}"/upgrade/remote_dependencies.sh 68 | 69 | # GNU or BSD sed is required. Homebrew on Mac installs GNU sed as gsed. 70 | # Try to detect gsed; otherwise, fall back to detecting OS for using sed. 71 | SED=() 72 | if type -P gsed &> /dev/null; then 73 | SED=(gsed -r) 74 | elif [ "$(uname -s)" = "Darwin" ] || uname -s | grep -- 'BSD$' &> /dev/null; then 75 | SED=(sed -E) 76 | else 77 | # assume Linux GNU sed 78 | SED=(sed -r) 79 | fi 80 | 81 | if [ -n "${USE_GRADLE_PROPERTIES:-}" ]; then 82 | NEW_VERSION="$(get_version)" 83 | if [ "${NEW_VERSION%.*}" = "${VERSION%.*}" ]; then 84 | "${SED[@]}" -i.bak -- "s/^(version)=.*/\\1=${VERSION}/" gradle.properties 85 | rm gradle.properties.bak 86 | fi 87 | fi 88 | -------------------------------------------------------------------------------- /scripts/admin-script-approval.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015-2023 Sam Gleske - https://github.com/samrocketman/jenkins-bootstrap-jervis 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | /* 17 | Automatically approve the post-build groovy script defined in the 18 | maintenance and seed jobs which are created by admins. 19 | 20 | This is the same thing as an admin approving the script so it will be run. 21 | */ 22 | 23 | import hudson.model.FreeStyleProject 24 | import hudson.model.Item 25 | import hudson.plugins.groovy.SystemGroovy 26 | import jenkins.model.Jenkins 27 | import org.jenkinsci.plugins.scriptsecurity.scripts.languages.GroovyLanguage 28 | import org.jvnet.hudson.plugins.groovypostbuild.GroovyPostbuildDescriptor 29 | import org.jenkinsci.plugins.workflow.job.WorkflowJob 30 | 31 | script_approval = Jenkins.get().getExtensionList('org.jenkinsci.plugins.scriptsecurity.scripts.ScriptApproval')[0] 32 | 33 | approvalsNotUpdated = true 34 | 35 | void approveGroovyScript(String fullName, String script, String type = '') { 36 | String hash = script_approval.DEFAULT_HASHER.hash(script, GroovyLanguage.get().getName()) 37 | if(!(hash in script_approval.approvedScriptHashes)) { 38 | println "${fullName} ${(type)? type + ' ' : ''}groovy script approved." 39 | script_approval.approveScript(hash) 40 | approvalsNotUpdated = false 41 | } 42 | } 43 | 44 | //find admin maintenance and seed jobs 45 | Jenkins.get().items.findAll { Item i -> 46 | (i in FreeStyleProject) && i.fullName.startsWith('_') 47 | }.each { FreeStyleProject j -> 48 | //approve groovy script builders (system groovy script build step) 49 | j.builders.findAll { 50 | (it in SystemGroovy) && it.source?.script?.script 51 | }*.source.script.script.each { 52 | approveGroovyScript(j.fullName, it, 'build step system') 53 | } 54 | //approve groovy script publishers (groovy postbuild step) 55 | j.publishers.findAll { k, v -> 56 | (k in GroovyPostbuildDescriptor) && v?.script?.script 57 | }.collect { k, v -> 58 | v.script.script 59 | }.each { 60 | approveGroovyScript(j.fullName, it, 'postbuild') 61 | } 62 | } 63 | 64 | Jenkins.get().items.findAll { Item i -> 65 | (i in WorkflowJob) && i.fullName.startsWith('_') 66 | }.each { WorkflowJob j -> 67 | j?.definition?.script.with { 68 | if(!it) { 69 | return 70 | } 71 | approveGroovyScript(j.fullName, it, 'pipeline script') 72 | } 73 | } 74 | 75 | if(approvalsNotUpdated) { 76 | println 'Nothing changed. Admin groovy scripts already approved.' 77 | } 78 | 79 | null 80 | -------------------------------------------------------------------------------- /scripts/create-view.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015-2023 Sam Gleske - https://github.com/samrocketman/jenkins-bootstrap-jervis 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | /* 18 | Create views using the script console 19 | Script should be prepended with the following properties. 20 | - String itemName 21 | - String xmlData 22 | 23 | Example Usage: 24 | curl --data-urlencode "script=String itemName='Welcome';String xmlData='''$(<./configs/view_welcome_config.xml)''';$(<./scripts/create-view.groovy)" http://localhost:8080/scriptText 25 | */ 26 | 27 | import hudson.model.View 28 | import javax.xml.transform.stream.StreamSource 29 | import jenkins.model.Jenkins 30 | 31 | String getViewXml(String name) { 32 | Jenkins instance = Jenkins.getInstance() 33 | View existingView = instance.getView(name) 34 | if(existingView) { 35 | ByteArrayOutputStream viewXml = new ByteArrayOutputStream() 36 | existingView.writeXml(viewXml) 37 | viewXml.toString() 38 | } 39 | else { 40 | '' 41 | } 42 | } 43 | 44 | void createOrUpdateView(String name, String xml) { 45 | Jenkins instance = Jenkins.getInstance() 46 | View newView = View.createViewFromXML(name, new ByteArrayInputStream(xml.getBytes())) 47 | Jenkins.checkGoodName(name) 48 | if(instance.getView(name) == null) { 49 | println "Created view \"${name}\"." 50 | instance.addView(newView) 51 | instance.save() 52 | } else if(getViewXml(name).trim() != xml.trim()) { 53 | instance.getView(name).updateByXml(new StreamSource(new ByteArrayInputStream(xml.getBytes()))) 54 | instance.getView(name).save() 55 | println "View \"${name}\" already exists. Updated using XML." 56 | } else { 57 | println "Nothing changed. View \"${name}\" already exists." 58 | } 59 | } 60 | 61 | 62 | try { 63 | //just by trying to access properties should throw an exception 64 | itemName == null 65 | xmlData == null 66 | } catch(MissingPropertyException e) { 67 | println 'ERROR Can\'t create view.' 68 | println 'ERROR Missing properties: itemName, xmlData' 69 | return 70 | } 71 | 72 | List plugins = Jenkins.instance.pluginManager.getPlugins() 73 | //get a list of installed plugins 74 | Set installed_plugins = [] 75 | plugins.each { 76 | installed_plugins << it.getShortName() 77 | } 78 | 79 | Set required_plugins = ['dashboard-view', 'view-job-filters'] 80 | 81 | if((required_plugins-installed_plugins).size() != 0) { 82 | throw new Exception("Missing required plugins: ${required_plugins-installed_plugins}") 83 | } 84 | 85 | createOrUpdateView(itemName, xmlData) 86 | -------------------------------------------------------------------------------- /scripts/configure-grape-ivy-xml.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015-2023 Sam Gleske - https://github.com/samrocketman/jenkins-bootstrap-jervis 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | /* 17 | Configure repositories in which @Grab will search in global shared libraries 18 | for pipelines. This will configure ~/.groovy/grapeConfig.xml 19 | */ 20 | 21 | import java.security.MessageDigest 22 | import jenkins.model.Jenkins 23 | 24 | ivyXml = ''' 25 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | '''.trim() 49 | 50 | //method for calculating sha256sum 51 | sha256sum = { input -> 52 | MessageDigest.getInstance('SHA-256').digest(input.bytes).encodeHex().toString() 53 | } 54 | 55 | //method for determining grapeConfig equality 56 | boolean isGrapeConfigEqual(File xmlFile, xml) { 57 | String xmlChecksum = sha256sum((xml + "\n").toString()) 58 | //compare equality 59 | xmlFile.exists() && 60 | sha256sum(xmlFile) == xmlChecksum 61 | } 62 | 63 | ivyXmlFile = new File("${(System.getenv('HOME'))?:Jenkins.instance.root}/.groovy/grapeConfig.xml") 64 | if(!ivyXmlFile.parentFile.exists()) { 65 | ivyXmlFile.getParentFile().mkdirs() 66 | } 67 | if(!isGrapeConfigEqual(ivyXmlFile, ivyXml)) { 68 | ivyXmlFile.withWriter { f -> 69 | f.write(ivyXml) 70 | //add newline to end for POSIX compatibility 71 | f.write("\n") 72 | } 73 | println 'Configured Grape repository config used by @Grab.' 74 | } 75 | else { 76 | println 'Nothing changed. Grape repository config used by @Grab already configured.' 77 | } 78 | -------------------------------------------------------------------------------- /tests/deb/goss.yaml: -------------------------------------------------------------------------------- 1 | file: 2 | /etc/cron.daily/jenkins: 3 | exists: true 4 | mode: "0755" 5 | owner: root 6 | group: root 7 | filetype: file 8 | contains: 9 | - dailycommit.sh 10 | /etc/default/jenkins: 11 | exists: true 12 | mode: "0600" 13 | owner: jenkins 14 | group: jenkins 15 | filetype: file 16 | contains: 17 | - JENKINS_HOME 18 | /etc/init.d/jenkins: 19 | exists: true 20 | mode: "0755" 21 | owner: root 22 | group: root 23 | filetype: file 24 | contains: [] 25 | /etc/logrotate.d/jenkins: 26 | exists: true 27 | mode: "0644" 28 | owner: root 29 | group: root 30 | filetype: file 31 | contains: 32 | - jenkins.log 33 | - access_log 34 | /var/cache/jenkins: 35 | exists: true 36 | mode: "0755" 37 | size: 4096 38 | owner: jenkins 39 | group: jenkins 40 | filetype: directory 41 | contains: [] 42 | /var/lib/jenkins: 43 | exists: true 44 | mode: "0755" 45 | size: 4096 46 | owner: jenkins 47 | group: jenkins 48 | filetype: directory 49 | contains: [] 50 | /var/lib/jenkins/.gitignore: 51 | exists: true 52 | mode: "0644" 53 | owner: jenkins 54 | group: jenkins 55 | filetype: file 56 | contains: 57 | - \/* 58 | - \!/jobs 59 | - \!/*.xml 60 | - jobs/** 61 | - \!jobs/**/ 62 | - \!config.xml 63 | /var/lib/jenkins/dailycommit.sh: 64 | exists: true 65 | mode: "0755" 66 | owner: jenkins 67 | group: jenkins 68 | filetype: file 69 | contains: 70 | - JENKINS_HOME 71 | - git init 72 | - git add 73 | - git commit 74 | - git push 75 | /var/lib/jenkins/init.groovy.d: 76 | exists: true 77 | mode: "0755" 78 | size: 4096 79 | owner: jenkins 80 | group: jenkins 81 | filetype: directory 82 | contains: [] 83 | /var/lib/jenkins/init.groovy.d/disable-all-update-sites.groovy: 84 | exists: true 85 | owner: jenkins 86 | group: jenkins 87 | filetype: file 88 | contains: [] 89 | sha256: 5a44405c277cb3ff0954ee14a3469b6a93e0f05f391ba13d0b1155ae50bc015d 90 | /var/lib/jenkins/init.groovy.d/disable-jenkins-cli.groovy: 91 | exists: true 92 | owner: jenkins 93 | group: jenkins 94 | filetype: file 95 | contains: [] 96 | sha256: 06defb6916c7b481bb48a34e96a2752de6bffc52e10990dce82be74076e037a4 97 | /var/lib/jenkins/jenkins-bootstrap-commit: 98 | exists: true 99 | size: 41 100 | owner: jenkins 101 | group: jenkins 102 | filetype: file 103 | contains: [] 104 | /var/lib/jenkins/jenkins-versions.manifest: 105 | exists: true 106 | owner: jenkins 107 | group: jenkins 108 | filetype: file 109 | contains: 110 | - jenkins-war 111 | /var/lib/jenkins/plugins: 112 | exists: true 113 | mode: "0755" 114 | size: 4096 115 | owner: jenkins 116 | group: jenkins 117 | filetype: directory 118 | contains: [] 119 | package: 120 | jenkins-bootstrap: 121 | installed: true 122 | service: 123 | jenkins: 124 | enabled: false 125 | running: false 126 | user: 127 | jenkins: 128 | exists: true 129 | groups: 130 | - jenkins 131 | home: /var/lib/jenkins 132 | shell: /sbin/nologin 133 | group: 134 | jenkins: 135 | exists: true 136 | -------------------------------------------------------------------------------- /tests/rpm/goss.yaml: -------------------------------------------------------------------------------- 1 | file: 2 | /etc/cron.daily/jenkins: 3 | exists: true 4 | mode: "0755" 5 | owner: root 6 | group: root 7 | filetype: file 8 | contains: 9 | - dailycommit.sh 10 | /etc/init.d/jenkins: 11 | exists: true 12 | mode: "0755" 13 | owner: root 14 | group: root 15 | filetype: file 16 | contains: [] 17 | /etc/logrotate.d/jenkins: 18 | exists: true 19 | mode: "0644" 20 | owner: root 21 | group: root 22 | filetype: file 23 | contains: 24 | - jenkins.log 25 | - access_log 26 | /etc/sysconfig/jenkins: 27 | exists: true 28 | mode: "0600" 29 | owner: jenkins 30 | group: jenkins 31 | filetype: file 32 | contains: 33 | - JENKINS_HOME 34 | /var/cache/jenkins: 35 | exists: true 36 | mode: "0755" 37 | size: 4096 38 | owner: jenkins 39 | group: jenkins 40 | filetype: directory 41 | contains: [] 42 | /var/lib/jenkins: 43 | exists: true 44 | mode: "0755" 45 | size: 4096 46 | owner: jenkins 47 | group: jenkins 48 | filetype: directory 49 | contains: [] 50 | /var/lib/jenkins/.gitignore: 51 | exists: true 52 | mode: "0644" 53 | owner: jenkins 54 | group: jenkins 55 | filetype: file 56 | contains: 57 | - \/* 58 | - \!/jobs 59 | - \!/*.xml 60 | - jobs/** 61 | - \!jobs/**/ 62 | - \!config.xml 63 | /var/lib/jenkins/dailycommit.sh: 64 | exists: true 65 | mode: "0755" 66 | owner: jenkins 67 | group: jenkins 68 | filetype: file 69 | contains: 70 | - JENKINS_HOME 71 | - git init 72 | - git add 73 | - git commit 74 | - git push 75 | /var/lib/jenkins/init.groovy.d: 76 | exists: true 77 | mode: "0755" 78 | size: 4096 79 | owner: jenkins 80 | group: jenkins 81 | filetype: directory 82 | contains: [] 83 | /var/lib/jenkins/init.groovy.d/disable-all-update-sites.groovy: 84 | exists: true 85 | owner: jenkins 86 | group: jenkins 87 | filetype: file 88 | contains: [] 89 | sha256: 5a44405c277cb3ff0954ee14a3469b6a93e0f05f391ba13d0b1155ae50bc015d 90 | /var/lib/jenkins/init.groovy.d/disable-jenkins-cli.groovy: 91 | exists: true 92 | owner: jenkins 93 | group: jenkins 94 | filetype: file 95 | contains: [] 96 | sha256: 06defb6916c7b481bb48a34e96a2752de6bffc52e10990dce82be74076e037a4 97 | /var/lib/jenkins/jenkins-bootstrap-commit: 98 | exists: true 99 | size: 41 100 | owner: jenkins 101 | group: jenkins 102 | filetype: file 103 | contains: [] 104 | /var/lib/jenkins/jenkins-versions.manifest: 105 | exists: true 106 | owner: jenkins 107 | group: jenkins 108 | filetype: file 109 | contains: 110 | - jenkins-war 111 | /var/lib/jenkins/plugins: 112 | exists: true 113 | mode: "0755" 114 | size: 4096 115 | owner: jenkins 116 | group: jenkins 117 | filetype: directory 118 | contains: [] 119 | package: 120 | jenkins-bootstrap: 121 | installed: true 122 | service: 123 | jenkins: 124 | enabled: true 125 | running: false 126 | user: 127 | jenkins: 128 | exists: true 129 | groups: 130 | - jenkins 131 | home: /var/lib/jenkins 132 | shell: /sbin/nologin 133 | group: 134 | jenkins: 135 | exists: true 136 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto init 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto init 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :init 68 | @rem Get command-line arguments, handling Windows variants 69 | 70 | if not "%OS%" == "Windows_NT" goto win9xME_args 71 | 72 | :win9xME_args 73 | @rem Slurp the command line arguments. 74 | set CMD_LINE_ARGS= 75 | set _SKIP=2 76 | 77 | :win9xME_args_slurp 78 | if "x%~1" == "x" goto execute 79 | 80 | set CMD_LINE_ARGS=%* 81 | 82 | :execute 83 | @rem Setup the command line 84 | 85 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 86 | 87 | @rem Execute Gradle 88 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 89 | 90 | :end 91 | @rem End local scope for the variables with windows NT shell 92 | if "%ERRORLEVEL%"=="0" goto mainEnd 93 | 94 | :fail 95 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 96 | rem the _cmd.exe /c_ return code! 97 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 98 | exit /b 1 99 | 100 | :mainEnd 101 | if "%OS%"=="Windows_NT" endlocal 102 | 103 | :omega 104 | -------------------------------------------------------------------------------- /scripts/upgrade/README.md: -------------------------------------------------------------------------------- 1 | # Plugin Refresh 2 | 3 | It is recommended to keep Jenkins lean and up to date that you follow the 4 | [plugin refresh guide](plugin_refresh.md). Instead of upgrading plugins you 5 | start from scratch with your minimally required plugins each time. This means 6 | any plugin dependencies dropped from your minimally required plugins will also 7 | be removed. 8 | 9 | To use plugin refresh practice the following should be a practice. 10 | 11 | - All Jenkins controller configuration is config as code either view [CaC 12 | plugin][cac] or via jenkins-bootstrap-shared script console scripts. 13 | - All Jenkins jobs should be generated from code with no manually created jobs. 14 | 15 | Managing Jenkins without the above conditions is risky because you might be 16 | required to regenerate all of your config from scratch. This is a typical 17 | scenario if you have long time periods between upgrades and the versions of 18 | plugins installed on your Jenkins controller. Old Jenkins plugins tend to not 19 | have clean configuration upgrade paths to the latest available Jenkins update 20 | center plugins. 21 | 22 | --- 23 | 24 | # Before and after upgrade 25 | 26 | [`show_plugin_artifacts.sh`](show_plugin_artifacts.sh) is meant to capture the 27 | plugin names from your `dependencies.gradle`. You should call this script 28 | before and after upgrade in order to verify you're not missing necessary 29 | plugins. 30 | 31 | Usage 32 | 33 | ./scripts/upgrade/show_plugin_artifacts.sh > ../before-upgrade 34 | 35 | # run the upgrade following instructions below 36 | 37 | ./scripts/upgrade/show_plugin_artifacts.sh > ../after-upgrade 38 | 39 | Once you have the two copies you can find the differences. 40 | 41 | diff -u ../before-upgrade ../after-upgrade 42 | 43 | This utility is meant to help catch potential upgrade issues. 44 | 45 | --- 46 | 47 | # Plugin Upgrade 48 | 49 | > **Please note:** upgrading plugins will keep around old plugins which may no 50 | > longer be dependencies of your minimally required plugins. 51 | 52 | The following descripts how to upgrade the version of Jenkins and plugins 53 | referenced in this repository. Both Jenkins core and plugins versions are 54 | pinned within [`build.gradle`](../../build.gradle). This process will outline 55 | how to painlessly upgrade the versions. 56 | 57 | ### Upgrade Jenkins and plugins 58 | 59 | From the root of the repository run: 60 | 61 | ./scripts/upgrade/upgrade_build_gradle.sh 62 | 63 | It executes the [`upgrade_build_gradle.sh`](upgrade_build_gradle.sh) script. It 64 | will automatically log into Jenkins, download core, upgrade all plugins, restart 65 | Jenkins, and edit the `build.gradle` file with the latest revisions. It is able 66 | to accomplish this by executing scripts within the [Jenkins script console][1]. 67 | 68 | 69 | ### What happens? 70 | 71 | Plugins are stored at [`repo.jenkins-ci.org`][2]. Gradle is used to download 72 | plugins using [GAV coordinates][3]. When running `upgrade_build_gradle.sh`, 73 | a local Jenkins instance is upgraded and all of the plugins are upgraded. Once 74 | Jenkins is upgraded then `build.gradle` and `dependencies.gradle` is updated 75 | with the new Jenkins controller and plugin versions. 76 | 77 | ### Override Jenkins Update Center 78 | 79 | During the upgrade process it might be desirable to use a different Jenkins 80 | Update Center (e.g. the [experimental update center][4]). 81 | 82 | export JENKINS_UPDATE_CENTER='https://updates.jenkins.io/experimental/update-center.json' 83 | ./scripts/upgrade/upgrade_build_gradle.sh 84 | 85 | [1]: https://wiki.jenkins-ci.org/display/JENKINS/Jenkins+Script+Console 86 | [2]: https://repo.jenkins-ci.org/ 87 | [3]: https://maven.apache.org/pom.html#Maven_Coordinates 88 | [4]: https://jenkins.io/doc/developer/publishing/releasing-experimental-updates/ 89 | [cac]: https://plugins.jenkins.io/configuration-as-code/ 90 | -------------------------------------------------------------------------------- /scripts/common.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #Created by Sam Gleske (https://github.com/samrocketman) 3 | #Copyright (c) 2015-2023 Sam Gleske - https://github.com/samrocketman/jenkins-bootstrap-jervis 4 | #Sun Jul 26 14:30:25 EDT 2015 5 | #Ubuntu 14.04.2 LTS 6 | #Linux 3.13.0-57-generic x86_64 7 | #GNU bash, version 4.3.11(1)-release (x86_64-pc-linux-gnu) 8 | #curl 7.35.0 (x86_64-pc-linux-gnu) libcurl/7.35.0 OpenSSL/1.0.1f zlib/1.2.8 libidn/1.28 librtmp/2.3 9 | 10 | export CURL="${CURL:-curl}" 11 | export JENKINS_WEB="${JENKINS_WEB:-http://localhost:8080}" 12 | export SCRIPT_LIBRARY_PATH="${SCRIPT_LIBRARY_PATH:-./scripts}" 13 | 14 | function jenkins_console() ( 15 | set -euo pipefail 16 | #parse options 17 | jenkins="${JENKINS_WEB}/scriptText" 18 | script=() 19 | while [ ! -z "${1:-}" ]; do 20 | case $1 in 21 | -j|--jenkins) 22 | shift 23 | jenkins="$1" 24 | shift 25 | ;; 26 | -s|--script) 27 | shift 28 | script+=(-d "$1") 29 | shift 30 | ;; 31 | esac 32 | done 33 | if [ "${#script}" -eq 0 ]; then 34 | echo 'ERROR Missing --script SCRIPT for jenkins_console() function.' 35 | exit 1 36 | fi 37 | 38 | "${SCRIPT_LIBRARY_PATH}"/jenkins_call.sh -m POST --data-string script= "${script[@]}" "${jenkins}" 39 | ) 40 | 41 | function curl_item_script() ( 42 | set -euo pipefail 43 | #parse options 44 | jenkins="${JENKINS_WEB}/scriptText" 45 | args=() 46 | while [ ! -z "${1:-}" ]; do 47 | case $1 in 48 | -j|--jenkins) 49 | shift 50 | args+=(--jenkins "$1") 51 | shift 52 | ;; 53 | -s|--script) 54 | shift 55 | script="$1" 56 | shift 57 | ;; 58 | -x|--xml-data) 59 | shift 60 | xml_data="$1" 61 | shift 62 | ;; 63 | -n|--item-name) 64 | shift 65 | item_name="$1" 66 | shift 67 | ;; 68 | esac 69 | done 70 | if [ -z "${script:-}" -o -z "${xml_data:-}" -o -z "${item_name}" ]; then 71 | echo 'ERROR Missing an option for curl_item_script() function.' 72 | exit 1 73 | fi 74 | jenkins_console -s <(echo "String itemName='${item_name}';String xmlData='''$(< "${xml_data}")''';") -s "${script}" "${args[@]:-}" 75 | ) 76 | 77 | function create_job() ( 78 | set -euo pipefail 79 | #parse options 80 | jenkins="${JENKINS_WEB}/scriptText" 81 | while [ ! -z "${1:-}" ]; do 82 | case $1 in 83 | -j|--jenkins) 84 | shift 85 | jenkins="$1" 86 | shift 87 | ;; 88 | -x|--xml-data) 89 | shift 90 | xml_data="$1" 91 | shift 92 | ;; 93 | -n|--job-name) 94 | shift 95 | job_name="$1" 96 | shift 97 | ;; 98 | esac 99 | done 100 | if [ -z "${xml_data:-}" -o -z "${job_name}" ]; then 101 | echo 'ERROR Missing an option for create_job() function.' 102 | exit 1 103 | fi 104 | curl_item_script -n "${job_name}" -x "${xml_data}" -s "${SCRIPT_LIBRARY_PATH}/create-job.groovy" 105 | ) 106 | 107 | function create_view() ( 108 | set -euo pipefail 109 | #parse options 110 | jenkins="${JENKINS_WEB}/scriptText" 111 | while [ ! -z "${1:-}" ]; do 112 | case $1 in 113 | -j|--jenkins) 114 | shift 115 | jenkins="$1" 116 | shift 117 | ;; 118 | -x|--xml-data) 119 | shift 120 | xml_data="$1" 121 | shift 122 | ;; 123 | -n|--view-name) 124 | shift 125 | view_name="$1" 126 | shift 127 | ;; 128 | esac 129 | done 130 | if [ -z "${xml_data:-}" -o -z "${view_name}" ]; then 131 | echo 'ERROR Missing an option for create_job() function.' 132 | exit 1 133 | fi 134 | curl_item_script -n "${view_name}" -x "${xml_data}" -s "${SCRIPT_LIBRARY_PATH}/create-view.groovy" 135 | ) 136 | -------------------------------------------------------------------------------- /MIGRATION.md: -------------------------------------------------------------------------------- 1 | # Migration notes Oct 2022 2 | 3 | A lot is changing this update over prior jenkins-bootstrap-shared upgrades. 4 | This document attempts to provide a short list of changes required of end users 5 | if they're updating. 6 | 7 | This update includes dependency updates because a lot has changed in upstream 8 | Jenkins. This update also contains terminology updates to match Jenkins 9 | community guidelines. 10 | 11 | - [Oracle JDK 8 dropped](#oracle-jdk-8-dropped) 12 | - [Remove configuration scripts](#remove-configuration-scripts) 13 | - [Some configuration scripts have been renamed](#some-configuration-scripts-have-been-renamed) 14 | - [Settings for scripts have changed](#settings-for-scripts-have-changed) 15 | - [configure-docker-cloud.groovy](#configure-docker-cloudgroovy) 16 | - [configure-jenkins-settings.groovy](#configure-jenkins-settingsgroovy) 17 | - [configure-yadocker-cloud.groovy](#configure-yadocker-cloudgroovy) 18 | 19 | # Before and after migration 20 | 21 | You should capture plugin names from `dependencies.gradle`. Compare differences 22 | before and after upgrade in order to avoid potential issues. 23 | 24 | ./scripts/upgrade/show_plugin_artifacts.sh > ../before-upgrade 25 | 26 | Run through upgrade following [upgrade README][1]. 27 | 28 | ./scripts/upgrade/show_plugin_artifacts.sh > ../after-upgrade 29 | 30 | Compare the differences. 31 | 32 | diff -u ../before-upgrade ../after-upgrade 33 | 34 | [1]: scripts/upgrade/README.md 35 | 36 | 37 | # Oracle JDK 8 dropped 38 | 39 | Oracle JDK 8 has been completely dropped. OpenJDK 11 is now required. If 40 | upgrading from older version of Jervis use the older Docker image which contains 41 | Oracle JDK 8. After update start with the latest Docker image because it 42 | contains OpenJDK 11. 43 | 44 | # Remove configuration scripts 45 | 46 | `security-disable-agent-master.groovy` is no longer supported in newer 47 | versions of Jenkins. Remove refrences from `jenkins_bootstrap.sh`. 48 | 49 | It has been renamed for future archival. 50 | 51 | security-disable-agent-master.groovy -> security-disable-agent-controller.groovy 52 | 53 | # Some configuration scripts have been renamed 54 | 55 | `scripts/` directory files have been renamed. Users will need to update 56 | `jenkins_bootstrap.sh` to reflect the following. 57 | 58 | configure-job-restrictions-master.groovy -> configure-job-restrictions-controller.groovy 59 | 60 | # Settings for scripts have changed 61 | 62 | Some configuration files have had their settings updated. 63 | 64 | ### configure-docker-cloud.groovy 65 | 66 | Settings containing `slave` have been renamed to use `agent. 67 | 68 | launcher_prefix_start_slave_cmd -> launcher_prefix_start_agent_cmd 69 | launcher_suffix_start_slave_cmd -> launcher_suffix_start_agent_cmd 70 | 71 | ### configure-jenkins-settings.groovy 72 | 73 | The variable used for customizing settings has changed. In your configs change 74 | the following. 75 | 76 | master_settings -> controller_settings 77 | 78 | Settings containing `master` have been renamed to use `controller`. 79 | 80 | master_executors -> controller_executors 81 | master_labels -> controller_labels 82 | master_usage -> controller_usage 83 | 84 | Settings containing `slave` have been renamed to use `agent. 85 | 86 | jnlp_slave_port -> jnlp_agent_port 87 | 88 | ### configure-yadocker-cloud.groovy 89 | 90 | Settings containing `slave` have been renamed to use `agent. 91 | 92 | launch_ssh_prefix_start_slave_command -> launch_ssh_prefix_start_agent_command 93 | launch_ssh_suffix_start_slave_command -> launch_ssh_suffix_start_agent_command 94 | launch_jnlp_slave_jar_options -> launch_jnlp_agent_jar_options 95 | launch_jnlp_slave_jvm_options -> launch_jnlp_agent_jvm_options 96 | 97 | Settings containing `master` have been renamed to use `controller`. 98 | 99 | launch_jnlp_different_jenkins_master_url -> launch_jnlp_different_jenkins_controller_url 100 | -------------------------------------------------------------------------------- /packaging/daemon/jenkins.config.in: -------------------------------------------------------------------------------- 1 | ## Path: Development/@@CAMELARTIFACTNAME@@ 2 | ## Description: @@SUMMARY@@ 3 | ## Type: string 4 | ## Default: "~~HOME~~" 5 | ## ServiceRestart: jenkins 6 | # 7 | # Directory where Jenkins store its configuration and working 8 | # files (checkouts, build reports, artifacts, ...). 9 | # 10 | JENKINS_HOME="~~HOME~~" 11 | 12 | ## Type: string 13 | ## Default: "" 14 | ## ServiceRestart: @@ARTIFACTNAME@@ 15 | # 16 | # Java executable to run Jenkins 17 | # When left empty, we'll try to find the suitable Java. 18 | # 19 | JENKINS_JAVA_CMD="~~JENKINS_JAVA_CMD~~" 20 | 21 | ## Type: string 22 | ## Default: "@@ARTIFACTNAME@@" 23 | ## ServiceRestart: @@ARTIFACTNAME@@ 24 | # 25 | # Unix user account that runs the Jenkins daemon 26 | # Be careful when you change this, as you need to update 27 | # permissions of $JENKINS_HOME and /var/log/jenkins. 28 | # 29 | JENKINS_USER="@@USER@@" 30 | 31 | ## Type: string 32 | ## Default: "false" 33 | ## ServiceRestart: jenkins 34 | # 35 | # Whether to skip potentially long-running chown at the 36 | # $JENKINS_HOME location. Do not enable this, "true", unless 37 | # you know what you're doing. See JENKINS-23273. 38 | # 39 | #JENKINS_INSTALL_SKIP_CHOWN="false" 40 | 41 | ## Type: string 42 | ## Default: "-Djava.awt.headless=true" 43 | ## ServiceRestart: @@ARTIFACTNAME@@ 44 | # 45 | # Options to pass to java when running Jenkins. 46 | # 47 | JENKINS_JAVA_OPTIONS="~~JENKINS_JAVA_OPTIONS~~" 48 | 49 | ## Type: integer(0:65535) 50 | ## Default: @@HTTP_PORT@@ 51 | ## ServiceRestart: jenkins 52 | # 53 | # Port Jenkins is listening on. 54 | # Set to -1 to disable 55 | # 56 | JENKINS_PORT="@@HTTP_PORT@@" 57 | 58 | ## Type: string 59 | ## Default: "" 60 | ## ServiceRestart: @@ARTIFACTNAME@@ 61 | # 62 | # IP address Jenkins listens on for HTTP requests. 63 | # Default is all interfaces (0.0.0.0). 64 | # 65 | JENKINS_LISTEN_ADDRESS="~~HTTP_LISTEN_ADDRESS~~" 66 | 67 | ## Type: integer(0:65535) 68 | ## Default: "" 69 | ## ServiceRestart: @@ARTIFACTNAME@@ 70 | # 71 | # HTTPS port Jenkins is listening on. 72 | # Default is disabled. 73 | # 74 | JENKINS_HTTPS_PORT="~~HTTPS_PORT~~" 75 | 76 | ## Type: string 77 | ## Default: "" 78 | ## ServiceRestart: @@ARTIFACTNAME@@ 79 | # 80 | # Path to the keystore in JKS format (as created by the JDK 'keytool'). 81 | # Default is disabled. 82 | # 83 | JENKINS_HTTPS_KEYSTORE="~~HTTPS_KEYSTORE~~" 84 | 85 | ## Type: string 86 | ## Default: "" 87 | ## ServiceRestart: @@ARTIFACTNAME@@ 88 | # 89 | # Password to access the keystore defined in JENKINS_HTTPS_KEYSTORE. 90 | # Default is disabled. 91 | # 92 | JENKINS_HTTPS_KEYSTORE_PASSWORD="~~HTTPS_KEYSTORE_PASSWORD~~" 93 | 94 | ## Type: string 95 | ## Default: "" 96 | ## ServiceRestart: @@ARTIFACTNAME@@ 97 | # 98 | # IP address Jenkins listens on for HTTPS requests. 99 | # Default is disabled. 100 | # 101 | JENKINS_HTTPS_LISTEN_ADDRESS="~~HTTPS_LISTEN_ADDRESS~~" 102 | 103 | 104 | ## Type: integer(1:9) 105 | ## Default: 5 106 | ## ServiceRestart: @@ARTIFACTNAME@@ 107 | # 108 | # Debug level for logs -- the higher the value, the more verbose. 109 | # 5 is INFO. 110 | # 111 | JENKINS_DEBUG_LEVEL="5" 112 | 113 | ## Type: yesno 114 | ## Default: no 115 | ## ServiceRestart: @@ARTIFACTNAME@@ 116 | # 117 | # Whether to enable access logging or not. 118 | # 119 | JENKINS_ENABLE_ACCESS_LOG="~~JENKINS_ENABLE_ACCESS_LOG~~" 120 | 121 | ## Type: yesno 122 | ## Default: no 123 | ## ServiceRestart: @@ARTIFACTNAME@@ 124 | # 125 | # Source configuration in SysV Init compatible way. Available for old SysV 126 | # Init systems. 127 | # 128 | JENKINS_ENABLE_SYSV_CONFIG="~~JENKINS_ENABLE_SYSV_CONFIG~~" 129 | 130 | ## Type: integer 131 | ## Default: 100 132 | ## ServiceRestart: @@ARTIFACTNAME@@ 133 | # 134 | # Maximum number of HTTP worker threads. 135 | # 136 | JENKINS_HANDLER_MAX="~~HTTP_MAX_WORKER_THREADS~~" 137 | 138 | ## Type: integer 139 | ## Default: 20 140 | ## ServiceRestart: @@ARTIFACTNAME@@ 141 | # 142 | # Maximum number of idle HTTP worker threads. 143 | # 144 | JENKINS_HANDLER_IDLE="~~HTTP_MAX_IDLE_THREADS~~" 145 | 146 | ## Type: string 147 | ## Default: "" 148 | ## ServiceRestart: @@ARTIFACTNAME@@ 149 | # 150 | # Pass arbitrary arguments to Jenkins. 151 | # Full option list: java -jar @@ARTIFACTNAME@@.war --help 152 | # 153 | JENKINS_ARGS="~~ADDITIONAL_JENKINS_ARGS~~" 154 | -------------------------------------------------------------------------------- /scripts/bootstrap.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015-2023 Sam Gleske - https://github.com/samrocketman/jenkins-bootstrap-jervis 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | import hudson.model.UpdateSite 17 | import hudson.PluginWrapper 18 | 19 | /* 20 | What is the purpose of this script? It does the following when run in the 21 | Jenkins script console. 22 | * Checks for settings and if they're different then updates them. 23 | * Checks for outdate plugins and upgrades them. 24 | * Checks for desired plugins and if missing installs them. 25 | * If any configuration has changed then save the configuration to disk. 26 | 27 | Examle Usage: 28 | curl --data-urlencode "script=$(<./scripts/bootstrap.groovy)" http://localhost:8080/scriptText 29 | */ 30 | 31 | /* 32 | Interesting settings 33 | */ 34 | 35 | //what plugins should be installed (by plugin ID) 36 | Set plugins_to_install = [ 37 | "git", "github", "github-oauth", "token-macro", 38 | "cloudbees-folder", "job-dsl", "view-job-filters", 39 | "embeddable-build-status", "groovy", "dashboard-view", "rich-text-publisher-plugin", "console-column-plugin", "docker-plugin" 40 | ] 41 | //should we dynamically load plugins when installed? 42 | Boolean dynamicLoad = false 43 | 44 | /* 45 | Install Jenkins plugins 46 | */ 47 | def install(Collection c, Boolean dynamicLoad, UpdateSite updateSite) { 48 | c.each { 49 | println "Installing ${it} plugin." 50 | UpdateSite.Plugin plugin = updateSite.getPlugin(it) 51 | Throwable error = plugin.deploy(dynamicLoad).get().getError() 52 | if(error != null) { 53 | println "ERROR installing ${it}, ${error}" 54 | } 55 | } 56 | null 57 | } 58 | 59 | /* 60 | Make it so... 61 | */ 62 | 63 | //some useful vars to set 64 | Boolean hasConfigBeenUpdated = false 65 | 66 | //check to see if using Jenkins Enterprise and if so then use its update site 67 | Set update_sites = [] 68 | Jenkins.getInstance().getUpdateCenter().getSiteList().each { 69 | update_sites << it.id 70 | } 71 | UpdateSite updateSite 72 | if('jenkins-enterprise' in update_sites) { 73 | updateSite = Jenkins.getInstance().getUpdateCenter().getById('jenkins-enterprise') 74 | } else { 75 | updateSite = Jenkins.getInstance().getUpdateCenter().getById('default') 76 | } 77 | println "Using update site ${updateSite.id}." 78 | 79 | List plugins = Jenkins.instance.pluginManager.getPlugins() 80 | 81 | //check the update site(s) for latest plugins 82 | println 'Checking plugin updates via Plugin Manager.' 83 | Jenkins.instance.pluginManager.doCheckUpdatesServer() 84 | 85 | //disable submitting usage statistics for privacy 86 | if(Jenkins.instance.isUsageStatisticsCollected()) { 87 | println "Disable submitting anonymous usage statistics to jenkins-ci.org for privacy." 88 | Jenkins.instance.setNoUsageStatistics(true) 89 | hasConfigBeenUpdated = true 90 | } 91 | 92 | //any plugins need updating? 93 | Set plugins_to_update = [] 94 | plugins.each { 95 | if(it.hasUpdate()) { 96 | plugins_to_update << it.getShortName() 97 | } 98 | } 99 | if(plugins_to_update.size() > 0) { 100 | println "Updating plugins..." 101 | install(plugins_to_update, dynamicLoad, updateSite) 102 | println "Done updating plugins." 103 | hasConfigBeenUpdated = true 104 | } 105 | 106 | 107 | //get a list of installed plugins 108 | Set installed_plugins = [] 109 | plugins.each { 110 | installed_plugins << it.getShortName() 111 | } 112 | 113 | //check to see if there are missing plugins to install 114 | Set missing_plugins = plugins_to_install - installed_plugins 115 | if(missing_plugins.size() > 0) { 116 | println "Install missing plugins..." 117 | install(missing_plugins, dynamicLoad, updateSite) 118 | println "Done installing missing plugins." 119 | hasConfigBeenUpdated = true 120 | } 121 | 122 | if(hasConfigBeenUpdated) { 123 | println "Saving Jenkins configuration to disk." 124 | Jenkins.instance.save() 125 | } else { 126 | println "Jenkins up-to-date. Nothing to do." 127 | } 128 | -------------------------------------------------------------------------------- /scripts/configure-jenkins-settings.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015-2023 Sam Gleske - https://github.com/samrocketman/jenkins-bootstrap-shared 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | /* 17 | This script will configure global settings for a Jenkins instance. These 18 | are all settings built into core. 19 | 20 | Configures Global settings: 21 | - Jenkins URL 22 | - Admin email 23 | - System message 24 | - Quiet Period 25 | - SCM checkout retry count 26 | - Number of controller executors 27 | - Controller labels 28 | - Controller usage (e.g. exclusive requiring built-in label or any job could run by default) 29 | - TCP port for JNLP build agents 30 | */ 31 | 32 | import jenkins.model.Jenkins 33 | import jenkins.model.JenkinsLocationConfiguration 34 | import net.sf.json.JSONObject 35 | 36 | if(!binding.hasVariable('controller_settings')) { 37 | controller_settings = [:] 38 | } 39 | if(!(controller_settings instanceof Map)) { 40 | throw new Exception('controller_settings must be a Map.') 41 | } 42 | 43 | controller_settings = controller_settings as JSONObject 44 | 45 | def requiredDefaultValues(def value, List values, def default_value) { 46 | (value in values)? value : default_value 47 | } 48 | 49 | //settings with sane defaults 50 | String frontend_url = controller_settings.optString('frontend_url', 'http://localhost:8080/') 51 | String admin_email = controller_settings.optString('admin_email') 52 | String system_message = controller_settings.optString('system_message') 53 | int quiet_period = controller_settings.optInt('quiet_period', 5) 54 | int scm_checkout_retry_count = controller_settings.optInt('scm_checkout_retry_count', 0) 55 | int controller_executors = controller_settings.optInt('controller_executors', 2) 56 | String controller_labels = controller_settings.optString('controller_labels') 57 | String controller_usage = requiredDefaultValues(controller_settings.optString('controller_usage').toUpperCase(), ['NORMAL', 'EXCLUSIVE'], 'EXCLUSIVE') 58 | int jnlp_agent_port = controller_settings.optInt('jnlp_agent_port', -1) 59 | 60 | Jenkins j = Jenkins.instance 61 | JenkinsLocationConfiguration location = j.getExtensionList('jenkins.model.JenkinsLocationConfiguration')[0] 62 | Boolean save = false 63 | 64 | if(location.url != frontend_url) { 65 | println "Updating Jenkins URL to: ${frontend_url}" 66 | location.url = frontend_url 67 | save = true 68 | } 69 | if(admin_email && location.adminAddress != admin_email) { 70 | println "Updating Jenkins Email to: ${admin_email}" 71 | location.adminAddress = admin_email 72 | save = true 73 | } 74 | if(j.systemMessage != system_message) { 75 | println 'System message has changed. Updating message.' 76 | j.systemMessage = system_message 77 | save = true 78 | } 79 | if(j.quietPeriod != quiet_period) { 80 | println "Setting Jenkins Quiet Period to: ${quiet_period}" 81 | j.quietPeriod = quiet_period 82 | save = true 83 | } 84 | if(j.scmCheckoutRetryCount != scm_checkout_retry_count) { 85 | println "Setting Jenkins SCM checkout retry count to: ${scm_checkout_retry_count}" 86 | j.scmCheckoutRetryCount = scm_checkout_retry_count 87 | save = true 88 | } 89 | if(j.numExecutors != controller_executors) { 90 | println "Setting controller num executors to: ${controller_executors}" 91 | j.numExecutors = controller_executors 92 | save = true 93 | } 94 | if(j.labelString != controller_labels) { 95 | println "Setting controller labels to: ${controller_labels}" 96 | j.setLabelString(controller_labels) 97 | save = true 98 | } 99 | if(j.mode.toString() != controller_usage) { 100 | println "Setting controller usage to: ${controller_usage}" 101 | j.mode = Node.Mode."${controller_usage}" 102 | save = true 103 | } 104 | if(j.slaveAgentPort != jnlp_agent_port) { 105 | if(jnlp_agent_port <= 65535 && jnlp_agent_port >= -1) { 106 | println "Set JNLP Agent port: ${jnlp_agent_port}" 107 | j.slaveAgentPort = jnlp_agent_port 108 | save = true 109 | } 110 | else { 111 | println "WARNING: JNLP port ${jnlp_agent_port} outside of TCP port range. Must be within -1 <-> 65535. Nothing changed." 112 | } 113 | } 114 | //save configuration to disk 115 | if(save) { 116 | j.save() 117 | location.save() 118 | } 119 | else { 120 | println 'Nothing changed. Jenkins settings already configured.' 121 | } 122 | -------------------------------------------------------------------------------- /scripts/upgrade/remote_dependencies.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ -z "${JENKINS_WEB}" -o -z "${SCRIPT_LIBRARY_PATH}" ]; then 4 | echo 'ERROR: required variables are empty and should not be.' >&2 5 | echo "JENKINS_WEB = ${JENKINS_WEB}" >&2 6 | echo "SCRIPT_LIBRARY_PATH = ${SCRIPT_LIBRARY_PATH}" >&2 7 | exit 1 8 | fi 9 | 10 | function gawk() { 11 | if [ "$(uname)" = Linux ]; then 12 | command awk "$@" 13 | elif type -P gawk &> /dev/null; then 14 | command gawk "$@" 15 | else 16 | echo 'ERROR: GNU awk is required.' >&2 17 | echo ' If on Mac, "brew install gawk".' >&2 18 | echo " Otherwise, install GNU awk for the platform you're using" >&2 19 | exit 1 20 | fi 21 | } 22 | 23 | # GNU or BSD sed is required. Homebrew on Mac installs GNU sed as gsed. 24 | # Try to detect gsed; otherwise, fall back to detecting OS for using sed. 25 | SED=() 26 | if type -P gsed &> /dev/null; then 27 | SED=(gsed -r) 28 | elif [ "$(uname -s)" = "Darwin" ] || uname -s | grep -- 'BSD$' &> /dev/null; then 29 | SED=(sed -E) 30 | else 31 | # assume Linux GNU sed 32 | SED=(sed -r) 33 | fi 34 | 35 | function cleanup_temp_on() { 36 | [ -z "${TMP_DIR}" ] || rm -rf "${TMP_DIR}" 37 | } 38 | trap cleanup_temp_on EXIT 39 | 40 | #temp files will automatically be cleaned up on exit because of trap function 41 | TMP_DIR=$(mktemp -d) 42 | TMPFILE="${TMP_DIR}/tmp" 43 | GAV_TMPFILE="${TMP_DIR}/gav" 44 | CUSTOM_TMPFILE="${TMP_DIR}/custom" 45 | 46 | echo 'Upgrade build.gradle file.' 47 | export JENKINS_CALL_ARGS="-m POST ${JENKINS_WEB}/scriptText --data-string script= -d" 48 | "${SCRIPT_LIBRARY_PATH}"/jenkins_call.sh "${SCRIPT_LIBRARY_PATH}"/upgrade/generateSedExpr.groovy > "${TMPFILE}" 49 | if [ -n "${USE_GRADLE_PROPERTIES:-}" ]; then 50 | VERSION_FILE=gradle.properties 51 | else 52 | VERSION_FILE=build.gradle 53 | fi 54 | "${SED[@]}" -i.bak -f "${TMPFILE}" "${VERSION_FILE}" 55 | rm "${VERSION_FILE}.bak" 56 | 57 | #generate a new dependencies.gradle file 58 | echo 'Upgrade dependencies.gradle file.' 59 | if [ ! "$("${SCRIPT_LIBRARY_PATH}"/jenkins_call.sh - <<< 'println Jenkins.instance.pluginManager.plugins.size()')" = '0' ]; then 60 | #create an index of installed plugins 61 | "${SCRIPT_LIBRARY_PATH}"/jenkins_call.sh "${SCRIPT_LIBRARY_PATH}"/upgrade/listShortNameVersion.groovy > "${TMPFILE}" 62 | [ -f "${GAV_TMPFILE}" ] || "${SCRIPT_LIBRARY_PATH}"/upgrade/plugins_gav.sh > "${GAV_TMPFILE}" 63 | 64 | JENKINS_WAR_VERSION=$("${SCRIPT_LIBRARY_PATH}"/jenkins_call.sh - <<< 'println Jenkins.instance.version') 65 | cat > dependencies.gradle <<-EOF 66 | dependencies { 67 | //get Jenkins 68 | getjenkins 'org.jenkins-ci.main:jenkins-war:${JENKINS_WAR_VERSION}@war' 69 | 70 | //custom plugins (if any) provided by custom-plugins.txt; format: G:A:V@hpi or G:A:V@jpi 71 | EOF 72 | 73 | #load custom plugins if a user has defined custom-plugins.txt 74 | if [ -f custom-plugins.txt ]; then 75 | #get a list of the custom entries 76 | gawk '$0 ~ /^([-.a-zA-Z0-9]+:){2}[-.a-zA-Z0-9]+@[hj]pi$/ { print $0 }' custom-plugins.txt | while read gav; do 77 | echo " getplugins '${gav}'" 78 | done >> dependencies.gradle 79 | #get a list of the plugin IDs only 80 | gawk 'BEGIN { FS=":" }; $0 ~ /^([-.a-zA-Z0-9]+:){2}[-.a-zA-Z0-9]+@[hj]pi$/ { print $2 }' custom-plugins.txt > "${CUSTOM_TMPFILE}" 81 | fi 82 | touch "${CUSTOM_TMPFILE}" 83 | 84 | if [ -f pinned-plugins.txt ]; then 85 | #minimally pinned plugins 86 | cat >> dependencies.gradle <<-EOF 87 | 88 | //Plugins from pinned-plugins.txt 89 | EOF 90 | while read x; do 91 | if grep -F "${x%:*}" "${CUSTOM_TMPFILE}" > /dev/null; then 92 | #skip custom plugins because they're already included in dependencies.gradle 93 | continue 94 | fi 95 | if ! grep "^${x%:*}\$" pinned-plugins.txt > /dev/null; then 96 | #skip if not a pinned plugin 97 | continue 98 | fi 99 | GROUP=$(gawk "BEGIN {FS=\":\"};\$2 == \"${x%:*}\" { print \$1 }" "${GAV_TMPFILE}") 100 | echo " getplugins '${GROUP}:${x}@hpi'" 101 | unset GROUP 102 | done < "${TMPFILE}" | LC_COLLATE=C sort >> dependencies.gradle 103 | cat pinned-plugins.txt >> "${CUSTOM_TMPFILE}" 104 | fi 105 | 106 | # list remaining dependencies 107 | cat >> dependencies.gradle <<-EOF 108 | 109 | //additional plugins (usually transitive dependencies) 110 | EOF 111 | while read x; do 112 | if grep -F "${x%:*}" "${CUSTOM_TMPFILE}" > /dev/null; then 113 | #skip plugins because they're already included in dependencies.gradle 114 | continue 115 | fi 116 | GROUP=$(gawk "BEGIN {FS=\":\"};\$2 == \"${x%:*}\" { print \$1 }" "${GAV_TMPFILE}") 117 | echo " getplugins '${GROUP}:${x}@hpi'" 118 | unset GROUP 119 | done < "${TMPFILE}" | LC_COLLATE=C sort >> dependencies.gradle 120 | echo '}' >> dependencies.gradle 121 | else 122 | "${SED[@]}" -i.bak -f "${TMPFILE}" dependencies.gradle 123 | rm dependencies.gradle.bak 124 | fi 125 | echo 'Done.' 126 | -------------------------------------------------------------------------------- /packaging/postInstall.sh.in: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | RPM_BASED=false 5 | DEB_BASED=false 6 | 7 | #detect using RedHat based package 8 | #if not RPM then it's DEB 9 | if [ -n "${RPM_PACKAGE_NAME}" ]; then 10 | RPM_BASED=true 11 | else 12 | DEB_BASED=true 13 | fi 14 | 15 | ${RPM_BASED} && [ -r '/etc/sysconfig/@@ARTIFACTNAME@@' ] && . '/etc/sysconfig/@@ARTIFACTNAME@@' 16 | ${DEB_BASED} && [ -r '/etc/default/@@ARTIFACTNAME@@' ] && . '/etc/default/@@ARTIFACTNAME@@' 17 | 18 | if ${DEB_BASED}; then 19 | case "$1" in 20 | configure) 21 | ;; 22 | abort-upgrade|abort-remove|abort-deconfigure) 23 | exit 0 24 | ;; 25 | *) 26 | echo "postinst called with unknown argument '$1'" >&2 27 | exit 1 28 | ;; 29 | esac 30 | fi 31 | 32 | JENKINS_HOME="${JENKINS_HOME:-@@HOME@@}" 33 | JENKINS_USER="${JENKINS_USER:-@@USER@@}" 34 | RPM_INSTALL_PREFIX="${RPM_INSTALL_PREFIX:-@@PREFIX@@}" 35 | 36 | if ${RPM_BASED} && ( ! chkconfig | grep -E '\b@@ARTIFACTNAME@@\b' &> /dev/null ); then 37 | chkconfig --add @@ARTIFACTNAME@@ 38 | fi 39 | 40 | #overwrite plugins with plugins in the package 41 | mkdir -p "${JENKINS_HOME}/plugins" "${JENKINS_HOME}/init.groovy.d" \ 42 | "${RPM_INSTALL_PREFIX}/lib/@@ARTIFACTNAME@@/plugins/" \ 43 | "${RPM_INSTALL_PREFIX}/lib/@@ARTIFACTNAME@@/scripts/init.groovy.d/" 44 | echo "Synchronizing packaged plugins with '${JENKINS_HOME}/plugins/'." 45 | rsync -a --delete-after "${RPM_INSTALL_PREFIX}/lib/@@ARTIFACTNAME@@/plugins/" "${JENKINS_HOME}/plugins/" 46 | if [ -f "$(ls -1 "${JENKINS_HOME}"/plugins/*.jpi 2> /dev/null | head -n1)" ]; then 47 | chown -R ${JENKINS_USER}: "${JENKINS_HOME}"/plugins/*.jpi 48 | fi 49 | echo "Synchronizing packaged hook scripts with '${JENKINS_HOME}/init.groovy.d/'." 50 | rsync -a --delete-after "${RPM_INSTALL_PREFIX}/lib/@@ARTIFACTNAME@@/scripts/init.groovy.d/" "${JENKINS_HOME}/init.groovy.d/" 51 | chown -R ${JENKINS_USER}: "${JENKINS_HOME}/init.groovy.d/" 52 | 53 | echo "Overwriting manifest in '${JENKINS_HOME}/plugins/@@MANIFEST@@'." 54 | cp -f "${RPM_INSTALL_PREFIX}/lib/@@ARTIFACTNAME@@/@@MANIFEST@@" "${JENKINS_HOME}/" 55 | echo '@@COMMIT@@' > "${JENKINS_HOME}/@@PACKAGENAME@@-commit" 56 | 57 | if ls "${JENKINS_HOME}"/plugins/*.jpi &> /dev/null; then 58 | echo "Pinning all plugins in '${JENKINS_HOME}/plugins/'." 59 | echo "See https://wiki.jenkins-ci.org/display/JENKINS/Pinned+Plugins for more info." 60 | for plugin in "${JENKINS_HOME}"/plugins/*.jpi;do 61 | #pin plugin versions 62 | #https://wiki.jenkins-ci.org/display/JENKINS/Pinned+Plugins 63 | if [ -f "${plugin}" ]; then 64 | touch "${plugin}.pinned" 65 | fi 66 | done 67 | fi 68 | 69 | #change ownership of just the plugins dir but not children 70 | chown ${JENKINS_USER}: "${JENKINS_HOME}/plugins" 71 | 72 | #fix init script prefix when RPMs customize prefix (does not affect DEB) 73 | if [ ! "${RPM_INSTALL_PREFIX}" = '@@PREFIX@@' ]; then 74 | sed -i -r $"s#^(JENKINS_WAR=\")@@PREFIX@@(/[^\"]+\")\$#\1${RPM_INSTALL_PREFIX}\2#" "/etc/init.d/@@ARTIFACTNAME@@" 75 | fi 76 | 77 | mkdir -p /var/cache/@@ARTIFACTNAME@@ 78 | mkdir -p /var/log/@@ARTIFACTNAME@@ 79 | mkdir -p "${JENKINS_HOME}" 80 | 81 | if [ -z "$JENKINS_INSTALL_SKIP_CHOWN" ];then 82 | chown -R ${JENKINS_USER}: /var/cache/@@ARTIFACTNAME@@ 83 | chown -R ${JENKINS_USER}: /var/log/@@ARTIFACTNAME@@ 84 | if [ ! "$(stat -c "%U:%G" "${JENKINS_HOME}")" = '@@USER@@:@@USER@@' ]; then 85 | echo "Taking ownership of '${JENKINS_HOME}' for @@USER@@. Skip this by setting JENKINS_INSTALL_SKIP_CHOWN=true env var." 86 | chown -R ${JENKINS_USER}: "${JENKINS_HOME}" 87 | fi 88 | 89 | fi 90 | 91 | if [ ! -e "${JENKINS_HOME}/.gitignore" ]; then 92 | echo "Creating '${JENKINS_HOME}/.gitignore'." 93 | cp "${RPM_INSTALL_PREFIX}/lib/@@ARTIFACTNAME@@/share/gitignore" "${JENKINS_HOME}/.gitignore" 94 | chmod 644 "${JENKINS_HOME}/.gitignore" 95 | chown ${JENKINS_USER}: "${JENKINS_HOME}/.gitignore" 96 | fi 97 | 98 | echo "Overwriting '${JENKINS_HOME}/dailycommit.sh'." 99 | cp -f "${RPM_INSTALL_PREFIX}/lib/@@ARTIFACTNAME@@/share/dailycommit.sh" "${JENKINS_HOME}/" 100 | chmod 755 "${JENKINS_HOME}/dailycommit.sh" 101 | chown ${JENKINS_USER}: "${JENKINS_HOME}/dailycommit.sh" 102 | 103 | if ! diff -q "${RPM_INSTALL_PREFIX}/lib/@@ARTIFACTNAME@@/share/gitignore" "${JENKINS_HOME}/.gitignore"; then 104 | diff -u "${RPM_INSTALL_PREFIX}/lib/@@ARTIFACTNAME@@/share/gitignore" "${JENKINS_HOME}/.gitignore" 105 | echo "To resolve the changes you can optionally:" 106 | echo " cp -f '${RPM_INSTALL_PREFIX}/lib/@@ARTIFACTNAME@@/share/gitignore' '${JENKINS_HOME}/.gitignore'" 107 | fi 108 | 109 | #create a symlink based on upstream RPM spec (see preUninstall.sh.in for removal) 110 | if type -P systemctl; then 111 | systemctl daemon-reload 112 | else 113 | if ${RPM_BASED} && [ ! -d "${RPM_INSTALL_PREFIX}/sbin" ]; then 114 | mkdir -p "${RPM_INSTALL_PREFIX}/sbin" 115 | chmod 555 "${RPM_INSTALL_PREFIX}/sbin" 116 | fi 117 | 118 | ${RPM_BASED} && ln -sf '/etc/init.d/@@ARTIFACTNAME@@' "${RPM_INSTALL_PREFIX}/sbin/rc@@ARTIFACTNAME@@" 119 | fi 120 | 121 | exit 0 122 | -------------------------------------------------------------------------------- /scripts/uploadRelease.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #Created by Sam Gleske 3 | #Thu Feb 15 21:10:20 PST 2018 4 | #Linux 4.13.0-32-generic x86_64 5 | #GNU bash, version 4.3.48(1)-release (x86_64-pc-linux-gnu) 6 | 7 | #Upload GitHub releases 8 | 9 | if [[ -z "${GITHUB_USER}" && -z "${GITHUB_REPO}" ]] && git config --get remote.origin.url | grep ':' > /dev/null; then 10 | GITHUB_USER="$(git config --get remote.origin.url)" 11 | GITHUB_USER="${GITHUB_USER#*:}" 12 | GITHUB_USER="${GITHUB_USER%.git}" 13 | GITHUB_REPO="${GITHUB_USER#*/}" 14 | GITHUB_USER="${GITHUB_USER%/*}" 15 | export GITHUB_USER GITHUB_REPO 16 | fi 17 | 18 | if [ -z "${GITHUB_TOKEN}" -o -z "${GITHUB_USER}" -o -z "${GITHUB_REPO}" ]; then 19 | echo $'ERROR: Missing required environment variables:\n - GITHUB_TOKEN\n - GITHUB_USER\n - GITHUB_REPO' 20 | [ -z "${!GITHUB_*}" ] || echo "You have defined: ${!GITHUB_*}" 21 | exit 1 22 | fi 23 | 24 | export GITHUB_API="${GITHUB_API:-https://api.github.com}" 25 | GITHUB_API="${GITHUB_API%/}" 26 | 27 | function tempdir() { 28 | TMP_DIR=$(mktemp -d) 29 | PATH="${TMP_DIR}:${PATH}" 30 | export TMP_DIR PATH 31 | } 32 | 33 | function checkForGawk() { 34 | if ! type -P gawk; then 35 | if [ "$(uname -s)" = Darwin ]; then 36 | echo 'ERROR: Missing required GNU Awk. If using homebrew then "brew install gawk".' 37 | else 38 | echo 'ERROR: Missing required GNU Awk.' 39 | fi 40 | return 1 41 | fi 42 | return 0 43 | } 44 | 45 | function checkGHRbin() { 46 | local url sha256 47 | local TAR=() 48 | case $(uname -s) in 49 | Linux) 50 | url='https://github.com/aktau/github-release/releases/download/v0.7.2/linux-amd64-github-release.tar.bz2' 51 | sha256='3feae868828f57a24d1bb21a5f475de4a116df3063747e3290ad561b4292185c' 52 | TAR+=( tar --transform 's/.*\///g' ) 53 | ;; 54 | Darwin) 55 | url='https://github.com/aktau/github-release/releases/download/v0.7.2/darwin-amd64-github-release.tar.bz2' 56 | sha256='92d7472d6c872aa5f614c5141e84ee0a67fbdae87c0193dcf0a0476d9f1bc250' 57 | TAR+=( tar --strip-components 3 ) 58 | ;; 59 | esac 60 | if ! type -P github-release && [ -n "${sha256}" ]; then 61 | pushd "${TMP_DIR}" 62 | curl -LO "${url}" 63 | "${SHASUM[@]}" -c - <<< "${sha256} ${url##*/}" 64 | "${TAR[@]}" -xjf "${url##*/}" 65 | \rm "${url##*/}" 66 | popd 67 | fi 68 | } 69 | 70 | #exit non-zero if no "repo" or "public_repo" OAuth scope found for API token 71 | function checkOAuthScopes() { 72 | set +ex 73 | curl -sIH "Authorization: token $GITHUB_TOKEN" "${GITHUB_API}/" | 74 | gawk ' 75 | BEGIN { 76 | code=1 77 | }; 78 | $0 ~ /^.*X-OAuth-Scopes:.*\y(public_)?repo,?\y.*/ { 79 | code=0; 80 | print $0 81 | exit 82 | }; 83 | END { exit code } 84 | ' 85 | local code=$? 86 | set -ex 87 | return ${code} 88 | } 89 | 90 | function read_err_on() { 91 | set +x 92 | exec >&2 93 | case $1 in 94 | 5) 95 | echo "ERROR: must specify a tag. example: ${0##*/} v1.0" 96 | ;; 97 | 10) 98 | echo 'ERROR: must be in root of repository to build a release.' 99 | ;; 100 | 11) 101 | echo 'ERROR: sha256sum command is missing.' 102 | ;; 103 | 12) 104 | echo 'ERROR: mktemp command is missing.' 105 | ;; 106 | 13) 107 | echo "ERROR: Local git tag ${2} does not exist." 108 | ;; 109 | 14) 110 | echo "ERROR: Remote git tag ${2} does not exist." 111 | ;; 112 | 15) 113 | echo $'ERROR: GITHUB_TOKEN must have one of the following scopes:\n - repo\n - public_repo' 114 | ;; 115 | 16) 116 | echo 'ERROR: github-release does not exist and could not be downloaded.' 117 | ;; 118 | 0) 119 | echo 'SUCCESS: all files released.' 120 | ;; 121 | *) 122 | echo 'ERROR: an error occured.' 123 | ;; 124 | esac 125 | if [ -n "${TMP_DIR}" -a -d "${TMP_DIR}" ]; then 126 | rm -rf "${TMP_DIR}" 127 | fi 128 | } 129 | trap 'read_err_on $? "${1}"' EXIT 130 | tempdir 131 | 132 | function determine_shasum_prog() { 133 | set +x 134 | if type -P sha256sum; then 135 | SHASUM+=( 'sha256sum' ) 136 | elif type -P shasum; then 137 | SHASUM+=( 'shasum' '-a' '256' ) 138 | else 139 | return 1 140 | fi 141 | set -x 142 | } 143 | 144 | SHASUM=() 145 | 146 | set -ex 147 | 148 | #pre-flight tests (any failures will exit non-zero) 149 | [ -n "${1}" ] || exit 5 150 | determine_shasum_prog || exit 11 151 | type -P mktemp || exit 12 152 | checkForGawk 153 | git tag | grep "${1}" || exit 13 154 | git ls-remote | grep "refs/tags/${1}" || exit 14 155 | checkOAuthScopes || exit 15 156 | checkGHRbin 157 | type -P github-release || exit 16 158 | 159 | cd build/distributions/ 160 | 161 | #cut a release 162 | github-release release --tag "${1}" 163 | XARGS=() 164 | case $(uname -s) in 165 | Linux) 166 | XARGS+=( xargs -P0 ) 167 | ;; 168 | Darwin) 169 | XARGS+=( xargs -P4 ) 170 | ;; 171 | esac 172 | 173 | #upload all files in parallel 174 | ls -1 | "${XARGS[@]}" -n1 -I{} -- \ 175 | github-release upload --tag "${1}" --name '{}' --file '{}' 176 | -------------------------------------------------------------------------------- /scripts/configure-github-plugin.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015-2023 Sam Gleske - https://github.com/samrocketman/jenkins-bootstrap-jervis 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | /* 17 | Configure GitHub plugin for GitHub servers and other global Jenkins 18 | configuration settings. 19 | 20 | github 1.28.0 21 | */ 22 | import net.sf.json.JSONObject 23 | import org.jenkinsci.plugins.github.config.GitHubPluginConfig 24 | import org.jenkinsci.plugins.github.config.GitHubServerConfig 25 | import org.jenkinsci.plugins.github.config.HookSecretConfig 26 | 27 | boolean isServerConfigsEqual(List s1, List s2) { 28 | s1.size() == s2.size() && 29 | !( 30 | false in [s1, s2].transpose().collect { c1, c2 -> 31 | c1.name == c2.name && 32 | c1.apiUrl == c2.apiUrl && 33 | c1.manageHooks == c2.manageHooks && 34 | c1.credentialsId == c2.credentialsId && 35 | c1.clientCacheSize == c2.clientCacheSize 36 | } 37 | ) 38 | } 39 | 40 | boolean isOverrideHookEqual(def global_settings, JSONObject github_plugin) { 41 | ( 42 | ( 43 | global_settings.isOverrideHookURL() && 44 | github_plugin.optString('hookUrl') && 45 | global_settings.hookUrl == new URL(github_plugin.optString('hookUrl')) 46 | ) || 47 | ( 48 | !global_settings.isOverrideHookURL() && 49 | !github_plugin.optString('hookUrl') 50 | ) 51 | ) 52 | } 53 | 54 | boolean isGlobalSettingsEqual(def global_settings, JSONObject github_plugin) { 55 | global_settings.hookSecretConfig && 56 | global_settings.hookSecretConfig.credentialsId == github_plugin.optString('hookSharedSecretId') && 57 | isOverrideHookEqual(global_settings, github_plugin) 58 | } 59 | 60 | /* Example configuration 61 | github_plugin = [ 62 | //hookUrl: 'http://localhost:8080/github-webhook/', 63 | hookSharedSecretId: 'webhook-shared-secret', 64 | servers: [ 65 | 'Public GitHub.com': [ 66 | apiUrl: 'https://api.github.com', 67 | manageHooks: true, 68 | credentialsId: 'github-token', 69 | ] 70 | ] 71 | ] 72 | */ 73 | 74 | if(!binding.hasVariable('github_plugin')) { 75 | github_plugin = [:] 76 | } 77 | 78 | if(!github_plugin in Map) { 79 | throw new Exception("github_plugin must be an instance of Map but instead is instance of: ${github_plugin.getClass()}") 80 | } 81 | 82 | github_plugin = github_plugin as JSONObject 83 | 84 | 85 | List configs = [] 86 | 87 | github_plugin.optJSONObject('servers').each { name, config -> 88 | if(name && config && config in Map) { 89 | def server = new GitHubServerConfig(config.optString('credentialsId')) 90 | server.name = name 91 | server.apiUrl = config.optString('apiUrl', 'https://api.github.com') 92 | server.manageHooks = config.optBoolean('manageHooks', false) 93 | server.clientCacheSize = 20 94 | //server.clientCacheSize = config.optInt('clientCacheSize', 20) 95 | configs << server 96 | } 97 | } 98 | 99 | def global_settings = Jenkins.instance.getExtensionList(GitHubPluginConfig.class)[0] 100 | 101 | if(github_plugin && (!isGlobalSettingsEqual(global_settings, github_plugin) || !isServerConfigsEqual(global_settings.configs, configs))) { 102 | if(global_settings.hookSecretConfig && global_settings.hookSecretConfig.credentialsId != github_plugin.optString('hookSharedSecretId')) { 103 | global_settings.hookSecretConfig = new HookSecretConfig(github_plugin.optString('hookSharedSecretId')) 104 | } 105 | if(!isOverrideHookEqual(global_settings, github_plugin)) { 106 | if(global_settings.isOverrideHookURL() && !github_plugin.optString('hookUrl')) { 107 | global_settings.overrideHookUrl = false 108 | global_settings.hookUrl = null 109 | } else if(global_settings.@hookUrl != new URL(github_plugin.optString('hookUrl'))) { 110 | global_settings.overrideHookUrl = true 111 | global_settings.hookUrl = new URL(github_plugin.optString('hookUrl')) 112 | } 113 | } 114 | if(!isServerConfigsEqual(global_settings.configs, configs)) { 115 | global_settings.configs = configs 116 | } 117 | global_settings.save() 118 | println 'Configured GitHub plugin.' 119 | } 120 | else { 121 | if(github_plugin) { 122 | println 'Nothing changed. GitHub plugin already configured.' 123 | } 124 | else { 125 | println 'Nothing changed. Skipped configuring GitHub plugin because settings are empty.' 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /jenkins_bootstrap.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #Created by Sam Gleske (https://github.com/samrocketman) 3 | #Wed May 20 23:22:07 EDT 2015 4 | #Ubuntu 14.04.2 LTS 5 | #Linux 3.13.0-52-generic x86_64 6 | #GNU bash, version 4.3.11(1)-release (x86_64-pc-linux-gnu) 7 | #curl 7.35.0 (x86_64-pc-linux-gnu) libcurl/7.35.0 OpenSSL/1.0.1f zlib/1.2.8 libidn/1.28 librtmp/2.3 8 | #Source: https://github.com/samrocketman/jenkins-bootstrap-jervis 9 | 10 | #A script which bootstraps a Jenkins installation for executing Jervis Job DSL 11 | #scripts 12 | 13 | function cleanup_on() { 14 | #clean up jenkins headers file 15 | [ -n "${JENKINS_HEADERS_FILE}" -a -f "${JENKINS_HEADERS_FILE}" ] && rm -f "${JENKINS_HEADERS_FILE}" 16 | [ -n "${VAGRANT_SSH_CONFIG}" -a -f "${VAGRANT_SSH_CONFIG}" ] && rm -f "${VAGRANT_SSH_CONFIG}" 17 | if [ "$1" = '0' ]; then 18 | echo "Jenkins is ready. Visit ${JENKINS_WEB}/" 19 | echo "User: ${JENKINS_USER}" 20 | [ ! "$JENKINS_USER" = 'admin' ] || echo "Password: ${JENKINS_PASSWORD}" 21 | fi 22 | } 23 | trap 'cleanup_on $?' EXIT 24 | 25 | source env.sh 26 | #set password if using vagrant 27 | [ -n "${VAGRANT_JENKINS}" ] && source "${SCRIPT_LIBRARY_PATH}/vagrant-env.sh" 28 | [ -n "${DOCKER_JENKINS}" ] && source "${SCRIPT_LIBRARY_PATH}/docker-env.sh" 29 | export JENKINS_HEADERS_FILE="$(mktemp)" 30 | export JENKINS_USER="${JENKINS_USER:-admin}" 31 | 32 | if [ "x$1" == 'x--skip-plugins' ]; then 33 | export REMOTE_JENKINS=1 NO_UPGRADE=1 SKIP_PLUGINS=1 34 | fi 35 | 36 | set -e 37 | 38 | 39 | if [ -e "${SCRIPT_LIBRARY_PATH}/common.sh" ]; then 40 | source "${SCRIPT_LIBRARY_PATH}/common.sh" 41 | else 42 | echo "ERROR could not find ${SCRIPT_LIBRARY_PATH}/common.sh" 43 | echo "Perhaps environment variable SCRIPT_LIBRARY_PATH is not set correctly." 44 | exit 1 45 | fi 46 | 47 | #provision jenkins and plugins 48 | if [ -z "${REMOTE_JENKINS}" -a -z "${VAGRANT_JENKINS}" -a -z "${DOCKER_JENKINS}" ]; then 49 | echo 'Downloading specific versions of Jenkins and plugins...' 50 | ./gradlew getjenkins getplugins 51 | elif [ -n "${SKIP_PLUGINS:-}" ]; then 52 | if [ -d plugins ]; then 53 | echo 'ERROR: you called bootstrap with --skip-plugins but plugins exist.' >&2 54 | echo 'Run "./gradlew clean" to clear the workspace and try again.' >&2 55 | exit 1 56 | fi 57 | echo 'Downloading Jenkins only.' 58 | ./gradlew getjenkins 59 | fi 60 | 61 | if [ -d "./plugins" ]; then 62 | mkdir -p "${JENKINS_HOME}/plugins" 63 | ( cd "./plugins/"; ls -1d * ) | while read x; do 64 | if [ ! -e "${JENKINS_HOME}/plugins/${x}" ]; then 65 | echo "Copying ${x} to JENKINS_HOME" 66 | cp -r "./plugins/${x}" "${JENKINS_HOME}/plugins/" 67 | #pin plugin versions 68 | if [ -f pinned-plugins.txt ] && grep "^${x%.jpi}$" pinned-plugins.txt &> /dev/null; then 69 | #https://wiki.jenkins-ci.org/display/JENKINS/Pinned+Plugins 70 | touch "${JENKINS_HOME}/plugins/${x}.pinned" 71 | fi 72 | fi 73 | done 74 | fi 75 | 76 | #download jenkins, start it up, and update the plugins 77 | if [ -z "${REMOTE_JENKINS}" -a -z "${VAGRANT_JENKINS}" -a -z "${DOCKER_JENKINS}" ]; then 78 | mkdir -p "${JENKINS_HOME}/init.groovy.d" 79 | if [ -d "./scripts/init.groovy.d" ]; then 80 | ( cd ./scripts/init.groovy.d/; ls -1d * ) | while read x; do 81 | if [ ! -e "${JENKINS_HOME}/init.groovy.d/${x}" ]; then 82 | echo "Copying init.groovy.d/${x} to JENKINS_HOME" 83 | command cp ./scripts/init.groovy.d/"${x}" "${JENKINS_HOME}/init.groovy.d/" 84 | fi 85 | done 86 | fi 87 | if [ -d "${SCRIPT_LIBRARY_PATH}/init.groovy.d" ]; then 88 | ( cd "${SCRIPT_LIBRARY_PATH}"/init.groovy.d/; ls -1d * ) | while read x; do 89 | if [ ! -e "${JENKINS_HOME}/init.groovy.d/${x}" ]; then 90 | echo "Copying init.groovy.d/${x} to JENKINS_HOME" 91 | command cp "${SCRIPT_LIBRARY_PATH}"/init.groovy.d/"${x}" "${JENKINS_HOME}/init.groovy.d/" 92 | fi 93 | done 94 | fi 95 | 96 | if [ ! -e "${JENKINS_WAR}" ]; then 97 | "${SCRIPT_LIBRARY_PATH}/provision_jenkins.sh" download-file "${jenkins_url}" "${JENKINS_WAR}" 98 | fi 99 | #check for running jenkins or try to start it 100 | if ! "${SCRIPT_LIBRARY_PATH}/provision_jenkins.sh" status; then 101 | "${SCRIPT_LIBRARY_PATH}/provision_jenkins.sh" start 102 | fi 103 | fi 104 | 105 | # start up Jenkins if doing a skip plugins run 106 | if [ -n "${SKIP_PLUGINS:-}" ]; then 107 | if ! "${SCRIPT_LIBRARY_PATH}/provision_jenkins.sh" status; then 108 | "${SCRIPT_LIBRARY_PATH}/provision_jenkins.sh" start 109 | fi 110 | fi 111 | 112 | #wait for jenkins to become available 113 | "${SCRIPT_LIBRARY_PATH}/provision_jenkins.sh" url-ready "${JENKINS_WEB}/jnlpJars/jenkins-cli.jar" 114 | 115 | #persist credentials 116 | export JENKINS_PASSWORD="${JENKINS_PASSWORD:-$(<"${JENKINS_HOME}"/secrets/initialAdminPassword)}" 117 | "${SCRIPT_LIBRARY_PATH}"/jenkins-call-url -a -m HEAD -o /dev/null ${JENKINS_WEB} 118 | 119 | if [ -n "${SKIP_PLUGINS:-}" ] || grep -- '^ \+getplugins' dependencies.gradle > /dev/null; then 120 | jenkins_console --script "${SCRIPT_LIBRARY_PATH}/console-skip-2.0-wizard.groovy" 121 | fi 122 | jenkins_console --script "${SCRIPT_LIBRARY_PATH}/configure-disable-usage-stats.groovy" 123 | 124 | # do not process any user configs 125 | if [ -n "${SKIP_PLUGINS:-}" ]; then 126 | exit 127 | fi 128 | 129 | #configure jenkins agent credentials 130 | #jenkins_console --script "${SCRIPT_LIBRARY_PATH}/credentials-jenkins-agent.groovy" 131 | #disable agent -> controller security 132 | #jenkins_console --script "${SCRIPT_LIBRARY_PATH}/security-disable-agent-controller.groovy" 133 | -------------------------------------------------------------------------------- /packaging/rpm/jenkins.init.in: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # SUSE system statup script for Jenkins 4 | # Copyright (C) 2007 Pascal Bleser 5 | # 6 | # This library is free software; you can redistribute it and/or modify it 7 | # under the terms of the GNU Lesser General Public License as published by 8 | # the Free Software Foundation; either version 2.1 of the License, or (at 9 | # your option) any later version. 10 | # 11 | # This library is distributed in the hope that it will be useful, but 12 | # WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | # Lesser General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU Lesser General Public 17 | # License along with this library; if not, write to the Free Software 18 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, 19 | # USA. 20 | # 21 | ### BEGIN INIT INFO 22 | # Provides: @@ARTIFACTNAME@@ 23 | # Required-Start: $local_fs $remote_fs $network $time $named 24 | # Should-Start: $time sendmail 25 | # Required-Stop: $local_fs $remote_fs $network $time $named 26 | # Should-Stop: $time sendmail 27 | # Default-Start: 3 5 28 | # Default-Stop: 0 1 2 6 29 | # Short-Description: @@SUMMARY@@ 30 | # Description: @@SUMMARY@@ 31 | ### END INIT INFO 32 | 33 | # Check for missing binaries (stale symlinks should not happen) 34 | JENKINS_WAR="~~WAR~~" 35 | test -r "$JENKINS_WAR" || { echo "$JENKINS_WAR not installed"; 36 | if [ "$1" = "stop" ]; then exit 0; 37 | else exit 5; fi; } 38 | 39 | # Check for existence of needed config file and read it 40 | JENKINS_CONFIG=/etc/sysconfig/@@ARTIFACTNAME@@ 41 | test -e "$JENKINS_CONFIG" || { echo "$JENKINS_CONFIG not existing"; 42 | if [ "$1" = "stop" ]; then exit 0; 43 | else exit 6; fi; } 44 | test -r "$JENKINS_CONFIG" || { echo "$JENKINS_CONFIG not readable. Perhaps you forgot 'sudo'?"; 45 | if [ "$1" = "stop" ]; then exit 0; 46 | else exit 6; fi; } 47 | 48 | JENKINS_PID_FILE="/var/run/@@ARTIFACTNAME@@.pid" 49 | 50 | # Source function library. 51 | . /etc/init.d/functions 52 | 53 | # Read config 54 | [ -f "$JENKINS_CONFIG" ] && . "$JENKINS_CONFIG" 55 | 56 | # Set up environment accordingly to the configuration settings 57 | [ -n "$JENKINS_HOME" ] || { echo "JENKINS_HOME not configured in $JENKINS_CONFIG"; 58 | if [ "$1" = "stop" ]; then exit 0; 59 | else exit 6; fi; } 60 | [ -d "$JENKINS_HOME" ] || { echo "JENKINS_HOME directory does not exist: $JENKINS_HOME"; 61 | if [ "$1" = "stop" ]; then exit 0; 62 | else exit 1; fi; } 63 | 64 | # Search usable Java. We do this because various reports indicated 65 | # that /usr/bin/java may not always point to Java >= 1.6 66 | # see http://www.nabble.com/guinea-pigs-wanted-----Hudson-RPM-for-RedHat-Linux-td25673707.html 67 | candidates=" 68 | /etc/alternatives/java 69 | /usr/lib/jvm/java-1.6.0/bin/java 70 | /usr/lib/jvm/jre-1.6.0/bin/java 71 | /usr/lib/jvm/java-1.7.0/bin/java 72 | /usr/lib/jvm/jre-1.7.0/bin/java 73 | /usr/lib/jvm/java-1.8.0/bin/java 74 | /usr/lib/jvm/jre-1.8.0/bin/java 75 | /usr/bin/java 76 | " 77 | for candidate in $candidates 78 | do 79 | [ -x "$JENKINS_JAVA_CMD" ] && break 80 | JENKINS_JAVA_CMD="$candidate" 81 | done 82 | 83 | JAVA_CMD="$JENKINS_JAVA_CMD $JENKINS_JAVA_OPTIONS -DJENKINS_HOME=$JENKINS_HOME -jar $JENKINS_WAR" 84 | PARAMS="--logfile=/var/log/@@ARTIFACTNAME@@/@@ARTIFACTNAME@@.log --daemon" 85 | 86 | if [ "$JENKINS_ENABLE_ACCESS_LOG" = "yes" ]; then 87 | PARAMS="$PARAMS --accessLoggerClassName=winstone.accesslog.SimpleAccessLogger --simpleAccessLogger.format=combined --simpleAccessLogger.file=/var/log/@@ARTIFACTNAME@@/access_log" 88 | fi 89 | 90 | RETVAL=0 91 | 92 | case "$1" in 93 | start) 94 | echo -n "Starting @@PRODUCTNAME@@ " 95 | daemon --user "$JENKINS_USER" --pidfile "$JENKINS_PID_FILE" @@PREFIX@@/lib/@@ARTIFACTNAME@@/distrib/daemon/run.sh $PARAMS > /dev/null 96 | RETVAL=$? 97 | if [ $RETVAL = 0 ]; then 98 | success 99 | echo > "$JENKINS_PID_FILE" # just in case we fail to find it 100 | MY_SESSION_ID=`/bin/ps h -o sess -p $$` 101 | # get PID 102 | /bin/ps hww -u "$JENKINS_USER" -o sess,ppid,pid,cmd | \ 103 | while read sess ppid pid cmd; do 104 | [ "$ppid" = 1 ] || continue 105 | # this test doesn't work because Jenkins sets a new Session ID 106 | # [ "$sess" = "$MY_SESSION_ID" ] || continue 107 | echo "$cmd" | grep $JENKINS_WAR > /dev/null 108 | [ $? = 0 ] || continue 109 | # found a PID 110 | echo $pid > "$JENKINS_PID_FILE" 111 | done 112 | else 113 | failure 114 | fi 115 | echo 116 | ;; 117 | stop) 118 | echo -n "Shutting down @@PRODUCTNAME@@ " 119 | killproc @@ARTIFACTNAME@@ 120 | RETVAL=$? 121 | echo 122 | ;; 123 | try-restart|condrestart) 124 | if test "$1" = "condrestart"; then 125 | echo "${attn} Use try-restart ${done}(LSB)${attn} rather than condrestart ${warn}(RH)${norm}" 126 | fi 127 | $0 status 128 | if test $? = 0; then 129 | $0 restart 130 | else 131 | : # Not running is not a failure. 132 | fi 133 | ;; 134 | restart) 135 | $0 stop 136 | $0 start 137 | ;; 138 | force-reload) 139 | echo -n "Reload service @@PRODUCTNAME@@ " 140 | $0 try-restart 141 | ;; 142 | reload) 143 | $0 restart 144 | ;; 145 | status) 146 | status @@ARTIFACTNAME@@ 147 | RETVAL=$? 148 | ;; 149 | probe) 150 | ## Optional: Probe for the necessity of a reload, print out the 151 | ## argument to this init script which is required for a reload. 152 | ## Note: probe is not (yet) part of LSB (as of 1.9) 153 | 154 | test "$JENKINS_CONFIG" -nt "$JENKINS_PID_FILE" && echo reload 155 | ;; 156 | *) 157 | echo "Usage: $0 {start|stop|status|try-restart|restart|force-reload|reload|probe}" 158 | exit 1 159 | ;; 160 | esac 161 | exit $RETVAL 162 | -------------------------------------------------------------------------------- /packaging/daemon/run.sh.in: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #Created by Sam Gleske 3 | #Tue Jan 30 23:53:18 PST 2018 4 | #Docker runner for Jenkins 5 | 6 | set -ex 7 | 8 | function sync_not_disabled() { 9 | [ ! -f "${JENKINS_HOME}"/disable-sync ] 10 | } 11 | 12 | function git_not_disabled() { 13 | [ ! -f "${JENKINS_HOME}"/disable-git ] 14 | } 15 | 16 | if [ "${JENKINS_ENABLE_SYSV_CONFIG:-@@JENKINS_ENABLE_SYSV_CONFIG@@}" = yes ]; then 17 | [ ! -f /etc/sysconfig/jenkins ] || source /etc/sysconfig/jenkins 18 | [ ! -f /etc/default/jenkins ] || source /etc/default/jenkins 19 | fi 20 | 21 | if [ -z "${JAVA_HOME}" ]; then 22 | echo "Error: JAVA_HOME not set." 23 | exit 1 24 | fi 25 | export PATH="${JAVA_HOME}/bin:${PATH}" 26 | 27 | JENKINS_WAR="~~WAR~~" 28 | 29 | # container configurable variables 30 | JENKINS_HOME="${JENKINS_HOME:-~~HOME~~}" 31 | [ -n "${JENKINS_JAVA_OPTIONS:-}" ] || JENKINS_JAVA_OPTIONS="~~JENKINS_JAVA_OPTIONS~~" 32 | JENKINS_PORT="${JENKINS_PORT:-@@HTTP_PORT@@}" 33 | JENKINS_KEYSTORE="${JENKINS_KEYSTORE:-~~HTTPS_KEYSTORE~~}" 34 | JENKINS_DEBUG_LEVEL="${JENKINS_DEBUG_LEVEL:-5}" 35 | JENKINS_GIT_USER="${JENKINS_GIT_USER:-Jenkins Automation}" 36 | JENKINS_GIT_EMAIL="${JENKINS_GIT_EMAIL:-noreply@example.com}" 37 | JENKINS_JAVA_CMD="${JENKINS_JAVA_CMD:-java}" 38 | [ -n "${JENKINS_ARGS:-}" ] || JENKINS_ARGS="-~~ADDITIONAL_JENKINS_ARGS~~" 39 | 40 | if [ -n "${JENKINS_KEYSTORE:-}" ]; then 41 | #smart HTTPS defaults for Jenkins 42 | JENKINS_HTTPS_PORT="${JENKINS_HTTPS_PORT:-8443}" 43 | JENKINS_HTTPS_KEYSTORE_PASSWORD_FILE="${JENKINS_HTTPS_KEYSTORE_PASSWORD_FILE:-/dev/null}" 44 | JENKINS_HTTPS_KEYSTORE_PASSWORD="${JENKINS_HTTPS_KEYSTORE_PASSWORD:-$(< "${JENKINS_HTTPS_KEYSTORE_PASSWORD_FILE}")}" 45 | JENKINS_HTTPS_KEYSTORE_PASSWORD="${JENKINS_HTTPS_KEYSTORE_PASSWORD:-changeit}" 46 | JENKINS_HTTPS_LISTEN_ADDRESS="0.0.0.0" 47 | fi 48 | 49 | eval "JAVA_OPTS=( ${JAVA_OPTS} )" 50 | eval "JENKINS_JAVA_OPTIONS=( ${JENKINS_JAVA_OPTIONS} )" 51 | JAVA_CMD=( "${JENKINS_JAVA_CMD}" "${JAVA_OPTS[@]}" "${JENKINS_JAVA_OPTIONS[@]}" "-DJENKINS_HOME=${JENKINS_HOME}" -jar "${JENKINS_WAR}" ) 52 | ARGS=( "--webroot=/var/cache/@@ARTIFACTNAME@@/war" "--pluginroot=/var/cache/@@ARTIFACTNAME@@/plugins" ) 53 | # pass shell arguments through as JENKINS_ARGS 54 | if [ "$#" -gt 0 ]; then 55 | ARGS+=( "$@" ) 56 | fi 57 | [ -z "${JENKINS_PORT}" ] || ARGS+=( "--httpPort=${JENKINS_PORT}" ) 58 | [ -z "${JENKINS_LISTEN_ADDRESS}" ] || ARGS+=( "--httpListenAddress=${JENKINS_LISTEN_ADDRESS}" ) 59 | if [ -n "${JENKINS_KEYSTORE:-}" ]; then 60 | [ -z "${JENKINS_HTTPS_PORT}" ] || ARGS+=( "--httpsPort=${JENKINS_HTTPS_PORT}" ) 61 | [ -z "${JENKINS_HTTPS_KEYSTORE}" ] || ARGS+=( "--httpsKeyStore=${JENKINS_HTTPS_KEYSTORE}" ) 62 | [ -z "${JENKINS_HTTPS_KEYSTORE_PASSWORD}" ] || ARGS+=( "--httpsKeyStorePassword=${JENKINS_HTTPS_KEYSTORE_PASSWORD}" ) 63 | [ -z "${JENKINS_HTTPS_LISTEN_ADDRESS}" ] || ARGS+=( "--httpsListenAddress=${JENKINS_HTTPS_LISTEN_ADDRESS}" ) 64 | fi 65 | [ -z "${JENKINS_DEBUG_LEVEL}" ] || ARGS+=( "--debug=${JENKINS_DEBUG_LEVEL}" ) 66 | if [ -n "${JENKINS_ARGS:-}" ]; then 67 | declare -a tmp_jenkins_args 68 | read -ra tmp_jenkins_args <<< "${JENKINS_ARGS}" 69 | ARGS+=( "${tmp_jenkins_args[@]}" ) 70 | fi 71 | 72 | #synchronize plugins 73 | if sync_not_disabled && [ -d "@@PREFIX@@/lib/@@ARTIFACTNAME@@/plugins/" ]; then 74 | echo "Overwriting Jenkins plugins." 75 | mkdir -p "${JENKINS_HOME}/plugins" 76 | rsync -a --delete-after "@@PREFIX@@/lib/@@ARTIFACTNAME@@/plugins/" "${JENKINS_HOME}/plugins/" 77 | for plugin in "${JENKINS_HOME}"/plugins/*.jpi;do 78 | #pin plugin versions 79 | #https://wiki.jenkins-ci.org/display/JENKINS/Pinned+Plugins 80 | touch "${plugin}.pinned" 81 | done 82 | fi 83 | #synchronize scripts 84 | if sync_not_disabled && [ -d "@@PREFIX@@/lib/@@ARTIFACTNAME@@/scripts/init.groovy.d/" ]; then 85 | echo "Overwriting Jenkins init scripts." 86 | mkdir -p "${JENKINS_HOME}/init.groovy.d" 87 | rsync -a --delete-after "@@PREFIX@@/lib/@@ARTIFACTNAME@@/scripts/init.groovy.d/" "${JENKINS_HOME}/init.groovy.d/" 88 | fi 89 | #configure tracking JENKINS_HOME with git 90 | if [ ! -e "${JENKINS_HOME}/.gitignore" ]; then 91 | echo "Creating '${JENKINS_HOME}/.gitignore'." 92 | cp "@@PREFIX@@/lib/@@ARTIFACTNAME@@/share/gitignore" "${JENKINS_HOME}/.gitignore" 93 | chmod 644 "${JENKINS_HOME}/.gitignore" 94 | fi 95 | 96 | #create git repo if it doesn't exist 97 | if git_not_disabled && [ ! -d "${JENKINS_HOME}/.git" ]; then 98 | ( 99 | cd "${JENKINS_HOME}" 100 | git init 101 | git config user.name "${JENKINS_GIT_USER}" 102 | git config user.email "${JENKINS_GIT_EMAIL}" 103 | git add .gitignore 104 | git commit -m 'initial commit' 105 | ) 106 | fi 107 | 108 | #commit unsaved configuration before starting up/upgrading 109 | if ! diff "@@PREFIX@@/lib/@@ARTIFACTNAME@@/@@MANIFEST@@" "${JENKINS_HOME}/@@MANIFEST@@" &> /dev/null; then 110 | if git_not_disabled; then 111 | git config --local --get user.name || git config user.name "${JENKINS_GIT_USER}" 112 | git config --local --get user.email || git config user.email "${JENKINS_GIT_EMAIL}" 113 | git add -A 114 | #commit only if changes exist 115 | git diff-index --quiet HEAD || git commit -m 'pre-start checkpoint commit saving configs' 116 | fi 117 | 118 | #save the current version of the bootstrapper to JENKINS_HOME on startup 119 | echo "Overwriting manifest in '${JENKINS_HOME}/plugins/@@MANIFEST@@'." 120 | cp -f "@@PREFIX@@/lib/@@ARTIFACTNAME@@/@@MANIFEST@@" "${JENKINS_HOME}/" 121 | echo '@@COMMIT@@' > "${JENKINS_HOME}/@@PACKAGENAME@@-commit" 122 | 123 | if sync_not_disabled; then 124 | #overwrite dailycommit which is meant for daily commit snapshots of jenkins config 125 | echo "Overwriting '${JENKINS_HOME}/dailycommit.sh'." 126 | cp -f "@@PREFIX@@/lib/@@ARTIFACTNAME@@/share/dailycommit.sh" "${JENKINS_HOME}/" 127 | chmod 755 "${JENKINS_HOME}/dailycommit.sh" 128 | 129 | #overwrite gitignore in case it has changed in the package 130 | echo "Overwriting GITIGNORE file in JENKINS_HOME" 131 | cp -f "@@PREFIX@@/lib/@@ARTIFACTNAME@@/share/gitignore" "${JENKINS_HOME}/.gitignore" 132 | fi 133 | fi 134 | 135 | for x in ~/.bash_profile ~/.bashrc; do 136 | grep JENKINS_HOME "${x}" || echo "export JENKINS_HOME=\"${JENKINS_HOME}\"" >> "${x}" 137 | done 138 | 139 | echo "Starting @@PRODUCTNAME@@" 140 | #launch in foreground for docker to collect logs on stdout 141 | exec "${JAVA_CMD[@]}" "${ARGS[@]}" 142 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | # Determine the Java command to use to start the JVM. 86 | if [ -n "$JAVA_HOME" ] ; then 87 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 88 | # IBM's JDK on AIX uses strange locations for the executables 89 | JAVACMD="$JAVA_HOME/jre/sh/java" 90 | else 91 | JAVACMD="$JAVA_HOME/bin/java" 92 | fi 93 | if [ ! -x "$JAVACMD" ] ; then 94 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 95 | 96 | Please set the JAVA_HOME variable in your environment to match the 97 | location of your Java installation." 98 | fi 99 | else 100 | JAVACMD="java" 101 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 102 | 103 | Please set the JAVA_HOME variable in your environment to match the 104 | location of your Java installation." 105 | fi 106 | 107 | # Increase the maximum file descriptors if we can. 108 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 109 | MAX_FD_LIMIT=`ulimit -H -n` 110 | if [ $? -eq 0 ] ; then 111 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 112 | MAX_FD="$MAX_FD_LIMIT" 113 | fi 114 | ulimit -n $MAX_FD 115 | if [ $? -ne 0 ] ; then 116 | warn "Could not set maximum file descriptor limit: $MAX_FD" 117 | fi 118 | else 119 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 120 | fi 121 | fi 122 | 123 | # For Darwin, add options to specify how the application appears in the dock 124 | if $darwin; then 125 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 126 | fi 127 | 128 | # For Cygwin or MSYS, switch paths to Windows format before running java 129 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 130 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 131 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 132 | JAVACMD=`cygpath --unix "$JAVACMD"` 133 | 134 | # We build the pattern for arguments to be converted via cygpath 135 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 136 | SEP="" 137 | for dir in $ROOTDIRSRAW ; do 138 | ROOTDIRS="$ROOTDIRS$SEP$dir" 139 | SEP="|" 140 | done 141 | OURCYGPATTERN="(^($ROOTDIRS))" 142 | # Add a user-defined pattern to the cygpath arguments 143 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 144 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 145 | fi 146 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 147 | i=0 148 | for arg in "$@" ; do 149 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 150 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 151 | 152 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 153 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 154 | else 155 | eval `echo args$i`="\"$arg\"" 156 | fi 157 | i=`expr $i + 1` 158 | done 159 | case $i in 160 | 0) set -- ;; 161 | 1) set -- "$args0" ;; 162 | 2) set -- "$args0" "$args1" ;; 163 | 3) set -- "$args0" "$args1" "$args2" ;; 164 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 165 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 166 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 167 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 168 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 169 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 170 | esac 171 | fi 172 | 173 | # Escape application args 174 | save () { 175 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 176 | echo " " 177 | } 178 | APP_ARGS=`save "$@"` 179 | 180 | # Collect all arguments for the java command, following the shell quoting and substitution rules 181 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 182 | 183 | exec "$JAVACMD" "$@" 184 | -------------------------------------------------------------------------------- /scripts/configure-matrix-authorization-strategy.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015-2023 Sam Gleske - https://github.com/samrocketman/jenkins-bootstrap-shared 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | /* 17 | Configure matrix authorization strategy with permissions for users and 18 | groups. This script is idempotent and will only change configuration if 19 | necessary. 20 | 21 | Example configuration: 22 | authz_strategy_config = [ 23 | strategy: 'GlobalMatrixAuthorizationStrategy', 24 | user_permissions: [ 25 | anonymous: ['Job Discover'], 26 | authenticated: ['Overall Read', 'Job Read', 'View Read'], 27 | admin: ['Overall Administer'] 28 | ] 29 | ] 30 | 31 | Available Authorization Strategies: 32 | GlobalMatrixAuthorizationStrategy 33 | ProjectMatrixAuthorizationStrategy 34 | 35 | Available user permissions: 36 | Overall Administer 37 | Overall Read 38 | Agent Configure 39 | Agent Delete 40 | Agent Create 41 | Agent Disconnect 42 | Agent Connect 43 | Agent Build 44 | Agent Provision 45 | Run Delete 46 | Run Update 47 | Job Create 48 | Job Delete 49 | Job Configure 50 | Job Read 51 | Job Discover 52 | Job Build 53 | Job Workspace 54 | Job Cancel 55 | SCM Tag 56 | Credentials Create 57 | Credentials Update 58 | Credentials View 59 | Credentials Delete 60 | Credentials ManageDomains 61 | Job Move 62 | View Create 63 | View Delete 64 | View Configure 65 | View Read 66 | Run Replay 67 | 68 | "Job ViewStatus" permission becomes available after installing the 69 | embeddable build status plugin. 70 | */ 71 | 72 | import hudson.security.GlobalMatrixAuthorizationStrategy 73 | import hudson.security.Permission 74 | import hudson.security.ProjectMatrixAuthorizationStrategy 75 | import jenkins.model.Jenkins 76 | import org.jenkinsci.plugins.matrixauth.PermissionEntry 77 | import org.jenkinsci.plugins.matrixauth.AuthorizationProperty 78 | 79 | /** 80 | * FUNCTIONS AND SETUP CODE 81 | */ 82 | String shortName(Permission p) { 83 | p.id.tokenize('.')[-2..-1].join(' ') 84 | .replace('Hudson','Overall') 85 | .replace('Computer', 'Agent') 86 | .replace('Item', 'Job') 87 | .replace('CredentialsProvider', 'Credentials') 88 | } 89 | 90 | Map getCurrentPermissions(Map config = [:]) { 91 | Map currentPermissions = [:].withDefault { [].toSet() } 92 | if(!('getGrantedPermissions' in Jenkins.instance.authorizationStrategy.metaClass.methods*.name.sort().unique())) { 93 | return currentPermissions 94 | } 95 | Closure merger = { Map nmap, Map m -> 96 | m.each { k, v -> 97 | nmap[k] += v 98 | } 99 | } 100 | Jenkins.instance.authorizationStrategy.grantedPermissions.collect { permission, userList -> 101 | userList.collect { user -> 102 | [ (user): shortName(permission) ] 103 | } 104 | }.flatten().each merger.curry(currentPermissions) 105 | currentPermissions 106 | } 107 | 108 | boolean isConfigurationEqual(Map config) { 109 | Map currentPermissions = getCurrentPermissions(config) 110 | Jenkins.instance.authorizationStrategy.class.name.endsWith(config['strategy']) && 111 | !(false in config['user_permissions'].collect { k, v -> currentPermissions[k] == v.toSet() }) && 112 | currentPermissions.keySet() == config['user_permissions'].keySet() 113 | } 114 | 115 | boolean isValidConfig(def config, List validPermissions) { 116 | Map currentPermissions = getCurrentPermissions() 117 | config instanceof Map && 118 | config.keySet().containsAll(['strategy', 'user_permissions']) && 119 | config['strategy'] && 120 | config['strategy'] instanceof String && 121 | config['strategy'] in ['GlobalMatrixAuthorizationStrategy', 'ProjectMatrixAuthorizationStrategy'] && 122 | config['user_permissions'] && 123 | !(false in config['user_permissions'].collect { k, v -> 124 | k instanceof String && 125 | (v instanceof List || v instanceof Set) && 126 | !(false in v.collect { 127 | validPermissions.contains(it) 128 | }) 129 | }) 130 | } 131 | 132 | Map permissionIds = Permission.all.findAll { permission -> 133 | List nonConfigurablePerms = ['RunScripts', 'UploadPlugins', 'ConfigureUpdateCenter'] 134 | permission.enabled && 135 | !permission.id.startsWith('hudson.security.Permission') && 136 | !(true in nonConfigurablePerms.collect { permission.id.endsWith(it) }) 137 | }.collect { permission -> 138 | [ (shortName(permission)): permission ] 139 | }.sum() 140 | 141 | /** 142 | * MAIN EXECUTION 143 | */ 144 | 145 | if(!binding.hasVariable('authz_strategy_config')) { 146 | authz_strategy_config = [:] 147 | } 148 | 149 | if(!isValidConfig(authz_strategy_config, permissionIds.keySet().toList())) { 150 | println([ 151 | 'Skip configuring matrix authorization strategy because no valid config was provided.', 152 | 'Available Authorization Strategies:\n GlobalMatrixAuthorizationStrategy\n ProjectMatrixAuthorizationStrategy', 153 | "Available Permissions:\n ${permissionIds.keySet().join('\n ')}" 154 | ].join('\n')) 155 | return 156 | } 157 | 158 | if(isConfigurationEqual(authz_strategy_config)) { 159 | println "Nothing changed. ${authz_strategy_config['strategy']} authorization strategy already configured." 160 | return 161 | } 162 | 163 | println "Configuring authorization strategy ${authz_strategy_config['strategy']}" 164 | 165 | def authz_strategy = Class.forName("hudson.security.${authz_strategy_config['strategy']}").newInstance() 166 | 167 | List jenkins_builtin_groups = [ 'authenticated' ] 168 | 169 | // build the permissions in the strategy 170 | authz_strategy_config['user_permissions'].each { user, permissions -> 171 | permissions.each { p -> 172 | Boolean isGroup = user.contains('*') || (user in jenkins_builtin_groups) || user.startsWith('group:') 173 | String user_type = isGroup ? "group" : "user" 174 | authz_strategy.add(permissionIds[p], PermissionEntry."${user_type}"(user -~ /^group:/)) 175 | println "For ${user_type} ${user} grant permission ${p}." 176 | } 177 | } 178 | 179 | // configure global authorization 180 | Jenkins.instance.authorizationStrategy = authz_strategy 181 | 182 | // save settings to persist across restarts 183 | Jenkins.instance.save() 184 | --------------------------------------------------------------------------------