flatcar-scripts/pkg_auto/impl/sync_with_gentoo.sh
Krzesimir Nowak 310a8716f4 pkg-auto: Fix syncing eclass files
eclass is a file, as opposed to the package, which is a directory. So
doing `git -C eclass/foo.eclass log -- .` will fail because we can't
do "cd eclass/foo.eclass", which is what `-C` is trying to do.
2025-01-22 13:16:06 +01:00

317 lines
7.9 KiB
Bash
Executable File

#!/bin/bash
##
## Used for syncing with gentoo. Needs to be called from the
## toplevel-directory of portage-stable. If syncing everything or
## syncing metadata/glsa specifically, it is expected that the Gentoo
## repo will have the GLSA files stored in metadata/glsa too.
##
## Parameters:
## -h: this help
## -b: be brief, print only names of changed entries and errors
## -s: skip adding source git commit hash information to commits
##
## Positional:
## 0: Gentoo repository
## #: Entries to update (can be a package name, eclass, category, some special
## directories like profiles or . for everything)
##
## Example invocations:
##
## sync_with_gentoo -h
##
## Print a help message.
##
## sync_with_gentoo dev-libs/nettle app-crypt/argon2
##
## This will update the packages, each in a separate commit. The
## commit message will contain the commit hash from gentoo repo.
##
## sync_with_gentoo dev-libs
##
## This will update all the packages in dev-libs category. The
## commit message will contain the commit hash from gentoo repo.
##
set -euo pipefail
source "$(dirname "${BASH_SOURCE[0]}")/util.sh"
BRIEF=
SKIP_GIT_INFO=
while true; do
case ${1-} in
-h)
print_help
exit 0
;;
-b)
BRIEF=x
shift
;;
-s)
SKIP_GIT_INFO=x
shift
;;
--)
shift
break
;;
-*)
fail "unknown flag '${1}'"
;;
*)
break
;;
esac
done
if [[ $# -lt 2 ]]; then
fail 'expected at least two positional parameters: a Gentoo repository and at least one package, use -h to print help'
fi
if [[ ! -e 'profiles/repo_name' ]]; then
fail 'sync is only possible from ebuild packages top-level directory (a directory from which "./profiles/repo_name" is accessible)'
fi
function vcall() {
if [[ -z ${BRIEF} ]]; then
"${@}"
fi
}
function bcall() {
if [[ -n ${BRIEF} ]]; then
"${@}"
fi
}
GENTOO=$(realpath "${1}"); shift
# rest are package names
if [[ $(realpath '.') = "${GENTOO}" ]]; then
fail 'trying to sync within a Gentoo repo?'
fi
if [[ -z ${SKIP_GIT_INFO} ]] && [[ ! -e ${GENTOO}/.git ]]; then
info "Skipping adding source git commit hash information to commits, ${GENTOO@Q} is not a git repository"
SKIP_GIT_INFO=x
fi
glsa_repo=${GENTOO}/metadata/glsa
if [[ -z ${SKIP_GIT_INFO} ]] && [[ -e ${glsa_repo} ]] && [[ ! -e ${glsa_repo}/.git ]] && [[ $(git -C "${GENTOO}" status --porcelain -- metadata/glsa) = '?? metadata/glsa' ]]; then
info "Skipping adding source git commit hash information to commits, ${glsa_repo@Q} exists, but it is not a git repository and is not a part of Gentoo git repository"
SKIP_GIT_INFO=x
fi
unset glsa_repo
# Synchronizes given path with its Gentoo counterpart. Returns true if
# there were changes.
#
# Params:
#
# 1 - path within ebuild repo
function sync_git_prepare() {
local path
path=${1}; shift
local gentoo_path
gentoo_path="${GENTOO}/${path}"
if [[ ! -e "${gentoo_path}" ]]; then
info "no ${path@Q} in Gentoo repository"
if [[ ${path} = 'metadata/glsa' ]]; then
info "did you forget to clone https://gitweb.gentoo.org/data/glsa.git/ into ${gentoo_path@Q}?"
fi
return 1
fi
local -a rsync_opts=( --archive --delete-before )
case ${path} in
profiles)
rsync_opts+=( --exclude /profiles/repo_name )
;;
esac
local parent
dirname_out "${path}" parent
mkdir --parents "${parent}"
rsync "${rsync_opts[@]}" "${gentoo_path}" "${parent}"
if [[ -n $(git status --porcelain -- "${path}") ]]; then
bcall info "updated ${path}"
git add "${path}"
return 0
fi
return 1
}
# Creates a git commit. If checking Gentoo commit ID is enabled the
# given path is used to get the ID of the commit with the last change
# in the path. Name parameter is used for denoting which part has
# changed, and sync parameter to denote if the commit is about adding
# new package or updating an existing one.
#
# Params:
#
# 1 - path
# 2 - name
# 3 - not empty if existing package was updated, or an empty string if
# the package is new
function commit_with_gentoo_sha() {
local path name sync
path=${1}; shift
name=${1}; shift
sync=${1:-}; shift
local -a commit_extra=()
if [[ -z ${SKIP_GIT_INFO} ]]; then
local commit
commit=$(git -C "${GENTOO}" log --pretty=oneline -1 -- "${path}" | cut -f1 -d' ')
commit_extra+=( --message "It's from Gentoo commit ${commit}." )
unset commit
fi
commit_msg="${name}: Add from Gentoo"
if [[ -n "${sync}" ]]; then
commit_msg="${name}: Sync with Gentoo"
fi
git commit --quiet --message "${commit_msg}" "${commit_extra[@]}"
GIT_PAGER='cat' vcall git show --stat
}
# Simple path sync and commit; takes the contents from Gentoo at the
# given path and puts it in the repo.
#
# 1 - path to sync
# 2 - name for commit message
function path_sync() {
local path name
path=${1}; shift
name=${1}; shift
local sync
sync=''
if [[ -e "${path}" ]]; then
sync='x'
fi
if sync_git_prepare "${path}"; then
commit_with_gentoo_sha "${path}" "${name}" "${sync}"
else
vcall info "no changes in ${path}"
fi
}
# Goes over the given directory and syncs its subdirectories or
# files. No commit is created.
function prepare_dir() {
local dir
dir=${1}; shift
local pkg mod=''
for pkg in "${dir}/"*; do
if sync_git_prepare "${pkg}"; then
mod=x
fi
done
if [[ -n ${mod} ]]; then
return 0
fi
return 1
}
# Synces entire category of packages and creates a commit. Note that
# if the category already exists, no new packages will be added.
#
# Params:
#
# 1 - path to the category directory
function category_sync() {
local path
path=${1}; shift
if [[ ! -e "${path}" ]]; then
if sync_git_prepare "${path}"; then
commit_with_gentoo_sha "${path}" "${path}"
fi
else
if prepare_dir "${path}"; then
commit_with_gentoo_sha "${path}" "${path}" 'x'
fi
fi
}
# Synces entire repo. No new packages will be added.
function everything_sync() {
local path mod
for path in *; do
case ${path} in
licenses|profiles|scripts)
if sync_git_prepare "${path}"; then
mod=x
fi
;;
metadata)
# do only metadata updates
if sync_git_prepare metadata/glsa; then
mod=x
fi
;;
eclass|virtual|*-*)
if prepare_dir "${path}"; then
mod=x
fi
;;
changelog|*.md)
# ignore those
:
;;
*)
info "Unknown entry ${path@Q}, ignoring"
;;
esac
done
if [[ -n ${mod} ]]; then
commit_with_gentoo_sha '.' '*' 'x'
fi
}
shopt -s extglob
for cpn; do
cpn=${cpn%%*(/)}
case ${cpn} in
.)
everything_sync
;;
licenses|profiles|scripts)
path_sync "${cpn}" "${cpn}"
;;
eclass/*.eclass)
path_sync "${cpn}" "${cpn%.eclass}"
;;
metadata/glsa)
path_sync "${cpn}" "${cpn}"
;;
metadata)
fail "metadata directory can't be synced, did you mean metadata/glsa?"
;;
virtual/*/*|*-*/*/*)
fail "invalid thing to sync: ${cpn}"
;;
virtual/*|*-*/*)
path_sync "${cpn}" "${cpn}"
;;
eclass|virtual|*-*)
category_sync "${cpn}"
;;
*)
fail "invalid thing to sync: ${cpn}"
;;
esac
done