254 lines
6.8 KiB
Bash
Executable File
254 lines
6.8 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
################################################################################
|
|
# #
|
|
# netoik-cicd-pipeline #
|
|
# #
|
|
# This binary is made to be run by git server, it asks runner server to check #
|
|
# code and eventually to deploy rpm package. #
|
|
# #
|
|
# This script is coded with respect of google shell styleguide: #
|
|
# https://google.github.io/styleguide/shellguide.html #
|
|
# #
|
|
################################################################################
|
|
|
|
# Exit immediately if any command fails.
|
|
set -e
|
|
|
|
# Exit with the last non-zero fail code.
|
|
set -o pipefail
|
|
|
|
# Program constants.
|
|
readonly PACKAGE_NAME="netoik-cicd"
|
|
readonly PROGRAM_NAME="${PACKAGE_NAME}-pipeline"
|
|
|
|
# Declare default variables.
|
|
readonly DEFAULT_CONFIG_FILE="/etc/${PACKAGE_NAME}/${PACKAGE_NAME}.conf"
|
|
readonly DEFAULT_ERRORS_FILE="/etc/${PACKAGE_NAME}/errors.conf"
|
|
|
|
fail() (
|
|
msg="$1"
|
|
if [ $# -eq 2 ]; then
|
|
code="$2"
|
|
else
|
|
code=1
|
|
fi
|
|
|
|
echo "${msg}" >&2
|
|
if [ "${code}" -gt 0 ]; then
|
|
exit "${code}"
|
|
else
|
|
echo "Undefined error code for '${msg}'." >&2
|
|
exit 1
|
|
fi
|
|
)
|
|
|
|
usage() (
|
|
echo "Usage: ${PROGRAM_NAME} [OPTION]... PIPELINE
|
|
Asks runner server to check code and eventually deploy rpm package. See section Pipelines for positionnal argument PIPELINE.
|
|
|
|
Mandatory argumentes for long options are mandatory for short options too.
|
|
|
|
-c, --conf=<config_file> name of configuration file, default
|
|
to ${DEFAULT_CONFIG_FILE}
|
|
-e, --errs=<errors_file> name of errors file, default to
|
|
${DEFAULT_ERRORS_FILE}
|
|
-H, --hash=<repo_hash> repo hash to checkout
|
|
-n, --name=<repo_name> repo name to clone
|
|
-t, --test just test config file and exit
|
|
-T, --tag=<repo_tag> repo tag to deploy
|
|
|
|
-h, --help display this help message and exit
|
|
|
|
Pipelines:
|
|
newcommit
|
|
should be called when a new commit is pushed to repo, it will clone repo and
|
|
check code validity
|
|
it needs options -H|--hash, -n|--name
|
|
newtag
|
|
should be called when a new tag is pushed to repo, it will clone repo,
|
|
check code validity and deploy rpm package
|
|
it needs options -H|--hash, -n|--name, -T|--tag
|
|
"
|
|
)
|
|
|
|
main() (
|
|
# Parse arguments.
|
|
testing="false"
|
|
config_file="${DEFAULT_CONFIG_FILE}"
|
|
errors_file="${DEFAULT_ERRORS_FILE}"
|
|
if ! args="$(getopt --name "${PROGRAM_NAME}" \
|
|
--options c:e:H:hn:tT: \
|
|
--longoptions conf:,errs:,hash:,help,name:,test,tag:,help \
|
|
-- "$@")"; then
|
|
usage
|
|
fail "Bad arguments."
|
|
fi
|
|
eval set -- "${args}"
|
|
|
|
while true; do
|
|
case "$1" in
|
|
-c | --conf)
|
|
config_file="$2"
|
|
shift 2
|
|
;;
|
|
-e | --errs)
|
|
errors_file="$2"
|
|
shift 2
|
|
;;
|
|
-H | --hash)
|
|
repo_hash="$2"
|
|
shift 2
|
|
;;
|
|
-h | --help)
|
|
usage
|
|
exit 0
|
|
;;
|
|
-n | --name)
|
|
repo_name="$2"
|
|
shift 2
|
|
;;
|
|
-t | --test)
|
|
testing="true"
|
|
shift
|
|
;;
|
|
-T | --tag)
|
|
repo_tag="$2"
|
|
shift 2
|
|
;;
|
|
--)
|
|
shift
|
|
break
|
|
;;
|
|
*)
|
|
usage
|
|
fail "Unexpected option '$1'."
|
|
;;
|
|
esac
|
|
done
|
|
if [ $# -ne 1 ]; then
|
|
usage
|
|
fail "Missing positionnal argument PIPELINE."
|
|
fi
|
|
pipeline="$1"
|
|
|
|
# Generate pipeline tmp sock.
|
|
tsp="$(date +%s)"
|
|
random="$(echo "${RANDOM}" | md5sum | head --bytes 32)"
|
|
rd_pipeline_sock_dir="${pipeline_sock_dir}/${pipeline}-${tsp}-${random}"
|
|
rd_pipeline_sock="${pipeline_sock_dir}/pipeline.sock"
|
|
|
|
# Check pipeline with options.
|
|
case "${pipeline}" in
|
|
newcommit)
|
|
if [ -z "${repo_name}" ]; then
|
|
usage
|
|
fail "Missing option -n|--name for pipeline '${pipeline}'."
|
|
fi
|
|
if [ -z "${repo_hash}" ]; then
|
|
usage
|
|
fail "Missing option -H|--hash for pipeline '${pipeline}'."
|
|
fi
|
|
runner_request="$(jq --null-input --compact-output \
|
|
--arg s "${rd_pipeline_sock}" \
|
|
--arg n "${repo_name}" \
|
|
--arg h "${repo_hash}" \
|
|
'{"response_sock":$s,"repo_name":$n,"repo_hash":$h}')"
|
|
;;
|
|
newtag)
|
|
if [ -z "${repo_name}" ]; then
|
|
usage
|
|
fail "Missing option -n|--name for pipeline '${pipeline}'."
|
|
fi
|
|
if [ -z "${repo_hash}" ]; then
|
|
usage
|
|
fail "Missing option -H|--hash for pipeline '${pipeline}'."
|
|
fi
|
|
if [ -z "${repo_tag}" ]; then
|
|
usage
|
|
fail "Missing option -T|--tag for pipeline '${pipeline}'."
|
|
fi
|
|
runner_request="$(jq --null-input --compact-output \
|
|
--arg s "${rd_pipeline_sock}" \
|
|
--arg n "${repo_name}" \
|
|
--arg h "${repo_hash}" \
|
|
--arg t "${repo_tag}" \
|
|
'{"response_sock":$s,"repo_name":$n,"repo_hash":$h,"repo_tag":$t}')"
|
|
;;
|
|
*)
|
|
usage
|
|
fail "Invalid pipeline '$1'."
|
|
;;
|
|
esac
|
|
|
|
# Load config file.
|
|
if [ ! -r "${config_file}" ]; then
|
|
fail "Config file '${config_file}' is not readable."
|
|
fi
|
|
# shellcheck source=./conf/netoik-cicd.conf.sample
|
|
source "${config_file}"
|
|
|
|
# Load errors file.
|
|
if [ ! -r "${errors_file}" ]; then
|
|
fail "Errors file '${errors_file}' is not reachable."
|
|
fi
|
|
# shellcheck source=./conf/errors.conf.sample
|
|
source "${errors_file}"
|
|
|
|
# Check variables in config file.
|
|
if [ -z "${runner_sock}" ]; then
|
|
fail "Variable runner_sock is empty." "${err_runner_sock_empty}"
|
|
fi
|
|
if [ ! -S "${runner_sock}" ]; then
|
|
fail "Sock runner_sock='${runner_sock}' does not exist." \
|
|
"${err_runner_sock_not_exist}"
|
|
fi
|
|
|
|
if [ -z "${runner_timeout}" ]; then
|
|
fail "Variable runner_timeout is empty." "${err_runner_timeout_empty}"
|
|
fi
|
|
if [ "${runner_timeout}" -lt 0 ]; then
|
|
fail "Runner timeout is not valid: ${runner_timeout}." \
|
|
"${err_runner_timeout_not_valid}"
|
|
fi
|
|
|
|
if [ -z "${git_runner_groupname}" ]; then
|
|
fail "Variable git_runner_groupname is empty." "${err_git_runner_groupname_empty}"
|
|
fi
|
|
if ! getent group "${git_runner_groupname}"; then
|
|
fail "Git-runner group '${git_runner_groupname}' does not exist." \
|
|
"${err_git_runner_group_not_exist}"
|
|
fi
|
|
|
|
# Stop now if testing.
|
|
if "${testing}"; then
|
|
exit 0
|
|
fi
|
|
|
|
# Send request to runner.
|
|
mkdir "${rd_pipeline_sock_dir}"
|
|
(
|
|
inotifywait --quiet --quiet --event create "${rd_pipeline_sock_dir}"
|
|
chmod 775 "${rd_pipeline_sock}"
|
|
chgrp "${git_runner_groupname}" "${rd_pipeline_sock}"
|
|
echo "${runner_request}" | ncat --unixsock "${runner_sock}"
|
|
) &
|
|
|
|
# Wait for runner response.
|
|
if [ "${runner_timeout}" -gt 0 ]; then
|
|
response="$(timeout "${runner_timeout}" ncat --listen \
|
|
--unixsock "${rd_pipeline_sock}")"
|
|
else
|
|
response="$(ncat --listen --unixsock "${rd_pipeline_sock}")"
|
|
fi
|
|
|
|
# Remove random directory.
|
|
rm --recursive "${rd_pipeline_sock_dir}"
|
|
|
|
# Display response.
|
|
echo -e "$(echo "${response}" | jq .msg)"
|
|
exit "$(echo "${response}" | jq .code)"
|
|
)
|
|
|
|
main "$@"
|