#!/usr/bin/env bash set -e shopt -s nullglob mkdir -p "$MSCI_HOME"/projects \ "$MSCI_HOME"/stdout/public \ "$MSCI_HOME"/stdout/private \ "$MSCI_HOME"/tmp [ ! -f "$MSCI_HOME"/cron ] && touch "$MSCI_HOME"/cron confirm_in() { read -rp "$1 (y/N): " confirm [[ $confirm == [yY]* ]] && return 0 || return 1 } find_project() { if list_projects | grep -xFq -- "$1"; then return 0 fi return 1 } list_projects() { paths=("$MSCI_HOME"/projects/*) names=("${paths[@]##*/}") projects=("${names[@]%.json}") printf '%s\n' "${projects[@]}" } write_cron() { cfile="MSCI_HOME=$MSCI_HOME\n" for project in $(list_projects); do ppath="$MSCI_HOME"/projects/"$project".json schedule=$(jq -r '.cron' "$ppath") [ "$schedule" = 'null' ] && continue cfile="${cfile}$schedule $MSCI_HOME/makeshiftci run $project\n" done echo -e "$cfile" >"$MSCI_HOME"/tmp.cron if crontab "$MSCI_HOME"/tmp.cron; then mv -fv "$MSCI_HOME"/tmp.cron "$MSCI_HOME"/cron else rm -fv "$MSCI_HOME"/tmp.cron fi } run_project() { ppath="$MSCI_HOME"/projects/"$1".json repo_name=$(jq -r '.name' "$ppath") repo_url=$(jq -r '.url' "$ppath") repo_path="$MSCI_HOME"/tmp/"$repo_name" echo "cloning repo: $repo_url" | tee -a "$2" git clone --quiet "$repo_url" "$repo_path" pushd "$repo_path" >/dev/null || return 1 for job in "$repo_path"/.makeshiftci/*; do pname=$(jq -r '.name' "$job") echo "running job: $pname" | tee -a "$2" pimage=$(jq -r '.image' "$job") psecrets=$(jq -r '.secrets' "$job") prun=$(jq -r '.run[]' "$job") env_secrets=() secret_mounts=() if [ ! "$psecrets" = 'null' ]; then for secret_key in $(echo "$psecrets" | jq -r '. | keys[]'); do secret_value=$(echo "$psecrets" | jq -r ".$secret_key") secret_name=$(openssl rand -hex 16) secret_mounts+=("--mount=type=bind,source=$secret_value,target=/$secret_name,readonly") env_secrets+=("$secret_key=/$secret_name") done fi docker run --rm \ "${env_secrets[@]/#/--env=}" \ --mount type=bind,source="$repo_path",target=/"$repo_name" \ "${secret_mounts[@]}" \ --workdir="/$repo_name" \ "$pimage" \ sh -c "exec $prun" | tee -a "$2" done popd >/dev/null && rm -rf "$repo_path" echo "finished" | tee -a "$2" } create_project() { name="$1" if find_project "$name"; then echo "project file already exists" return 1 fi read -rp "project name: " pname read -rp ".makeshiftci repo URL: " purl phidden=$(confirm_in "hidden?" && echo true || echo false) read -rp "cron (optional): " pcron jq -n \ --arg pname "$pname" \ --arg purl "$purl" \ --argjson phidden "$phidden" \ --arg pcron "$pcron" \ '{ name: $pname, url: $purl, hidden: $phidden, cron: (if $pcron == "" then null else $pcron end) }' \ >"$MSCI_HOME"/projects/"$name".json [ -n "$pcron" ] && write_cron ([ "$phidden" = "true" ] && mkdir -p "$MSCI_HOME"/stdout/private/"$1") || mkdir -p "$MSCI_HOME"/stdout/public/"$1" } edit_project() { name="$1" if ! find_project "$name"; then echo "project file doesn't exist" return 1 fi ppath="$MSCI_HOME"/projects/"$name".json echo "editing $ppath" oldname=$(jq -r '.name' "$ppath") oldurl=$(jq -r '.url' "$ppath") washidden=$(jq '.hidden' "$ppath") keephidden=$([ "$washidden" = 'true' ] && echo "keep" || echo "make") oldcron=$(jq -r '.cron' "$ppath") echo "project name: $oldname" read -rp "new name (optional): " pname [ -z "$pname" ] && pname=$oldname echo ".makeshiftci repo URL: $oldurl" read -rp "new URL (optional): " purl [ -z "$purl" ] && purl=$oldurl echo "hidden: $washidden" phidden=$(confirm_in "$keephidden hidden?" && echo true || echo false) echo "cron: $oldcron" read -rp "new cron (optional): " pcron [ -z "$pcron" ] && pcron=$oldcron jq -n \ --arg pname "$pname" \ --arg purl "$purl" \ --argjson phidden "$phidden" \ --arg pcron "$pcron" \ '{ name: $pname, url: $purl, hidden: $phidden, cron: (if $pcron == "null" then null else $pcron end) }' \ >"$ppath" [ ! "$pcron" = "$oldcron" ] && write_cron privout="$MSCI_HOME"/stdout/private/"$1" pubout="$MSCI_HOME"/stdout/public/"$1" ( [ "$phidden" = "true" ] && ([ ! -d "$privout" ] && mv "$pubout" "$privout") ) || ([ ! -d "$pubout" ] && mv "$privout" "$pubout") } case $1 in create) if [ -z "$2" ]; then echo "no project file name is supplied" exit 1 fi create_project "$2" ;; edit) if [ -z "$2" ]; then echo "no project file name is supplied" exit 1 fi edit_project "$2" ;; delete) if [ -z "$2" ]; then echo "no project file name is supplied" exit 1 fi if ! find_project "$2"; then echo "project file doesn't exist" exit 1 fi ppath="$MSCI_HOME"/projects/"$2".json cat "$ppath" confirm_in "delete '$2' (details above)?" && rm -vf "$MSCI_HOME"/projects/"$2".json || exit 0 rm -rf "$MSCI_HOME"/stdout/**/"$2" write_cron ;; run) if [ -z "$2" ]; then echo "no project file name is supplied" exit 1 fi if ! find_project "$2"; then echo "project file doesn't exist" exit 1 fi ppath="$MSCI_HOME"/projects/"$2".json phidden=$(jq -r '.hidden' "$ppath") stdout_type=$([ "$phidden" = 'false' ] && echo "public" || echo "private") stdout_path="$MSCI_HOME"/stdout/"$stdout_type"/"$2" last_run=$( find "$stdout_path" \ -maxdepth 1 \ -mindepth 1 \ -type f -printf '%f\n' | grep -E '^[0-9]+$' | sort -n | tail -n 1 ) next_run=$( [ -z "$last_run" ] && echo "1" || echo $((last_run + 1)) ) stdout_path="$stdout_path"/"$next_run" run_project "$2" "$stdout_path" ;; list) list_projects ;; *) echo "unknown option '$1'" exit 1 ;; esac # vim: set filetype=bash: