aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore4
-rw-r--r--README.md3
-rwxr-xr-xmakeshiftci227
l---------msci1
4 files changed, 235 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..503e5df
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+projects/
+stdout/
+tmp/
+cron
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..25d73c5
--- /dev/null
+++ b/README.md
@@ -0,0 +1,3 @@
+## makeshiftci
+
+a makeshift CI solution
diff --git a/makeshiftci b/makeshiftci
new file mode 100755
index 0000000..b2a533c
--- /dev/null
+++ b/makeshiftci
@@ -0,0 +1,227 @@
+#!/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:
diff --git a/msci b/msci
new file mode 120000
index 0000000..701f6e9
--- /dev/null
+++ b/msci
@@ -0,0 +1 @@
+makeshiftci \ No newline at end of file