From cd5eaaacbb69939ef41e1b526a2e38042664fb97 Mon Sep 17 00:00:00 2001 From: anon Date: Thu, 17 Oct 2024 18:02:21 +0200 Subject: [PATCH] +documentation --- README.md | 27 ++ git-r3-tor.eclass | 1183 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 1210 insertions(+) create mode 100644 README.md create mode 100644 git-r3-tor.eclass diff --git a/README.md b/README.md new file mode 100644 index 0000000..45449a9 --- /dev/null +++ b/README.md @@ -0,0 +1,27 @@ +# Personal and /g/chad related gentoo overlay + +## Instructions + +A running `tor` deamon is an obvious requirement. + +The `eselect-repo.conf` entry must look something along the lines of this: +```ini +[agvxov] +location = /var/db/repos/agvxov +sync-type = git +sync-git-clone-extra-opts = -c 'http.https://*.onion.proxy=socks5h://127.0.0.1:9050' +sync-uri = https://bis64wqhh3louusbd45iyj76kmn4rzw5ysawyan5bkxwyzihj67c5lid.onion/anon/agvxov-overlay.git +``` + +Pulling from onion remotes requires its own eclass, `git-r3-tor.eclass`. +This is a slightly modified version of `git-r3.eclass`. +Move it next to original, wherever you find it on your system +(there has to be a proper way to install it, ill look into it later). + +Lastly, +you must add the site ssl certificate to your trusted system certificates, +so `git` wont refuse to connect: +[https://wiki.gentoo.org/wiki/Certificates#Adding\_trusted\_certificates](https://wiki.gentoo.org/wiki/Certificates#Adding_trusted_certificates) + +After an `emerge --sync agvxov`, +you should be able to install stuff. diff --git a/git-r3-tor.eclass b/git-r3-tor.eclass new file mode 100644 index 0000000..712da67 --- /dev/null +++ b/git-r3-tor.eclass @@ -0,0 +1,1183 @@ +# Copyright 1999-2024 Gentoo Authors +# Distributed under the terms of the GNU General Public License v2 + +# @ECLASS: git-r3.eclass +# @MAINTAINER: +# Michał Górny +# @SUPPORTED_EAPIS: 7 8 +# @BLURB: Eclass for fetching and unpacking git repositories. +# @DESCRIPTION: +# Third generation eclass for easing maintenance of live ebuilds using +# git as remote repository. + +# @ECLASS_VARIABLE: EGIT_LFS +# @PRE_INHERIT +# @DEFAULT_UNSET +# @DESCRIPTION: +# If set, git lfs support will be enabled. +# Set before inheriting this eclass. + +# @ECLASS_VARIABLE: _NUM_LFS_FILTERS_FOUND +# @INTERNAL +# @DEFAULT_UNSET +# @DESCRIPTION: +# This is used to provide QA warnings if a repo has git lfs filters +# defined but EGIT_LFS is not turned on and vice versa. +# If non-empty, then the repo likely needs EGIT_LFS to clone properly. + +case ${EAPI} in + 7|8) ;; + *) die "${ECLASS}: EAPI ${EAPI:-0} not supported" ;; +esac + +if [[ -z ${_GIT_R3_ECLASS} ]]; then +_GIT_R3_ECLASS=1 + +PROPERTIES+=" live" + +BDEPEND=">=dev-vcs/git-1.8.2.1[curl]" +[[ ${EGIT_LFS} ]] && BDEPEND+=" dev-vcs/git-lfs" + +# @ECLASS_VARIABLE: EGIT_CLONE_TYPE +# @USER_VARIABLE +# @DESCRIPTION: +# Type of clone that should be used against the remote repository. +# This can be either of: 'mirror', 'single', 'shallow'. +# +# This is intended to be set by user in make.conf. Ebuilds are supposed +# to set EGIT_MIN_CLONE_TYPE if necessary instead. +# +# The 'mirror' type clones all remote branches and tags with complete +# history and all notes. EGIT_COMMIT can specify any commit hash. +# Upstream-removed branches and tags are purged from the local clone +# while fetching. This mode is suitable for cloning the local copy +# for development or hosting a local git mirror. However, clones +# of repositories with large diverged branches may quickly grow large. +# +# The 'single+tags' type clones the requested branch and all tags +# in the repository. All notes are fetched as well. EGIT_COMMIT +# can safely specify hashes throughout the current branch and all tags. +# No purging of old references is done (if you often switch branches, +# you may need to remove stale branches yourself). This mode is intended +# mostly for use with broken git servers such as Google Code that fail +# to fetch tags along with the branch in 'single' mode. +# +# The 'single' type clones only the requested branch or tag. Tags +# referencing commits throughout the branch history are fetched as well, +# and all notes. EGIT_COMMIT can safely specify only hashes +# in the current branch. No purging of old references is done (if you +# often switch branches, you may need to remove stale branches +# yourself). This mode is suitable for general use. +# +# The 'shallow' type clones only the newest commit on requested branch +# or tag. EGIT_COMMIT can only specify tags, and since the history is +# unavailable calls like 'git describe' will not reference prior tags. +# No purging of old references is done. This mode is intended mostly for +# embedded systems with limited disk space. +: "${EGIT_CLONE_TYPE:=single}" + +# @ECLASS_VARIABLE: EGIT_MIN_CLONE_TYPE +# @DESCRIPTION: +# 'Minimum' clone type supported by the ebuild. Takes same values +# as EGIT_CLONE_TYPE. When user sets a type that's 'lower' (that is, +# later on the list) than EGIT_MIN_CLONE_TYPE, the eclass uses +# EGIT_MIN_CLONE_TYPE instead. +# +# This variable is intended to be used by ebuilds only. Users are +# supposed to set EGIT_CLONE_TYPE instead. +# +# A common case is to use 'single' whenever the build system requires +# access to full branch history, or 'single+tags' when Google Code +# or a similar remote is used that does not support shallow clones +# and fetching tags along with commits. Please use sparingly, and to fix +# fatal errors rather than 'non-pretty versions'. +: "${EGIT_MIN_CLONE_TYPE:=shallow}" + +# @ECLASS_VARIABLE: EGIT_LFS_CLONE_TYPE +# @USER_VARIABLE +# @DESCRIPTION: +# Type of lfs clone that should be used against the remote repository. +# This can be either of: 'mirror', 'single', 'shallow'. +# +# This works a bit differently than EGIT_CLONE_TYPE. +# +# The 'mirror' type clones all LFS files that is available from the +# cloned repo. Is is mostly useful for backup or rehosting purposes as +# the disk usage will be excessive. +# +# The 'single' type clones only the LFS files from the current commit. +# However unlike 'shallow', it will not cleanup stale LFS files. +# +# The 'shallow' type clones only the LFS files from the current commit. +# LFS files that are not referenced by the current commit and more than +# a few days old will be automatically removed to save disk space. +# This is the recommended mode for LFS repos to prevent excessive disk +# usage. +: "${EGIT_LFS_CLONE_TYPE:=shallow}" + +# @ECLASS_VARIABLE: EVCS_STORE_DIRS +# @OUTPUT_VARIABLE +# @DESCRIPTION: +# Record of names of all the repositories directories being cloned in the git3_src. +# This is useful in the case of ebuild that fetch multiple repos and +# it would be used by eclean to clean them up. +EVCS_STORE_DIRS=() + +# @ECLASS_VARIABLE: EGIT3_STORE_DIR +# @USER_VARIABLE +# @DEFAULT_UNSET +# @DESCRIPTION: +# Storage directory for git sources. +# +# This is intended to be set by user in make.conf. Ebuilds must not set +# it. +# +# EGIT3_STORE_DIR=${DISTDIR}/git3-src + +# @ECLASS_VARIABLE: EGIT_MIRROR_URI +# @DEFAULT_UNSET +# @DESCRIPTION: +# 'Top' URI to a local git mirror. If specified, the eclass will try +# to fetch from the local mirror instead of using the remote repository. +# +# The mirror needs to follow EGIT3_STORE_DIR structure. The directory +# created by eclass can be used for that purpose. +# +# Example: +# @CODE +# EGIT_MIRROR_URI="git://mirror.lan/" +# @CODE + +# @ECLASS_VARIABLE: EGIT_REPO_URI +# @REQUIRED +# @DESCRIPTION: +# URIs to the repository, e.g. https://foo. If multiple URIs are +# provided, the eclass will consider the remaining URIs as fallbacks +# to try if the first URI does not work. For supported URI syntaxes, +# read the manpage for git-clone(1). +# +# URIs should be using https:// whenever possible. http:// and git:// +# URIs are completely insecure and their use (even if only as +# a fallback) renders the ebuild completely vulnerable to MITM attacks. +# +# Can be a whitespace-separated list or an array. +# +# Example: +# @CODE +# EGIT_REPO_URI="https://a/b.git https://c/d.git" +# @CODE + +# @ECLASS_VARIABLE: EVCS_OFFLINE +# @DEFAULT_UNSET +# @DESCRIPTION: +# If non-empty, this variable prevents any online operations. + +# @ECLASS_VARIABLE: EVCS_UMASK +# @DEFAULT_UNSET +# @DESCRIPTION: +# Set this variable to a custom umask. This is intended to be set by +# users. By setting this to something like 002, it can make life easier +# for people who do development as non-root (but are in the portage +# group), and then switch over to building with FEATURES=userpriv. +# Or vice-versa. Shouldn't be a security issue here as anyone who has +# portage group write access already can screw the system over in more +# creative ways. + +# @ECLASS_VARIABLE: EGIT_BRANCH +# @DEFAULT_UNSET +# @DESCRIPTION: +# The branch name to check out. If unset, the upstream default (HEAD) +# will be used. + +# @ECLASS_VARIABLE: EGIT_COMMIT +# @DEFAULT_UNSET +# @DESCRIPTION: +# The tag name or commit identifier to check out. If unset, newest +# commit from the branch will be used. Note that if set to a commit +# not on HEAD branch, EGIT_BRANCH needs to be set to a branch on which +# the commit is available. + +# @ECLASS_VARIABLE: EGIT_COMMIT_DATE +# @DEFAULT_UNSET +# @DESCRIPTION: +# Attempt to check out the repository state for the specified timestamp. +# The date should be in format understood by 'git rev-list'. The commits +# on EGIT_BRANCH will be considered. +# +# The eclass will select the last commit with commit date preceding +# the specified date. When merge commits are found, only first parents +# will be considered in order to avoid switching into external branches +# (assuming that merges are done correctly). In other words, each merge +# will be considered alike a single commit with date corresponding +# to the merge commit date. + +# @ECLASS_VARIABLE: EGIT_CHECKOUT_DIR +# @DEFAULT_UNSET +# @DESCRIPTION: +# The directory to check the git sources out to. +# +# EGIT_CHECKOUT_DIR=${WORKDIR}/${P} + +# @ECLASS_VARIABLE: EGIT_SUBMODULES +# @DEFAULT_UNSET +# @DESCRIPTION: +# An array of inclusive and exclusive wildcards on submodule names, +# stating which submodules are fetched and checked out. Exclusions +# start with '-', and exclude previously matched submodules. +# +# If unset, all submodules are enabled. Empty list disables all +# submodules. In order to use an exclude-only list, start the array +# with '*'. +# +# Remember that wildcards need to be quoted in order to prevent filename +# expansion. +# +# Examples: +# @CODE +# # Disable all submodules +# EGIT_SUBMODULES=() +# +# # Include only foo and bar +# EGIT_SUBMODULES=( foo bar ) +# +# # Use all submodules except for test-* but include test-lib +# EGIT_SUBMODULES=( '*' '-test-*' test-lib ) +# @CODE + +# @FUNCTION: _git-r3-tor_env_setup +# @INTERNAL +# @DESCRIPTION: +# Set the eclass variables as necessary for operation. This can involve +# setting EGIT_* to defaults or ${PN}_LIVE_* variables. +_git-r3-tor_env_setup() { + debug-print-function ${FUNCNAME} "$@" + + # check the clone type + case "${EGIT_CLONE_TYPE}" in + mirror|single+tags|single|shallow) + ;; + *) + die "Invalid EGIT_CLONE_TYPE=${EGIT_CLONE_TYPE}" + esac + case "${EGIT_MIN_CLONE_TYPE}" in + shallow) + ;; + single) + if [[ ${EGIT_CLONE_TYPE} == shallow ]]; then + einfo "git-r3: ebuild needs to be cloned in 'single' mode, adjusting" + EGIT_CLONE_TYPE=single + fi + ;; + single+tags) + if [[ ${EGIT_CLONE_TYPE} == shallow || ${EGIT_CLONE_TYPE} == single ]]; then + einfo "git-r3: ebuild needs to be cloned in 'single+tags' mode, adjusting" + EGIT_CLONE_TYPE=single+tags + fi + ;; + mirror) + if [[ ${EGIT_CLONE_TYPE} != mirror ]]; then + einfo "git-r3: ebuild needs to be cloned in 'mirror' mode, adjusting" + EGIT_CLONE_TYPE=mirror + fi + ;; + *) + die "Invalid EGIT_MIN_CLONE_TYPE=${EGIT_MIN_CLONE_TYPE}" + esac + + if [[ ${EGIT_SUBMODULES[@]+1} && $(declare -p EGIT_SUBMODULES) != "declare -a"* ]] + then + die 'EGIT_SUBMODULES must be an array.' + fi + + local esc_pn livevar + esc_pn=${PN//[-+]/_} + [[ ${esc_pn} == [0-9]* ]] && esc_pn=_${esc_pn} + + # note: deprecated, use EGIT_OVERRIDE_* instead + livevar=${esc_pn}_LIVE_REPO + EGIT_REPO_URI=${!livevar-${EGIT_REPO_URI}} + [[ ${!livevar} ]] \ + && ewarn "Using ${livevar}, no support will be provided" + + livevar=${esc_pn}_LIVE_BRANCH + EGIT_BRANCH=${!livevar-${EGIT_BRANCH}} + [[ ${!livevar} ]] \ + && ewarn "Using ${livevar}, no support will be provided" + + livevar=${esc_pn}_LIVE_COMMIT + EGIT_COMMIT=${!livevar-${EGIT_COMMIT}} + [[ ${!livevar} ]] \ + && ewarn "Using ${livevar}, no support will be provided" + + livevar=${esc_pn}_LIVE_COMMIT_DATE + EGIT_COMMIT_DATE=${!livevar-${EGIT_COMMIT_DATE}} + [[ ${!livevar} ]] \ + && ewarn "Using ${livevar}, no support will be provided" + + if [[ ${EGIT_COMMIT} && ${EGIT_COMMIT_DATE} ]]; then + die "EGIT_COMMIT and EGIT_COMMIT_DATE can not be specified simultaneously" + fi +} + +# @FUNCTION: _git-r3-tor_set_gitdir +# @USAGE: +# @INTERNAL +# @DESCRIPTION: +# Obtain the local repository path and set it as GIT_DIR. Creates +# a new repository if necessary. +# +# may be used to compose the path. It should therefore be +# a canonical URI to the repository. +_git-r3-tor_set_gitdir() { + debug-print-function ${FUNCNAME} "$@" + + local repo_name=${1#*://*/} + + # strip the trailing slash + repo_name=${repo_name%/} + + # strip common prefixes to make paths more likely to match + # e.g. git://X/Y.git vs https://X/git/Y.git + # (but just one of the prefixes) + case "${repo_name}" in + # gnome.org... who else? + browse/*) repo_name=${repo_name#browse/};; + # cgit can proxy requests to git + cgit/*) repo_name=${repo_name#cgit/};; + # pretty common + git/*) repo_name=${repo_name#git/};; + # gentoo.org + gitroot/*) repo_name=${repo_name#gitroot/};; + # sourceforge + p/*) repo_name=${repo_name#p/};; + # kernel.org + pub/scm/*) repo_name=${repo_name#pub/scm/};; + esac + # ensure a .git suffix, same reason + repo_name=${repo_name%.git}.git + # now replace all the slashes + repo_name=${repo_name//\//_} + + local distdir=${PORTAGE_ACTUAL_DISTDIR:-${DISTDIR}} + : "${EGIT3_STORE_DIR:=${distdir}/git3-src}" + + GIT_DIR=${EGIT3_STORE_DIR}/${repo_name} + + EVCS_STORE_DIRS+=( "${GIT_DIR}" ) + + if [[ ! -d ${EGIT3_STORE_DIR} && ! ${EVCS_OFFLINE} ]]; then + ( + addwrite / + mkdir -p "${EGIT3_STORE_DIR}" + ) || die "Unable to create ${EGIT3_STORE_DIR}" + fi + + addwrite "${EGIT3_STORE_DIR}" + if [[ ! -d ${GIT_DIR} ]]; then + if [[ ${EVCS_OFFLINE} ]]; then + eerror "A clone of the following repository is required to proceed:" + eerror " ${1}" + eerror "However, networking activity has been disabled using EVCS_OFFLINE and there" + eerror "is no local clone available." + die "No local clone of ${1}. Unable to proceed with EVCS_OFFLINE." + fi + + local saved_umask + if [[ ${EVCS_UMASK} ]]; then + saved_umask=$(umask) + umask "${EVCS_UMASK}" || die "Bad options to umask: ${EVCS_UMASK}" + fi + mkdir "${GIT_DIR}" || die + git init --bare -b __init__ || die + if [[ ${saved_umask} ]]; then + umask "${saved_umask}" || die + fi + fi +} + +# @FUNCTION: _git-r3-tor_set_submodules +# @USAGE: +# @INTERNAL +# @DESCRIPTION: +# Parse .gitmodules contents passed as +# as in "$(cat .gitmodules)"). Composes a 'submodules' array that +# contains in order (name, URL, path) for each submodule. +# +# specifies path to current submodule (empty if top repo), +# and is used to support recursively specifying submodules. The path +# must include a trailing slash if it's not empty. +_git-r3-tor_set_submodules() { + debug-print-function ${FUNCNAME} "$@" + + local parent_path=${1} + local data=${2} + [[ -z ${parent_path} || ${parent_path} == */ ]] || die + + # ( name url path ... ) + submodules=() + + local l + while read l; do + # submodule..path= + # submodule..url= + [[ ${l} == submodule.*.url=* ]] || continue + + l=${l#submodule.} + local subname=${l%%.url=*} + local is_manually_specified= + + # filter out on EGIT_SUBMODULES + if declare -p EGIT_SUBMODULES &>/dev/null; then + local p l_res res= + for p in "${EGIT_SUBMODULES[@]}"; do + if [[ ${p} == -* ]]; then + p=${p#-} + l_res= + else + l_res=1 + fi + + [[ ${parent_path}${subname} == ${p} ]] && res=${l_res} + done + + if [[ ! ${res} ]]; then + einfo "Skipping submodule ${parent_path}${subname}" + continue + else + einfo "Using submodule ${parent_path}${subname}" + is_manually_specified=1 + fi + fi + + # skip modules that have 'update = none', bug #487262. + local upd=$(echo "${data}" | git config -f /dev/fd/0 \ + submodule."${subname}".update) + [[ ${upd} == none && ! ${is_manually_specified} ]] && continue + + # https://github.com/git/git/blob/master/refs.c#L31 + # we are more restrictive than git itself but that should not + # cause any issues, #572312, #606950 + # TODO: check escaped names for collisions + local enc_subname=${subname//[^a-zA-Z0-9-]/_} + + submodules+=( + "${enc_subname}" + "$(echo "${data}" | git config -f /dev/fd/0 \ + submodule."${subname}".url || die)" + "$(echo "${data}" | git config -f /dev/fd/0 \ + submodule."${subname}".path || die)" + ) + done < <(echo "${data}" | git config -f /dev/fd/0 -l || die) +} + +# @FUNCTION: _git-r3-tor_set_subrepos +# @USAGE: ... +# @INTERNAL +# @DESCRIPTION: +# Create 'subrepos' array containing absolute (canonical) submodule URIs +# for the given . If the URI is relative, URIs will be +# constructed using all s. Otherwise, this single URI +# will be placed in the array. +_git-r3-tor_set_subrepos() { + debug-print-function ${FUNCNAME} "$@" + + local suburl=${1} + subrepos=( "${@:2}" ) + + if [[ ${suburl} == ./* || ${suburl} == ../* ]]; then + # drop all possible trailing slashes for consistency + subrepos=( "${subrepos[@]%%/}" ) + + while true; do + if [[ ${suburl} == ./* ]]; then + suburl=${suburl:2} + elif [[ ${suburl} == ../* ]]; then + suburl=${suburl:3} + + # XXX: correctness checking + + # drop the last path component + subrepos=( "${subrepos[@]%/*}" ) + # and then the trailing slashes, again + subrepos=( "${subrepos[@]%%/}" ) + else + break + fi + done + + # append the preprocessed path to the preprocessed URIs + subrepos=( "${subrepos[@]/%//${suburl}}") + else + subrepos=( "${suburl}" ) + fi +} + + +# @FUNCTION: _git-r3-tor_is_local_repo +# @USAGE: +# @INTERNAL +# @DESCRIPTION: +# Determine whether the given URI specifies a local (on-disk) +# repository. +_git-r3-tor_is_local_repo() { + debug-print-function ${FUNCNAME} "$@" + + local uri=${1} + + [[ ${uri} == file://* || ${uri} == /* ]] +} + +# @FUNCTION: git-r3-tor_fetch +# @USAGE: [ [ [ []]]] +# @DESCRIPTION: +# Fetch new commits to the local clone of repository. +# +# specifies the repository URIs to fetch from, as a space- +# -separated list. The first URI will be used as repository group +# identifier and therefore must be used consistently. When not +# specified, defaults to ${EGIT_REPO_URI}. +# +# specifies the remote ref or commit id to fetch. +# It is preferred to use 'refs/heads/' for branches +# and 'refs/tags/' for tags. Other options are 'HEAD' +# for upstream default branch and hexadecimal commit SHA1. Defaults +# to the first of EGIT_COMMIT, EGIT_BRANCH or literal 'HEAD' that +# is set to a non-null value. +# +# specifies the local branch identifier that will be used to +# locally store the fetch result. It should be unique to multiple +# fetches within the repository that can be performed at the same time +# (including parallel merges). It defaults to ${CATEGORY}/${PN}/${SLOT%/*}. +# This default should be fine unless you are fetching multiple trees +# from the same repository in the same ebuild. +# +# requests attempting to use repository state as of specific +# date. For more details, see EGIT_COMMIT_DATE. +# +# The fetch operation will affect the EGIT_STORE only. It will not touch +# the working copy, nor export any environment variables. +# If the repository contains submodules, they will be fetched +# recursively. +git-r3-tor_fetch() { + debug-print-function ${FUNCNAME} "$@" + + # disable password prompts, https://bugs.gentoo.org/701276 + local -x GIT_TERMINAL_PROMPT=0 + + # process repos first since we create repo_name from it + local repos + if [[ ${1} ]]; then + repos=( ${1} ) + elif [[ $(declare -p EGIT_REPO_URI) == "declare -a"* ]]; then + repos=( "${EGIT_REPO_URI[@]}" ) + else + repos=( ${EGIT_REPO_URI} ) + fi + + [[ ${repos[@]} ]] || die "No URI provided and EGIT_REPO_URI unset" + + local r + for r in "${repos[@]}"; do + if [[ ${r} == git:* || ${r} == http:* ]]; then + ewarn "git-r3: ${r%%:*} protocol is completely insecure and may render the ebuild" + ewarn "easily susceptible to MITM attacks (even if used only as fallback). Please" + ewarn "use https instead." + ewarn "[URI: ${r}]" + fi + done + + local -x GIT_DIR + _git-r3-tor_set_gitdir "${repos[0]}" + + einfo "Repository id: ${GIT_DIR##*/}" + + # prepend the local mirror if applicable + if [[ ${EGIT_MIRROR_URI} ]]; then + repos=( + "${EGIT_MIRROR_URI%/}/${GIT_DIR##*/}" + "${repos[@]}" + ) + fi + + # get the default values for the common variables and override them + local branch_name=${EGIT_BRANCH} + local commit_id=${2:-${EGIT_COMMIT}} + local commit_date=${4:-${EGIT_COMMIT_DATE}} + + # get the name and do some more processing: + # 1) kill .git suffix, + # 2) underscore (remaining) non-variable characters, + # 3) add preceding underscore if it starts with a digit, + # 4) uppercase. + local override_name=${GIT_DIR##*/} + override_name=${override_name%.git} + override_name=${override_name//[^a-zA-Z0-9_]/_} + override_name=${override_name^^} + + local varmap=( + REPO:repos + BRANCH:branch_name + COMMIT:commit_id + COMMIT_DATE:commit_date + ) + + local localvar livevar live_warn= override_vars=() + for localvar in "${varmap[@]}"; do + livevar=EGIT_OVERRIDE_${localvar%:*}_${override_name} + localvar=${localvar#*:} + override_vars+=( "${livevar}" ) + + if [[ -n ${!livevar} ]]; then + [[ ${localvar} == repos ]] && repos=() + live_warn=1 + ewarn "Using ${livevar}=${!livevar}" + declare "${localvar}=${!livevar}" + fi + done + + if [[ ${live_warn} ]]; then + ewarn "No support will be provided." + else + einfo "To override fetched repository properties, use:" + local x + for x in "${override_vars[@]}"; do + einfo " ${x}" + done + einfo + fi + + # set final variables after applying overrides + local branch=${branch_name:+refs/heads/${branch_name}} + local remote_ref=${commit_id:-${branch:-HEAD}} + local local_id=${3:-${CATEGORY}/${PN}/${SLOT%/*}} + local local_ref=refs/git-r3/${local_id}/__main__ + + # try to fetch from the remote + local success saved_umask + if [[ ${EVCS_UMASK} ]]; then + saved_umask=$(umask) + umask "${EVCS_UMASK}" || die "Bad options to umask: ${EVCS_UMASK}" + fi + for r in "${repos[@]}"; do + if [[ ! ${EVCS_OFFLINE} ]]; then + einfo "Fetching ${r} ..." + + local fetch_command=( git -c 'http.https://*.onion.proxy=socks5h://127.0.0.1:9050' fetch "${r}" ) + local clone_type=${EGIT_CLONE_TYPE} + + if [[ ${clone_type} == mirror ]]; then + fetch_command+=( + --prune + # mirror the remote branches as local branches + "+refs/heads/*:refs/heads/*" + # pull tags explicitly in order to prune them properly + "+refs/tags/*:refs/tags/*" + # notes in case something needs them + "+refs/notes/*:refs/notes/*" + # pullrequest refs are useful for testing incoming changes + "+refs/pull/*/head:refs/pull/*" + # and HEAD in case we need the default branch + # (we keep it in refs/git-r3 since otherwise --prune interferes) + "+HEAD:refs/git-r3/HEAD" + # fetch the specifc commit_ref to deal with orphan commits + "${remote_ref}" + ) + else # single or shallow + local fetch_l fetch_r + + if [[ ${remote_ref} == HEAD ]]; then + # HEAD + fetch_l=HEAD + elif [[ ${remote_ref} == refs/* ]]; then + # regular branch, tag or some other explicit ref + fetch_l=${remote_ref} + else + # tag or commit id... + # let ls-remote figure it out + local tagref=$(git ls-remote "${r}" "refs/tags/${remote_ref}") + + # if it was a tag, ls-remote obtained a hash + if [[ ${tagref} ]]; then + # tag + fetch_l=refs/tags/${remote_ref} + else + # commit id + # so we need to fetch the whole branch + if [[ ${branch} ]]; then + fetch_l=${branch} + else + fetch_l=HEAD + fi + + # fetching by commit in shallow mode? can't do. + if [[ ${clone_type} == shallow ]]; then + clone_type=single + fi + fi + fi + + # checkout by date does not make sense in shallow mode + if [[ ${commit_date} && ${clone_type} == shallow ]]; then + clone_type=single + fi + + if [[ ${fetch_l} == HEAD ]]; then + fetch_r=refs/git-r3/HEAD + else + fetch_r=${fetch_l} + fi + + fetch_command+=( + "+${fetch_l}:${fetch_r}" + ) + + if [[ ${clone_type} == single+tags ]]; then + fetch_command+=( + # pull tags explicitly as requested + "+refs/tags/*:refs/tags/*" + ) + fi + fi + + if [[ ${clone_type} == shallow ]]; then + if _git-r3-tor_is_local_repo; then + # '--depth 1' causes sandbox violations with local repos + # bug #491260 + clone_type=single + elif [[ ! $(git rev-parse --quiet --verify "${fetch_r}") ]] + then + # use '--depth 1' when fetching a new branch + fetch_command+=( --depth 1 ) + fi + else # non-shallow mode + if [[ -f ${GIT_DIR}/shallow ]]; then + fetch_command+=( --unshallow ) + fi + fi + + set -- "${fetch_command[@]}" + echo "${@}" >&2 + "${@}" || continue + + if [[ ${clone_type} == mirror || ${fetch_l} == HEAD ]]; then + # update our HEAD to match our remote HEAD ref + git symbolic-ref HEAD refs/git-r3/HEAD \ + || die "Unable to update HEAD" + fi + fi + + # now let's see what the user wants from us + if [[ ${commit_date} ]]; then + local dated_commit_id=$( + git rev-list --first-parent --before="${commit_date}" \ + -n 1 "${remote_ref}" + ) + if [[ ${?} -ne 0 ]]; then + die "Listing ${remote_ref} failed (wrong ref?)." + elif [[ ! ${dated_commit_id} ]]; then + die "Unable to find commit for date ${commit_date}." + else + set -- git update-ref --no-deref "${local_ref}" "${dated_commit_id}" + fi + else + local full_remote_ref=$( + git rev-parse --verify --symbolic-full-name "${remote_ref}" + ) + + if [[ ${full_remote_ref} ]]; then + # when we are given a ref, create a symbolic ref + # so that we preserve the actual argument + set -- git symbolic-ref "${local_ref}" "${full_remote_ref}" + else + # otherwise, we were likely given a commit id + set -- git update-ref --no-deref "${local_ref}" "${remote_ref}" + fi + fi + + echo "${@}" >&2 + if ! "${@}"; then + if [[ ${EVCS_OFFLINE} ]]; then + eerror "A clone of the following repository is required to proceed:" + eerror " ${r}" + eerror "However, networking activity has been disabled using EVCS_OFFLINE and the local" + eerror "clone does not have requested ref:" + eerror " ${remote_ref}" + die "Local clone of ${r} does not have requested ref: ${remote_ref}. Unable to proceed with EVCS_OFFLINE." + else + die "Referencing ${remote_ref} failed (wrong ref?)." + fi + fi + + if [[ ${EGIT_LFS} ]]; then + # Fetch the LFS files from the current ref (if any) + local lfs_fetch_command=( git lfs fetch "${r}" "${remote_ref}" ) + + case "${EGIT_LFS_CLONE_TYPE}" in + shallow) + if [[ -d ${GIT_DIR}/lfs/objects ]] && ! rmdir "${GIT_DIR}"/lfs/objects 2> /dev/null; then + # Only prune if the lfs directory is not empty. + # The prune command can take a very long time to resolve even if there are no lfs objects. + lfs_fetch_command+=( + --prune + ) + fi + ;; + single) + ;; + mirror) + lfs_fetch_command+=( + --all + ) + ;; + *) + die "Invalid EGIT_LFS_CLONE_TYPE=${EGIT_LFS_CLONE_TYPE}" + esac + + set -- "${lfs_fetch_command[@]}" + echo "${@}" >&2 + "${@}" || die + elif [[ -d ${GIT_DIR}/lfs && ${EGIT_LFS_CLONE_TYPE} == shallow ]]; then + # Cleanup the LFS files from old checkouts if LFS support has been turned off. + rm -fr ${GIT_DIR}/lfs || die + fi + + success=1 + break + done + if [[ ${saved_umask} ]]; then + umask "${saved_umask}" || die + fi + [[ ${success} ]] || die "Unable to fetch from any of EGIT_REPO_URI" + + # submodules can reference commits in any branch + # always use the 'mirror' mode to accommodate that, bug #503332 + local EGIT_CLONE_TYPE=mirror + + # recursively fetch submodules + if git cat-file -e "${local_ref}":.gitmodules &>/dev/null; then + local submodules + _git-r3-tor_set_submodules "${_GIT_SUBMODULE_PATH}" \ + "$(git cat-file -p "${local_ref}":.gitmodules || die)" + + while [[ ${submodules[@]} ]]; do + local subname=${submodules[0]} + local url=${submodules[1]} + local path=${submodules[2]} + + # use only submodules for which path does exist + # (this is in par with 'git submodule'), bug #551100 + # note: git cat-file does not work for submodules + if [[ $(git ls-tree -d "${local_ref}" "${path}") ]] + then + local commit=$(git rev-parse "${local_ref}:${path}" || die) + + if [[ ! ${commit} ]]; then + die "Unable to get commit id for submodule ${subname}" + fi + + local subrepos + _git-r3-tor_set_subrepos "${url}" "${repos[@]}" + + _GIT_SUBMODULE_PATH=${_GIT_SUBMODULE_PATH}${path}/ \ + git-r3-tor_fetch "${subrepos[*]}" "${commit}" \ + "${local_id}/${subname}" "" + fi + + submodules=( "${submodules[@]:3}" ) # shift + done + fi +} + +# @FUNCTION: git-r3-tor_checkout +# @USAGE: [ [ [ [...]]]] +# @DESCRIPTION: +# Check the previously fetched tree to the working copy. +# +# specifies the repository URIs, as a space-separated list. +# The first URI will be used as repository group identifier +# and therefore must be used consistently with git-r3-tor_fetch. +# The remaining URIs are not used and therefore may be omitted. +# When not specified, defaults to ${EGIT_REPO_URI}. +# +# specifies the path to place the checkout. It defaults +# to ${EGIT_CHECKOUT_DIR} if set, otherwise to ${WORKDIR}/${P}. +# +# needs to specify the local identifier that was used +# for respective git-r3-tor_fetch. +# +# If are specified, then the specified paths are passed +# to 'git checkout' to effect a partial checkout. Please note that such +# checkout will not cause the repository to switch branches, +# and submodules will be skipped at the moment. The submodules matching +# those paths might be checked out in a future version of the eclass. +# +# The checkout operation will write to the working copy, and export +# the repository state into the environment. If the repository contains +# submodules, they will be checked out recursively. +git-r3-tor_checkout() { + debug-print-function ${FUNCNAME} "$@" + + local repos + if [[ ${1} ]]; then + repos=( ${1} ) + elif [[ $(declare -p EGIT_REPO_URI) == "declare -a"* ]]; then + repos=( "${EGIT_REPO_URI[@]}" ) + else + repos=( ${EGIT_REPO_URI} ) + fi + + local out_dir=${2:-${EGIT_CHECKOUT_DIR:-${WORKDIR}/${P}}} + local local_id=${3:-${CATEGORY}/${PN}/${SLOT%/*}} + local checkout_paths=( "${@:4}" ) + + local -x GIT_DIR + _git-r3-tor_set_gitdir "${repos[0]}" + + einfo "Checking out ${repos[0]} to ${out_dir} ..." + + if ! git cat-file -e refs/git-r3/"${local_id}"/__main__; then + die "Logic error: no local clone of ${repos[0]}. git-r3-tor_fetch not used?" + fi + local remote_ref=$( + git symbolic-ref --quiet refs/git-r3/"${local_id}"/__main__ + ) + local new_commit_id=$( + git rev-parse --verify refs/git-r3/"${local_id}"/__main__ + ) + + git-r3-tor_sub_checkout() { + local orig_repo=${GIT_DIR} + local -x GIT_DIR=${out_dir}/.git + local -x GIT_WORK_TREE=${out_dir} + + mkdir -p "${out_dir}" || die + + # use git init+fetch instead of clone since the latter doesn't like + # non-empty directories. + + git init --quiet -b __init__ || die + if [[ ${EGIT_LFS} ]]; then + # The "skip-repo" flag will just skip the installation of the pre-push hooks. + # We don't use these hook as we don't do any pushes + git lfs install --local --skip-repo || die + fi + # setup 'alternates' to avoid copying objects + echo "${orig_repo}/objects" > "${GIT_DIR}"/objects/info/alternates || die + # now copy the refs + cp -R "${orig_repo}"/refs/* "${GIT_DIR}"/refs/ || die + if [[ -f ${orig_repo}/packed-refs ]]; then + cp "${orig_repo}"/packed-refs "${GIT_DIR}"/packed-refs || die + fi + + # mark this directory as "safe" so that src_install() can access it + # https://bugs.gentoo.org/879353 + git config --global --add safe.directory \ + "$(cd "${out_dir}" && echo "${PWD}")" || die + + # (no need to copy HEAD, we will set it via checkout) + + if [[ -f ${orig_repo}/shallow ]]; then + cp "${orig_repo}"/shallow "${GIT_DIR}"/ || die + fi + + set -- git checkout --quiet + if [[ ${remote_ref} ]]; then + set -- "${@}" "${remote_ref#refs/heads/}" + else + set -- "${@}" "${new_commit_id}" + fi + if [[ ${checkout_paths[@]} ]]; then + set -- "${@}" -- "${checkout_paths[@]}" + fi + echo "${@}" >&2 + "${@}" || die "git checkout ${remote_ref:-${new_commit_id}} failed" + + # If any filters in any of the ".gitattributes" files specifies lfs, + # then this repo is most likely storing files with git lfs. + local has_git_lfs_filters=$( + git grep "filter=lfs" -- ".gitattributes" "**/.gitattributes" + ) + if [[ $has_git_lfs_filters ]]; then + # This is used for issuing QA warnings regarding LFS files in the repo (or lack thereof) + _EGIT_LFS_FILTERS_FOUND="yes" + fi + } + git-r3-tor_sub_checkout + unset -f git-r3-tor_sub_checkout + + local old_commit_id=$( + git rev-parse --quiet --verify refs/git-r3/"${local_id}"/__old__ + ) + if [[ ! ${old_commit_id} ]]; then + echo "GIT NEW branch -->" + echo " repository: ${repos[0]}" + echo " at the commit: ${new_commit_id}" + else + # diff against previous revision + echo "GIT update -->" + echo " repository: ${repos[0]}" + # write out message based on the revisions + if [[ "${old_commit_id}" != "${new_commit_id}" ]]; then + echo " updating from commit: ${old_commit_id}" + echo " to commit: ${new_commit_id}" + + set -- git --no-pager diff --stat \ + ${old_commit_id}..${new_commit_id} + if [[ ${checkout_paths[@]} ]]; then + set -- "${@}" -- "${checkout_paths[@]}" + fi + "${@}" + else + echo " at the commit: ${new_commit_id}" + fi + fi + git update-ref --no-deref refs/git-r3/"${local_id}"/{__old__,__main__} || die + + # recursively checkout submodules + if [[ -f ${out_dir}/.gitmodules && ! ${checkout_paths} ]]; then + local submodules + _git-r3-tor_set_submodules "${_GIT_SUBMODULE_PATH}" \ + "$(<"${out_dir}"/.gitmodules)" + + while [[ ${submodules[@]} ]]; do + local subname=${submodules[0]} + local url=${submodules[1]} + local path=${submodules[2]} + + # use only submodules for which path does exist + # (this is in par with 'git submodule'), bug #551100 + if [[ -d ${out_dir}/${path} ]]; then + local subrepos + _git-r3-tor_set_subrepos "${url}" "${repos[@]}" + + _GIT_SUBMODULE_PATH=${_GIT_SUBMODULE_PATH}${path}/ \ + git-r3-tor_checkout "${subrepos[*]}" "${out_dir}/${path}" \ + "${local_id}/${subname}" + fi + + submodules=( "${submodules[@]:3}" ) # shift + done + fi + + # keep this *after* submodules + export EGIT_DIR=${GIT_DIR} + export EGIT_VERSION=${new_commit_id} +} + +# @FUNCTION: git-r3-tor_peek_remote_ref +# @USAGE: [ []] +# @DESCRIPTION: +# Peek the reference in the remote repository and print the matching +# (newest) commit SHA1. +# +# specifies the repository URIs to fetch from, as a space- +# -separated list. When not specified, defaults to ${EGIT_REPO_URI}. +# +# specifies the remote ref to peek. It is preferred to use +# 'refs/heads/' for branches and 'refs/tags/' +# for tags. Alternatively, 'HEAD' may be used for upstream default +# branch. Defaults to the first of EGIT_COMMIT, EGIT_BRANCH or literal +# 'HEAD' that is set to a non-null value. +# +# The operation will be done purely on the remote, without using local +# storage. If commit SHA1 is provided as , the function will +# fail due to limitations of git protocol. +# +# On success, the function returns 0 and writes hexadecimal commit SHA1 +# to stdout. On failure, the function returns 1. +git-r3-tor_peek_remote_ref() { + debug-print-function ${FUNCNAME} "$@" + + local repos + if [[ ${1} ]]; then + repos=( ${1} ) + elif [[ $(declare -p EGIT_REPO_URI) == "declare -a"* ]]; then + repos=( "${EGIT_REPO_URI[@]}" ) + else + repos=( ${EGIT_REPO_URI} ) + fi + + local branch=${EGIT_BRANCH:+refs/heads/${EGIT_BRANCH}} + local remote_ref=${2:-${EGIT_COMMIT:-${branch:-HEAD}}} + + [[ ${repos[@]} ]] || die "No URI provided and EGIT_REPO_URI unset" + + local r success + for r in "${repos[@]}"; do + einfo "Peeking ${remote_ref} on ${r} ..." >&2 + + local lookup_ref + if [[ ${remote_ref} == refs/* || ${remote_ref} == HEAD ]] + then + lookup_ref=${remote_ref} + else + # ls-remote by commit is going to fail anyway, + # so we may as well pass refs/tags/ABCDEF... + lookup_ref=refs/tags/${remote_ref} + fi + + # split on whitespace + local ref=( + $(git ls-remote "${r}" "${lookup_ref}") + ) + + if [[ ${ref[0]} ]]; then + echo "${ref[0]}" + return 0 + fi + done + + return 1 +} + +git-r3-tor_src_fetch() { + debug-print-function ${FUNCNAME} "$@" + + if [[ ! ${EGIT3_STORE_DIR} && ${EGIT_STORE_DIR} ]]; then + ewarn "You have set EGIT_STORE_DIR but not EGIT3_STORE_DIR. Please consider" + ewarn "setting EGIT3_STORE_DIR for git-r3.eclass. It is recommended to use" + ewarn "a different directory than EGIT_STORE_DIR to ease removing old clones" + ewarn "when git-2 eclass becomes deprecated." + fi + + _git-r3-tor_env_setup + git-r3-tor_fetch +} + +git-r3-tor_src_unpack() { + debug-print-function ${FUNCNAME} "$@" + + _git-r3-tor_env_setup + git-r3-tor_src_fetch + git-r3-tor_checkout + + if [[ ! ${EGIT_LFS} && ${_EGIT_LFS_FILTERS_FOUND} ]]; then + eqawarn "QA Notice: There are Git LFS filters setup in the cloned repo, consider using EGIT_LFS!" + fi + if [[ ${EGIT_LFS} && ! ${_EGIT_LFS_FILTERS_FOUND} ]]; then + eqawarn "QA Notice: There are no Git LFS filters setup in the cloned repo. EGIT_LFS will do nothing!" + fi +} + +# https://bugs.gentoo.org/show_bug.cgi?id=482666 +git-r3-tor_pkg_needrebuild() { + debug-print-function ${FUNCNAME} "$@" + + local new_commit_id=$(git-r3-tor_peek_remote_ref) + [[ ${new_commit_id} && ${EGIT_VERSION} ]] || die "Lookup failed" + + if [[ ${EGIT_VERSION} != ${new_commit_id} ]]; then + einfo "Update from ${EGIT_VERSION} to ${new_commit_id}" + else + einfo "Local and remote at ${EGIT_VERSION}" + fi + + [[ ${EGIT_VERSION} != ${new_commit_id} ]] +} + +# 'export' locally until this gets into EAPI +pkg_needrebuild() { git-r3-tor_pkg_needrebuild; } + +fi + +EXPORT_FUNCTIONS src_unpack