From 14dd109d73e4c45798049f48481a70af43556203 Mon Sep 17 00:00:00 2001 From: oklopfer <104327997+oklopfer@users.noreply.github.com> Date: Wed, 17 Jul 2024 17:10:54 -0400 Subject: [PATCH] upd: refactor with srclist (#26) Co-authored-by: ook37 --- Makefile.PL | 2 +- pacup | 18 +- scripts/{srcinfo.sh => pacup-srcinfo} | 304 ++++++++++++++++++++++++-- 3 files changed, 293 insertions(+), 31 deletions(-) rename scripts/{srcinfo.sh => pacup-srcinfo} (69%) diff --git a/Makefile.PL b/Makefile.PL index e963e79..08e4c1b 100644 --- a/Makefile.PL +++ b/Makefile.PL @@ -8,7 +8,7 @@ WriteMakefile( VERSION_FROM => 'pacup', LICENSE => 'gpl_3', AUTHOR => 'Vigress ', - EXE_FILES => ['pacup', 'scripts/srcinfo.sh'], + EXE_FILES => ['pacup', 'scripts/pacup-srcinfo'], INSTALLDIRS => 'vendor', dist => { COMPRESS => 'gzip', SUFFIX => 'gz' }, PREREQ_PM => { diff --git a/pacup b/pacup index 0e85c9f..b4762b5 100755 --- a/pacup +++ b/pacup @@ -4,7 +4,7 @@ package main; use strict; use warnings qw(all -experimental::signatures); use feature qw(say signatures); -our $VERSION = '3.3.2'; +our $VERSION = '3.3.3'; #use Data::Dumper; use open ':std', ':encoding(UTF-8)'; @@ -34,7 +34,7 @@ my $opt_origin_remote = 'origin'; my $opt_custom_version; my $opt_push_force = 0; -my $srcinfo_script = "srcinfo.sh"; +my $srcinfo_script = "pacup-srcinfo"; my @HASHTYPES = qw(b2 md5 sha1 sha224 sha256 sha384 sha512); my $REPOLOGY_API_ROOT = 'https://repology.org/api/v1/project'; @@ -381,6 +381,12 @@ sub writelines ( $ref, $infile ) { } sub main ($pkg) { + if ($opt_ship) { + unless ( -d "packages" && -d "scripts" ) { + throw + "Running with '--ship' must be done from the head of the repository"; + } + } my $ppath = -f $pkg ? $pkg : "packages/$pkg/$pkg.pacscript"; my ( $pacscript, $dir ) = fileparse abs_path $ppath; $ppath = $dir . $pacscript; @@ -549,9 +555,10 @@ sub main ($pkg) { return 1 unless $opt_ship; + system $srcinfo_script, "build", "srclist"; my $commit_msg = qq/upd($pkgname): \`$pkgver\` -> \`$newestver\`/; - system 'git', ( 'add', $ppath, $srcinfo ); + system 'git', ( 'add', $ppath, $srcinfo, 'srclist' ); my $ship_branch = "ship-$pkgname"; my $current_branch = capture 'git', ( 'rev-parse', '--abbrev-ref', 'HEAD' ); @@ -580,8 +587,7 @@ sub main ($pkg) { if ( ask 'Create PR? (must have gh installed and authenticated to GitHub)' ) { - system 'gh', - ( 'pr', 'create', '--title', $commit_msg, '--body', '' ); + system 'gh', ( 'pr', 'create', '--title', $commit_msg, '--body', '' ); } info "Done!"; @@ -669,7 +675,7 @@ Vigress - =head1 VERSION -Pacup (Perl edition) v3.3.2 +Pacup (Perl edition) v3.3.3 =cut diff --git a/scripts/srcinfo.sh b/scripts/pacup-srcinfo similarity index 69% rename from scripts/srcinfo.sh rename to scripts/pacup-srcinfo index 1c5ef1e..4309f60 100755 --- a/scripts/srcinfo.sh +++ b/scripts/pacup-srcinfo @@ -42,7 +42,7 @@ function srcinfo.is_array() { } function srcinfo.die() { - printf >&2 'ERROR: %s\n' "$@" + printf >&2 '[\033[1;31mERROR\033[0m]: %s\n' "$@" exit 1 } @@ -221,6 +221,7 @@ function srcinfo.write_global() { eval "${_//${seek}/${rep}}" fi done + # shellcheck disable=SC2076 if [[ " ${multivalued_arch_attrs[*]} " =~ " ${ar} " ]]; then unset "${ar}" fi @@ -313,6 +314,7 @@ function srcinfo._contains() { function srcinfo._create_array() { local pkgbase="${1}" var_name="${2}" var_pref="${3}" if [[ -n ${pkgbase} ]]; then + pkgbase="${pkgbase//./_}" var_name="${var_name//./_}" if ! [[ -v "${var_pref}_${pkgbase}_array_${var_name}" ]]; then declare -ag "${var_pref}_${pkgbase}_array_${var_name}" fi @@ -344,7 +346,7 @@ function srcinfo.parse() { # shellcheck disable=SC2064 trap "$(shopt -p extglob)" RETURN shopt -s extglob - local srcinfo_file var_prefix locbase temp_array ref total_list loop part i part_two split_up + local srcinfo_file var_prefix locbase temp_array ref total_list loop part i part_two split_up suffix srcinfo_file="${1:?No .SRCINFO passed to srcinfo.parse}" var_prefix="${2:?Variable prefix not passed to srcinfo.parse}" srcinfo.cleanup "${var_prefix}" @@ -370,7 +372,7 @@ function srcinfo.parse() { # Do we have pkgbase first? if [[ ${temp_line[key]} == "pkgbase" ]]; then locbase="pkgbase_${temp_line[value]//-/_}" - export globase="${temp_line[value]}" + export globase="${temp_line[value]//./_}" else locbase="${temp_line[value]//-/_}" export globase="temporary_pacstall_pkgbase" @@ -398,10 +400,12 @@ function srcinfo.parse() { declare -n var_name="${var_prefix}_access" [[ ${loop} == "${var_prefix}_pkgbase"* ]] && global="pkgbase_" for i in "${!part[@]}"; do + suffix="${global}${part[${i}]//-/_}" + suffix="${suffix//./_}" # Create our inner part - declare -ga "${var_prefix}_${global}${part[${i}]//-/_}" + declare -ga "${var_prefix}_${suffix}" # Declare that relationship - var_name["${var_prefix}_${global}${part[${i}]//-/_}"]="${var_prefix}_${global}${part[${i}]//-/_}" + var_name["${var_prefix}_${suffix}"]="${var_prefix}_${suffix}" done unset global fi @@ -466,6 +470,7 @@ function srcinfo.reformat_assoc_arr() { IFS='_' read -r -a pfs <<< "${in_name}" for pfx in "${!in_arr[@]}"; do base="${pfx%-*}" ida="${pfx##*-}" new="${base//-/_}" + new="${new//./_}" app+=("$(printf "%s[%s]=\"%s\"\n" "${pfs[0]}_${pfs[1]}_${new}" "${ida}" "${in_arr[${pfx}]}")") done } @@ -491,6 +496,7 @@ function srcinfo.print_var() { fi fi for var in "${bases[@]}"; do + var="${var//./_}" declare -n output="${var}_array_${found}" declare -n name="${var}_array_pkgname" if [[ -n ${output[*]} ]]; then @@ -548,6 +554,7 @@ function srcinfo.print_var() { # @arg $3 string Package name or base to get output for function srcinfo.match_pkg() { local declares d bases b guy match out srcfile="${1}" search="${2}" pkg="${3}" + pkg="${pkg//./_}" if [[ ${pkg} == "pkgbase:"* || ${search} == "pkgbase" ]]; then pkg="${pkg/pkgbase:/}" match="srcinfo_${search%%_*}_${pkg//-/_}_pkgbase" @@ -593,7 +600,7 @@ function srcinfo.pkg_list() { local origname outlist inlist pkgnames mapfile -t inlist < <(ls -1 packages) for i in "${inlist[@]}"; do - if [[ -n $(awk '/^pkgbase=/' "packages/${i}/${i}.pacscript") ]]; then + if awk 'BEGIN { found = 0 } /^pkgbase=/ { found = 1; exit } END { exit !found }' "packages/${i}/${i}.pacscript"; then mapfile -t pkgnames < <(srcinfo.match_pkg packages/"${i}"/.SRCINFO pkgname) outlist+=("${i}:pkgbase") for j in "${pkgnames[@]}"; do @@ -607,6 +614,7 @@ function srcinfo.pkg_list() { } function srcinfo.repo_check() { + local tracked repolist ret mapfile -t tracked < <(git ls-files) if ((${#@} < 1)); then mapfile -t repolist < <(ls packages/*/*.pacscript) @@ -626,28 +634,276 @@ function srcinfo.repo_check() { ret=1 fi done - exit "${ret}" + return "${ret}" } -function srcinfo.cmd() { - if [[ ${1} == "write" ]]; then - shift 1 - # list of packages to write to - srcinfo.write_out "${@}" - elif [[ ${1} == "check" ]]; then - shift 1 - # check if .SRCINFOs exist - srcinfo.repo_check "${@}" - elif [[ ${1} == "packagelist" ]]; then - # no args - srcinfo.pkg_list > packagelist - elif [[ ${1} == "read" ]]; then - # see srcinfo.match_pkg for description - # shellcheck disable=SC2086 - srcinfo.match_pkg "${2:?No SRCINFO file provided}" "${3:?No variable provided}" ${4} +function srcinfo.list_build() { + local FILE="${1}" filelist tmploc + tmploc="$(mktemp)" + printf "### Auto-generated for pacstall-programs\n### DO NOT EDIT. Use scripts/srcinfo.sh to build.\n" > "${tmploc}" + mapfile -t filelist < <(ls packages/*/.SRCINFO) + for i in "${filelist[@]}"; do + echo "---" >> "${tmploc}" + while IFS= read -r line; do + echo "${line}" >> "${tmploc}" + done < "${i}" + done + mv "${tmploc}" "${FILE}" +} + +function srcinfo.list_search() { + local FILE="${1}" + awk -v kw="${2}" ' + BEGIN { + FS = "[[:space:]]*=[[:space:]]*" + OFS = " - " + found = 0 + kw = tolower(kw) + } + function print_pkgbase_and_pkgname() { + if (pkgbase != "") { + print pkgbase, pkgbase_desc + if (pkgname != "") { + desc = (pkgname_desc != "" ? pkgname_desc : pkgbase_desc) + print pkgbase ":" pkgname, desc + } + } + } + /^---$/ { + if (pkgbase != "" && (pkgbase ~ kw || tolower(pkgbase_desc) ~ kw)) { + print_pkgbase_and_pkgname() + found = 1 + } else if (pkgname != "" && (pkgname ~ kw || tolower(pkgname_desc) ~ kw)) { + print_pkgbase_and_pkgname() + found = 1 + } + pkgname = ""; pkgbase = ""; pkgbase_desc = ""; pkgname_desc = ""; next + } + /^[[:space:]]*pkgbase[[:space:]]*=/ { + pkgbase = $2 + pkgbase_desc = "" + } + /^[[:space:]]*pkgname[[:space:]]*=/ { + if (pkgname != "") { + desc = (pkgname_desc != "" ? pkgname_desc : pkgbase_desc) + if (pkgname ~ kw || tolower(desc) ~ kw) { + print pkgbase ":" pkgname, desc + found = 1 + } + } + pkgname = $2 + pkgname_desc = "" + } + /^[[:space:]]*pkgdesc[[:space:]]*=/ { + if (pkgname == "") { + pkgbase_desc = $2 + } else { + pkgname_desc = $2 + } + } + END { + if (!found) { + print "No matching packages found" + } + } + ' "${FILE}" +} + +function srcinfo.list_parse() { + local SRCFILE="${1}" PKGFILE="${2}" KWD="${3}" SEARCH CHILD searchlist pkglist foundname exact=false + SEARCH="${KWD%% *}" + if [[ ${KWD} == \'*\' ]]; then + exact=true + KWD="${KWD%*\'}" + KWD="${KWD#\'*}" + elif [[ ${KWD} == \"*\" ]]; then + exact=true + KWD="${KWD%*\"}" + KWD="${KWD#\"*}" else - srcinfo.die "options: read | write | packagelist | check" + KWD="${KWD,,}" fi + if [[ ${SEARCH} == *':'* && ${SEARCH} == "${KWD##* }" ]]; then + CHILD="${SEARCH#*:}" KWD="${SEARCH%:*}" + fi + mapfile -t searchlist < <(srcinfo.list_search "${1}" "${KWD}") + mapfile -t pkglist < "${PKGFILE}" + for i in "${searchlist[@]}"; do + foundname="${i%% -*}" + if [[ -n ${CHILD} && ${CHILD} != "pkgbase" && ${foundname} != "${KWD}:${CHILD}" ]] \ + || [[ ${CHILD} == "pkgbase" && ${foundname} =~ ':' && ${foundname} != *":${CHILD}" ]] \ + || [[ ${exact} == true && ${i} != *"${KWD}"* ]] \ + || [[ ${exact} == false && -n ${KWD} && ${i,,} != *"${KWD}"* ]]; then + continue + fi + if srcinfo._contains pkglist "${foundname}"; then + printf '\033[0;35m%s\033[0m - %s\n' "${foundname}" "${i#* - }" + elif srcinfo._contains pkglist "${foundname}:pkgbase"; then + printf '\033[0;35m%s:pkgbase\033[0m - %s\n' "${foundname}" "${i#* - }" + fi + done +} + +function srcinfo.list_info() { + local FILE="${1}" SEARCH="${2}" NAME FIELD + NAME="${SEARCH#*:}" PARENT="${SEARCH%:*}" + if [[ ${SEARCH} != *':'* || ${NAME} == "pkgbase" ]]; then + FIELD="pkgbase" + else + FIELD="pkgname" + fi + [[ ${NAME} == "pkgbase" ]] && NAME="${PARENT}" + awk -v pkg="${NAME}" -v field="${FIELD}" ' + BEGIN { print_pkg = 0 } + /^[[:space:]]*$/ || /^---$/ { + if (print_pkg && field == "pkgname") { + exit + } + } + /^---$/ { + print_pkg = 0 + } + { + if ($1 == "pkgbase" && $3 != pkg && field == "pkgname") { + print_pkg = 0 + } + if ($1 == field && $3 == pkg) { + print_pkg = 1 + } + if (print_pkg) { + print + } + } + ' "${FILE}" +} + +function srcinfo.head_only() { + if ! [[ -d "packages" && -d "scripts" && -f "distrolist" ]]; then + srcinfo.die "This command can only be run from the head of a repository" + fi +} + +function srcinfo.cmd() { + export GREEN=$'\033[0;32m' NC=$'\033[0m' + case "${1}" in + write) + [[ ! -f "distrolist" ]] && srcinfo.die "distrolist file required to generate .SRCINFO" + shift 1 + # list of packages to write to + ((${#@} < 1)) && srcinfo.die "Usage: ${GREEN}write${NC} { ...}" + srcinfo.write_out "${@}" + ;; + check) + shift 1 + # check if .SRCINFOs exist + ((${#@} < 1)) && srcinfo.die "Usage: ${GREEN}check${NC} { ...}" + srcinfo.repo_check "${@}" + ;; + read) + [[ -z ${3} || ${2} == *".pacscript" ]] && srcinfo.die "Usage: ${GREEN}read${NC} {pkgbase | pkgname | [ | pkgbase:]}" + # shellcheck disable=SC2086 + srcinfo.match_pkg "${2}" "${3}" ${4} + ;; + add) + srcinfo.head_only + shift 1 + # check if .SRCINFOs exist + ((${#@} < 1)) && srcinfo.die "Usage: ${GREEN}add${NC} { ...}" + local allin + for i in "${@}"; do + allin+=("packages/${i}/${i}.pacscript") + done + srcinfo.write_out "${allin[@]}" \ + && srcinfo.repo_check "${allin[@]}" \ + && srcinfo.pkg_list > "packagelist" \ + && srcinfo.list_build "srclist" + ;; + build) + srcinfo.head_only + case "${2}" in + packagelist) + srcinfo.pkg_list > "packagelist" + ;; + srclist) + srcinfo.list_build "srclist" + ;; + all) + srcinfo.pkg_list > "packagelist" + srcinfo.list_build "srclist" + ;; + *) + srcinfo.die "Usage: ${GREEN}build${NC} {packagelist | srclist | all}" + ;; + esac + ;; + search) + srcinfo.head_only + [[ -z ${2} ]] && srcinfo.die "Usage: ${GREEN}search${NC} { | : | | <'keyword string'>}" + [[ ! -f "packagelist" ]] && srcinfo.pkg_list > "packagelist" + [[ ! -f "srclist" ]] && srcinfo.list_build "srclist" + srcinfo.list_parse "srclist" "packagelist" "${2}" + ;; + info) + srcinfo.head_only + [[ -z ${2} ]] && srcinfo.die "Usage: ${GREEN}info${NC} { | :}" + [[ ! -f "srclist" ]] && srcinfo.list_build "srclist" + srcinfo.list_info "srclist" "${2}" + ;; + help) + local all_cmds=("add" "search" "info" "build" "read" "write" "check") option usage + case "${2}" in + add) + usage="{ ...}" + purpose="Generate .SRCINFOs, check their statuses, and update the packagelist + srclist" + ;; + search) + usage="{ | : | | <'keyword string'>}" + purpose="Search for matching packages and descriptions from the srclist file" + ;; + info) + usage="{ | :}" + purpose="Parse a package or child .SRCINFO from the srclist file" + ;; + build) + usage="{packagelist | srclist | all}" + purpose="Update the packagelist and/or srclist" + ;; + read) + usage=" {pkgbase | pkgname | [ | pkgbase:]}" + purpose="Parse variables and arrays from a .SRCINFO file" + ;; + write) + usage="{ ...}" + purpose="Generate .SRCINFOs for pacscripts" + ;; + check) + usage="{ ...}" + purpose="Check the git status of .SRCINFOs for pacscripts" + ;; + all) + for i in "${all_cmds[@]}"; do + ${0} help "${i}" + done + exit 0 + ;; + *) + usage="{add | search | info | build | read | write | check | help [all | ]}" + purpose="Help maintainers and repo owners easily manage package data generation" + ;; + esac + if srcinfo._contains all_cmds "${2}"; then + option="${2}" + else + option="${0##*/}" + fi + printf '[\033[0;33mHELP\033[0m] Command: \033[0;32m%s\033[0m\n' "${option}" + printf ' [\033[1;33m?\033[0m] \033[0;32mUsage\033[0m: %s %s\n' "${option}" "${usage}" + printf ' [\033[1;33m?\033[0m] \033[0;32mPurpose\033[0m: %s\n' "${purpose}" + ;; + *) + srcinfo.die "Usage: ${GREEN}${0##*/}${NC} {add | search | info | build | read | write | check | help}" + ;; + esac } srcinfo.cmd "${@}"