flatcar-scripts/pkg_auto/impl/pkg_auto_lib.sh
Krzesimir Nowak 4bb175152f pkg-auto: Mark in-rootfs sysexts as sysexts in reports
They were showing up as "docker" or "containerd" in the reports, which
is confusing. "sysext-docker" or "sysext-containerd" makes it clear.
2025-04-30 12:45:01 +02:00

3149 lines
106 KiB
Bash

#!/bin/bash
#
# TODO:
#
# - Generate a report about missing build deps of board packages in
# sdk. These reports can be generated by processing sdk-pkgs-kv and
# board-bdeps reports, I think.
#
# - Mount Gentoo repo into the SDK container and set up emerge to use
# Gentoo as a primary repo, and portage-stable and coreos-overlay as
# overlays. That way if an updated package pulls in a new package we
# can notice it when it comes from Gentoo (emerge reports also
# source repo like sys-libs/glibc-2.35-r5::gentoo or something like
# this). This would make this script more robust.
#
# - Instead of having a list of packages to update, rather update them
# all in a single commit and have a list of exclusions. The reason
# is that, at this point, almost all of the packages in
# portage-stable are automatically updated, exclusions being usually
# temporary, so it would be better to have a short file with
# temporary exclusions rather than a long file with some commented
# out entries. This probably would render the sort_packages_list.py
# script unnecessary. On the other hand, the current mode of
# operation may be still useful for the coreos-overlay packages,
# because none of them are under automation (some are, though, under
# an ad-hoc automation via github actions).
#
# - Handle package appearance or disappearance. Currently, when a
# package ends up being unused (so it exists, but is not picked up,
# because some other package stopped depending on it) or removed,
# the package ends up in the manual-work-needed file. This probably
# could be handled as an entry in the summary stubs about being
# dropped.
#
# - Find unused packages and eclasses.
#
# - The rename handling should probably also change all instances of
# the old name everywhere outside portage-stable, otherwise emerge
# may fail when some our ebuild still uses the old name, maybe.
#
# Needed to be enabled here to parse some globs inside the functions.
shopt -s extglob
# Saner defaults.
shopt -s nullglob
shopt -s dotglob
if [[ -z ${__PKG_AUTO_LIB_SH_INCLUDED__:-} ]]; then
__PKG_AUTO_LIB_SH_INCLUDED__=x
source "$(dirname "${BASH_SOURCE[0]}")/util.sh"
source "${PKG_AUTO_IMPL_DIR}/cleanups.sh"
source "${PKG_AUTO_IMPL_DIR}/debug.sh"
source "${PKG_AUTO_IMPL_DIR}/gentoo_ver.sh"
source "${PKG_AUTO_IMPL_DIR}/md5_cache_diff_lib.sh"
# Sets up the workdir using the passed config. The config can be
# created basing on the config_template file or using the
# generate_config script.
#
# The path to the workdir can be empty - the function will then create
# a temporary directory.
#
# This also sets the WORKDIR global variable, allowing other function
# to be invoked.
#
# Params:
#
# 1 - path to the workdir
# 2 - path to the config file
function setup_workdir_with_config() {
local workdir file
workdir=${1}; shift
config_file=${1}; shift
local cfg_scripts cfg_aux cfg_reports cfg_old_base cfg_new_base cfg_sdk_image_override
local -a cfg_cleanups cfg_debug_packages
# some defaults
cfg_old_base='origin/main'
cfg_new_base=''
cfg_cleanups=('ignore')
cfg_sdk_image_override=''
cfg_debug_packages=()
local line key value swwc_stripped var_name arch
while read -r line; do
strip_out "${line%%:*}" swwc_stripped
key=${swwc_stripped}
strip_out "${line#*:}" swwc_stripped
value=${swwc_stripped}
if [[ -z ${value} ]]; then
fail "empty value for ${key} in config"
fi
case ${key} in
scripts|aux|reports)
var_name="cfg_${key//-/_}"
local -n var_ref=${var_name}
var_ref=$(realpath "${value}")
unset -n var_ref
;;
old-base|new-base|sdk-image-override)
var_name="cfg_${key//-/_}"
local -n var_ref=${var_name}
var_ref=${value}
unset -n var_ref
;;
cleanups|debug-packages)
var_name="cfg_${key//-/_}"
mapfile -t "${var_name}" <<<"${value//,/$'\n'}"
;;
esac
done < <(cat_meaningful "${config_file}")
if [[ -z "${cfg_new_base}" ]]; then
cfg_new_base=${cfg_old_base}
fi
for key in scripts aux reports; do
var_name="cfg_${key//-/_}"
if [[ -z "${!var_name}" ]]; then
fail "${key} was not specified in config"
fi
done
setup_cleanups "${cfg_cleanups[@]}"
setup_workdir "${workdir}"
add_cleanup "rm -f ${WORKDIR@Q}/config"
cp -a "${config_file}" "${WORKDIR}/config"
setup_worktrees_in_workdir "${cfg_scripts}" "${cfg_old_base}" "${cfg_new_base}" "${cfg_reports}" "${cfg_aux}"
if [[ -n ${cfg_sdk_image_override} ]]; then
override_sdk_image_name "${cfg_sdk_image_override}"
fi
pkg_debug_add "${cfg_debug_packages[@]}"
}
# Goes over the list of automatically updated packages and synces them
# with packages from Gentoo repo. Cleans up missing packages.
#
# The function can only be called after the workdir has been set up
# with setup_workdir_with_config.
#
# Params:
#
# 1 - a path to the Gentoo repo
function perform_sync_with_gentoo() {
local gentoo
gentoo=$(realpath "${1}"); shift
run_sync "${gentoo}"
handle_missing_in_scripts
handle_missing_in_gentoo "${gentoo}"
}
# Generates package update reports. Duh.
#
# The function can only be called after the workdir has been set up
# with setup_workdir_with_config.
#
# The location of the reports is specified in the config that was
# passed to setup_workdir_with_config.
function generate_package_update_reports() {
generate_sdk_reports
handle_gentoo_sync
}
# Saves the new state to a git branch in scripts.
#
# The function can only be called after the workdir has been set up
# with setup_workdir_with_config.
#
# Params:
#
# 1 - name of the new branch
function save_new_state() {
local branch_name
branch_name=${1}; shift
# shellcheck source=for-shellcheck/globals
source "${WORKDIR}/globals"
info "saving new state to branch ${branch_name}"
git -C "${SCRIPTS}" branch --force "${branch_name}" "${NEW_STATE_BRANCH}"
}
#
#
# Implementation details, do not use directly in scripts sourcing this file.
#
#
# Creates a workdir, the path to which is stored in WORKDIR global
# variable.
#
# 1 - predefined work directory path (optional)
function setup_workdir() {
local workdir
workdir=${1:-}
if [[ -z "${workdir}" ]]; then
workdir=$(mktemp --tmpdir --directory 'pkg-auto-workdir.XXXXXXXX')
else
if [[ -e ${workdir} ]] && [[ ! -d ${workdir} ]]; then
fail "Expected ${workdir@Q} to be a directory"
fi
if [[ -d ${workdir} ]] && ! dir_is_empty "${workdir}"; then
fail "Expected ${workdir@Q} to be an empty directory"
fi
fi
declare -g WORKDIR
WORKDIR=$(realpath "${workdir}")
add_cleanup "rmdir ${WORKDIR@Q}"
mkdir -p "${WORKDIR}"
setup_initial_globals_file
}
# Sets up worktrees for the old and new state inside WORKDIR. Creates
# the globals file inside WORKDIR.
#
# 1 - path to scripts repo
# 2 - base for the old state worktree (e.g. origin/main)
# 3 - base for the new state worktree (e.g. origin/main)
# 4 - path to reports directory
function setup_worktrees_in_workdir() {
local scripts old_base new_base reports_dir aux_dir
scripts=${1}; shift
old_base=${1}; shift
new_base=${1}; shift
reports_dir=${1}; shift
aux_dir=${1}; shift
local old_state new_state
old_state="${WORKDIR}/old_state"
new_state="${WORKDIR}/new_state"
# create reports directory now - there may be some developer
# warnings afoot
mkdir -p "${reports_dir}"
setup_worktree "${scripts}" "${old_base}" "old-state-${RANDOM}" "${old_state}"
setup_worktree "${scripts}" "${new_base}" "new-state-${RANDOM}" "${new_state}"
extend_globals_file "${scripts}" "${old_state}" "${new_state}" "${reports_dir}" "${aux_dir}"
}
# Adds an overridden SDK image name to the globals file.
#
# Params:
#
# 1 - image name
function override_sdk_image_name() {
local image_name=${1}; shift
append_to_globals "SDK_IMAGE=${image_name@Q}"
}
# Appends passed lines to the globals file.
#
# Params:
#
# @ - lines to append
function append_to_globals() {
local globals_file
globals_file="${WORKDIR}/globals"
if [[ ! -e "${globals_file}" ]]; then
fail "globals not set yet in workdir"
fi
lines_to_file "${globals_file}" "${@}"
}
# Processes the update files in portage-stable and coreos-overlay to
# figure out potential package renames. The results are stored in the
# passed map.
#
# Params:
#
# 1 - name of a map variable; will be a mapping of old package name to
# new package name
function process_profile_updates_directory() {
local from_to_map_var_name=${1}; shift
local -a ppud_ordered_names
get_ordered_update_filenames ppud_ordered_names
# shellcheck source=for-shellcheck/globals
source "${WORKDIR}/globals"
local bf ps_f co_f pkg f line old new
local -a fields
local -A from_to_f=()
mvm_declare ppud_to_from_set_mvm mvm_mvc_set
for bf in "${ppud_ordered_names[@]}"; do
# coreos-overlay updates may overwrite updates from
# portage-stable, but only from the file of the same name
ps_f=${NEW_PORTAGE_STABLE}/profiles/updates/${bf}
co_f=${NEW_COREOS_OVERLAY}/profiles/updates/${bf}
for f in "${ps_f}" "${co_f}"; do
if [[ ! -f ${f} ]]; then
continue
fi
while read -r line; do
if [[ ${line} != 'move '* ]]; then
# other possibility is "slotmove" - we don't care
# about those.
continue
fi
mapfile -t fields <<<"${line// /$'\n'}"
if [[ ${#fields[@]} -ne 3 ]]; then
fail_lines \
"Malformed line ${line@Q} in updates file ${f@Q}." \
"The line should have 3 fields, has ${#fields[*]}."
fi
from_to_f["${fields[1]}"]=${fields[2]}
done <"${f}"
done
for old in "${!from_to_f[@]}"; do
new=${from_to_f["${old}"]}
update_rename_maps "${from_to_map_var_name}" ppud_to_from_set_mvm "${old}" "${new}"
done
done
mvm_unset ppud_to_from_set_mvm
}
# Gets a merged and ordered list of update files from portage-stable
# and coreos-overlay and stores the in the passed array. The files
# have names like Q1-2018, Q1-2023, Q2-2019 and so on. We need to sort
# them by year, then by quarter.
#
# Params:
#
# 1 - name of a array variable where the ordered names will be stored
function get_ordered_update_filenames() {
local ordered_names_var_name=${1}; shift
# shellcheck source=for-shellcheck/globals
source "${WORKDIR}/globals"
local -A names_set=()
local f
for f in "${NEW_PORTAGE_STABLE}/profiles/updates/"* "${NEW_COREOS_OVERLAY}/profiles/updates/"*; do
names_set["${f##*/}"]=x
done
mapfile -t "${ordered_names_var_name}" < <(printf '%s\n' "${!names_set[@]}" | sort --field-separator=- --key=2n --key=1n)
}
# Updates the rename map with the new "old to new package rename". It
# tries to be smart about the rename sequences (though not sure if
# this is necessary, really). If in older update file package foo was
# renamed to bar and in current update file the bar package is renamed
# to quux, then this function adds an entry about the "bar to quux"
# rename, but also updates the older entry about "foo to bar" rename
# to "foo to quux" rename.
#
# Params:
#
# 1 - name of the renames map variable; should be a mapping of old to
# new names
# 2 - name of the set mvm variable; should be a mapping of new name to
# a set of old names (a reverse mapping to the renames map)
# 3 - old name
# 4 - new name
function update_rename_maps() {
local -n ft_map_ref=${1}; shift
local tf_set_mvm_var_name=${1}; shift
local old_name=${1}; shift
local new_name=${1}; shift
local prev_new_name=${ft_map_ref["${old_name}"]:-}
if [[ -n ${prev_new_name} ]] && [[ ${prev_new_name} != "${new_name}" ]]; then
fail_lines \
"Invalid package rename from ${old_name@Q} to ${new_name@Q}." \
"There was already a rename from ${old_name@Q} to ${prev_new_name@Q}."
fi
local -a new_set=()
local urm_set_var_name
mvm_get "${tf_set_mvm_var_name}" "${old_name}" urm_set_var_name
if [[ -n ${urm_set_var_name} ]]; then
local -n old_set_ref=${urm_set_var_name}
new_set+=( "${!old_set_ref[@]}" )
unset -n old_set_ref
fi
new_set+=( "${old_name}" )
mvm_add "${tf_set_mvm_var_name}" "${new_name}" "${new_set[@]}"
local old
for old in "${new_set[@]}"; do
ft_map_ref["${old}"]=${new_name}
done
unset -n ft_map_ref
}
# Sets up a worktree and necessary cleanups.
#
# Params:
#
# 1 - path to the git repo
# 2 - name of a branch to be used as a base of a new worktree branch
# 3 - name of the new worktree branch
# 4 - path where the new worktree will be created
function setup_worktree() {
local repo base branch worktree_dir
repo=${1}; shift
base=${1}; shift
branch=${1}; shift
worktree_dir=${1}; shift
add_cleanup \
"git -C ${worktree_dir@Q} reset --hard HEAD" \
"git -C ${worktree_dir@Q} clean -ffdx" \
"git -C ${repo@Q} worktree remove ${worktree_dir@Q}" \
"git -C ${repo@Q} branch -D ${branch@Q}"
git -C "${repo}" worktree add -b "${branch}" "${worktree_dir}" "${base}"
}
# Creates an initial globals file. It's initial because it contains
# data known up-front, so mostly things that are defined in one place
# to avoid repeating them everywhere.
#
# More stuff will be added later to the globals file based on config
# or else.
function setup_initial_globals_file() {
local sync_script pkg_list_sort_script
sync_script="${PKG_AUTO_IMPL_DIR}/sync_with_gentoo.sh"
pkg_list_sort_script="${PKG_AUTO_IMPL_DIR}/sort_packages_list.py"
local globals_file
globals_file="${WORKDIR}/globals"
local -a sigf_arches
get_valid_arches sigf_arches
add_cleanup "rm -f ${globals_file@Q}"
cat <<EOF >"${globals_file}"
local -a GIT_ENV_VARS ARCHES WHICH REPORTS
local SDK_PKGS BOARD_PKGS
local SYNC_SCRIPT PKG_LIST_SORT_SCRIPT
GIT_ENV_VARS=(
GIT_{AUTHOR,COMMITTER}_{NAME,EMAIL}
)
SYNC_SCRIPT=${sync_script@Q}
PKG_LIST_SORT_SCRIPT=${pkg_list_sort_script@Q}
ARCHES=( ${sigf_arches[*]@Q} )
WHICH=('old' 'new')
SDK_PKGS='sdk-pkgs'
BOARD_PKGS='board-pkgs'
REPORTS=( "\${SDK_PKGS}" "\${BOARD_PKGS}" )
EOF
}
# Extend the globals file with information from config and other
# information derived from it.
#
# Params:
#
# 1 - path to scripts repository
# 2 - path to scripts worktree with old state
# 3 - path to scripts worktree with new state
# 4 - path to reports directory
# 5 - path to aux directory
function extend_globals_file() {
local scripts old_state new_state reports_dir aux_dir
scripts=${1}; shift
old_state=${1}; shift
new_state=${1}; shift
reports_dir=${1}; shift
aux_dir=${1}; shift
local globals_file
globals_file="${WORKDIR}/globals"
if [[ ! -e "${globals_file}" ]]; then
fail 'an initial version of globals file should already exist'
fi
local old_state_branch new_state_branch
old_state_branch=$(git -C "${old_state}" rev-parse --abbrev-ref HEAD)
new_state_branch=$(git -C "${new_state}" rev-parse --abbrev-ref HEAD)
local portage_stable_suffix old_portage_stable new_portage_stable
portage_stable_suffix='sdk_container/src/third_party/portage-stable'
old_portage_stable="${old_state}/${portage_stable_suffix}"
new_portage_stable="${new_state}/${portage_stable_suffix}"
local coreos_overlay_suffix old_coreos_overlay new_coreos_overlay
coreos_overlay_suffix='sdk_container/src/third_party/coreos-overlay'
old_coreos_overlay="${old_state}/${coreos_overlay_suffix}"
new_coreos_overlay="${new_state}/${coreos_overlay_suffix}"
cat <<EOF >>"${globals_file}"
local SCRIPTS OLD_STATE NEW_STATE OLD_STATE_BRANCH NEW_STATE_BRANCH
local PORTAGE_STABLE_SUFFIX OLD_PORTAGE_STABLE NEW_PORTAGE_STABLE REPORTS_DIR
local NEW_STATE_PACKAGES_LIST AUX_DIR
local COREOS_OVERLAY_SUFFIX OLD_COREOS_OVERLAY NEW_COREOS_OVERLAY
SCRIPTS=${scripts@Q}
OLD_STATE=${old_state@Q}
NEW_STATE=${new_state@Q}
OLD_STATE_BRANCH=${old_state_branch@Q}
NEW_STATE_BRANCH=${new_state_branch@Q}
PORTAGE_STABLE_SUFFIX=${portage_stable_suffix@Q}
OLD_PORTAGE_STABLE=${old_portage_stable@Q}
NEW_PORTAGE_STABLE=${new_portage_stable@Q}
REPORTS_DIR=${reports_dir@Q}
COREOS_OVERLAY_SUFFIX=${coreos_overlay_suffix@Q}
OLD_COREOS_OVERLAY=${old_coreos_overlay@Q}
NEW_COREOS_OVERLAY=${new_coreos_overlay@Q}
NEW_STATE_PACKAGES_LIST="\${NEW_STATE}/.github/workflows/portage-stable-packages-list"
AUX_DIR=${aux_dir@Q}
EOF
# shellcheck source=for-shellcheck/globals
source "${globals_file}"
local last_nightly_version_id last_nightly_build_id
# shellcheck source=for-shellcheck/version.txt
last_nightly_version_id=$(source "${NEW_STATE}/sdk_container/.repo/manifests/version.txt"; printf '%s' "${FLATCAR_VERSION_ID}")
# shellcheck source=for-shellcheck/version.txt
last_nightly_build_id=$(source "${NEW_STATE}/sdk_container/.repo/manifests/version.txt"; printf '%s' "${FLATCAR_BUILD_ID}")
local -a locals=() definitions=()
local sdk_image_name sdk_image_var_name=SDK_IMAGE
sdk_image_name="ghcr.io/flatcar/flatcar-sdk-all:${last_nightly_version_id}-${last_nightly_build_id}"
locals+=( "${sdk_image_var_name@Q}" )
definitions+=( "${sdk_image_var_name}=${sdk_image_name@Q}" )
append_to_globals \
'' \
"local ${locals[*]}" \
'' \
"${definitions[@]}"
local -A listing_kinds
local packages_file tag filename stripped old
for arch in "${ARCHES[@]}"; do
for packages_file in "${AUX_DIR}/${arch}/"*_packages.txt; do
filename=${packages_file##*/}
stripped=${filename%_packages.txt}
case ${stripped} in
'flatcar_developer_container')
tag='dev'
;;
'flatcar_production_image')
tag='prod'
;;
'flatcar-'*)
tag="sysext-${stripped#flatcar-}"
;;
'oem-'*)
tag=${stripped#oem-}
;;
*'-flatcar')
tag="sysext-${stripped%-flatcar}"
;;
*)
devel_warn "Unknown listing file ${packages_file@Q}"
continue
;;
esac
old=${listing_kinds["${tag}"]:-}
if [[ -n ${old} ]]; then
if [[ ${old} != "${filename}" ]]; then
devel_warn "Two different packages files (${old@Q} and ${filename@Q} for a single tag ${tag@Q}"
fi
else
listing_kinds["${tag}"]=${filename}
fi
done
done
local -a sorted_tags sorted_lines
mapfile -t sorted_tags < <(printf '%s\n' "${!listing_kinds[@]}" | sort)
for tag in "${sorted_tags[@]}"; do
filename=${listing_kinds["${tag}"]}
sorted_lines+=(" [${tag@Q}]=${filename@Q}")
done
append_to_globals \
'' \
'local -A LISTING_KINDS' \
'' \
'LISTING_KINDS=(' \
"${sorted_lines[@]}" \
')'
}
# Sets up environment variables for some git commands.
#
# Make sure to call the following beforehand:
#
# local -x "${GIT_ENV_VARS[@]}"
#
# The GIT_ENV_VARS array comes from the globals file.
function setup_git_env() {
local bot_name bot_email role what
bot_name='Flatcar Buildbot'
bot_email='buildbot@flatcar-linux.org'
for role in AUTHOR COMMITTER; do
for what in name email; do
local -n var_ref="GIT_${role}_${what^^}"
local -n value_ref="bot_${what}"
var_ref=${value_ref}
unset -n value_ref
unset -n var_ref
done
done
}
# Goes over the packages list and synces them with the passed Gentoo
# repo.
#
# Params:
#
# 1 - path to the Gentoo repo
function run_sync() {
local gentoo
gentoo=${1}; shift
local -a missing_in_scripts missing_in_gentoo
missing_in_scripts=()
missing_in_gentoo=()
# shellcheck source=for-shellcheck/globals
source "${WORKDIR}/globals"
local -x "${GIT_ENV_VARS[@]}"
setup_git_env
local -a packages_to_update
packages_to_update=()
local package
while read -r package; do
if [[ ! -e "${NEW_PORTAGE_STABLE}/${package}" ]]; then
# If this happens, it means that the package was moved to overlay
# or dropped, the list ought to be updated.
missing_in_scripts+=("${package}")
continue
fi
if [[ ! -e "${gentoo}/${package}" ]]; then
# If this happens, it means that the package was obsoleted or moved
# in Gentoo. The obsoletion needs to be handled in the case-by-case
# manner, while move should be handled by doing the same move
# in portage-stable. The build should not break because of the move,
# because most likely it's already reflected in the profiles/updates
# directory.
missing_in_gentoo+=("${package}")
continue
fi
packages_to_update+=( "${package}" )
done < <(cat_meaningful "${NEW_STATE_PACKAGES_LIST}")
env --chdir="${NEW_PORTAGE_STABLE}" "${SYNC_SCRIPT}" -b -- "${gentoo}" "${packages_to_update[@]}"
save_missing_in_scripts "${missing_in_scripts[@]}"
save_missing_in_gentoo "${missing_in_gentoo[@]}"
}
# A helper function that prints the contents of a file skipping empty
# lines and lines starting with a hash.
#
# Params:
#
# 1 - path to a file to print
function cat_meaningful() {
local file
file=${1}; shift
xgrep '^[^#]' "${file}"
}
# Saves a list of package names to a file and adds a cleanup for
# it. The names can be loaded again with load_simple_package_list.
#
# Params:
#
# 1 - path to a file where package names will be stored
# @ - the package names
function save_simple_package_list() {
local file
file=${1}; shift
# rest are packages
add_cleanup "rm -f ${file@Q}"
if [[ ${#} -eq 0 ]]; then
truncate --size=0 "${file}"
else
printf '%s\n' "${@}" >"${file}"
fi
}
# Loads a list of packages saved previously with
# save_simple_package_list.
#
# Params:
#
# 1 - path to a file where packages were stored
# 2 - name of an array variable; will contain package names
function load_simple_package_list() {
local file packages_var_name
file=${1}; shift
packages_var_name=${1}; shift
mapfile -t "${packages_var_name}" <"${file}"
}
# General function for saving missing packages. Takes care of creating
# a directory for the listing.
#
# Params:
#
# 1 - path to a directory which will contain the listing
# 2 - name of the listing file
function save_missing_packages() {
local dir file
dir=${1}; shift
file=${1}; shift
create_cleanup_dir "${dir}"
save_simple_package_list "${dir}/${file}" "${@}"
}
# Creates a directory and adds a cleanup if the directory was missing.
#
# Params:
#
# 1 - path to the directory
function create_cleanup_dir() {
local dir
dir=${1}; shift
if [[ ! -d "${dir}" ]]; then
add_cleanup "rmdir ${dir@Q}"
mkdir "${dir}"
fi
}
# Saves a list of package names that we missing in scripts repo (which
# means that we were asked to sync a package that isn't in scripts to
# begin with).
#
# Params:
#
# @ - package names
function save_missing_in_scripts() {
save_missing_packages "${WORKDIR}/missing_in_scripts" "saved_list" "${@}"
}
# Saves a list of package names that we missing in Gentoo repo (which
# means that we were asked to sync a possibly obsolete or renamed
# package).
#
# Params:
#
# @ - package names
function save_missing_in_gentoo() {
save_missing_packages "${WORKDIR}/missing_in_gentoo" "saved_list" "${@}"
}
# Loads a list of package names that were missing in scripts repo.
#
# Params:
#
# 1 - name of an array variable; will contain package names
function load_missing_in_scripts() {
local packages_var_name
packages_var_name=${1}; shift
load_simple_package_list "${WORKDIR}/missing_in_scripts/saved_list" "${packages_var_name}"
}
# Loads a list of package names that were missing in Gentoo repo.
#
# Params:
#
# 1 - name of an array variable; will contain package names
function load_missing_in_gentoo() {
local packages_var_name
packages_var_name=${1}; shift
load_simple_package_list "${WORKDIR}/missing_in_gentoo/saved_list" "${packages_var_name}"
}
# Handles package names that were missing in scripts by dropping them
# from the listing of packages that should be updated automatically.
function handle_missing_in_scripts() {
local -a hmis_missing_in_scripts
hmis_missing_in_scripts=()
load_missing_in_scripts hmis_missing_in_scripts
# shellcheck source=for-shellcheck/globals
source "${WORKDIR}/globals"
if [[ ${#hmis_missing_in_scripts[@]} -eq 0 ]]; then
return 0;
fi
# Remove missing in scripts entries from package automation
local dir
dir="${WORKDIR}/missing_in_scripts"
create_cleanup_dir "${dir}"
local missing_re
join_by missing_re '\|' "${missing_in_scripts[@]}"
add_cleanup "rm -f ${dir@Q}/pkg_list"
xgrep --invert-match --line-regexp --fixed-strings --regexp="${missing_re}" "${NEW_STATE_PACKAGES_LIST}" >"${dir}/pkg_list"
"${PKG_LIST_SORT_SCRIPT}" "${dir}/pkg_list" >"${NEW_STATE_PACKAGES_LIST}"
local -x "${GIT_ENV_VARS[@]}"
setup_git_env
git -C "${NEW_STATE}" add "${NEW_STATE_PACKAGES_LIST}"
git -C "${NEW_STATE}" commit --quiet --message '.github: Drop missing packages from automation'
info_lines 'dropped missing packages from automation' "${missing_in_scripts[@]/#/- }"
}
# Helper function to print lines to a file clobbering the old
# contents.
#
# Params:
#
# 1 - path to the file
# @ - lines to print
function lines_to_file_truncate() {
truncate --size=0 "${1}"
lines_to_file "${@}"
}
# Helper function to append lines to a file.
#
# Params:
#
# 1 - path to the file
# @ - lines to print
function lines_to_file() {
printf '%s\n' "${@:2}" >>"${1}"
}
# Adds lines to "manual work needed" file in reports.
#
# Params:
#
# @ - lines to add
function manual() {
# shellcheck source=for-shellcheck/globals
source "${WORKDIR}/globals"
pkg_debug_lines 'manual work needed:' "${@}"
lines_to_file "${REPORTS_DIR}/manual-work-needed" "${@}"
}
# Adds lines to "warnings" file in reports. Should be used to report
# some issues with the processed packages.
#
# Params:
#
# @ - lines to add
function pkg_warn() {
# shellcheck source=for-shellcheck/globals
source "${WORKDIR}/globals"
pkg_debug_lines 'pkg warn:' "${@}"
lines_to_file "${REPORTS_DIR}/warnings" "${@}"
}
# Adds lines to "developer warnings" file in reports. Should be used
# to report some failed assumption in the automation, or bugs.
#
# Params:
#
# @ - lines to add
function devel_warn() {
# shellcheck source=for-shellcheck/globals
source "${WORKDIR}/globals"
pkg_debug_lines 'developer warn:' "${@}"
lines_to_file "${REPORTS_DIR}/developer-warnings" "${@}"
}
# Handles package names that were missing from Gentoo by either
# renaming and syncing them if a rename exists or by adding the
# package to the "manual work needed" file.
function handle_missing_in_gentoo() {
local gentoo
gentoo=${1}; shift
local -a hmig_missing_in_gentoo
hmig_missing_in_gentoo=()
load_missing_in_gentoo hmig_missing_in_gentoo
if [[ ${#hmig_missing_in_gentoo[@]} -eq 0 ]]; then
return 0;
fi
# shellcheck source=for-shellcheck/globals
source "${WORKDIR}/globals"
local -A hmig_rename_map=()
process_profile_updates_directory hmig_rename_map
local -a renamed_from renamed_to
renamed_from=()
renamed_to=()
local -x "${GIT_ENV_VARS[@]}"
setup_git_env
local missing new_name hmig_old_basename hmig_new_basename ebuild ebuild_version_ext new_ebuild_filename
for missing in "${hmig_missing_in_gentoo[@]}"; do
new_name=${hmig_rename_map["${missing}"]:-}
if [[ -z "${new_name}" ]]; then
manual "- package ${missing} is gone from Gentoo and no rename found"
continue
fi
mkdir -p "${NEW_PORTAGE_STABLE}/${new_name%/*}"
git -C "${NEW_STATE}" mv "${NEW_PORTAGE_STABLE}/${missing}" "${NEW_PORTAGE_STABLE}/${new_name}"
basename_out "${missing}" hmig_old_basename
basename_out "${new_name}" hmig_new_basename
if [[ "${hmig_old_basename}" != "${hmig_new_basename}" ]]; then
for ebuild in "${NEW_PORTAGE_STABLE}/${new_name}/${hmig_old_basename}-"*'.ebuild'; do
# 1.2.3-r4.ebuild
ebuild_version_ext=${ebuild##*/"${hmig_old_basename}-"}
new_ebuild_filename="${hmig_new_basename}-${ebuild_version_ext}"
git -C "${NEW_STATE}" mv "${ebuild}" "${NEW_PORTAGE_STABLE}/${new_name}/${new_ebuild_filename}"
done
fi
git -C "${NEW_STATE}" commit --quiet --message "${new_name}: Renamed from ${missing}"
info "renamed ${missing} to ${new_name}"
renamed_from+=("${missing}")
renamed_to+=("${new_name}")
done
if [[ ${#renamed_from[@]} -eq 0 ]]; then
return 0
fi
env --chdir="${NEW_PORTAGE_STABLE}" "${SYNC_SCRIPT}" -b -- "${gentoo}" "${renamed_to[@]}"
local dir renamed_re
dir="${WORKDIR}/missing_in_gentoo"
create_cleanup_dir "${dir}"
join_by renamed_re '\|' "${renamed_from[@]}"
add_cleanup "rm -f ${dir@Q}/pkg_list"
{
xgrep --invert-match --line-regexp --regexp="${renamed_re}" "${NEW_STATE_PACKAGES_LIST}"
printf '%s\n' "${renamed_to[@]}"
} >"${dir}/pkg_list"
"${PKG_LIST_SORT_SCRIPT}" "${dir}/pkg_list" >"${NEW_STATE_PACKAGES_LIST}"
git -C "${NEW_STATE}" add "${NEW_STATE_PACKAGES_LIST}"
git -C "${NEW_STATE}" commit --quiet --message '.github: Update package names in automation'
info 'updated packages names in automation'
}
# Process the package listings stored in the aux directory to find out
# the package tags that describe the kind of image the package is used
# in (base image, developer container, sysext image, etc.)
#
# Params:
#
# 1 - a name to an array mvm variable; will be a mapping of a package
# name to an array of tags
function process_listings() {
local pkg_to_tags_mvm_var_name
pkg_to_tags_mvm_var_name=${1}
# shellcheck source=for-shellcheck/globals
source "${WORKDIR}/globals"
#mvm_debug_enable pl_pkg_to_tags_set_mvm
mvm_declare pl_pkg_to_tags_set_mvm mvm_mvc_set
local arch kind file listing pkg
for arch in "${ARCHES[@]}"; do
for kind in "${!LISTING_KINDS[@]}"; do
file=${LISTING_KINDS["${kind}"]}
listing="${AUX_DIR}/${arch}/${file}"
if [[ ! -e "${listing}" ]]; then
# some listings are arch-specific, so they will be
# missing for other arches
continue
fi
# lines are like as follows:
#
# acct-group/adm-0-r2::portage-stable
while read -r pkg; do
pkg_debug_enable "${pkg}"
pkg_debug "processing listing ${arch}/${file}: adding tag ${kind^^}"
pkg_debug_disable
mvm_add pl_pkg_to_tags_set_mvm "${pkg}" "${kind^^}"
# VER_ERE_UNBOUNDED and PKG_ERE_UNBOUNDED come from gentoo_ver.sh
done < <(sed -E -e 's#^('"${PKG_ERE_UNBOUNDED}"')-'"${VER_ERE_UNBOUNDED}"'::.*#\1#' "${listing}")
done
done
mvm_iterate pl_pkg_to_tags_set_mvm set_mvm_to_array_mvm_cb "${pkg_to_tags_mvm_var_name}"
mvm_unset pl_pkg_to_tags_set_mvm
#mvm_debug_disable pl_pkg_to_tags_set_mvm
local -a pl_debug_pkgs pl_tags_array_name
pkg_debug_packages pl_debug_pkgs
for pkg in "${pl_debug_pkgs[@]}"; do
mvm_get "${pkg_to_tags_mvm_var_name}" "${pkg}" pl_tags_array_name
local -n tags_ref=${pl_tags_array_name:-EMPTY_ARRAY}
pkg_debug_print_c "${pkg}" "tags stored in ${pkg_to_tags_mvm_var_name}: ${tags_ref[*]}"
unset -n tags_ref
done
}
# A callback to mvm_iterate that turns a set mvm to an array mvm. It
# makes sure that the tag for the production image (or base image) is
# always first in the array.
#
# Params:
#
# 1 - name of the array mvm variable that will be filled (extra arg of
# the callback)
# 2 - name of the package
# 3 - name of the set variable holding tags
# @ - tags
function set_mvm_to_array_mvm_cb() {
local pkg_to_tags_mvm_var_name pkg
pkg_to_tags_mvm_var_name=${1}; shift
pkg=${1}; shift
local -n set_ref=${1}; shift
# rest are set items
local removed
removed=''
local -a prod_item
prod_item=()
if [[ -n ${set_ref['PROD']:-} ]]; then
prod_item+=('PROD')
unset "set_ref['PROD']"
removed=x
fi
local -a sorted_items
mapfile -t sorted_items < <(printf '%s\n' "${!set_ref[@]}" | sort)
if [[ -n ${removed} ]]; then
set_ref['PROD']=x
fi
mvm_add "${pkg_to_tags_mvm_var_name}" "${pkg}" "${prod_item[@]}" "${sorted_items[@]}"
}
# Generate package reports inside SDKs for all arches and states. In
# case of failure, whatever reports where generated so far will be
# stored in salvaged-reports subdirectory of the reports directory.
# Otherwise they will end up in reports-from-sdk subdirectory.
function generate_sdk_reports() {
# shellcheck source=for-shellcheck/globals
source "${WORKDIR}/globals"
add_cleanup "rmdir ${WORKDIR@Q}/pkg-reports"
mkdir "${WORKDIR}/pkg-reports"
if ! docker images --format '{{.Repository}}:{{.Tag}}' | grep --quiet --line-regexp --fixed-strings "${SDK_IMAGE}"; then
fail "No SDK image named ${SDK_IMAGE@Q} available locally, pull it before running this script"
fi
local sdk_run_kind state_var_name sdk_run_state state_branch_var_name sdk_run_state_branch
local pkg_auto_copy rv
local sdk_reports_dir top_dir dir entry full_path
local -a dir_queue all_dirs all_files
for sdk_run_kind in "${WHICH[@]}"; do
state_var_name="${sdk_run_kind^^}_STATE"
sdk_run_state="${!state_var_name}_sdk_run"
state_branch_var_name="${sdk_run_kind^^}_STATE_BRANCH"
sdk_run_state_branch="${!state_branch_var_name}-sdk-run"
add_cleanup \
"git -C ${sdk_run_state@Q} reset --hard HEAD" \
"git -C ${sdk_run_state@Q} clean -ffdx" \
"git -C ${SCRIPTS@Q} worktree remove ${sdk_run_state@Q}" \
"git -C ${SCRIPTS@Q} branch -D ${sdk_run_state_branch@Q}"
git -C "${SCRIPTS}" \
worktree add -b "${sdk_run_state_branch}" "${sdk_run_state}" "${!state_branch_var_name}"
pkg_auto_copy=$(mktemp --tmpdir="${WORKDIR}" --directory "pkg-auto-copy.XXXXXXXX")
add_cleanup "rm -rf ${pkg_auto_copy@Q}"
cp -a "${PKG_AUTO_DIR}"/* "${pkg_auto_copy}"
local -a run_sdk_container_args=(
-C "${SDK_IMAGE}"
-n "pkg-${sdk_run_kind}"
-U
-m "${pkg_auto_copy}:/mnt/host/source/src/scripts/pkg_auto"
--rm
./pkg_auto/inside_sdk_container.sh pkg-reports "${ARCHES[@]}"
)
rv=0
env --chdir "${sdk_run_state}" ./run_sdk_container "${run_sdk_container_args[@]}" || rv=${?}
unset run_sdk_container_args
if [[ ${rv} -ne 0 ]]; then
local salvaged_dir
salvaged_dir="${REPORTS_DIR}/salvaged-reports"
{
info "run_sdk_container finished with exit status ${rv}, printing the warnings below for a clue"
info
for file in "${sdk_run_state}/pkg-reports/"*'-warnings'; do
info "from ${file}:"
echo
cat "${file}"
echo
done
info
info 'whatever reports generated by the failed run are saved in'
info "${salvaged_dir@Q} directory"
info
} >&2
rm -rf "${salvaged_dir}"
cp -a "${sdk_run_state}/pkg-reports" "${salvaged_dir}"
unset salvaged_dir
fail "copying done, stopping now"
fi
sdk_reports_dir="${WORKDIR}/pkg-reports/${sdk_run_kind}"
top_dir="${sdk_run_state}/pkg-reports"
dir_queue=( "${top_dir}" )
all_dirs=()
all_files=()
while [[ ${#dir_queue[@]} -gt 0 ]]; do
dir=${dir_queue[0]}
dir_queue=( "${dir_queue[@]:1}" )
entry=${dir#"${top_dir}"}
if [[ -z ${entry} ]]; then
all_dirs=( "${sdk_reports_dir}" "${all_dirs[@]}" )
else
entry=${entry#/}
all_dirs=( "${sdk_reports_dir}/${entry}" "${all_dirs[@]}" )
fi
for full_path in "${dir}/"*; do
if [[ -d ${full_path} ]]; then
dir_queue+=( "${full_path}" )
else
entry=${full_path##"${top_dir}/"}
all_files+=( "${sdk_reports_dir}/${entry}" )
fi
done
done
add_cleanup \
"rm -f ${all_files[*]@Q}" \
"rmdir ${all_dirs[*]@Q}"
mv "${sdk_run_state}/pkg-reports" "${sdk_reports_dir}"
done
cp -a "${WORKDIR}/pkg-reports" "${REPORTS_DIR}/reports-from-sdk"
}
source "${PKG_AUTO_IMPL_DIR}/mvm.sh"
# pkginfo mvm is a map mvm that has the following Go-like type:
#
# map[pkg]map[slot]version
#
# pkg, slot and version are strings
# Generate a name for pkginfo mvm based on passed information.
#
# Params:
#
# 1 - which state it refers to (old or new)
# 2 - architecture
# 3 - which report (board packages or SDK packages)
# 4 - name of a variable that will contain the name
function pkginfo_name() {
local which arch report
which=${1}; shift
arch=${1}; shift
report=${1}; shift
local -n pi_name_ref=${1}; shift
pi_name_ref="pkginfo_${which}_${arch}_${report//-/_}_pimap_mvm"
}
# Constructor callback used by mvm_declare for pkginfo mvms.
function pkginfo_constructor() {
mvm_mvc_map_constructor "${@}"
}
# Destructor callback used by mvm_declare for pkginfo mvms.
function pkginfo_destructor() {
mvm_mvc_map_destructor "${@}"
}
# Adder callback used by mvm_declare for pkginfo mvms.
function pkginfo_adder() {
local -n map_ref=${1}; shift
local mark
while [[ ${#} -gt 1 ]]; do
mark=${map_ref["${1}"]:-}
if [[ -n "${mark}" ]]; then
fail "multiple versions for a single slot for a package in a single report"
fi
map_ref["${1}"]=${2}
shift 2
done
}
# Creates a pkginfo mvm.
#
# Params:
#
# 1 - which state it refers to (old or new)
# 2 - architecture
# 3 - which report (board packages or SDK packages)
# 4 - name of a variable that will contain the name of the created
# pkginfo mvm
function pkginfo_declare() {
local which arch report pi_name_var_name
which=${1}; shift
arch=${1}; shift
report=${1}; shift
pi_name_var_name=${1}; shift
pkginfo_name "${which}" "${arch}" "${report}" "${pi_name_var_name}"
local -a extras
extras=(
'which' "${which}"
'arch' "${arch}"
'report' "${report}"
)
mvm_declare "${!pi_name_var_name}" pkginfo -- "${extras[@]}"
}
# Destroys a pkginfo mvm.
#
# Params:
#
# 1 - which state it refers to (old or new)
# 2 - architecture
# 3 - which report (board packages or SDK packages)
function pkginfo_unset() {
local which arch report
which=${1}; shift
arch=${1}; shift
report=${1}; shift
local piu_pi_name
pkginfo_name "${which}" "${arch}" "${report}" piu_pi_name
mvm_unset "${piu_pi_name}"
}
# Processes the report file associated to the passed pkginfo mvm. The
# pkginfo mvm is filled with info about packages, slots and
# versions. Additional information is put into passed package set and
# package to slots set mvm.
#
# Params:
#
# 1 - name of the pkginfo mvm variable
# 2 - name of the set variable, will contain packages in the report
# 3 - name of the set mvm variable, will contain a map of package to
# slots
function pkginfo_process_file() {
mvm_call "${1}" pkginfo_c_process_file "${@:2}"
}
# Helper function for pkginfo_process_file, used by mvm_call.
function pkginfo_c_process_file() {
local pkg_slots_set_mvm_var_name
local -n pkg_set_ref=${1}; shift
pkg_slots_set_mvm_var_name=${1}; shift
# shellcheck source=for-shellcheck/globals
source "${WORKDIR}/globals"
local which arch report
mvm_c_get_extra 'which' which
mvm_c_get_extra 'arch' arch
mvm_c_get_extra 'report' report
local report_file
case ${report}:${arch} in
"${SDK_PKGS}:arm64")
# short-circuit it, there's no arm64 sdk
return 0
;;
"${SDK_PKGS}:amd64")
report_file="${WORKDIR}/pkg-reports/${which}/${report}"
;;
"${BOARD_PKGS}:"*)
report_file="${WORKDIR}/pkg-reports/${which}/${arch}-${report}"
;;
*)
local c=${report}:${arch}
devel_warn "unknown report-architecture combination (${c@Q})"
return 0
esac
local pkg version_slot throw_away v s
while read -r pkg version_slot throw_away; do
pkg_debug_enable "${pkg}"
pkg_debug "${which} ${arch} ${report}: ${version_slot}"
v=${version_slot%%:*}
s=${version_slot##*:}
mvm_c_add "${pkg}" "${s}" "${v}"
pkg_set_ref["${pkg}"]='x'
mvm_add "${pkg_slots_set_mvm_var_name}" "${pkg}" "${s}"
pkg_debug_disable
done <"${report_file}"
}
# Gets a profile of the pkginfo mvm. The "profile" is a confusing
# misnomer as it has nothing to do with Gentoo profiles, but rather a
# description of the pkginfo (which is a which-arch-report triplet)
# that is used for reporting.
function pkginfo_profile() {
mvm_call "${1}" pkginfo_c_profile "${@:2}"
}
# Helper function for pkginfo_profile, used by mvm_call.
function pkginfo_c_profile() {
local profile_var_name
profile_var_name=${1}; shift
local which arch report
mvm_c_get_extra 'which' which
mvm_c_get_extra 'arch' arch
mvm_c_get_extra 'report' report
printf -v "${profile_var_name}" '%s-%s-%s' "${which}" "${arch}" "${report}"
}
# Creates pkginfo maps for all the reports and processes the
# reports. Additional information is stored in passed packages array
# and packages to slots set mvm variables.
#
# Params:
#
# 1 - name of the array variable, will contain sorted packages from
# all the reports
# 2 - name of the set mvm variable, will contain a map of package to
# slots
function read_reports() {
local all_pkgs_var_name pkg_slots_set_mvm_var_name
all_pkgs_var_name=${1}; shift
pkg_slots_set_mvm_var_name=${1}; shift
# shellcheck source=for-shellcheck/globals
source "${WORKDIR}/globals"
local -A rr_all_packages_set
rr_all_packages_set=()
local arch which report rr_pimap_mvm_var_name
for arch in "${ARCHES[@]}"; do
for which in "${WHICH[@]}"; do
for report in "${REPORTS[@]}"; do
pkginfo_declare "${which}" "${arch}" "${report}" rr_pimap_mvm_var_name
pkginfo_process_file "${rr_pimap_mvm_var_name}" rr_all_packages_set "${pkg_slots_set_mvm_var_name}"
done
done
done
mapfile -t "${all_pkgs_var_name}" < <(printf '%s\n' "${!rr_all_packages_set[@]}" | sort)
}
# Destroys the pkginfo maps for all the reports.
function unset_report_mvms() {
# shellcheck source=for-shellcheck/globals
source "${WORKDIR}/globals"
local arch which report
for arch in "${ARCHES[@]}"; do
for which in "${WHICH[@]}"; do
for report in "${REPORTS[@]}"; do
pkginfo_unset "${which}" "${arch}" "${report}"
done
done
done
}
# Finds out the highest and the lowest version from the passed versions.
#
# Params:
#
# 1 - name of a variable where the min version will be stored
# 2 - name of a variable where the min version will be stored
# @ - the versions
function ver_min_max() {
local -n min_ref=${1}; shift
local -n max_ref=${1}; shift
local min max v
min=''
max=''
for v; do
if [[ -z ${min} ]] || ver_test "${v}" -lt "${min}"; then
min=${v}
fi
if [[ -z ${max} ]] || ver_test "${v}" -gt "${max}"; then
max=${v}
fi
done
min_ref=${min}
max_ref=${max}
}
# Does consistency checks on two profiles for a package using an
# additional map for slots information for a package. Checks if common
# slots for the package in both profiles are using the same
# versions. This is to catch version discrepancies that sometimes
# happen when e.g. a package gets stabilized for one arch, but not for
# the other.
#
# While at it, store package, slot and version range information into
# the passed map.
#
# 1 - package
# 2 - name of the pkginfo mvm for profile 1
# 3 - name of the pkginfo mvm for profile 2
# 4 - name of the pkg to slots to version range map mvm # TODO: This should be the last parameter
# 5 - name of the pkg to all slots set mvm
function consistency_check_for_package() {
local pkg pi1_pimap_mvm_var_name pi2_pimap_mvm_var_name pkg_slot_verminmax_map_mvm_var_name pkg_slots_set_mvm_var_name
pkg=${1}; shift
pi1_pimap_mvm_var_name=${1}; shift
pi2_pimap_mvm_var_name=${1}; shift
pkg_slot_verminmax_map_mvm_var_name=${1}; shift
pkg_slots_set_mvm_var_name=${1}; shift
local ccfp_slot_version1_map_var_name ccfp_slot_version2_map_var_name
mvm_get "${pi1_pimap_mvm_var_name}" "${pkg}" ccfp_slot_version1_map_var_name
mvm_get "${pi2_pimap_mvm_var_name}" "${pkg}" ccfp_slot_version2_map_var_name
local -A empty_map
empty_map=()
local -n slot_version1_map_ref=${ccfp_slot_version1_map_var_name:-empty_map}
local -n slot_version2_map_ref=${ccfp_slot_version2_map_var_name:-empty_map}
local ccfp_slots_set_var_name
mvm_get "${pkg_slots_set_mvm_var_name}" "${pkg}" ccfp_slots_set_var_name
local -n slots_set_ref=${ccfp_slots_set_var_name}
local -a profile_1_slots profile_2_slots common_slots
profile_1_slots=()
profile_2_slots=()
common_slots=()
local ccfp_profile_1 ccfp_profile_2
pkginfo_profile "${pi1_pimap_mvm_var_name}" ccfp_profile_1
pkginfo_profile "${pi2_pimap_mvm_var_name}" ccfp_profile_2
local s v1 v2 ccfp_min ccfp_max mm
pkg_debug "all slots iterated over: ${!slots_set_ref[*]}"
for s in "${!slots_set_ref[@]}"; do
v1=${slot_version1_map_ref["${s}"]:-}
v2=${slot_version2_map_ref["${s}"]:-}
pkg_debug "v1: ${v1}, v2: ${v2}"
if [[ -n ${v1} ]] && [[ -n ${v2} ]]; then
pkg_debug "${s} is a common slot for ${ccfp_profile_1} and ${ccfp_profile_2}"
common_slots+=( "${s}" )
if [[ ${v1} != "${v2}" ]]; then
pkg_warn \
"- version mismatch:" \
" - package: ${pkg}" \
" - slot: ${s}" \
" - profile 1: ${ccfp_profile_1}" \
" - version: ${v1}" \
" - profile 1: ${ccfp_profile_2}" \
" - version: ${v2}"
fi
ver_min_max ccfp_min ccfp_max "${v1}" "${v2}"
mm="${ccfp_min}:${ccfp_max}"
elif [[ -n ${v1} ]]; then
# only side1 has the slot
pkg_debug "${s} is a slot only in ${ccfp_profile_1}"
profile_1_slots+=( "${s}" )
mm="${v1}:${v1}"
elif [[ -n ${v2} ]]; then
# only side 2 has the slot
pkg_debug "${s} is a slot only in ${ccfp_profile_2}"
profile_2_slots+=( "${s}" )
mm="${v2}:${v2}"
else
pkg_debug "${s} is a slot absent from both ${ccfp_profile_1} and ${ccfp_profile_2}"
continue
fi
mvm_add "${pkg_slot_verminmax_map_mvm_var_name}" "${pkg}" "${s}" "${mm}"
done
pkg_debug "common slots: ${common_slots[*]}"
pkg_debug "profile 1 slots: ${profile_1_slots[*]}"
pkg_debug "profile 2 slots: ${profile_2_slots[*]}"
local s1 s2
if [[ ${#common_slots[@]} -gt 0 ]]; then
if [[ ${#profile_1_slots[@]} -gt 0 ]] || [[ ${#profile_2_slots[@]} -gt 0 ]]; then
pkg_warn \
"- suspicious:" \
" - package: ${pkg}" \
" - profile 1: ${ccfp_profile_1}" \
" - profile 2: ${ccfp_profile_2}" \
" - common slots: ${common_slots[*]}" \
" - slots only in profile 1: ${profile_1_slots[*]}" \
" - slots only in profile 2: ${profile_2_slots[*]}" \
" - what: there are slots that exist only on one profile while both profiles also have some common slots"
fi
elif [[ ${#profile_1_slots[@]} -eq 1 ]] && [[ ${#profile_2_slots[@]} -eq 1 ]]; then
s1=${profile_1_slots[0]}
s2=${profile_2_slots[0]}
v1=${slot_version1_map_ref["${s1}"]:-}
v2=${slot_version2_map_ref["${s2}"]:-}
if [[ ${v1} != "${v2}" ]]; then
pkg_warn \
"- version mismatch:" \
" - package ${pkg}" \
" - profile 1: ${ccfp_profile_1}" \
" - slot: ${profile_1_slots[0]}" \
" - version: ${v1}" \
" - profile 2: ${ccfp_profile_2}" \
" - slot: ${profile_2_slots[0]}" \
" - version: ${v2}"
fi
fi
}
# Do consistency checks for the following pairs of profiles for a passed state:
#
# ${arch} sdk <-> ${arch} board
# ${arch1} board <-> ${arch2} board
# ${arch1} sdk <-> ${arch2} sdk
#
# While at it, store package, slot and version range information into
# the passed map.
#
# Params:
#
# 1 - which state should be checked (old or new)
# 2 - name of an array variable that contains all the package names
# 3 - name of the pkg to all slots set mvm variable
# 4 - name of the pkg to slot to version range map mvm variable
function consistency_checks() {
local which pkg_slots_set_mvm_var_name pkg_slot_verminmax_mvm_var_name
which=${1}; shift
local -n all_pkgs_ref=${1}; shift
pkg_slots_set_mvm_var_name=${1}; shift
pkg_slot_verminmax_mvm_var_name=${1}; shift
# shellcheck source=for-shellcheck/globals
source "${WORKDIR}/globals"
local cc_pimap_mvm_1_var_name cc_pimap_mvm_2_var_name pkg
local -a all_mvm_names=()
local arch name
# ${arch} SDK <-> ${arch} board
for arch in "${ARCHES[@]}"; do
# currently we only have amd64 SDK, so skip others
if [[ ${arch} != 'amd64' ]]; then
continue
fi
pkginfo_name "${which}" "${arch}" "${SDK_PKGS}" cc_pimap_mvm_1_var_name
pkginfo_name "${which}" "${arch}" "${BOARD_PKGS}" cc_pimap_mvm_2_var_name
name="cc_${arch}_sdk_board_pkg_slot_verminmax_map_mvm"
all_mvm_names+=( "${name}" )
mvm_declare "${name}" mvm_mvc_map
for pkg in "${all_pkgs_ref[@]}"; do
pkg_debug_enable "${pkg}"
pkg_debug "${which} ${arch} sdk <-> ${arch} board"
consistency_check_for_package "${pkg}" "${cc_pimap_mvm_1_var_name}" "${cc_pimap_mvm_2_var_name}" "${name}" "${pkg_slots_set_mvm_var_name}"
pkg_debug_disable
done
done
# We want to check consistency between each pair of arches.
local -a cc_all_arch_pairs
all_pairs cc_all_arch_pairs ':' "${ARCHES[@]}"
local pair arch1 arch2
# ${arch1} board <-> ${arch2} board
for pair in "${cc_all_arch_pairs[@]}"; do
arch1=${pair%:*}
arch2=${pair#*:}
pkginfo_name "${which}" "${arch1}" "${BOARD_PKGS}" cc_pimap_mvm_1_var_name
pkginfo_name "${which}" "${arch2}" "${BOARD_PKGS}" cc_pimap_mvm_2_var_name
name="cc_${arch1}_${arch2}_board_pkg_slot_verminmax_map_mvm"
all_mvm_names+=( "${name}" )
mvm_declare "${name}" mvm_mvc_map
for pkg in "${all_pkgs_ref[@]}"; do
pkg_debug_enable "${pkg}"
pkg_debug "${which} ${arch1} board <-> ${arch2} board"
consistency_check_for_package "${pkg}" "${cc_pimap_mvm_1_var_name}" "${cc_pimap_mvm_2_var_name}" "${name}" "${pkg_slots_set_mvm_var_name}"
pkg_debug_disable
done
done
# ${arch1} sdk <-> ${arch2} sdk
for pair in "${cc_all_arch_pairs[@]}"; do
arch1=${pair%:*}
arch2=${pair#*:}
# We currently only have amd64 SDK, so this loop will
# effectively iterate zero times. When we get the arm64 SDK
# too, this if could be dropped. Getting the listing of arm64
# packages inside amd64 SDK is going to be problem to solve,
# though.
if [[ ${arch1} != 'amd64' ]] || [[ ${arch2} != 'amd64' ]]; then
continue
fi
pkginfo_name "${which}" "${arch1}" "${SDK_PKGS}" cc_pimap_mvm_1_var_name
pkginfo_name "${which}" "${arch2}" "${SDK_PKGS}" cc_pimap_mvm_2_var_name
name="cc_${arch1}_${arch2}_sdk_pkg_slot_verminmax_map_mvm"
all_mvm_names+=( "${name}" )
mvm_declare "${name}" mvm_mvc_map
for pkg in "${all_pkgs_ref[@]}"; do
pkg_debug_enable "${pkg}"
pkg_debug "${which} ${arch1} sdk <-> ${arch2} sdk"
consistency_check_for_package "${pkg}" "${cc_pimap_mvm_1_var_name}" "${cc_pimap_mvm_2_var_name}" "${name}" "${pkg_slots_set_mvm_var_name}"
pkg_debug_disable
done
done
local cc_slots_set_var_name s cc_min cc_max verminmax
local -A empty_map=()
local -a verminmax_map_var_names verminmaxes
local cc_slot_verminmax_map_var_name
for pkg in "${all_pkgs_ref[@]}"; do
pkg_debug_enable "${pkg}"
pkg_debug "${which} verminmax stuff"
verminmax_map_var_names=()
for name in "${all_mvm_names[@]}"; do
mvm_get "${name}" "${pkg}" cc_slot_verminmax_map_var_name
verminmax_map_var_names+=("${cc_slot_verminmax_map_var_name}")
done
if pkg_debug_enabled; then
for name in "${verminmax_map_var_names[@]}"; do
local -n slot_verminmax_map_ref=${name:-empty_map}
pkg_debug_print "all slots in ${name}: ${!slot_verminmax_map_ref[*]}"
pkg_debug_print "all vmms in ${name}: ${slot_verminmax_map_ref[*]}"
unset -n slot_verminmax_map_ref
done
fi
mvm_get "${pkg_slots_set_mvm_var_name}" "${pkg}" cc_slots_set_var_name
local -n slots_set_ref=${cc_slots_set_var_name}
pkg_debug "all slots iterated over: ${!slots_set_ref[*]}"
for s in "${!slots_set_ref[@]}"; do
verminmaxes=()
for name in "${verminmax_map_var_names[@]}"; do
local -n slot_verminmax_map_ref=${name:-empty_map}
verminmax=${slot_verminmax_map_ref["${s}"]:-}
if [[ -n ${verminmax} ]]; then
verminmaxes+=( "${verminmax}" )
fi
unset -n slot_verminmax_map_ref
done
if [[ ${#verminmaxes[@]} -gt 1 ]]; then
ver_min_max cc_min cc_max "${verminmaxes[@]%%:*}" "${verminmaxes[@]##*:}"
verminmax="${cc_min}:${cc_max}"
elif [[ ${#verminmaxes[@]} -eq 1 ]]; then
verminmax=${verminmaxes[0]}
else
continue
fi
pkg_debug "adding vmm ${verminmax} for slot ${s}"
mvm_add "${pkg_slot_verminmax_mvm_var_name}" "${pkg}" "${s}" "${verminmax}"
done
unset -n slots_set_ref
pkg_debug_disable
done
for name in "${all_mvm_names[@]}"; do
mvm_unset "${name}"
done
}
# Read a report describing from which repo the package came and store
# in the passed map.
#
# Params:
#
# 1 - name of a map variable, will contain a mapping of package name
# to repository name
function read_package_sources() {
local -n package_sources_map_ref=${1}; shift
# shellcheck source=for-shellcheck/globals
source "${WORKDIR}/globals"
local -a files=()
local which arch
for which in "${WHICH[@]}"; do
files+=( "${WORKDIR}/pkg-reports/${which}/sdk-package-repos" )
for arch in "${ARCHES[@]}"; do
files+=( "${WORKDIR}/pkg-reports/${which}/${arch}-board-package-repos" )
done
done
local file pkg repo saved_repo
for file in "${files[@]}"; do
while read -r pkg repo; do
saved_repo=${package_sources_map_ref["${pkg}"]:-}
if [[ -n ${saved_repo} ]]; then
if [[ ${saved_repo} != "${repo}" ]]; then
pkg_warn \
'- different repos used for the package:' \
" - package: ${pkg}" \
' - repos:' \
" - ${saved_repo}" \
" - ${repo}"
fi
else
package_sources_map_ref["${pkg}"]=${repo}
fi
done <"${file}"
done
}
# This monstrosity takes renames map and package tags information,
# reads the reports, does consistency checks and uses the information
# from previous steps to write out package differences between the old
# and new state into the reports directory.
#
# Params:
#
# 1 - name of the renames map variable
# 2 - name of the package tags map mvm variable
function handle_package_changes() {
local pkg_to_tags_mvm_var_name
local -n renamed_old_to_new_map_ref=${1}; shift
pkg_to_tags_mvm_var_name=${1}; shift
# shellcheck source=for-shellcheck/globals
source "${WORKDIR}/globals"
local -a hpc_all_pkgs
hpc_all_pkgs=()
# map[package]map[slot]interface{}
mvm_declare hpc_pkg_slots_set_mvm mvm_mvc_set
read_reports hpc_all_pkgs hpc_pkg_slots_set_mvm
# map[package]map[slot]string (string being "min version:max version")
mvm_declare hpc_old_pkg_slot_verminmax_map_mvm mvm_mvc_map
mvm_declare hpc_new_pkg_slot_verminmax_map_mvm mvm_mvc_map
consistency_checks old hpc_all_pkgs hpc_pkg_slots_set_mvm hpc_old_pkg_slot_verminmax_map_mvm
consistency_checks new hpc_all_pkgs hpc_pkg_slots_set_mvm hpc_new_pkg_slot_verminmax_map_mvm
unset_report_mvms
# TODO: when we handle moving packages between repos, then there
# should be two maps, for old and new state
local -A hpc_package_sources_map
hpc_package_sources_map=()
read_package_sources hpc_package_sources_map
mkdir -p "${REPORTS_DIR}/updates"
local -a old_pkgs new_pkgs
old_pkgs=()
new_pkgs=()
# The following loop fills the old_pkgs and new_pkgs arrays sorted
# package names, where old package name at index I has it's new
# counterpart at the same index. For the most part, both old and
# new names will be the same, since the renames are rather rare.
# map[package]index
local -A added_pkg_to_index_map=()
local pkg other
for pkg in "${hpc_all_pkgs[@]}"; do
other=${renamed_old_to_new_map_ref["${pkg}"]:-}
if [[ -n "${other}" ]]; then
# There seem be a rename from ${pkg} to ${other}
pkg_debug_enable "${pkg}" "${other}"
pkg_debug "${pkg} renamed to ${other}"
pkg_debug_disable
local other_idx
other_idx=${added_pkg_to_index_map["${other}"]:-}
if [[ -n ${other_idx} ]]; then
# Looks like we have already processed the ${other}
# name. In this case, both old_pkgs[${other_idx}] and
# new_pkgs[${other_idx}] should just be
# ${other}. Since ${pkg} is the old name (${other} is
# new), we update old_pkgs to hold the old name. Just
# make sure that old_pkgs indeed had the new name
# first.
local other_old
other_old=${old_pkgs["${other_idx}"]}
if [[ ${other_old} = "${other}" ]]; then
old_pkgs["${other_idx}"]=${pkg}
else
manual \
'- there seem to be two old packages in our repos that are supposed to be renamed to the same name:' \
" - old package 1: ${pkg}" \
" - old package 2: ${other_old}" \
" - new package: ${other}"
fi
unset other_idx other_old
continue
else
unset other_idx
fi
# Looks like we haven't processed the ${other} name yet,
# it probably will come up later, which will be taken care
# of by the "pkg_debug 'handled already through some
# rename'" part below, after else.
local pkg_idx
# doesn't matter if it's length of new_pkgs or old_pkgs,
# both are assumed to have the same length
pkg_idx=${#old_pkgs[@]}
old_pkgs+=("${pkg}")
new_pkgs+=("${other}")
added_pkg_to_index_map["${pkg}"]=${pkg_idx}
added_pkg_to_index_map["${other}"]=${pkg_idx}
unset pkg_idx
else
pkg_debug_enable "${pkg}"
if [[ -n ${added_pkg_to_index_map["${pkg}"]:-} ]]; then
pkg_debug 'handled already through some rename'
else
pkg_debug "${pkg} is not renamed"
local pkg_idx
# doesn't matter if it's length of new_pkgs or old_pkgs,
# both are assumed to have the same length
pkg_idx=${#old_pkgs[@]}
old_pkgs+=("${pkg}")
new_pkgs+=("${pkg}")
added_pkg_to_index_map["${pkg}"]=${pkg_idx}
fi
pkg_debug_disable
fi
done
unset added_pkg_to_index_map
# The loop below goes over the pairs of old and new package
# names. For each name there will be some checks done (like does
# this package even exist). Each name in the pair has a set of
# used slots associated with it (the most common situation is that
# each have just one slot, but there are some packages that we
# have multiple slots installed, like
# app-text/docbook-xml-dtd). Some of the slots will appear in both
# old and new package name, sometimes there will be slots
# available only in the old state or only in the new state. Each
# slot for each package name has an associated min version and max
# version. So for common slots we usually compare min version for
# old package with max version for new package. Any
# inconsistencies with the versions should be reported by
# now. There are some edge cases with the slots that are not
# handled by the automation - in such cases there will be a
# "manual action needed" report.
local pkg_idx=0
local old_name new_name old_repo new_repo
local hpc_old_slots_set_var_name hpc_new_slots_set_var_name
local hpc_old_slot_verminmax_map_var_name hpc_new_slot_verminmax_map_var_name
local s hpc_old_s hpc_new_s
local old_verminmax new_verminmax
local old_version new_version
local hpc_cmp_result
local -A hpc_only_old_slots_set hpc_only_new_slots_set hpc_common_slots_set
local -a lines
local hpc_update_dir
local -A empty_map_or_set
local hpc_changed hpc_slot_changed hpc_update_dir_non_slot hpc_category_dir
local which slots_set_var_name_var_name slot_verminmax_map_var_name_var_name filtered_slots_set_var_name verminmax
local -A hpc_old_filtered_slots_set hpc_new_filtered_slots_set
empty_map_or_set=()
while [[ ${pkg_idx} -lt ${#old_pkgs[@]} ]]; do
old_name=${old_pkgs["${pkg_idx}"]}
new_name=${new_pkgs["${pkg_idx}"]}
if [[ ${old_name} = "${new_name}" ]]; then
info "handling update of ${new_name}"
else
info "handling update of ${new_name} (renamed from ${old_name})"
fi
pkg_debug_enable "${old_name}" "${new_name}"
pkg_debug 'handling updates'
pkg_idx=$((pkg_idx + 1))
old_repo=${hpc_package_sources_map["${old_name}"]:-}
new_repo=${hpc_package_sources_map["${new_name}"]:-}
if [[ -z ${old_repo} ]]; then
pkg_warn \
'- package not in old state' \
" - old package: ${old_name}" \
" - new package: ${new_name}"
pkg_debug_disable
continue
fi
if [[ -z ${new_repo} ]]; then
pkg_warn \
'- package not in new state' \
" - old package: ${old_name}" \
" - new package: ${new_name}"
pkg_debug_disable
continue
fi
if [[ ${old_repo} != "${new_repo}" ]]; then
# This is pretty much an arbitrary limitation and I don't
# remember any more why we have it.
pkg_warn \
'- package has moved between repos? unsupported for now' \
" - old package and repo: ${old_name} ${old_repo}" \
" - new package and repo: ${new_name} ${new_repo}"
pkg_debug_disable
continue
fi
if [[ ${new_repo} != 'portage-stable' ]]; then
# coreos-overlay packages will need a separate handling
pkg_debug 'not a portage-stable package'
pkg_debug_disable
continue
fi
mvm_get hpc_pkg_slots_set_mvm "${old_name}" hpc_old_slots_set_var_name
mvm_get hpc_pkg_slots_set_mvm "${new_name}" hpc_new_slots_set_var_name
: "${hpc_old_slots_set_var_name:=empty_map_or_set}"
: "${hpc_new_slots_set_var_name:=empty_map_or_set}"
mvm_get hpc_old_pkg_slot_verminmax_map_mvm "${old_name}" hpc_old_slot_verminmax_map_var_name
mvm_get hpc_new_pkg_slot_verminmax_map_mvm "${new_name}" hpc_new_slot_verminmax_map_var_name
: "${hpc_old_slot_verminmax_map_var_name:=empty_map_or_set}"
: "${hpc_new_slot_verminmax_map_var_name:=empty_map_or_set}"
local -n old_slot_verminmax_map_ref=${hpc_old_slot_verminmax_map_var_name}
local -n new_slot_verminmax_map_ref=${hpc_new_slot_verminmax_map_var_name}
# Filter out slots for old and new package name that comes out
# without versions. This may happen, because we collect all
# slot names for the package name, without differentiating
# whether such a slot existed in the old state or still exists
# in the new state. If slot didn't exist in either one then it
# will come without version information. Such a slot is
# dropped. An example would be an update of sys-devel/binutils
# from 2.42 to 2.43. Each binutils version has a separate slot
# which is named after the version. So the slots set would be
# (2.42 2.43). Slot "2.42" does not exist in the new state any
# more, "2.43" does not yet exist in the old state. So those
# slots for those states will be dropped. Thus filtered slots
# set for the old state will only contain 2.42, while for the
# new state - only 2.43.
for which in old new; do
slots_set_var_name_var_name="hpc_${which}_slots_set_var_name"
slot_verminmax_map_var_name_var_name="hpc_${which}_slot_verminmax_map_var_name"
filtered_slots_set_var_name="hpc_${which}_filtered_slots_set"
local -n which_slots_set_ref=${!slots_set_var_name_var_name}
local -n which_slot_verminmax_map_ref=${!slot_verminmax_map_var_name_var_name}
local -n which_filtered_slots_set_ref=${filtered_slots_set_var_name}
pkg_debug "all unfiltered slots for ${which} name: ${!which_slots_set_ref[*]}"
which_filtered_slots_set_ref=()
for s in "${!which_slots_set_ref[@]}"; do
verminmax=${which_slot_verminmax_map_ref["${s}"]:-}
if [[ -n ${verminmax} ]]; then
which_filtered_slots_set_ref["${s}"]=x
fi
done
pkg_debug "all filtered slots for ${which} name: ${!which_filtered_slots_set_ref[*]}"
unset -n which_filtered_slots_set_ref
unset -n which_slot_verminmax_map_ref
unset -n which_slots_set_ref
done
hpc_only_old_slots_set=()
hpc_only_new_slots_set=()
hpc_common_slots_set=()
sets_split \
hpc_old_filtered_slots_set hpc_new_filtered_slots_set \
hpc_only_old_slots_set hpc_only_new_slots_set hpc_common_slots_set
pkg_debug "all common slots: ${!hpc_common_slots_set[*]}"
pkg_debug "slots only for old name: ${!hpc_only_old_slots_set[*]}"
pkg_debug "slots only for new name: ${!hpc_only_new_slots_set[*]}"
update_dir_non_slot "${new_name}" hpc_update_dir_non_slot
mkdir -p "${hpc_update_dir_non_slot}"
generate_non_ebuild_diffs "${OLD_PORTAGE_STABLE}" "${NEW_PORTAGE_STABLE}" "${old_name}" "${new_name}"
generate_full_diffs "${OLD_PORTAGE_STABLE}" "${NEW_PORTAGE_STABLE}" "${old_name}" "${new_name}"
generate_package_mention_reports "${NEW_STATE}" "${old_name}" "${new_name}"
hpc_changed=
pkg_debug 'going over common slots'
for s in "${!hpc_common_slots_set[@]}"; do
old_verminmax=${old_slot_verminmax_map_ref["${s}"]:-}
new_verminmax=${new_slot_verminmax_map_ref["${s}"]:-}
pkg_debug "slot: ${s}, vmm old: ${old_verminmax}, vmm new: ${new_verminmax}"
if [[ -z "${old_verminmax}" ]] || [[ -z "${new_verminmax}" ]]; then
devel_warn \
"- no minmax info available for old and/or new:" \
" - old package: ${old_name}" \
" - slot: ${s}" \
" - minmax: ${old_verminmax}" \
" - new package: ${new_name}" \
" - slot: ${s}" \
" - minmax: ${new_verminmax}"
continue
fi
update_dir "${new_name}" "${s}" "${s}" hpc_update_dir
mkdir -p "${hpc_update_dir}"
old_version=${old_verminmax%%:*}
new_version=${new_verminmax##*:}
gentoo_ver_cmp_out "${new_version}" "${old_version}" hpc_cmp_result
case ${hpc_cmp_result} in
"${GV_GT}")
handle_pkg_update "${pkg_to_tags_mvm_var_name}" "${old_name}" "${new_name}" "${s}" "${s}" "${old_version}" "${new_version}"
hpc_changed=x
;;
"${GV_EQ}")
hpc_slot_changed=
handle_pkg_as_is "${pkg_to_tags_mvm_var_name}" "${old_name}" "${new_name}" "${s}" "${s}" "${old_version}" hpc_slot_changed
if [[ -z ${hpc_slot_changed} ]]; then
rm -rf "${hpc_update_dir}"
else
hpc_changed=x
fi
;;
"${GV_LT}")
handle_pkg_downgrade "${pkg_to_tags_mvm_var_name}" "${old_name}" "${new_name}" "${s}" "${s}" "${old_version}" "${new_version}"
hpc_changed=x
;;
esac
done
# A "sys-devel/binutils update" case - one old slot and one
# new slot, but different from each other.
if [[ ${#hpc_only_old_slots_set[@]} -eq 1 ]] && [[ ${#hpc_only_new_slots_set[@]} -eq 1 ]]; then
get_first_from_set hpc_only_old_slots_set hpc_old_s
old_verminmax=${old_slot_verminmax_map_ref["${hpc_old_s}"]:-}
get_first_from_set hpc_only_new_slots_set hpc_new_s
new_verminmax=${new_slot_verminmax_map_ref["${hpc_new_s}"]:-}
pkg_debug "jumping from slot ${hpc_old_s} (vmm: ${old_verminmax}) to slot ${hpc_new_s} (vmm: ${new_verminmax})"
if [[ -z "${old_verminmax}" ]] || [[ -z "${new_verminmax}" ]]; then
devel_warn \
"- no verminmax info available for old and/or new:" \
" - old package: ${old_name}" \
" - slot: ${hpc_old_s}" \
" - minmax: ${old_verminmax}" \
" - new package: ${new_name}" \
" - slot: ${hpc_new_s}" \
" - minmax: ${new_verminmax}"
else
update_dir "${new_name}" "${hpc_old_s}" "${hpc_new_s}" hpc_update_dir
mkdir -p "${hpc_update_dir}"
old_version=${old_verminmax%%:*}
new_version=${new_verminmax##*:}
gentoo_ver_cmp_out "${new_version}" "${old_version}" hpc_cmp_result
case ${hpc_cmp_result} in
"${GV_GT}")
handle_pkg_update "${pkg_to_tags_mvm_var_name}" "${old_name}" "${new_name}" "${hpc_old_s}" "${hpc_new_s}" "${old_version}" "${new_version}"
hpc_changed=x
;;
"${GV_EQ}")
hpc_slot_changed=
handle_pkg_as_is "${pkg_to_tags_mvm_var_name}" "${old_name}" "${new_name}" "${hpc_old_s}" "${hpc_new_s}" "${old_version}" hpc_slot_changed
if [[ -z ${hpc_slot_changed} ]]; then
rm -rf "${hpc_update_dir}"
else
hpc_changed=x
fi
;;
"${GV_LT}")
handle_pkg_downgrade "${pkg_to_tags_mvm_var_name}" "${old_name}" "${new_name}" "${hpc_old_s}" "${hpc_new_s}" "${old_version}" "${new_version}"
hpc_changed=x
;;
esac
fi
elif [[ ${#hpc_only_old_slots_set[@]} -gt 0 ]] || [[ ${#hpc_only_new_slots_set[@]} -gt 0 ]]; then
pkg_debug 'complicated slots situation, needs manual intervention'
lines=(
'- handle package update:'
' - old package name:'
" - name: ${old_name}"
' - slots:'
)
for s in "${!hpc_old_filtered_slots_set[@]}"; do
old_verminmax=${old_slot_verminmax_map_ref["${s}"]:-}
lines+=(" - ${s}, minmax: ${old_verminmax}")
done
lines+=(
' - new package name:'
" - name: ${new_name}"
' - slots:'
)
for s in "${!hpc_new_filtered_slots_set[@]}"; do
new_verminmax=${new_slot_verminmax_map_ref["${s}"]:-}
lines+=(" - ${s}, minmax: ${new_verminmax}")
done
manual "${lines[@]}"
fi
unset -n new_slot_verminmax_map_ref old_slot_verminmax_map_ref
# if nothing changed, drop the entire update directory for the
# package, and possibly the parent directory if it became
# empty (parent directory being a category directory, like
# sys-apps)
if [[ -z ${hpc_changed} ]]; then
pkg_debug 'no changes, dropping reports'
rm -rf "${hpc_update_dir_non_slot}"
dirname_out "${hpc_update_dir_non_slot}" hpc_category_dir
if dir_is_empty "${hpc_category_dir}"; then
rmdir "${hpc_category_dir}"
fi
fi
pkg_debug_disable
done
mvm_unset hpc_new_pkg_slot_verminmax_map_mvm
mvm_unset hpc_old_pkg_slot_verminmax_map_mvm
mvm_unset hpc_pkg_slots_set_mvm
}
# Gets the first item from the passed set.
#
# Mostly intended to "unwrap" a single-element set.
#
# Params:
#
# 1 - name of the set variable
# 2 - name of the variable where the element will be stored
function get_first_from_set() {
local -n set_ref=${1}; shift
local -n return_ref=${1}; shift
local item
for item in "${!set_ref[@]}"; do
return_ref=${item}
return 0
done
return_ref=''
}
# Write information to reports directory about the package update
# (meaning specifically that the new version is greater than the old
# one).
#
# Params:
#
# 1 - name of the package tags set mvm variable
# 2 - old package name
# 3 - new package name
# 4 - old package slot
# 5 - new package slot
# 6 - old version
# 7 - new version
function handle_pkg_update() {
local pkg_to_tags_mvm_var_name old_pkg new_pkg old_s new_s old new
pkg_to_tags_mvm_var_name=${1}; shift
old_pkg=${1}; shift
new_pkg=${1}; shift
old_s=${1}; shift
new_s=${1}; shift
old=${1}; shift
new=${1}; shift
# shellcheck source=for-shellcheck/globals
source "${WORKDIR}/globals"
local old_no_r new_no_r
old_no_r=${old%-r+([0-9])}
new_no_r=${new%-r+([0-9])}
local pkg_name
pkg_name=${new_pkg#*/}
local -a lines
lines=( "0:from ${old} to ${new}")
if [[ ${old_pkg} != "${new_pkg}" ]]; then
lines+=( "0:renamed from ${old_pkg}" )
fi
generate_ebuild_diff "${OLD_PORTAGE_STABLE}" "${NEW_PORTAGE_STABLE}" "${old_pkg}" "${new_pkg}" "${old_s}" "${new_s}" "${old}" "${new}"
local hpu_update_dir hpu_update_dir_non_slot
update_dir_non_slot "${new_pkg}" hpu_update_dir_non_slot
update_dir "${new_pkg}" "${old_s}" "${new_s}" hpu_update_dir
local diff_report_name
gen_varname diff_report_name
diff_report_declare "${diff_report_name}"
generate_cache_diff_report "${diff_report_name}" "${WORKDIR}/pkg-reports/old/portage-stable-cache" "${WORKDIR}/pkg-reports/new/portage-stable-cache" "${old_pkg}" "${new_pkg}" "${old}" "${new}"
local -n diff_report_ref=${diff_report_name}
local -n diff_lines_ref=${diff_report_ref[${DR_LINES_IDX}]}
lines+=( "${diff_lines_ref[@]}" )
unset -n diff_lines_ref
unset -n diff_report_ref
diff_report_unset "${diff_report_name}"
if [[ -s "${hpu_update_dir}/ebuild.diff" ]]; then
lines+=( '0:TODO: review ebuild.diff' )
fi
if [[ -s "${hpu_update_dir_non_slot}/other.diff" ]]; then
lines+=( '0:TODO: review other.diff' )
fi
lines+=( '0:TODO: review occurences' )
if [[ ${old_pkg} != "${new_pkg}" ]]; then
lines+=( '0:TODO: review occurences-for-old-name' )
fi
local -a hpu_tags
tags_for_pkg "${pkg_to_tags_mvm_var_name}" "${new_pkg}" hpu_tags
if ver_test "${new_no_r}" -gt "${old_no_r}"; then
# version bump
generate_changelog_entry_stub "${pkg_name}" "${new_no_r}" "${hpu_tags[@]}"
lines+=( '0:release notes: TODO' )
fi
generate_summary_stub "${new_pkg}" "${hpu_tags[@]}" -- "${lines[@]}"
}
# Write information to reports directory about the modified package
# (meaning specifically that the new version is equal than the old
# one).
#
# Params:
#
# 1 - name of the package tags set mvm variable
# 2 - old package name
# 3 - new package name
# 4 - old package slot
# 5 - new package slot
# 6 - version
# 7 - name of a "bool" variable where info is stored if relevant files
# has changed (empty means nothing changed, non-empty means
# something has changed)
function handle_pkg_as_is() {
local pkg_to_tags_mvm_var_name old_pkg new_pkg old_s new_s v
pkg_to_tags_mvm_var_name=${1}; shift
old_pkg=${1}; shift
new_pkg=${1}; shift
old_s=${1}; shift
new_s=${1}; shift
v=${1}; shift
local -n changed_ref=${1}; shift
# shellcheck source=for-shellcheck/globals
source "${WORKDIR}/globals"
local hpai_update_dir
update_dir "${new_pkg}" "${old_s}" "${new_s}" hpai_update_dir
local pkg_name
pkg_name=${new_pkg#/}
local -a lines
lines=( "0:still at ${v}" )
local renamed
renamed=
if [[ ${old_pkg} != "${new_pkg}" ]]; then
lines+=( "0:renamed from ${old_pkg}" )
renamed=x
fi
generate_ebuild_diff "${OLD_PORTAGE_STABLE}" "${NEW_PORTAGE_STABLE}" "${old_pkg}" "${new_pkg}" "${old_s}" "${new_s}" "${v}" "${v}"
local hpai_update_dir_non_slot hpai_update_dir
update_dir_non_slot "${new_pkg}" hpai_update_dir_non_slot
update_dir "${new_pkg}" "${old_s}" "${new_s}" hpai_update_dir
local modified
modified=
local diff_report_name
gen_varname diff_report_name
diff_report_declare "${diff_report_name}"
generate_cache_diff_report "${diff_report_name}" "${WORKDIR}/pkg-reports/old/portage-stable-cache" "${WORKDIR}/pkg-reports/new/portage-stable-cache" "${old_pkg}" "${new_pkg}" "${v}" "${v}"
local -n diff_report_ref=${diff_report_name}
local -n diff_lines_ref=${diff_report_ref[${DR_LINES_IDX}]}
if [[ ${#diff_lines_ref[@]} -gt 0 ]]; then
lines+=( "${diff_lines_ref[@]}" )
modified=x
fi
unset -n diff_lines_ref
unset -n diff_report_ref
diff_report_unset "${diff_report_name}"
if [[ -s "${hpai_update_dir}/ebuild.diff" ]]; then
lines+=( '0:TODO: review ebuild.diff' )
modified=x
fi
if [[ -s "${hpai_update_dir_non_slot}/other.diff" ]]; then
lines+=( '0:TODO: review other.diff' )
modified=x
fi
if [[ -z ${renamed} ]] && [[ -z ${modified} ]]; then
# Nothing relevant has changed, return early.
return 0
fi
changed_ref=x
lines+=( '0:TODO: review occurences' )
if [[ ${old_pkg} != "${new_pkg}" ]]; then
lines+=( '0:TODO: review occurences-for-old-name' )
fi
local -a hpai_tags
tags_for_pkg "${pkg_to_tags_mvm_var_name}" "${new_pkg}" hpai_tags
generate_summary_stub "${new_pkg}" "${hpai_tags[@]}" -- "${lines[@]}"
}
# Write information to reports directory about the package downgrade
# (meaning specifically that the new version is lower than the old
# one).
#
# Params:
#
# 1 - name of the package tags set mvm variable
# 2 - old package name
# 3 - new package name
# 4 - old package slot
# 5 - new package slot
# 6 - old version
# 7 - new version
function handle_pkg_downgrade() {
local pkg_to_tags_mvm_var_name old_pkg new_pkg old_s new_s old new
pkg_to_tags_mvm_var_name=${1}; shift
old_pkg=${1}; shift
new_pkg=${1}; shift
old_s=${1}; shift
new_s=${1}; shift
old=${1}; shift
new=${1}; shift
# shellcheck source=for-shellcheck/globals
source "${WORKDIR}/globals"
local old_no_r new_no_r
old_no_r=${old%-r+([0-9])}
new_no_r=${new%-r+([0-9])}
local pkg_name
pkg_name=${new_pkg#*/}
local -a lines
lines=( "0:downgraded from ${old} to ${new}" )
if [[ ${old_pkg} != "${new_pkg}" ]]; then
lines+=( "0:renamed from ${old_pkg}" )
fi
generate_ebuild_diff "${OLD_PORTAGE_STABLE}" "${NEW_PORTAGE_STABLE}" "${old_pkg}" "${new_pkg}" "${old_s}" "${new_s}" "${old}" "${new}"
local hpd_update_dir hpd_update_dir_non_slot
update_dir_non_slot "${new_pkg}" hpd_update_dir_non_slot
update_dir "${new_pkg}" "${old_s}" "${new_s}" hpd_update_dir
local diff_report_name
gen_varname diff_report_name
diff_report_declare "${diff_report_name}"
generate_cache_diff_report "${diff_report_name}" "${WORKDIR}/pkg-reports/old/portage-stable-cache" "${WORKDIR}/pkg-reports/new/portage-stable-cache" "${old_pkg}" "${new_pkg}" "${old}" "${new}"
local -n diff_report_ref=${diff_report_name}
local -n diff_lines_ref=${diff_report_ref[${DR_LINES_IDX}]}
lines+=( "${diff_lines_ref[@]}" )
unset -n diff_lines_ref
unset -n diff_report_ref
diff_report_unset "${diff_report_name}"
if [[ -s "${hpd_update_dir}/ebuild.diff" ]]; then
lines+=( '0:TODO: review ebuild.diff' )
fi
if [[ -s "${hpd_update_dir_non_slot}/other.diff" ]]; then
lines+=( '0:TODO: review other.diff' )
fi
lines+=( '0:TODO: review occurences' )
if [[ ${old_pkg} != "${new_pkg}" ]]; then
lines+=( '0:TODO: review occurences-for-old-name' )
fi
local -a hpd_tags
tags_for_pkg "${pkg_to_tags_mvm_var_name}" "${new_pkg}" hpd_tags
if ver_test "${new_no_r}" -lt "${old_no_r}"; then
# version bump
generate_changelog_entry_stub "${pkg_name}" "${new_no_r}" "${hpd_tags[@]}"
lines+=( "0:release notes: TODO" )
fi
generate_summary_stub "${new_pkg}" "${hpd_tags[@]}" -- "${lines[@]}"
}
# Retrieves tags for a package.
#
# Params:
#
# 1 - name of the package tags set mvm variable
# 2 - package name
# 3 - name of the array variable, where the tags will be stored
function tags_for_pkg() {
local pkg_to_tags_mvm_var_name pkg
pkg_to_tags_mvm_var_name=${1}; shift
pkg=${1}; shift
local -n tags_ref=${1}; shift
local tfp_tags_var_name
mvm_get "${pkg_to_tags_mvm_var_name}" "${pkg}" tfp_tags_var_name
pkg_debug_enable "${pkg}"
pkg_debug "checking for tags in ${pkg_to_tags_mvm_var_name}"
if [[ -z ${tfp_tags_var_name} ]]; then
pkg_debug "no tags available"
tags_ref=()
else
local -n tags_in_mvm_ref=${tfp_tags_var_name}
tags_ref=( "${tags_in_mvm_ref[@]}" )
pkg_debug "tags available: ${tags_in_mvm_ref[*]}"
fi
pkg_debug_disable
}
# Adds a changelog stub to changelog file in reports directory.
#
# Params:
# 1 - package name (shortened, without the category)
# 2 - version
# @ - package tags
function generate_changelog_entry_stub() {
local pkg_name v
pkg_name=${1}; shift
v=${1}; shift
# rest are tags
local -a applied_tags=()
for tag; do
case ${tag} in
PROD)
applied_tags+=( 'base' )
;;
*)
# add lower-cased tag
applied_tags+=( "${tag,,}" )
;;
esac
done
local gces_tags=''
if [[ ${#applied_tags[@]} -gt 0 ]]; then
join_by gces_tags ', ' "${applied_tags[@]}"
else
# no tags, it means it's an SDK package
gces_tags='SDK'
fi
# shellcheck source=for-shellcheck/globals
source "${WORKDIR}/globals"
printf '%s %s: %s ([%s](TODO))\n' '-' "${gces_tags}" "${pkg_name}" "${v}" >>"${REPORTS_DIR}/updates/changelog_stubs"
}
# Adds a stub to the summary file in reports directory.
#
# Params:
# 1 - package
# @ - tags followed by double dash followed by lines to append to the
# file
function generate_summary_stub() {
local pkg
pkg=${1}; shift
# rest are tags separated followed by double dash followed by lines
# shellcheck source=for-shellcheck/globals
source "${WORKDIR}/globals"
local -a tags
tags=()
while [[ ${#} -gt 0 ]]; do
if [[ ${1} = '--' ]]; then
shift
break
fi
tags+=( "${1}" )
shift
done
# rest are lines
{
printf '%s %s:' '-' "${pkg}"
if [[ ${#tags[@]} -gt 0 ]]; then
printf ' [%s]' "${tags[@]}"
fi
printf '\n'
local indent_line indent line
for indent_line; do
indent=${indent_line%%:*}
line=${indent_line#*:}
if [[ ${indent} -gt 0 ]]; then
printf -- ' %.0s' $(seq 1 "${indent}")
fi
printf ' - %s\n' "${line}"
done
printf '\n'
} >>"${REPORTS_DIR}/updates/summary_stubs"
}
# Generate diffs between directories in old state and new state for a
# package.
#
# Params:
#
# 1 - path to portage-stable in old state
# 2 - path to portage-stable in new state
# 3 - old package name
# 4 - new package name
function generate_full_diffs() {
local old_ps new_ps old_pkg new_pkg
old_ps=${1}; shift
new_ps=${1}; shift
old_pkg=${1}; shift
new_pkg=${1}; shift
local old_path new_path
old_path="${old_ps}/${old_pkg}"
new_path="${new_ps}/${new_pkg}"
local gfd_update_dir
update_dir_non_slot "${new_pkg}" gfd_update_dir
local -a common_diff_opts=(
--recursive
--unified=3
)
xdiff "${common_diff_opts[@]}" --new-file "${old_path}" "${new_path}" >"${gfd_update_dir}/full.diff"
xdiff "${common_diff_opts[@]}" --brief "${old_path}" "${new_path}" >"${gfd_update_dir}/brief-summary"
}
# Generate a diff between non-ebuild, non-Manifest files for old and
# new package.
#
# Params:
#
# 1 - path to portage-stable in old state
# 2 - path to portage-stable in new state
# 3 - old package name
# 4 - new package name
function generate_non_ebuild_diffs() {
local old_ps new_ps old_pkg new_pkg
old_ps=${1}; shift
new_ps=${1}; shift
old_pkg=${1}; shift
new_pkg=${1}; shift
local old_path new_path
old_path="${old_ps}/${old_pkg}"
new_path="${new_ps}/${new_pkg}"
local gned_update_dir
update_dir_non_slot "${new_pkg}" gned_update_dir
local -a diff_opts=(
--recursive
--unified=3
# Show contents of deleted or added files too.
--new-file
# Ignore ebuilds and the Manifest file.
--exclude='*.ebuild'
--exclude='Manifest'
)
xdiff "${diff_opts[@]}" "${old_path}" "${new_path}" >"${gned_update_dir}/other.diff"
}
# Generate a diff between specific ebuilds for old and new package.
#
# Params:
#
# 1 - path to portage-stable in old state
# 2 - path to portage-stable in new state
# 3 - old package name
# 4 - new package name
# 5 - old package slot
# 6 - new package slot
# 7 - old package version
# 8 - new package version
function generate_ebuild_diff() {
local old_ps new_ps old_pkg new_pkg old_s new_s old new
old_ps=${1}; shift
new_ps=${1}; shift
old_pkg=${1}; shift
new_pkg=${1}; shift
old_s=${1}; shift
new_s=${1}; shift
old=${1}; shift
new=${1}; shift
local old_pkg_name new_pkg_name
old_pkg_name=${old_pkg#*/}
new_pkg_name=${new_pkg#*/}
local old_path new_path
old_path="${old_ps}/${old_pkg}/${old_pkg_name}-${old}.ebuild"
new_path="${new_ps}/${new_pkg}/${new_pkg_name}-${new}.ebuild"
local ged_update_dir
update_dir "${new_pkg}" "${old_s}" "${new_s}" ged_update_dir
xdiff --unified=3 "${old_path}" "${new_path}" >"${ged_update_dir}/ebuild.diff"
}
function generate_cache_diff_report() {
local diff_report_var_name=${1}; shift
local old_cache_dir=${1}; shift
local new_cache_dir=${1}; shift
local old_pkg=${1}; shift
local new_pkg=${1}; shift
local old=${1}; shift
local new=${1}; shift
# shellcheck source=for-shellcheck/globals
source "${WORKDIR}/globals"
local old_entry=${old_cache_dir}/${old_pkg}-${old}
local new_entry=${new_cache_dir}/${new_pkg}-${new}
local old_cache_name new_cache_name
gen_varname old_cache_name
gen_varname new_cache_name
cache_file_declare "${old_cache_name}" "${new_cache_name}"
parse_cache_file "${old_cache_name}" "${old_entry}" "${ARCHES[@]}"
parse_cache_file "${new_cache_name}" "${new_entry}" "${ARCHES[@]}"
diff_cache_data "${old_cache_name}" "${new_cache_name}" "${diff_report_var_name}"
cache_file_unset "${old_cache_name}" "${new_cache_name}"
}
# Generate a report with information where the old and new packages
# are mentioned in entire scripts repository. May result in two
# separate reports if the package got renamed.
#
# 1 - path to scripts repo
# 2 - old package name
# 3 - new package name
function generate_package_mention_reports() {
local scripts old_pkg new_pkg
scripts=${1}; shift
old_pkg=${1}; shift
new_pkg=${1}; shift
local gpr_update_dir
update_dir_non_slot "${new_pkg}" gpr_update_dir
generate_mention_report_for_package "${scripts}" "${new_pkg}" >"${gpr_update_dir}/occurences"
if [[ ${old_pkg} != "${new_pkg}" ]]; then
generate_mention_report_for_package "${scripts}" "${old_pkg}" >"${gpr_update_dir}/occurences-for-old-name"
fi
}
# Generate a report with information where the package is mentioned in
# entire scripts repository.
#
# 1 - path to scripts repo
# 3 - package name
function generate_mention_report_for_package() {
local scripts pkg
scripts=${1}; shift
pkg=${1}; shift
local ps co
ps='sdk_container/src/third_party/portage-stable'
co='sdk_container/src/third_party/coreos-overlay'
yell "${pkg} in overlay profiles"
grep_pkg "${scripts}" "${pkg}" "${co}/profiles"
yell "${pkg} in Gentoo profiles"
grep_pkg "${scripts}" "${pkg}" "${ps}/profiles"
# shellcheck disable=SC2164 # we use set -e, so the script will exit if it fails
pushd "${scripts}/${co}" >/dev/null
yell "${pkg} in env overrides"
cat_entries "coreos/config/env/${pkg}"@(|-+([0-9])*)
yell "${pkg} in user patches"
local dir
for dir in "coreos/user-patches/${pkg}"@(|-+([0-9])*); do
echo "BEGIN DIRECTORY: ${dir}"
cat_entries "${dir}"/*
echo "END DIRECTORY: ${dir}"
done
# shellcheck disable=SC2164 # we use set -e, so the script will exit if it fails
popd >/dev/null
yell "${pkg} in overlay (outside profiles)"
grep_pkg "${scripts}" "${pkg}" "${co}" ":(exclude)${co}/profiles"
yell "${pkg} in Gentoo (outside profiles)"
grep_pkg "${scripts}" "${pkg}" "${ps}" ":(exclude)${ps}/profiles"
yell "${pkg} in scripts (outside overlay and Gentoo)"
grep_pkg "${scripts}" "${pkg}" ":(exclude)${ps}" ":(exclude)${co}"
}
# Gets a toplevel update reports directory for a package. This is
# where occurences and non-ebuild diffs are stored.
#
# Params:
#
# 1 - package name
# 2 - name of a variable where the path will be stored
function update_dir_non_slot() {
local pkg
pkg=${1}; shift
local -n dir_ref=${1}; shift
# shellcheck source=for-shellcheck/globals
source "${WORKDIR}/globals"
dir_ref="${REPORTS_DIR}/updates/${pkg}"
}
# Gets a slot specific update reports directory for a package. This is
# where ebuild diffs are stored.
#
# Params:
#
# 1 - package name
# 2 - old slot
# 3 - new slot
# 4 - name of a variable where the path will be stored
function update_dir() {
local pkg old_s new_s
pkg=${1}; shift
old_s=${1}; shift
new_s=${1}; shift
local -n dir_ref=${1}; shift
# slots may have slashes in them - replace them with "-slash-"
local slot_dir
if [[ ${old_s} = "${new_s}" ]]; then
slot_dir=${old_s//\//-slash-}
else
slot_dir="${old_s//\//-slash-}-to-${new_s//\//-slash-}"
fi
local ud_non_slot_dir
update_dir_non_slot "${pkg}" ud_non_slot_dir
dir_ref="${ud_non_slot_dir}/${slot_dir}"
}
# Greps for a package name in selected directories of the passed
# repo. It prints, so the invocation needs to be captured.
#
# Params:
#
# 1 - path to scripts repo
# 2 - package name
# @ - directories in the repo to limit the search for
function grep_pkg() {
local scripts pkg
scripts=${1}; shift
pkg=${1}; shift
# rest are directories
GIT_PAGER='' git -C "${scripts}" grep "${pkg}"'\(-[0-9]\|[^a-zA-Z0-9_-]\|$\)' -- "${@}" || :
}
# Prints the passed files preceding and following with BEGIN ENTRY and
# END ENTRY markers.
#
# Params:
#
# @ - the files to print
function cat_entries() {
for entry; do
echo "BEGIN ENTRY: ${entry}"
cat "${entry}"
echo "END ENTRY: ${entry}"
done
}
# Reads the listings and renames, handles updates of both packages and
# non-packages (eclasses, licenses, profiles, etc.)
function handle_gentoo_sync() {
#mvm_debug_enable hgs_pkg_to_tags_mvm
mvm_declare hgs_pkg_to_tags_mvm
process_listings hgs_pkg_to_tags_mvm
local -A hgs_renames_old_to_new_map=()
process_profile_updates_directory hgs_renames_old_to_new_map
handle_package_changes hgs_renames_old_to_new_map hgs_pkg_to_tags_mvm
mvm_unset hgs_pkg_to_tags_mvm
#mvm_debug_disable hgs_pkg_to_tags_mvm
# shellcheck source=for-shellcheck/globals
source "${WORKDIR}/globals"
local old_head new_head
old_head=$(git -C "${OLD_STATE}" rev-parse HEAD)
new_head=$(git -C "${NEW_STATE}" rev-parse HEAD)
local -A non_package_updates_set
non_package_updates_set=()
local path in_ps category
if [[ "${old_head}" != "${new_head}" ]]; then
while read -r path; do
if [[ ${path} != "${PORTAGE_STABLE_SUFFIX}/"* ]]; then
continue
fi
in_ps=${path#"${PORTAGE_STABLE_SUFFIX}/"}
category=${in_ps%%/*}
case "${category}" in
eclass)
if [[ ${in_ps} != 'eclass/'+([^/])'.eclass' ]]; then
fail "unexpected updated file inside eclass directory: '${path}'"
fi
non_package_updates_set["${in_ps}"]=x
;;
licenses|metadata|profiles|scripts)
non_package_updates_set["${category}"]=x
;;
virtual|*-*)
# Package update, already handled
:
;;
*)
fail "unexpected updated file '${path}'"
;;
esac
done < <(git -C "${NEW_STATE}" diff-tree --no-commit-id --name-only -r "${old_head}" "${new_head}")
fi
local entry
for entry in "${!non_package_updates_set[@]}"; do
case "${entry}" in
eclass/*)
handle_eclass "${entry}"
;;
licenses)
handle_licenses
;;
metadata)
info "not handling metadata updates, skipping"
;;
profiles)
handle_profiles
;;
scripts)
handle_scripts
;;
*)
fail "unknown non-package update for ${entry}"
;;
esac
done
sort_summary_stubs
sort_changelog_stubs
}
# Sorts entries in the summary file if it exists.
function sort_summary_stubs() {
if [[ -f "${REPORTS_DIR}/updates/summary_stubs" ]]; then
sort_like_summary_stubs "${REPORTS_DIR}/updates/summary_stubs"
fi
}
# Sorts entries in the summary file.
#
# Lines look like as follows:
#
# -BEGIN-
# - dev-lang/python: [DEV]
# - from 3.11.4 to 3.11.5
# - no changes in ebuild
# - release notes: TODO
#
# - app-emulation/qemu:
# - from 8.0.3 to 8.0.4
# - no changes in ebuild
# - release notes: TODO
#
# -END-
function sort_like_summary_stubs() {
local f
f=${1}; shift
mvm_declare groups_mvm
local -a lines entries
lines=()
entries=()
local -A dups
dups=()
local REPLY line entry sss_lines_name dup_count
while read -r; do
if [[ -z ${REPLY} ]]; then
if [[ ${#lines[@]} -gt 0 ]]; then
line=${lines[0]}
entry=${line#-+([[:space:]])}
entry=${entry%%:*}
dup_count=${dups["${entry}"]:-0}
if [[ ${dup_count} -gt 0 ]]; then
dup_count=$((dup_count + 1))
mvm_add groups_mvm "${entry}@${dup_count}" "${lines[@]}"
dups["${entry}"]=${dup_count}
else
mvm_get groups_mvm "${entry}" sss_lines_name
if [[ -n ${sss_lines_name} ]]; then
local -n lines_ref=${sss_lines_name}
mvm_add groups_mvm "${entry}@1" "${lines_ref[@]}"
unset -n lines_ref
mvm_remove groups_mvm "${entry}"
mvm_add groups_mvm "${entry}@2" "${lines[@]}"
dups["${entry}"]=2
else
mvm_add groups_mvm "${entry}" "${lines[@]}"
entries+=( "${entry}" )
fi
fi
lines=()
fi
else
lines+=( "${REPLY}" )
fi
done < <(cat "${f}"; echo) # echo for final empty line, just in case
if [[ ${#entries[@]} -eq 0 ]]; then
return 0
fi
local idx
{
while read -r line; do
dup_count=${dups["${line}"]:-0}
if [[ ${dup_count} -gt 0 ]]; then
idx=0
while [[ ${idx} -lt ${dup_count} ]]; do
idx=$((idx + 1))
mvm_get groups_mvm "${line}@${idx}" sss_lines_name
local -n lines_ref=${sss_lines_name}
printf '%s\n' "${lines_ref[@]}" ''
unset -n lines_ref
done
else
mvm_get groups_mvm "${line}" sss_lines_name
local -n lines_ref=${sss_lines_name}
printf '%s\n' "${lines_ref[@]}" ''
unset -n lines_ref
fi
done < <(printf '%s\n' "${entries[@]}" | csort)
} >"${f}"
mvm_unset groups_mvm
}
# Sorts entries in changelog stub if it exists.
function sort_changelog_stubs() {
if [[ -f "${REPORTS_DIR}/updates/changelog_stubs" ]]; then
sort_like_changelog_stubs "${REPORTS_DIR}/updates/changelog_stubs"
fi
}
# Sorts entries in changelog stub.
function sort_like_changelog_stubs() {
local f t
f=${1}; shift
t="${f}.tmp"
csort --output="${t}" "${f}"
mv -f "${t}" "${f}"
}
# Invokes sort with C locale. Meant to be used in bash pipelines.
#
# Params:
#
# @ - additional parameters to passed to sort
function csort() {
LC_ALL=C sort "${@}"
}
# Handle an eclass update. Basically generate a diff.
#
# Params:
#
# 1 - path to eclass file within an ebuild repo
function handle_eclass() {
local eclass
eclass=${1}; shift
# shellcheck source=for-shellcheck/globals
source "${WORKDIR}/globals"
local -a lines
lines=()
if [[ -e "${OLD_PORTAGE_STABLE}/${eclass}" ]] && [[ -e "${NEW_PORTAGE_STABLE}/${eclass}" ]]; then
mkdir -p "${REPORTS_DIR}/updates/${eclass}"
xdiff --unified=3 "${OLD_PORTAGE_STABLE}/${eclass}" "${NEW_PORTAGE_STABLE}/${eclass}" >"${REPORTS_DIR}/updates/${eclass}/eclass.diff"
lines+=( '0:TODO: review the diff' )
elif [[ -e "${OLD_PORTAGE_STABLE}/${eclass}" ]]; then
lines+=( '0:unused, dropped' )
else
lines+=( '0:added from Gentoo' )
fi
generate_summary_stub "${eclass}" -- "${lines[@]}"
}
# Handle profile changes. Generates three different diffs - changes in
# relevant profiles (ancestors of the profiles used by board packages
# and SDK), a full diff between all the profiles, and a list of
# possibly irrelevant files that has changed too.
function handle_profiles() {
# shellcheck source=for-shellcheck/globals
source "${WORKDIR}/globals"
local -a files=()
local which arch
for which in "${WHICH[@]}"; do
files+=("${WORKDIR}/pkg-reports/${which}/sdk-profiles")
for arch in "${ARCHES[@]}"; do
files+=("${WORKDIR}/pkg-reports/${which}/${arch}-board-profiles")
done
done
local -A profile_dirs_set
profile_dirs_set=()
local line
while read -r line; do
profile_dirs_set["${line}"]=x
done < <(xgrep --no-filename '^portage-stable:' "${files[@]}" | cut -d: -f2-)
local -a diff_opts
diff_opts=(
--recursive
--unified=3
--new-file # treat absent files as empty
)
local out_dir
out_dir="${REPORTS_DIR}/updates/profiles"
mkdir -p "${out_dir}"
xdiff "${diff_opts[@]}" \
"${OLD_PORTAGE_STABLE}/profiles" "${NEW_PORTAGE_STABLE}/profiles" >"${out_dir}/full.diff"
local relevant
relevant=''
local -a relevant_lines possibly_irrelevant_files
relevant_lines=()
possibly_irrelevant_files=()
local REPLY path dir mark
while read -r; do
if [[ ${REPLY} = "diff "* ]]; then
path=${REPLY##*"${NEW_PORTAGE_STABLE}/profiles/"}
dirname_out "${path}" dir
relevant=''
mark=${profile_dirs_set["${dir}"]:-}
if [[ -n "${mark}" ]]; then
relevant=x
else
case ${dir} in
.|desc|desc/*|updates|updates/*)
relevant=x
;;
esac
fi
if [[ -z ${relevant} ]]; then
possibly_irrelevant_files+=( "profiles/${path}" )
fi
fi
if [[ -n ${relevant} ]]; then
relevant_lines+=( "${REPLY}" )
fi
done <"${out_dir}/full.diff"
lines_to_file_truncate "${out_dir}/relevant.diff" "${relevant_lines[@]}"
lines_to_file_truncate "${out_dir}/possibly-irrelevant-files" "${possibly_irrelevant_files[@]}"
generate_summary_stub profiles -- '0:TODO: review the diffs'
}
# Handles changes in license directory. Generates brief reports and
# diffs about dropped, added or modified licenses.
function handle_licenses() {
# shellcheck source=for-shellcheck/globals
source "${WORKDIR}/globals"
local -a dropped=() added=() changed=()
local line hl_stripped
# Lines are:
#
# Only in <PORTAGE_STABLE_X>/licenses: BSL-1.1
#
# or
#
# Files <PORTAGE_STABLE_1>/licenses/BSL-1.1 and <PORTAGE_STABLE_2>/licenses/BSL-1.1 differ
while read -r line; do
if [[ ${line} = 'Only in '* ]]; then
strip_out "${line##*:}" hl_stripped
if [[ ${line} = *"${OLD_STATE}"* ]]; then
dropped+=( "${hl_stripped}" )
elif [[ ${line} = *"${NEW_STATE}"* ]]; then
added+=( "${hl_stripped}" )
else
devel_warn "- unhandled license change: ${line}"
fi
elif [[ ${line} = 'Files '*' differ' ]]; then
line=${line##"Files ${OLD_PORTAGE_STABLE}/licenses/"}
line=${line%% *}
strip_out "${line}" hl_stripped
changed+=( "${hl_stripped}" )
else
devel_warn \
'- unhandled diff --brief line:' \
" - ${line}"
fi
done < <(xdiff --brief --recursive "${OLD_PORTAGE_STABLE}/licenses" "${NEW_PORTAGE_STABLE}/licenses")
local out_dir
out_dir="${REPORTS_DIR}/updates/licenses"
mkdir -p "${out_dir}"
lines_to_file_truncate \
"${out_dir}/brief-summary" \
'- removed:' \
"${dropped[@]/#/ - }" \
'- added:' \
"${added[@]/#/ - }" \
'- modified:' \
"${changed[@]/#/ - }"
truncate --size=0 "${out_dir}/modified.diff"
local c
for c in "${changed[@]}"; do
xdiff --unified=3 "${OLD_PORTAGE_STABLE}/licenses/${c}" "${NEW_PORTAGE_STABLE}/licenses/${c}" >>"${out_dir}/modified.diff"
done
local -a lines
lines=()
local joined
if [[ ${#dropped[@]} -gt 0 ]]; then
join_by joined ', ' "${dropped[@]}"
lines+=( "0:dropped ${joined}" )
fi
if [[ ${#added[@]} -gt 0 ]]; then
join_by joined ', ' "${added[@]}"
lines+=( "0:added ${joined}" )
fi
if [[ ${#changed[@]} -gt 0 ]]; then
join_by joined ', ' "${changed[@]}"
lines+=( "0:updated ${joined}" )
fi
generate_summary_stub licenses -- "${lines[@]}"
}
# Generates reports about changes inside the scripts directory.
function handle_scripts() {
# shellcheck source=for-shellcheck/globals
source "${WORKDIR}/globals"
local out_dir
out_dir="${REPORTS_DIR}/updates/scripts"
mkdir -p "${out_dir}"
xdiff --unified=3 --recursive "${OLD_PORTAGE_STABLE}/scripts" "${NEW_PORTAGE_STABLE}/scripts" >"${out_dir}/scripts.diff"
generate_summary_stub scripts -- '0:TODO: review the diffs'
}
fi