From cab49ea108b4f45b19cb8486b328ca0488a47d13 Mon Sep 17 00:00:00 2001 From: Mathias Kaufmann Date: Wed, 22 Mar 2017 22:28:04 +0100 Subject: [PATCH] Added Jenkinsfile MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added Jenkins-Pipeline to automatically build the project. The Pipepline depends on: - 3 Jenkins-Slaves which are labeld „linux“, „osx“, and „win64“ - 2(3) Tools: „rust“ and „docker“ ( and „rustup“ on Windows) Assign those labels to your build-slaves in jenkins. Signed-off-by: Mathias Kaufmann --- Jenkinsfile | 34 +++++++++ ci/Dockerfile | 10 +++ ci/build.Dockerfile | 10 +++ ci/environment | 177 ++++++++++++++++++++++++++++++++++++++++++++ ci/release | 47 ++++++++++++ 5 files changed, 278 insertions(+) create mode 100644 Jenkinsfile create mode 100644 ci/Dockerfile create mode 100644 ci/build.Dockerfile create mode 100755 ci/environment create mode 100755 ci/release diff --git a/Jenkinsfile b/Jenkinsfile new file mode 100644 index 000000000..74795bc7f --- /dev/null +++ b/Jenkinsfile @@ -0,0 +1,34 @@ +def environment(args) { + sh "ci/environment " + args +} + +stage("Build") { + parallel ( + "linux": { + node("linux && docker") { + environment("cargo build --release") + environment("strip -s target/release/asciii") + sh("test -f output/asciii-linux && exit 0; mkdir output; cp target/release/asciii output/asciii-linux") + archiveArtifacts artifacts: "output/*" + } + }, + "osx": { + node("osx && rust") { + environment("cargo build --release") + environment("strip target/release/asciii") + sh("test -f output/asciii-macos && exit 0; mkdir output; cp target/release/asciii output/asciii-macos") + archiveArtifacts artifacts: "output/*" + } + }, + "win64": { + node("win64 && rust") { + // TODO: fix line-endings + sh("sed -i 's/\\r\$//' ci/environment") + // builds on stable fail because of reasons, so use nighly for now + environment("rustup run nightly-x86_64-pc-windows-msvc cargo build --release") + sh("test -f output/asciii-windows.exe && exit 0; mkdir output; cp target/release/asciii.exe output/asciii-windows.exe") + archiveArtifacts artifacts: "output/*" + } + } + ) +} diff --git a/ci/Dockerfile b/ci/Dockerfile new file mode 100644 index 000000000..372f21e3a --- /dev/null +++ b/ci/Dockerfile @@ -0,0 +1,10 @@ +FROM frolvlad/alpine-glibc +MAINTAINER \ +[Mathias Kaufmann ] + +RUN apk add --no-cache --virtual .runtime-dep \ + git + +ADD target/release/asciii /bin/asciii + +ENTRYPOINT ["asciii"] \ No newline at end of file diff --git a/ci/build.Dockerfile b/ci/build.Dockerfile new file mode 100644 index 000000000..af8174057 --- /dev/null +++ b/ci/build.Dockerfile @@ -0,0 +1,10 @@ +FROM scorpil/rust + +# this image never gets pushed +# so run prepare in minimal steps +# allow to retain most of them +RUN apt-get update +RUN apt-get install -y cmake +RUN apt-get install -y git +RUN apt-get install -y zlib1g-dev +RUN apt-get install -y binutils \ No newline at end of file diff --git a/ci/environment b/ci/environment new file mode 100755 index 000000000..de7f1af09 --- /dev/null +++ b/ci/environment @@ -0,0 +1,177 @@ +#!/usr/bin/env bash + +export UNAME="${UNAME:-$(uname -s)}" + +# Windows build detaches on "-o pipefail" +case "$UNAME" in + Linux|Darwin) set -eo pipefail;; + *) set -e;; +esac +[[ -z "${TRACE:+x}" ]] || set -x + +export PROJECT_DIR="$(realpath "$(dirname "$(dirname "$0")")")" + +fail() { + echo "$*" + exit 1 +} + +require() { + for command in $*; do + command -v "$command" /dev/null 2>&1 || return 1 + if [[ "${FORCE_DOCKER:+x}" ]]; then + [[ "$command" = "docker" ]] && return 0 || return 1 + fi + done + return 0 +} + +assert() { + mode="$1" + [[ $# -le 0 ]] || shift + case "$mode" in + command) + require "${@}" && return 0 || true + for cmd in "${@}"; do echo "'$cmd' is required"; done + exit 1 + ;; + esac + return 0 +} + +size_of() { + case "$(uname -s)" in + Darwin) + stat -f '%z' "$1" + ;; + *) + stat -c '%s' "$1" + ;; + esac +} + +docker_solves_problems_for_me() { + # dockerize(windows) is windows + # dockerize(linux) is linux + # dockerize(darwin) is linux and is worthless + [[ "$UNAME" = "Darwin" ]] || return 0 + + return 1 +} + +dockerignore_add() { + local block_begin="begin automatic build ignores" + + [[ -f "$PROJECT_DIR"/.dockerignore ]] || touch "$PROJECT_DIR"/.dockerignore + grep -q "^# $block_begin" "$PROJECT_DIR"/.dockerignore \ + || echo "# $block_begin" >> "$PROJECT_DIR"/.dockerignore + echo "$*" >> "$PROJECT_DIR"/.dockerignore +} + +dockerignore_commit() { + local block_end="end automatic build ignores" + + [[ -f "$PROJECT_DIR"/.dockerignore ]] || touch "$PROJECT_DIR"/.dockerignore + grep -q "^# $block_end" "$PROJECT_DIR"/.dockerignore \ + || echo "# $block_end" >> "$PROJECT_DIR"/.dockerignore +} + +dockerignore_clear() { + local block_begin="begin automatic build ignores" + local block_end="end automatic build ignores" + + sed "/^# $block_begin$/,/^# $block_end$/d" "$PROJECT_DIR"/.dockerignore > "$PROJECT_DIR"/.dockerignore2 + mv "$PROJECT_DIR"/.dockerignore2 "$PROJECT_DIR"/.dockerignore + [[ "$(size_of "$PROJECT_DIR"/.dockerignore)" -gt 0 ]] || rm "$PROJECT_DIR"/.dockerignore +} + +prepare_docker_image() { + dockerignore_add 'target/*' + dockerignore_add '.cargo-cache/*' + dockerignore_commit + + docker build --file="$DOCKERFILE" --tag="ci/${DOCKER_IMAGE}" "$PROJECT_DIR" + + dockerignore_clear +} + +dockerize() { + # default IN_DOCKER to 0 + IN_DOCKER="${IN_DOCKER:-0}" + # fail if IN_DOCKER is greater than 1 + [[ "${IN_DOCKER}" -lt 1 ]] || fail "already in docker" + # increase by 1 + let IN_DOCKER+=1 + + # fail if docker not in $PATH + assert command docker + + # checks if docker is suitable + docker_solves_problems_for_me \ + || fail "docker cannot solve this problem" + + # build environment + prepare_docker_image + + # exec docker command + exec docker run \ + --rm \ + --entrypoint="$(realpath "$0")" \ + --volume="$(realpath "$0")":"$(realpath "$0")":ro \ + --volume="$PROJECT_DIR":"$PROJECT_DIR" \ + --volume="$CARGO_CACHE:$CARGO_HOME" \ + --env=CARGO_HOME="$CARGO_HOME" \ + --env=IN_DOCKER="$IN_DOCKER" \ + --env=TRACE=$TRACE \ + --workdir="$PROJECT_DIR" \ + --tmpfs=/tmp:rw,noexec,nosuid,size=65536k \ + "ci/${DOCKER_IMAGE}" \ + "${@}" +} + +vars() { + export DOCKER_IMAGE="${DOCKER_IMAGE:-asciii-build-environment}" + export DOCKERFILE="${DOCKERFILE:-$PROJECT_DIR/ci/build.Dockerfile}" + export CARGO_CACHE="${CARGO_CACHE:-$PROJECT_DIR/.cargo}" + export CARGO_HOME="${CARGO_HOME:-$PROJECT_DIR/.cargo}" +} + +__cargo() { + require cargo \ + || dockerize "$0" cargo "${@}" + exec cargo "${@}" +} + +__strip() { + require strip \ + || dockerize "$0" strip "${@}" + exec strip "${@}" +} + +__rustup_install() { + assert command curl + curl https://sh.rustup.rs -sSf | sh +} + +__rustup() { + # __rustup_install || true + assert command rustup + rustup "${@}" +} + +main() { + # dockerize environment if cargo or rustc not in $PATH + cmd="$1" + [[ $# -le 0 ]] || shift + case "$cmd" in + cargo|rustup|strip) + "__$cmd" "${@}" + ;; + *) + exec "$cmd" "${@}" + ;; + esac +} + +vars +main "${@}" \ No newline at end of file diff --git a/ci/release b/ci/release new file mode 100755 index 000000000..ec1fb56ef --- /dev/null +++ b/ci/release @@ -0,0 +1,47 @@ +#!/usr/bin/env bash + +set -eo pipefail +[[ -z "${TRACE:+x}" ]] || set -x + +vars() { + true +} + +release_version() { + echo "3.1.0" +} + +release_on_github() { + local src="$1" + local dst="$(basename "$src")" + echo "releasing $src as $bin as version $(release_version)" +} + +github_release_process() { + local artifact_dir="$1" + for release_binary in "$artifact_dir"/*; do + release_on_github "$(realpath "$release_binary")" + done + true +} + +docker_release_process() { + local docker_image="$1" + true +} + +main() { + local platform="$1" + [[ $# -le 0 ]] || shift + case "$platform" in + github) + github_release_process "${@}" + ;; + docker) + docker_release_process "${@}" + ;; + esac +} + +vars +main "${@}"