...
 
Commits (10)
......@@ -3,7 +3,7 @@
# Birb CI: The minimal Continuous Integration system
This is a minimalistic continuous integration system which will periodically try to build a given set of target projects and report the status. Systemd is used to create the build daemons, but it could also be modified to support other init systems.
This is a minimalistic continuous integration system which will periodically try to build a given set of target projects and report the status.
## Installation
......@@ -51,32 +51,28 @@ So that it's accessible to the build system you may want to save this script int
Install a target to be built with:
``` bash
sudo birbci --repo [your git repo url] --branch master --script /etc/birbci/build.sh -w /var/www/html/index.html --install [daemon name]
sudo birbci --repo [your git repo url] --branch master --script /etc/birbci/build.sh -w /var/www/html/index.html --install [build name]
```
The daemon name could be similar to the name of the system being tested, but not identical so as to avoid potential clashes. The *-w* option defines a html file which will contain the results.
The build name could be similar to the name of the system being tested, but not identical so as to avoid potential clashes. The *-w* option defines a html file which will contain the results.
The above install could be performed multiple times for different test repos with different build scripts.
Optionally you can also use the *--email* option to specify an email address which will be notified if a build failure occurs. Whether you use this depends upon if you have an email server locally installed.
## Running as root
Normally the build script is run as an unprivileged user, but sometimes you may need to run as the root user so that you can run *make install*. If that is the case then add the extra option: *--root yes*
## XMPP notifications
If you wish to receive build failure notifications via XMPP then the --xmpp option can be used. For this to work you'll need to have prosody installed and configured, and be running as root.
If you wish to receive build failure notifications via XMPP then the --xmpp option can be used. For this to work you'll need to have prosody installed and configured.
``` bash
sudo birbci --repo [your git repo url] --branch master --script /etc/birbci/build.sh -w /var/www/html/index.html --install [daemon name] --root yes --xmpp user@xmppdomain
sudo birbci --repo [your git repo url] --branch master --script /etc/birbci/build.sh -w /var/www/html/index.html --install [build name] --xmpp user@xmppdomain
```
## Removing Builds
If you later want to remove a repo from the CI system:
``` bash
sudo birbci --remove [daemon name]
sudo birbci --remove [build name]
```
## Viewing the Current Build Status
......@@ -85,4 +81,4 @@ To view the results navigate to or open the html file. The result should look so
<img src="https://code.freedombone.net/bashrc/birbci/raw/master/images/birbciresults.png?raw=true" width=800/>
Selecting the daemon name will show the build log, so that you can view any compile errors or unit test failures.
Selecting the build name will show the build log, so that you can view any compile errors or unit test failures.
......@@ -37,19 +37,87 @@ HEADING=$'Birb Continuous Integration Build Results'
EMAIL_ADDRESS=
XMPP_ADDRESS=
COPY_IMAGES=
RUN_AS_ROOT=
SYNCHRONOUS=
USE_BUILD_LOG=no
# frequency that the timer checks for new builds
BUILD_TIMER_MIN=20
function create_daemon {
daemon="$1"
# how long to wait after boot to start the daemon
BOOT_DELAY_SEC=$((20 + RANDOM % 100))
# Create a systemd daemon which will run builds for this app
if [ ! -d /etc/${APP_NAME} ]; then
mkdir /etc/${APP_NAME}
fi
# Create an unprivileged user to run the daemon as
if [ ! "$(id -u ${APP_NAME})" ]; then
useradd -d /etc/${APP_NAME}/ -s /bin/false ${APP_NAME}
fi
BUILD_DIR="/etc/${APP_NAME}/$daemon"
# Set a default output html file to show build status
if [ ! "$WEB_FILE" ]; then
WEB_FILE=/var/www/html/index.html
fi
# Copy images and icons to the html file location
if [ -d /usr/share/${APP_NAME}/images ]; then
IMAGES_DIR=$(dirname "${WEB_FILE}")/${APP_NAME}_img
if [ ! -d "$IMAGES_DIR" ]; then
mkdir "$IMAGES_DIR"
cp /usr/share/${APP_NAME}/images/* "$IMAGES_DIR"
else
# force the update of images
if [[ "$COPY_IMAGES" == "y"* || "$COPY_IMAGES" == "Y"* ]]; then
cp /usr/share/${APP_NAME}/images/* "$IMAGES_DIR"
fi
fi
fi
# add daemon to list
if [ -f "/etc/${APP_NAME}/builds.txt" ]; then
if ! grep -q "$daemon," "/etc/${APP_NAME}/builds.txt"; then
echo "$daemon,$BUILD_DIR,$REPO,$BRANCH,$BUILD_SCRIPT,$WEB_FILE,$XMPP_ADDRESS,$USE_BUILD_LOG" >> "/etc/${APP_NAME}/builds.txt"
else
sed -i "s|$daemon,.*|$daemon,$BUILD_DIR,$REPO,$BRANCH,$BUILD_SCRIPT,$WEB_FILE,$XMPP_ADDRESS,$USE_BUILD_LOG|g" "/etc/${APP_NAME}/builds.txt"
fi
else
echo "$daemon,$BUILD_DIR,$REPO,$BRANCH,$BUILD_SCRIPT,$WEB_FILE,$XMPP_ADDRESS,$USE_BUILD_LOG" > "/etc/${APP_NAME}/builds.txt"
fi
# create script to be run via cron
{ echo '#!/bin/bash';
echo "if [ ! -f /etc/${APP_NAME}/builds.txt ]; then";
echo ' exit 0';
echo 'fi';
echo '';
echo "existing_builds=\$(ps aux | grep \"${APP_NAME} -d\" | grep -vc \"grep\")";
echo "if [ \$existing_builds -eq 0 ]; then";
echo " no_of_builds=\$(cat /etc/${APP_NAME}/builds.txt | wc -l)";
echo " line_number=\$((RANDOM % \$no_of_builds))";
echo " build_name=\$(sed \"\${line_number}q;d\" /etc/${APP_NAME}/builds.txt | awk -F ',' '{print \$1}')";
echo " BUILD_DIR=\$(sed \"\${line_number}q;d\" /etc/${APP_NAME}/builds.txt | awk -F ',' '{print \$2}')";
echo " REPO=\$(sed \"\${line_number}q;d\" /etc/${APP_NAME}/builds.txt | awk -F ',' '{print \$3}')";
echo " BRANCH=\$(sed \"\${line_number}q;d\" /etc/${APP_NAME}/builds.txt | awk -F ',' '{print \$4}')";
echo " BUILD_SCRIPT=\$(sed \"\${line_number}q;d\" /etc/${APP_NAME}/builds.txt | awk -F ',' '{print \$5}')";
echo " WEB_FILE=\$(sed \"\${line_number}q;d\" /etc/${APP_NAME}/builds.txt | awk -F ',' '{print \$6}')";
echo " XMPP_ADDRESS=\$(sed \"\${line_number}q;d\" /etc/${APP_NAME}/builds.txt | awk -F ',' '{print \$7}')";
echo " USE_BUILD_LOG=\$(sed \"\${line_number}q;d\" /etc/${APP_NAME}/builds.txt | awk -F ',' '{print \$8}')";
echo " /usr/local/bin/${APP_NAME} -d \"\$BUILD_DIR\" -r \"\$REPO\" -b \"\$BRANCH\" -s \"\$BUILD_SCRIPT\" -w \"\$WEB_FILE\" -x \"\$XMPP_ADDRESS\" --build-log \$USE_BUILD_LOG";
echo 'fi'; } > /usr/bin/birbci-cron
chmod +x /usr/bin/birbci-cron
if ! grep -q 'birbci-cron' /etc/crontab; then
echo "*/1 * * * * root /usr/bin/birbci-cron" >> /etc/crontab
fi
# Set permissions to the unprivileged user
chown -R ${APP_NAME}: /etc/${APP_NAME}
}
function show_help {
echo ''
echo $"${APP_NAME} -d [directory] --repo [git repo url] --branch [branch] --script [build script] --install [daemon name] -w [html file] --remove [daemon name] --header [text] -e [email address] --images [yes|no] --root [yes|no] --xmpp [adminJID] --synchronous [yes|no] --build-log [yes|no] --timer [mins]"
echo $"${APP_NAME} -d [directory] --repo [git repo url] --branch [branch] --script [build script] --install [daemon name] -w [html file] --remove [daemon name] --header [text] -e [email address] --images [yes|no] --xmpp [adminJID] --synchronous [yes|no] --build-log [yes|no]"
echo ''
exit 0
}
......@@ -58,10 +126,6 @@ function xmpp_send {
XMPP_ADDRESS="$1"
MESSAGE="$2"
if [ ! $RUN_AS_ROOT ]; then
return
fi
if [[ "$XMPP_ADDRESS" != *'@'* ]]; then
return
fi
......@@ -303,7 +367,7 @@ function update {
# Save the systemd journal so that any build failures can be seen
# via the web page
DAEMON=$(basename $BUILD_DIR)
DAEMON=$(basename "$BUILD_DIR")
journalctl -u "$DAEMON" > "/etc/${APP_NAME}/${DAEMON}/build.txt"
if [ ! $build_success ]; then
......@@ -381,14 +445,6 @@ case $key in
shift
COPY_IMAGES="$1"
;;
--root)
shift
RUN_AS_ROOT="$1"
;;
--timer|--freq|--frequency)
shift
BUILD_TIMER_MIN="$1"
;;
--synchronous|--noparallel)
shift
SYNCHRONOUS="$1"
......@@ -401,16 +457,10 @@ shift
done
if [ "$REMOVE_DAEMON" ]; then
if [ -f "/etc/systemd/system/${REMOVE_DAEMON}.service" ]; then
systemctl stop "${REMOVE_DAEMON}.service"
systemctl disable "${REMOVE_DAEMON}.service"
rm "/etc/systemd/system/${REMOVE_DAEMON}.service"
fi
if [ -f "/etc/systemd/system/${REMOVE_DAEMON}.timer" ]; then
systemctl stop "${REMOVE_DAEMON}.timer"
systemctl disable "${REMOVE_DAEMON}.timer"
rm "/etc/systemd/system/${REMOVE_DAEMON}.timer"
if [ -f /etc/${APP_NAME}/builds.txt ]; then
sed -i "/${REMOVE_DAEMON},/d" /etc/${APP_NAME}/builds.txt
fi
if [ -d "/etc/$APP_NAME/$REMOVE_DAEMON" ]; then
rm -rf "/etc/${APP_NAME:?}/$REMOVE_DAEMON"
fi
......@@ -451,76 +501,7 @@ if [ ! "$BRANCH" ]; then
fi
if [ "$DAEMON" ]; then
# Create a systemd daemon which will run builds for this app
if [ ! -d /etc/${APP_NAME} ]; then
mkdir /etc/${APP_NAME}
fi
# Create an unprivileged user to run the daemon as
useradd -d /etc/${APP_NAME}/ -s /bin/false ${APP_NAME}
BUILD_DIR="/etc/${APP_NAME}/$DAEMON"
# Set a default output html file to show build status
if [ ! "$WEB_FILE" ]; then
WEB_FILE=/var/www/html/index.html
fi
# Copy images and icons to the html file location
if [ -d /usr/share/${APP_NAME}/images ]; then
IMAGES_DIR=$(dirname "${WEB_FILE}")/${APP_NAME}_img
if [ ! -d "$IMAGES_DIR" ]; then
mkdir "$IMAGES_DIR"
cp /usr/share/${APP_NAME}/images/* "$IMAGES_DIR"
else
# force the update of images
if [[ "$COPY_IMAGES" == "y"* || "$COPY_IMAGES" == "Y"* ]]; then
cp /usr/share/${APP_NAME}/images/* "$IMAGES_DIR"
fi
fi
fi
# Set permissions to the unprivileged user
chown -R ${APP_NAME}: /etc/${APP_NAME}
daemon_user=${APP_NAME}
if [[ "$RUN_AS_ROOT" == 'y'* || "$RUN_AS_ROOT" == 'Y'* ]]; then
daemon_user='root'
fi
# Create the systemd daemon
{ echo '[Unit]';
echo "Description=birb daemon for $DAEMON";
echo 'After=syslog.target';
echo 'After=network.target';
echo '';
echo '[Service]';
echo 'Type=forking';
echo "User=${daemon_user}";
echo "WorkingDirectory=/etc/${APP_NAME}";
echo "ExecStart=/usr/local/bin/${APP_NAME} -d \"$BUILD_DIR\" -r \"$REPO\" -b \"$BRANCH\" -s \"$BUILD_SCRIPT\" -w \"$WEB_FILE\" -x \"$XMPP_ADDRESS\" --build-log $USE_BUILD_LOG";
echo 'StandardInput=tty';
echo 'TTYPath=/dev/tty6';
echo 'TTYReset=yes';
echo 'TTYVHangup=yes';
echo '';
echo '[Install]';
echo 'WantedBy=multi-user.target'; } > "/etc/systemd/system/${DAEMON}.service"
{ echo '[Unit]';
echo "Description=birb timer for $DAEMON";
echo '';
echo '[Timer]';
echo "OnUnitActiveSec=${BUILD_TIMER_MIN}min";
echo "OnBootSec=${BOOT_DELAY_SEC}s";
echo '';
echo '[Install]';
echo 'WantedBy=timers.target'; } > "/etc/systemd/system/${DAEMON}.timer"
systemctl enable "/etc/systemd/system/${DAEMON}.service"
systemctl enable "/etc/systemd/system/${DAEMON}.timer"
systemctl daemon-reload
systemctl start "${DAEMON}.service" &
systemctl start "${DAEMON}.timer" &
create_daemon "$DAEMON"
else
# Run the build
update
......
No preview for this file type