#!/bin/bash workspace_fail() { echo "bootstrap_workspace.sh: $*" >&2 return 1 } split_repository_slug() { local repo_slug=$1 local owner repo_name if [[ "${repo_slug}" != */* ]]; then workspace_fail "repository slug must look like owner/repo: ${repo_slug}" return 1 fi owner=${repo_slug%%/*} repo_name=${repo_slug#*/} printf '%s\n%s\n' "${owner}" "${repo_name}" } sanitize_job_name() { local value=${1:-job} value=$(printf '%s' "${value}" | tr ' /:@' '-----') value=$(printf '%s' "${value}" | tr -cd '[:alnum:]._-') value=$(printf '%s' "${value}" | sed -E 's/-+/-/g; s/^-//; s/-$//') if [ -z "${value}" ]; then value="job" fi printf '%s\n' "${value}" } build_job_identity() { local run_id=${1:-0} local run_attempt=${2:-1} local job_name=${3:-job} printf '%s-%s-%s\n' "${run_id}" "${run_attempt}" "${job_name}" } build_mirror_path() { local mirror_root=$1 local owner=$2 local repo_name=$3 printf '%s/%s/%s.git\n' "${mirror_root}" "${owner}" "${repo_name}" } build_mirror_lock_path() { local mirror_root=$1 local owner=$2 local repo_name=$3 printf '%s.lock\n' "$(build_mirror_path "${mirror_root}" "${owner}" "${repo_name}")" } build_job_workspace_root() { local workspace_root=$1 local owner=$2 local repo_name=$3 local job_identity=$4 printf '%s/%s/%s/%s\n' "${workspace_root}" "${owner}" "${repo_name}" "${job_identity}" } build_job_repo_dir() { local workspace_root=$1 local owner=$2 local repo_name=$3 local job_identity=$4 printf '%s/repo\n' "$(build_job_workspace_root "${workspace_root}" "${owner}" "${repo_name}" "${job_identity}")" } with_repo_lock() { local lock_path=$1 shift mkdir -p "$(dirname "${lock_path}")" if ! command -v flock >/dev/null 2>&1; then workspace_fail "flock is required for mirror synchronization" return 1 fi exec {lock_fd}>"${lock_path}" flock "${lock_fd}" "$@" local status=$? flock -u "${lock_fd}" eval "exec ${lock_fd}>&-" return "${status}" } ensure_bare_mirror_unlocked() { local remote_url=$1 local mirror_path=$2 mkdir -p "$(dirname "${mirror_path}")" if [ -d "${mirror_path}" ]; then git -C "${mirror_path}" remote set-url origin "${remote_url}" git -C "${mirror_path}" fetch --prune --prune-tags --tags origin else git clone --mirror "${remote_url}" "${mirror_path}" >/dev/null fi } ensure_bare_mirror() { local remote_url=$1 local mirror_path=$2 local lock_path lock_path="${mirror_path}.lock" with_repo_lock "${lock_path}" ensure_bare_mirror_unlocked "${remote_url}" "${mirror_path}" } create_job_workspace_from_mirror() { local remote_url=$1 local mirror_path=$2 local job_workspace=$3 local repo_dir=$4 rm -rf "${job_workspace}" mkdir -p "${job_workspace}" git clone --shared "${mirror_path}" "${repo_dir}" >/dev/null git -C "${repo_dir}" remote set-url origin "${remote_url}" } cleanup_job_workspace() { local job_workspace=$1 if [ -z "${job_workspace}" ] || [ "${job_workspace}" = "/" ]; then workspace_fail "refusing to remove invalid workspace path: ${job_workspace}" return 1 fi rm -rf "${job_workspace}" } prepare_job_workspace() { local repo_slug=$1 local remote_url=$2 local mirror_root=$3 local workspace_root=$4 local run_id=$5 local run_attempt=${6:-1} local job_name=${7:-job} local owner repo_name safe_job_name job_identity mirror_path job_workspace repo_dir local slug_parts mapfile -t slug_parts < <(split_repository_slug "${repo_slug}") owner=${slug_parts[0]} repo_name=${slug_parts[1]} safe_job_name=$(sanitize_job_name "${job_name}") job_identity=$(build_job_identity "${run_id}" "${run_attempt}" "${safe_job_name}") mirror_path=$(build_mirror_path "${mirror_root}" "${owner}" "${repo_name}") job_workspace=$(build_job_workspace_root "${workspace_root}" "${owner}" "${repo_name}" "${job_identity}") repo_dir=$(build_job_repo_dir "${workspace_root}" "${owner}" "${repo_name}" "${job_identity}") ensure_bare_mirror "${remote_url}" "${mirror_path}" create_job_workspace_from_mirror "${remote_url}" "${mirror_path}" "${job_workspace}" "${repo_dir}" printf 'REPO_OWNER=%s\n' "${owner}" printf 'REPO_NAME=%s\n' "${repo_name}" printf 'JOB_IDENTITY=%s\n' "${job_identity}" printf 'MIRROR_PATH=%s\n' "${mirror_path}" printf 'JOB_WORKSPACE=%s\n' "${job_workspace}" printf 'REPO_DIR=%s\n' "${repo_dir}" } print_usage() { cat <<'EOF' Usage: bootstrap_workspace.sh prepare-job-workspace bootstrap_workspace.sh cleanup-job-workspace EOF } main() { local command=${1:-} case "${command}" in prepare-job-workspace) [ $# -eq 8 ] || { print_usage return 1 } shift prepare_job_workspace "$@" ;; cleanup-job-workspace) [ $# -eq 2 ] || { print_usage return 1 } shift cleanup_job_workspace "$1" ;; *) print_usage return 1 ;; esac } if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then set -euo pipefail main "$@" fi