diff --git a/check-install.sh b/check-install.sh index 4902db5..6364a11 100755 --- a/check-install.sh +++ b/check-install.sh @@ -5,23 +5,23 @@ source "$DIR/lib/util.sh" function show_usage { echo - echo "Usage: $0 " + echo "Usage: $0 deb_file [image]" echo echo "Paramètres: " echo - echo " - Chemin vers le paquet Debian dont on doit vérifier l'installation" - echo " - Nom de l'image Docker à utiliser comme environnement pour tester l'installation" + echo " - deb_file Chemin vers le paquet Debian dont on doit vérifier l'installation" + echo " - image Optionnel - Nom de l'image Docker à utiliser comme environnement pour tester l'installation. Défaut: debian:jessie" echo } function create_container { # Escape image name - escaped_basename=$(echo "$BASE_IMAGE" | sed 's/[^a-z0-9\-\_\.]/\_/gi') + local escaped_basename=$(echo "$BASE_IMAGE" | sed 's/[^a-z0-9\-\_\.]/\_/gi') # Generate container tag container_tag="tamarin:${escaped_basename}_$(date +%s)" # Create temporary dir for the Dockerfile - temp_dir="$(mktemp -d)" + local temp_dir="$(mktemp -d)" # Link lib folder ln -s $(readlink -f "$DIR/lib") "$temp_dir/lib" @@ -44,20 +44,17 @@ function create_container { EOF # Build image - tar -C "$temp_dir" -czh . | docker build -t "$container_tag" - 1>&2 + tar -C "$temp_dir" -czh . | docker build -t "$container_tag" - 2> >(error) 1> >(info) # Delete temporary folder rm -rf "$temp_dir" - # Return newly created container's tag - echo $container_tag - } function main { # Create container image - container_tag=$(create_container) + create_container # Run container and install package docker run -e "DISTRIB=$BASE_IMAGE" --rm -v="$DEB_DIR:/deb" "$container_tag" "/deb/$DEB_NAME" @@ -72,7 +69,7 @@ function main { } # Test for arguments -if [ -z "$1" ] || [ -z "$2" ]; then +if [ -z "$1" ]; then show_usage exit 1 fi @@ -80,6 +77,6 @@ fi DEB_PATH=$(readlink -f "$1") DEB_NAME=$(basename "$DEB_PATH") DEB_DIR=$(dirname "$DEB_PATH") -BASE_IMAGE="$2" +BASE_IMAGE="${2:-debian:jessie}" main diff --git a/get-updated-manifest-from-git.sh b/get-updated-manifest-from-git.sh deleted file mode 100755 index c45a5cf..0000000 --- a/get-updated-manifest-from-git.sh +++ /dev/null @@ -1,77 +0,0 @@ -#!/usr/bin/env bash - -function show_usage { - echo - echo "Usage: $0 " - echo - echo "Paramètres: " - echo - echo " - Chemin vers le répertoire des sources du projet. Le projet doit être un dépôt Git valide." - echo -} - -function main { - - cd "$SRC_DIR" - - if [ ! -d '.git' ]; then - fatal "The directory $SRC_DIR seems not to be a valid Git repository." - fi - - MANIFEST=$(cat tamarin.json 2>/dev/null) - - if [ -z "$MANIFEST" ]; then - MANIFEST="{}" - fi - - # Extract project info from Git - repo_name=$(basename `git rev-parse --show-toplevel`) - current_commit=$(git log -n 1 --pretty=format:"%h") - # Get commits log as changelog - logs=$(git log --pretty=format:"%an - %h : %s" | sed 's/"/\\"/g') - # Set the top commiter as the maintainer of the project - git_maintainer=$(git log --pretty=short | git shortlog -s -n -e | sed 's/^\s*[0-9]*\s*//g' | head -n 1) - - # Get current version from manifest - current_name=$(echo "$MANIFEST" | jq -r ".name" | sed 's/null//') - current_version=$(echo "$MANIFEST" | jq -r ".version" | sed 's/null//') - current_changelog=$(echo "$MANIFEST" | jq -r ".changelog" | sed 's/null//') - current_maintainer=$(echo "$MANIFEST" | jq -r ".maintainer" | sed 's/null//') - - # Complete manifest - - # Add commit number to version - MANIFEST=$(echo "$MANIFEST" | jq -r ".version = \"${current_version:-0.0.0}~$current_commit\"") - # Set name if not defined - MANIFEST=$(echo "$MANIFEST" | jq -r ".name = \"${current_name:-$repo_name}\"") - # Set maintainer if not defined - MANIFEST=$(echo "$MANIFEST" | jq -r ".maintainer = \"${current_maintainer:-$git_maintainer}\"") - - # Set changelog from git log if not defined - if [ -z "$current_changelog" ]; then - - MANIFEST=$(echo "$MANIFEST" | jq -r ".changelog = []") - - while read -r entry; do - MANIFEST=$(echo "$MANIFEST" | jq -r ".changelog += [\"$entry\"]") - done <<< "$logs" - - fi - - echo "$MANIFEST" - -} - -# Load util lib -DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) -source "$DIR/lib/util.sh" - -# Test for arguments -if [ -z "$1" ]; then - show_usage - exit 1 -fi - -SRC_DIR=$(readlink -f "$1") - -main diff --git a/hooks/debian/install-build-depends-prebuild b/hooks/debian/install-build-depends-prebuild new file mode 100755 index 0000000..f98c2a1 --- /dev/null +++ b/hooks/debian/install-build-depends-prebuild @@ -0,0 +1,8 @@ +#!/bin/bash + +source "${TAMARIN_UTIL}" + +if [ -f debian/control ]; then + info "Installing build dependencies..." + mk-build-deps --install debian/control +fi diff --git a/hooks/git/create-changelog-prebuild b/hooks/git/create-changelog-prebuild new file mode 100755 index 0000000..dbd0bcc --- /dev/null +++ b/hooks/git/create-changelog-prebuild @@ -0,0 +1,38 @@ +#!/usr/bin/env bash + +source "${TAMARIN_UTIL}" + +if [ -f debian/changelog ] || [ ! -d .git ]; then + info "Not a Git repository or Debian changelog already exists !" + exit +fi + +# Get commits log as changelog + +logs=$(git log --pretty=format:"%an - %h : %s" | sed 's/"/\\"/g') + +# Set the top commiter as the maintainer of the project if not defined +top_contributor=$(git log --pretty=short | git shortlog -s -n -e | sed 's/^\s*[0-9]*\s*//g' | head -n 1) +maintainer=$(get_opt maintainer "${top_contributor}") + +project_name=$(get_opt project_name) +version=$(get_opt version 0.0.0) +distribution=$(get_opt distribution UNRELEASED) +urgency=$(get_opt urgency low) + +commit_count=$(git rev-list --count --first-parent HEAD) +current_commit=$(git log -n 1 --pretty=format:"%h") +version_suffix=${commit_count}~${current_commit} + +echo "${project_name} (${version}.tamarin${version_suffix}) ${distribution}; urgency=${urgency}" > debian/changelog + +echo >> debian/changelog + +while read -r entry; do + echo " * ${entry}" >> debian/changelog +done <<< "$(echo -e "${logs}" | sed 's/^"//')" + +echo >> debian/changelog + +echo " -- ${maintainer} $(date -R)" >> debian/changelog +echo >> debian/changelog diff --git a/hooks/git/install-git-containerbuild b/hooks/git/install-git-containerbuild new file mode 100755 index 0000000..585549f --- /dev/null +++ b/hooks/git/install-git-containerbuild @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +echo "RUN apt-get install --yes --no-install-recommends git-core" >> Dockerfile diff --git a/hooks/tamarin/export-dist-postbuild b/hooks/tamarin/export-dist-postbuild new file mode 100755 index 0000000..cd06a60 --- /dev/null +++ b/hooks/tamarin/export-dist-postbuild @@ -0,0 +1,10 @@ +#!/bin/bash + +# Create new directory +mkdir -p /dist + +# Move generated files +mv ../*.deb /dist +mv ../*.changes /dist +mv ../*.dsc /dist +mv ../*.tar.xz /dist diff --git a/lib/build.sh b/lib/build.sh index 9899bd6..4268f31 100755 --- a/lib/build.sh +++ b/lib/build.sh @@ -1,205 +1,37 @@ #!/usr/bin/env bash -DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) -source "${DIR}/util.sh" +LIB_DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) +export TAMARIN_UTIL="${LIB_DIR}/util.sh" + +source "${TAMARIN_UTIL}" DIST_DIR="${BASE_DIR}/dist" SRC_DIR="${BASE_DIR}/src" -function get_project_opt { - filter=${1} - manifest_path=${SRC_DIR}/tamarin.json - jq -r "${filter}" ${manifest_path} | sed 's/null//g' -} - -function get_build_dir { - project_name="${1}" - temp_dir=$(mktemp -d) - build_dir="${temp_dir}/${project_name}" - mkdir -p "${build_dir}" - echo ${build_dir} -} - -function exec_hook { - - hook_name=${1} - build_dir=${2} - - hook_script="${SRC_DIR}/$(get_project_opt .hooks.${hook_name})" - - # Test hook script existence - if [ ! -f "${hook_script}" ]; then - info "No ${hook_name} hook." - return - fi - - info "Executing ${hook_name} hook..." - - # Ensure the hook script is executable - chmod +x "${hook_script}" - - # Execute hook in a subshell - ( cd ${SRC_DIR} && DESTDIR="${build_dir}" SRCDIR="${SRC_DIR}" exec "${hook_script}" ) - - # If the script did not execute properly, we stop here - if [ $? != 0 ]; then - fatal "The '${hook_name}' hook script did not finished properly !" - fi - -} - -function create_debian_control_file { - - debian_dir="${1}" - - control_file="${debian_dir}/control" - touch "${control_file}" - - package_name=$(get_project_opt .name) - echo "Package: ${package_name}" > "${control_file}" - - package_version=$(get_project_opt .version) - echo "Version: ${package_version:-0.0.0}" >> "${control_file}" - - package_section=$(get_project_opt .section) - echo "Section: ${package_section:-unknown}" >> "${control_file}" - - package_priority=$(get_project_opt .priority) - echo "Priority: ${package_priority:-optional}" >> "${control_file}" - - package_arch=$(get_project_opt .arch) - echo "Architecture: ${package_arch:-all}" >> "${control_file}" - - dependencies=$( get_project_opt ".dependencies | .[\"${DISTRIB}\"] | @sh" | sed "s/' '/, /g" | sed "s/'//g" ) - - debug "Package dependencies: ${dependencies:-None}" - - echo "Depends: ${dependencies}" >> "${control_file}" - - package_maintainer=$(get_project_opt .maintainer) - echo "Maintainer: ${package_maintainer:-Unknown}" >> "${control_file}" - - package_description=$(get_project_opt .description) - echo "Description: ${package_description:-No description}" >> "${control_file}" - -} - -function create_debian_hooks { - - debian_dir="${1}" - - pre_install="$(get_project_opt .hooks.preInstall)" - if [ ! -z "${pre_install}" ]; then - cp "${SRC_DIR}/${pre_install}" "${debian_dir}/preinst" - chmod +x "${debian_dir}/preinst" - fi - - post_install="$(get_project_opt .hooks.postInstall)" - if [ ! -z "${post_install}" ]; then - cp "${SRC_DIR}/${post_install}" "${debian_dir}/postinst" - chmod +x "${debian_dir}/postinst" - fi - - pre_remove="$(get_project_opt .hooks.preRemove)" - if [ ! -z "${pre_remove}" ]; then - cp "${SRC_DIR}/${pre_remove}" "${debian_dir}/prerm" - chmod +x "${debian_dir}/prerm" - fi - - post_remove="$(get_project_opt .hooks.postRemove)" - if [ ! -z "${post_remove}" ]; then - cp "${SRC_DIR}/${post_remove}" "${debian_dir}/postrm" - chmod +x "${debian_dir}/postrm" - fi - -} - -function create_debian_changelog { - - debian_dir="${1}" - changelog="${debian_dir}/changelog" - - logs="$(get_project_opt '.changelog | map(.+"\n") | add')" - package_name=$(get_project_opt .name) - package_version=$(get_project_opt .version) - maintainer=$(get_project_opt .maintainer) - - echo "${package_name} (${package_version:-0.0.0}), ${DISTRIB}; urgency=low" > "${changelog}" - echo >> "${changelog}" - - while read -r entry; do - echo " * ${entry}" >> "${changelog}" - done <<< "$(echo -e "${logs}" | sed 's/^"//')" - - echo >> "${changelog}" - - echo "-- ${maintainer} $(date -R)" >> "${changelog}" - -} - -function create_debian_metadata { - - build_dir="${1}" - debian_dir="${build_dir}/DEBIAN" - - # Ensure debian dir exists - mkdir -p "${debian_dir}" - - create_debian_control_file "${debian_dir}" - create_debian_hooks "${debian_dir}" - create_debian_changelog "${debian_dir}" - -} - function build_project { - project_name="$(get_project_opt '.name')" + info "Building project ${PROJECT_NAME}..." - info "Building project '${project_name}'..." + set_opt project_name "${PROJECT_NAME}" - build_dir="$(get_build_dir "${project_name}")" + local workspace=$(mktemp -d)/${PROJECT_NAME} + mkdir -p "${workspace}" - debug "Build dir: ${build_dir}" + # Copy sources to workspace + cd ${SRC_DIR} + cp -r ${SRC_DIR}/. "${workspace}" - # We don't generate Debian metadata files if a debian directory is present - if [ ! -d "${SRC_DIR}/DEBIAN" ] && [ ! -d "${SRC_DIR}/debian" ]; then - info "No Debian directory detected in sources." - info "Generating Debian metadata files from manifest..." - create_debian_metadata "${build_dir}" - else - info "A Debian directory is already present in sources." - fi + exec_hooks "prebuild" "${workspace}" - exec_hook "preBuild" "${build_dir}" - - # Ensure $DIST_DIR exists - mkdir -p "${DIST_DIR}" - - cd "${build_dir}/.." - dpkg-deb --build "${project_name}" ${DIST_DIR} - - info "Package created ! (${build_dir}/${project_name}.deb)" + cd "${workspace}" + dpkg-buildpackage -us -uc 2> >(error) 1> >(info) if [ $? != 0 ]; then fatal "The build process has not completed successfuly !" fi - cd ${SRC_DIR} - - exec_hook "postBuild" "${build_dir}" + exec_hooks "postbuild" "${workspace}" } -function main { - - manifest_path=${SRC_DIR}/tamarin.json - - if [ ! -f "${manifest_path}" ] && [ ! -d "${SRC_DIR}/debian" ] && [ ! -d "${SRC_DIR}/DEBIAN" ]; then - fatal "There is no 'tamarin.json' nor debian packaging files in the project directory !" - fi - - build_project - -} - -main +build_project diff --git a/lib/install.sh b/lib/install.sh index 775793c..68ea518 100644 --- a/lib/install.sh +++ b/lib/install.sh @@ -6,7 +6,7 @@ DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) source "${DIR}/util.sh" info "Updating packages definition..." -apt-get update +apt-get update 2> >(error) 1> >(info) info "Installing package $1..." -gdebi --n "$1" +gdebi --n "$1" 2> >(error) 1> >(info) diff --git a/lib/util.sh b/lib/util.sh index 8310de4..cb404e2 100644 --- a/lib/util.sh +++ b/lib/util.sh @@ -1,18 +1,90 @@ #!/usr/bin/env bash +HOOKS_DIR="${BASE_DIR}/hooks" +OPT_FILE="${BASE_DIR}/tmp/.tamarin_opts" + function info { - echo "[${HOSTNAME}] [INFO] $@" + if [ -z "$@" ]; then + while read str; do + log INFO "${str}" + done + else + log INFO "$@" + fi } function debug { - echo "[${HOSTNAME}] [DEBUG] $@" + if [ -z "$@" ]; then + while read str; do + log DEBUG "${str}" + done + else + log DEBUG "$@" + fi } function error { - echo "[${HOSTNAME}] [ERROR] $@" >&2 + if [ -z "$@" ]; then + while read str; do + log ERROR "${str}" >&2 + done + else + log ERROR "$@" >&2 + fi } function fatal { - echo "[${HOSTNAME}] [FATAL] $@" >&2 + if [ -z "$@" ]; then + while read str; do + log FATAL "${str}" >&2 + done + else + log FATAL "$@" >&2 + fi exit 1 } + +function log { + local args=( $@ ) + echo "[${HOSTNAME}] [${args[0]}] ${args[@]:1}" +} + +function get_opt { + local opt_name=${1} + local default_value=${2} + touch "${OPT_FILE}" + source "${OPT_FILE}" + echo ${!opt_name:-${default_value}} +} + +function set_opt { + local opt_name=${1} + local opt_value=${2} + touch "${OPT_FILE}" + sed -i "s/^${opt_name}*$//" "${OPT_FILE}" + echo "${opt_name}=\"${opt_value}\"" >> "${OPT_FILE}" +} + +function exec_hooks { + + local hook=${1} + local workspace=${2} + + local hook_scripts=$( find "${HOOKS_DIR}" -type f -name "*${hook}" -executable) + + for hook_script in ${hook_scripts}; do + + info "[${hook}] Executing ${hook_script}" + + ( cd "${workspace}" && "${hook_script}" ) 2> >(error) 1> >(info) + + # If the script did not execute properly, we stop here + if [ $? != 0 ]; then + fatal "The '${hook_script}' hook script did not finished properly !" + fi + + info "[${hook}] ${hook_script} Done." + + done + +} diff --git a/package-project.sh b/package-project.sh deleted file mode 100755 index d68f264..0000000 --- a/package-project.sh +++ /dev/null @@ -1,86 +0,0 @@ -#!/usr/bin/env bash - -set -e - -DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) -source "$DIR/lib/util.sh" - -function show_usage { - echo - echo "Usage: $0 " - echo - echo "Paramètres: " - echo - echo " - Chemin vers le répertoire des sources du projet à empaqueter" - echo " - Chemin vers le répertoire de destination du paquet à créer" - echo " - Nom de l'image Docker à utiliser comme base pourl'environnement de build. Exemple: debian:jessie" - echo -} - -# Create a build container based on the $BASE_IMAGE argument -function create_container { - - # Escape image name - escaped_basename=$(echo "$BASE_IMAGE" | sed 's/[^a-z0-9\-\_\.]/\_/gi') - # Generate container tag - container_tag="tamarin:${escaped_basename}_$(date +%s)" - # Create temporary dir for the Dockerfile - temp_dir="$(mktemp -d)" - - # Link lib folder - ln -s $(readlink -f "$DIR/lib") "$temp_dir/lib" - - # Create Dockerfile - cat << EOF > "$temp_dir/Dockerfile" - FROM $BASE_IMAGE - - ENV DEBIAN_FRONTEND noninteractive - - RUN apt-get update &&\ - apt-get install jq - - ADD ./lib /root/.tamarin - RUN chmod +x /root/.tamarin/build.sh - - VOLUME /src - VOLUME /dist - - CMD /root/.tamarin/build.sh -EOF - - # Build image - tar -C "$temp_dir" -czh . | docker build -t "$container_tag" - 1>&2 - - # Delete temporary folder - rm -rf "$temp_dir" - - # Return newly created container's tag - echo $container_tag - -} - -# Main function -function main { - - info "Building container from $BASE_IMAGE..." - container_tag="$(create_container)" - - info "Launching container..." - docker run -e "DISTRIB=$BASE_IMAGE" --rm -v="$PROJECT_PATH:/src" -v="$PROJECT_DIST:/dist" "$container_tag" - - info "Cleaning container data..." - - info "Done" -} - -# Test for arguments -if [ -z "$1" ] || [ -z "$2" ] || [ -z "$3" ]; then - show_usage - exit 1 -fi - -PROJECT_PATH="$(readlink -f $1)" -PROJECT_DIST="$(readlink -f $2)" -BASE_IMAGE="$3" - -main diff --git a/package.sh b/package.sh new file mode 100755 index 0000000..252722e --- /dev/null +++ b/package.sh @@ -0,0 +1,96 @@ +#!/usr/bin/env bash + +set -e + +TAMARIN_DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) + + +BASE_DIR="$TAMARIN_DIR" source "$TAMARIN_DIR/lib/util.sh" + +function show_usage { + echo + echo "Usage: $0 src dist [image]" + echo + echo "Paramètres: " + echo + echo " - src Chemin vers le répertoire des sources du projet à empaqueter" + echo " - dist Chemin vers le répertoire de destination du paquet à créer" + echo " - image Optionel - Nom de l'image Docker à utiliser comme base pourl'environnement de build. Défaut: debian:jessie" + echo +} + +# Create a build container based on the $BASE_IMAGE argument +function create_container { + + # Escape image name + local escaped_basename=$(echo "$BASE_IMAGE" | sed 's/[^a-z0-9\-\_\.]/\_/gi') + # Generate container tag + container_tag="tamarin:${escaped_basename}_$(date +%s)" + # Create temporary dir for the Dockerfile + local temp_dir="$(mktemp -d)" + + # Link lib & hooks folders + ln -s $(readlink -f "$TAMARIN_DIR/lib") "$temp_dir/lib" + ln -s $(readlink -f "$TAMARIN_DIR/hooks") "$temp_dir/hooks" + + # Create Dockerfile + cat << EOF > "$temp_dir/Dockerfile" + FROM $BASE_IMAGE + + ENV DEBIAN_FRONTEND noninteractive + + RUN apt-get update &&\ + apt-get install --yes build-essential devscripts + + RUN mkdir /root/.tamarin + RUN mkdir /project + + ADD ./lib /root/.tamarin/lib + ADD ./hooks /hooks + RUN chmod +x /root/.tamarin/lib/build.sh + + VOLUME /src + VOLUME /dist + + CMD /root/.tamarin/lib/build.sh + +EOF + + exec_hooks "containerbuild" "$temp_dir" + + # Build image + tar -C "$temp_dir" -czh . | docker build -t "$container_tag" - 2> >(error) 1> >(info) + + # Delete temporary folder + rm -rf "$temp_dir" + +} + +# Main function +function main { + + info "Building container from $BASE_IMAGE..." + + # Create container & "$container_tag" variable + create_container + + local project_name="$(basename "${PROJECT_PATH}")" + + info "Switching to container..." + docker run -e "DISTRIB=$BASE_IMAGE" -e "PROJECT_NAME=$project_name" --rm -v="$PROJECT_PATH:/src" -v="$PROJECT_DIST:/dist" "$container_tag" + + info "Done" + +} + +# Test for arguments +if [ -z "$1" ] || [ -z "$2" ]; then + show_usage + exit 1 +fi + +PROJECT_PATH="$(readlink -f $1)" +PROJECT_DIST="$(readlink -f $2)" +BASE_IMAGE="${3:-debian:jessie}" + +main diff --git a/src-example/tamarin.json b/src-example/tamarin.json index 565ecf9..be7a6b0 100644 --- a/src-example/tamarin.json +++ b/src-example/tamarin.json @@ -1,14 +1,13 @@ { "name": "hello-world", "dependencies": { - "debian:jessie": [ - "apache2 (>= 2.4)", - "php5 (>= 5.4)" - ] + "debian:jessie": { + "apache2": ">= 2.4", + "php5": ">= 5.4" + } }, "hooks": { "preBuild": "./scripts/pre-build.sh", - "postBuild": "./scripts/post-build.sh", "preInstall": "./scripts/pre-install.sh", "preRemove": "", "postInstall": "",