Skip to content

Instantly share code, notes, and snippets.

@Gorniv
Created February 15, 2017 08:37
Show Gist options
  • Save Gorniv/3ea896c89ea944f702feac5c2c8a6bf8 to your computer and use it in GitHub Desktop.
Save Gorniv/3ea896c89ea944f702feac5c2c8a6bf8 to your computer and use it in GitHub Desktop.
Пример jenkins в контейнере для сборки докер образов + Jenkinsfile по которому идет сборка
version: "2"
services:
jenkins:
build: ./jenkins
volumes:
- /var/jenkins_home:/var/jenkins_home
- /var/run/docker.sock:/var/run/docker.sock
environment:
JAVA_OPTS: "-Dhudson.model.DirectoryBrowserSupport.CSP=\"default-src *;style-src 'unsafe-inline';script-src 'unsafe-inline';img-src * data: blob: filesystem:\""
restart: always
FROM jenkins
USER root
RUN apt-get update \
&& apt-get install -y ruby python make openssl ca-certificates gcc g++\
&& gem install sass
# Install docker cli
RUN curl -o docker.tgz https://get.docker.com/builds/Linux/x86_64/docker-latest.tgz \
&& tar -xvzf docker.tgz \
&& mv docker/docker /usr/bin/docker && chmod +x /usr/bin/docker \
&& rm -rf docker.tgz docker
#This allows jenkins access to /var/run/docker.sock
RUN groupadd -g 999 hostroot
RUN adduser jenkins hostroot
#Install docker-compose
RUN curl -L https://github.com/docker/compose/releases/download/1.8.0/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose \
&& chmod +x /usr/local/bin/docker-compose
#Install docker-machine
RUN curl -L https://github.com/docker/machine/releases/download/v0.8.1/docker-machine-`uname -s`-`uname -m` > /usr/local/bin/docker-machine && \
chmod +x /usr/local/bin/docker-machine
# Replace shell with bash so we can source files
RUN rm /bin/sh && ln -s /bin/bash /bin/sh
ENV NVM_DIR /usr/local/nvm
ENV NODE_VERSION 5.12.0
# Install nvm with node and npm
RUN curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.31.4/install.sh | bash \
&& source $NVM_DIR/nvm.sh \
&& nvm install $NODE_VERSION \
&& nvm alias default $NODE_VERSION \
&& nvm use default
ENV NODE_PATH $NVM_DIR/versions/node/v$NODE_VERSION/lib/node_modules
ENV PATH $NVM_DIR/versions/node/v$NODE_VERSION/bin:$PATH
USER jenkins
#!groovy
def project = 'projectname'
properties properties: [pipelineTriggers([]), [$class: 'GithubProjectProperty', displayName: 'Jenkins', projectUrlStr: "https://github.com/organizationname/${project}/"]]
env.NODE_ENV = "testing"
env.DOCKER_IMAGE = "${project}"
env.DOCKER_IMAGE_TAG = "${env.BRANCH_NAME}b${env.BUILD_NUMBER}"
env.PROJECT_NAME = "${project}${env.BRANCH_NAME}b${env.BUILD_NUMBER}"
env.PROJECT_NAME = env.PROJECT_NAME.replaceAll(~/[^\d\w]/,'').toLowerCase()
env.NUM_OF_WORKERS = 1
env.SLACK_CHAT = "#${project}"
print "Environment will be : ${env.NODE_ENV}"
print "Image name will be : ${env.DOCKER_IMAGE}"
print "Docker compose project name will be : ${env.PROJECT_NAME}"
node {
step([$class: 'GitHubCommitStatusSetter'])
def error
def image
def imageStatic
try {
stage('Checkout') {
checkout scm
}
stage('Check configs') {
testComposeConfigs "staging"
testComposeConfigs "production"
testComposeConfigs "testing"
testComposeConfigs "local"
}
stage('Build') {
sh 'node -v'
sh 'cd ./client && npm prune && npm install'
sh 'npm prune && npm install'
sh 'cp tests/config.jenkins.js ./config.js'
sh 'npm run build'
}
stage('Build Docker Image'){
parallel 'image': {
image = docker.build("${env.DOCKER_IMAGE}:${env.DOCKER_IMAGE_TAG}")
}, 'image-static': {
if (env.BRANCH_NAME == 'master' || env.BRANCH_NAME == 'production') {
imageStatic = docker.build("${env.DOCKER_IMAGE}-static:${env.DOCKER_IMAGE_TAG}", '-f ./client/Dockerfile .')
}
}
}
stage('Start services') {
env.WORKSPACE = sh(returnStdout: true, script: 'pwd').trim()
compose "up -d"
compose "scale selenium-chrome=${env.NUM_OF_WORKERS}"
sleep 5
compose "exec -T mongodb mongo --eval 'rs.initiate()'"
compose "exec -T app mkdir -p tests/out"
env.SELENIUM = compose('port selenium 4444').trim().replace(/0.0.0.0:/,'')
env.MONGODB = compose('port mongodb 27017').trim().replace(/0.0.0.0/,'server.organizationname.com')
env.APP_API = compose('port app 3000').trim().replace(/0.0.0.0/,'http://server.organizationname.com')
env.MAILDEV_WEB = compose('port maildev 80').trim().replace(/0.0.0.0:/,'')
env.MAILDEV_SMTP = compose('port maildev 25').trim().replace(/0.0.0.0:/,'')
env.MAILDEV_HOST = 'ci.organizationname.com'
}
stage('Test') {
sh 'rm -rf ./tests/out && mkdir -p ./tests/out/reports'
parallel 'eslint':{
sh 'npm run eslint'
}, 'unittest':{
sh "npm run unittest"
}, 'karma':{
sh "npm run karma"
}, 'selenium': {
sh "node --harmony node_modules/.bin/parallel-cucumber-js -r tests/bootstrap.js -r tests/features -w ${env.NUM_OF_WORKERS} -f json:./tests/out/output.json tests/features"
}
}
if (env.BRANCH_NAME == 'master' || env.BRANCH_NAME == 'production') {
def tag
//todo: fix server name
if (env.BRANCH_NAME == 'master'){
tag = 'staging'
env.DEPLOY_SERVER = 'docker'
} else {
tag = 'production'
env.DEPLOY_SERVER = 'docker'
}
//this prevents parallel deploy
lock("deploy:${project}:${tag}") {
stage ('Push Docker Images') {
docker.withRegistry('https://registry.organizationname.com/v2/', 'docker-registry') {
parallel 'tag': {
image.push(tag)
}, 'tag-static': {
imageStatic.push(tag)
}
}
}
stage("Deploy to ${tag}") {
remoteDocker "docker-compose -f docker/${tag}.yaml -p ${project}_${tag} ps"
remoteDocker "docker-compose -f docker/${tag}.yaml -p ${project}_${tag} pull app backend"
remoteDocker "docker-compose -f docker/${tag}.yaml -p ${project}_${tag} restart consul"
remoteDocker "docker-compose -f docker/${tag}.yaml -p ${project}_${tag} up -d --remove-orphans"
remoteDocker "docker-compose -f docker/${tag}.yaml -p ${project}_${tag} restart app"
remoteDocker "docker-compose -f docker/${tag}.yaml -p ${project}_${tag} ps"
}
}
}
currentBuild.result = "SUCCESS"
step([$class: 'GitHubCommitStatusSetter'])
} catch (err) {
currentBuild.result = "FAILURE"
print err
error = err
}
stage('Cleanup') {
if (currentBuild.result == "FAILURE") {
step([$class: 'GitHubCommitStatusSetter'])
slackSend channel: env.SLACK_CHAT, color: "danger", message: "${currentBuild.result}: Build <${env.BUILD_URL}|${env.BUILD_DISPLAY_NAME}> <${env.CHANGE_URL}|${env.CHANGE_TITLE}> by ${env.CHANGE_AUTHOR} (${env.CHANGE_TARGET}) with error: ${error}"
}
try {
stopAndRemove()
} catch (err) {
print "${err}"
slackSend channel: '#ci', color: "danger", message: "Build <${env.BUILD_URL}|${env.BUILD_DISPLAY_NAME}> ${err}"
}
}
}
def stopAndRemove(){
try {
copyTestOut()
} catch (err) {
print "${err}"
slackSend channel: '#ci', color: "danger", message: "Build <${env.BUILD_URL}|${env.BUILD_DISPLAY_NAME}> ${err}"
}
compose("stop")
compose("rm -fv")
sh "docker network rm \$(docker network ls -q -f 'name=${env.PROJECT_NAME}')"
try {
sh "docker images --format '{{.ID}} {{.Repository}} {{.Tag}}' | grep ${env.DOCKER_IMAGE_TAG} | cut -d ' ' -f 1 | xargs -n 1 docker rmi -f"
} catch(err) {
print "${err}"
slackSend channel: '#ci', color: "danger", message: "Build <${env.BUILD_URL}|${env.BUILD_DISPLAY_NAME}> ${err}"
}
}
def compose(command){
return sh(returnStdout: true, script:"docker-compose -f docker/testing.yaml -p ${env.PROJECT_NAME} ${command}")
}
def testComposeConfigs(config) {
return sh(returnStdout: true, script:"docker-compose -f docker/${config}.yaml config -q")
}
def remoteDocker(command){
sh "eval \$(docker-machine env ${env.DEPLOY_SERVER} --shell bash) && ${command}"
}
def copyTestOut(){
try {
sh 'node tests/html-report.js'
publishHTML(target: [allowMissing: false, alwaysLinkToLastBuild: true, keepAll: false, reportDir: 'tests/out/reports', reportFiles: 'report.html', reportName: 'Selenium Report'])
} catch (err) {
print "${err}"
slackSend channel: '#ci', color: "danger", message: "Build <${env.BUILD_URL}|${env.BUILD_DISPLAY_NAME}> ${err}"
}
sh 'mkdir -p tests/out/logs'
compose("config --services | xargs -I {} -t bash -c 'docker-compose -f docker/testing.yaml -p ${env.PROJECT_NAME} logs --no-color {} > tests/out/logs/{}.log'")
archiveArtifacts artifacts: 'tests/out/**/*.*', excludes: null
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment