actions-template/.gitea/ci/bootstrap_workspace.sh

216 lines
5.4 KiB
Bash

#!/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 --local "${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 <repo-slug> <remote-url> <mirror-root> <workspace-root> <run-id> <run-attempt> <job-name>
bootstrap_workspace.sh cleanup-job-workspace <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