# shellcheck shell=sh# doesn't do anything on its own. must be sourced.# The script which sources this script must set the following variables:# GITHUB_REPO = the relative repo name of the submodule on github# SUBDIR = the location in the working tree where the submodule goes# We also expect `set -eu`, but set them ourselves for shellcheck.set -eu[ -n "${GITHUB_REPO}" ][ -n "${SUBDIR}" ]# Set by git-rebase for spawned actionsunset GIT_DIR GIT_EXEC_PATH GIT_PREFIX GIT_REFLOG_ACTION GIT_WORK_TREEGITHUB_BASE="${GITHUB_BASE:-cockpit-project/cockpit}"GITHUB_REPOSITORY="${GITHUB_BASE%/*}/${GITHUB_REPO}"HTTPS_REMOTE="https://github.com/${GITHUB_REPOSITORY}"# shellcheck disable=SC2034 # used in other scriptsSSH_REMOTE="git@github.com:${GITHUB_REPOSITORY}"CACHE_DIR="${XDG_CACHE_HOME-${HOME}/.cache}/cockpit-dev/${GITHUB_REPOSITORY}.git"if [ "${V-}" = 0 ]; thenmessage() { printf " %-8s %s\n" "$1" "$2" >&2; }quiet='--quiet'elsemessage() { :; }quiet=''fiinit_cache() {if [ ! -d "${CACHE_DIR}" ]; thenmessage INIT "${CACHE_DIR}"mkdir -p "${CACHE_DIR}"git init --bare --template='' ${quiet} "${CACHE_DIR}"git --git-dir "${CACHE_DIR}" remote add origin "${HTTPS_REMOTE}"fi}# runs a git command on the cache dirgit_cache() {init_cachegit --git-dir "${CACHE_DIR}" "$@"}# reads the named gitlink from the current state of the index# returns (ie: prints) a 40-character commit IDget_index_gitlink() {if ! git ls-files -s "$1" | grep -E -o '\<[[:xdigit:]]{40}\>'; thenecho "*** couldn't read gitlink for file $1 from the index" >&2exit 1fi}# This checks if the given argument "$1" (already) exists in the repository# we use git rev-list --objects to to avoid problems with incomplete fetches:# we want to make sure the complete commit is therecheck_ref() {git_cache rev-list --quiet --objects "$1" -- 2>/dev/null}# Fetch a specific commit ID into the cache# Either we have this commit available locally (in which case this function# does nothing), or we need to fetch it. There's no chance that the object# changed on the server, because we define it by its checksum.fetch_sha_to_cache() {sha="$1"# No "offline mode" here: we either have the commit, or we don'tif ! check_ref "${sha}"; thenmessage FETCH "${SUBDIR} [ref: ${sha}]"git_cache fetch --no-tags ${quiet} origin "${sha}"# tag it to keep it from being GC'd.git_cache tag "sha-${sha}" "${sha}" "--no-sign"fi}# General purpose "fetch" function to be used with tags, refs, or nothing at# all (to fetch everything). This checks the server for updates, because all# of those things might change at any given time. Supports an "offline" mode# to skip the fetch and use the possibly-stale local version, if we have it.fetch_to_cache() {# We're fetching a named ref (or all refs), which means:# - we should always do the fetch because it might have changed. but# - we might be able to skip updating in case we already have itif [ -z "${OFFLINE-}" ]; thenfor retry in $(seq 3); domessage FETCH "${SUBDIR} ${1+[ref: $*]}"if git_cache fetch --prune ${quiet} origin "$@"; thenreturnfisleep $((retry * retry * 5))doneecho "repeated git fetch failure, giving up" >&2exit 1fi}# Get the content of "$2" from cache commit "$1"cat_from_cache() {git_cache cat-file blob "$1:$2"}# Consistency checking: for a given cache commit "$1", check if it contains a# file "$2" which is equal to the file "$3" present in the working tree.cmp_from_cache() {cat_from_cache "$1" "$2" | cmp "$3"}# Like `git clone` except that it uses the original origin url and supports# checking out commit IDs as detached heads. The target directory must either# be empty, or not exist.clone_from_cache() {message CLONE "${SUBDIR} [ref: $1]"[ ! -e "${SUBDIR}" ] || rmdir "${SUBDIR}"mkdir "${SUBDIR}"cp -a --reflink=auto "${CACHE_DIR}" "${SUBDIR}/.git"git --git-dir "${SUBDIR}/.git" config --unset core.baregit -c advice.detachedHead=false -C "${SUBDIR}" checkout ${quiet} "$1"}# This stores a .tar file from stdin into the cache as a tree object.# Returns the ID. Opposite of `git archive`, basically.tar_to_cache() {# Need to do this before we set the GIT_* variablesinit_cache# Use a sub-shell to enable cleanup of the temporary directory(tmpdir="$(mktemp --tmpdir --directory cockpit-tar-to-git.XXXXXX)"# shellcheck disable=SC2064 # we want ${tmpdir} expanded nowtrap "rm -r '${tmpdir}'" EXITexport GIT_INDEX_FILE="${tmpdir}/tmp-index"export GIT_WORK_TREE="${tmpdir}/work"mkdir "${GIT_WORK_TREE}"cd "${GIT_WORK_TREE}"tar --extract --exclude '.git*'message INDEX "${SUBDIR}"git_cache add --allgit_cache write-tree)}# Small helper to run a git command on the cache directorycmd_git() {git_cache "$@"}