#!/bin/sh set -eu usage() { cat <<-'EOF' Usage: cicd [-n] NAME [SHA] cicd -h EOF } help() { cat <<-'EOF' Options: -n build the system, but don't switch to it (dry-run) -h, --help show this message NAME the name of the project SHA the repository SHA to checkout (default: main) Do a CI/CD run of the project called NAME, located at /srv/git/$NAME.git. If -n is given, only build, otherwise also do the deploy when the build is successfull. A "build" consists of: - doing a fresh clone of the project on a temporary directory; - checkout the project to version $SHA; - when a "manifest.scm" file exists in the root of the project, use it to launch a containerized Guix shell; otherwise use a fallback template for a containerized Guix shell; - build the "dev" target of the Makefile via "make dev". A "deploy" consists of: - copying the "description" metadata file to /srv/git/$NAME.git/description, in order to update the project repository's description; - upgrading the "pre-receive" Git hook, so that future runs are affected by it; - copying the "public/" directory that the "dev" target built to the /srv/www/s/$NAME/ directory, so that the projects "public/" directory is accessible via the web address "https://euandre.org/s/$NAME/". This command must be ran as root. Examples: Build and deploy the "remembering" project on the default branch: $ sudo cicd remembering Build the "urubu" project on a specific commit, but don't deploy: $ sudo cicd -n urubu 916dafc092f797349a54515756f2c8e477326511 EOF } for flag in "$@"; do case "$flag" in --) break ;; --help) usage help exit ;; *) ;; esac done DRY_RUN=false while getopts 'nh' flag; do case "$flag" in n) DRY_RUN=true ;; h) usage help exit ;; *) usage >&2 exit 2 ;; esac done shift $((OPTIND - 1)) NAME="${1:-}" SHA="${2:-main}" REPO="/srv/git/$NAME.git" if [ -z "$NAME" ]; then printf 'Missing NAME.\n\n' >&2 usage >&2 exit 2 fi if [ "$(id -un)" != 'root' ]; then printf 'This script must be run as root.\n\n' >&2 usage >&2 exit 2 fi set +eu # shellcheck source=/dev/null . /etc/rc set -eu uuid() { od -xN20 /dev/urandom | head -n1 | awk '{OFS="-"; print $2$3,$4,$5,$6,$7$8$9}' } tmpname() { printf '%s/uuid-tmpname with spaces.%s' "${TMPDIR:-/tmp}" "$(uuid)" } mkdtemp() { name="$(tmpname)" mkdir -- "$name" printf '%s' "$name" } TMP="$(mkdtemp)" trap 'rm -rf "$TMP"' EXIT set -x chown deployer:deployer "$TMP" cd "$TMP" sudo -u deployer git clone "$REPO" . sudo -u deployer --preserve-env=GIT_CONFIG_GLOBAL git checkout "$SHA" guix system describe if [ -f manifest.scm ]; then guix shell -Cv3 -m manifest.scm -- make dev else guix shell -Cv3 -- make dev fi if [ "$DRY_RUN" = false ]; then # COMMENT: pre-receive is always running the previous version! # The same is true for the reconfigure script itself. sudo cp description "$REPO"/description sudo cp aux/ci/git-pre-receive.sh "$REPO"/hooks/pre-receive sudo -u deployer rsync \ --delete \ --chmod=D775,F664 \ --chown=deployer:deployer \ --exclude 'ci/*' \ -a \ public/ /srv/www/s/"$NAME"/ fi