From 15514b1013a22977a451c64b6df229414a9dde6b Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Wed, 23 Sep 2020 13:13:05 +0300 Subject: Add package-archive.bash public module --- bpkg-rep/buildfile | 10 +- bpkg-rep/package-archive.bash.in | 167 ++++++++++++++++++++++++++ build/export.build | 3 +- manifest | 1 + repositories.manifest | 4 + tests/.gitignore | 1 + tests/build/root.build | 6 +- tests/buildfile | 4 +- tests/package-archive/buildfile | 8 ++ tests/package-archive/driver.in | 20 +++ tests/package-archive/libhello-0.1.0+1.tar.gz | Bin 0 -> 3045 bytes tests/package-archive/libhello-0.1.0.tar.gz | Bin 0 -> 3033 bytes tests/package-archive/testscript | 127 ++++++++++++++++++++ tests/publish.testscript | 8 -- tests/publish/buildfile | 8 ++ tests/publish/testscript | 8 ++ 16 files changed, 360 insertions(+), 15 deletions(-) create mode 100644 bpkg-rep/package-archive.bash.in create mode 100644 tests/package-archive/buildfile create mode 100644 tests/package-archive/driver.in create mode 100644 tests/package-archive/libhello-0.1.0+1.tar.gz create mode 100644 tests/package-archive/libhello-0.1.0.tar.gz create mode 100644 tests/package-archive/testscript delete mode 100644 tests/publish.testscript create mode 100644 tests/publish/buildfile create mode 100644 tests/publish/testscript diff --git a/bpkg-rep/buildfile b/bpkg-rep/buildfile index 6f84fd1..9177409 100644 --- a/bpkg-rep/buildfile +++ b/bpkg-rep/buildfile @@ -1,8 +1,16 @@ # file : bpkg-rep/buildfile # license : MIT; see accompanying LICENSE file -./: exe{bpkg-rep-publish} +import mods = libbutl.bash%bash{manifest-parser} + +./: exe{bpkg-rep-publish} bash{package-archive} exe{bpkg-rep-publish}: in{publish} bash{utility} +# Public modules. +# +bash{package-archive}: in{package-archive} $mods + +# Private modules. +# bash{utility}: in{utility} diff --git a/bpkg-rep/package-archive.bash.in b/bpkg-rep/package-archive.bash.in new file mode 100644 index 0000000..3669a1b --- /dev/null +++ b/bpkg-rep/package-archive.bash.in @@ -0,0 +1,167 @@ +# file : bpkg-rep/package-archive.bash.in +# license : MIT; see accompanying LICENSE file + +# Utility functions useful for managing package archives. + +if [ "$bpkg_rep_package_archive" ]; then + return 0 +else + bpkg_rep_package_archive=true +fi + +@import libbutl/manifest-parser@ + +# We expect the user to set the bpkg_rep_bpkg variable to the bpkg program +# path. +# +if [ ! -v bpkg_rep_bpkg ]; then + echo "error: variable bpkg_rep_bpkg is not set" >&2 + exit 1 +fi + +# Extract the package information from a package archive and print it in the +# ' ' form, where the project field is empty if the +# project value is not specified in the manifest. +# +function bpkg_rep_pkg_verify_archive () # +{ + # We can't use the process substitution for input redirect here, since such + # a process failure is not trapped. Thus, read the manifest file into a + # variable and parse it afterwards, which is probably ok since package + # manifests are normally not too big. + # + # Note that alternatively we could use the process substitution for running + # bpkg, treat the name value absence as indication of a failure, and exit + # with non-zero status if that's the case. Feels a bit hackish though. + # + local m + m="$("$bpkg_rep_bpkg" pkg-verify --manifest "$1")" + + butl_manifest_parser_start <<<"$m" + + local name= + local version= + local project= + + local n v + while IFS=: read -ru "$butl_manifest_parser_ofd" -d '' n v; do + case "$n" in + name) name="$v" ;; + version) version="$v" ;; + project) project="$v" ;; + esac + done + + butl_manifest_parser_finish + + echo -n "$name $version $project" +} + +# Search for package archives in a directory using the package name and +# version pattern and printing their paths newline-separated. If the version +# argument is '*', then print archives for all package versions. Otherwise if +# the version contains the trailing '*', then print archives for all revisions +# of the specified version and for the exact version otherwise. For example: +# +# bpkg_rep_pkg_find_archives foo '*' dir/ +# bpkg_rep_pkg_find_archives foo '1.0*' dir/ +# bpkg_rep_pkg_find_archives foo '1.0' dir/ +# +# Note that the resulting archive paths include the specified directory as a +# prefix. +# +# NOTE: this function can be called with overriden IFS. +# +function bpkg_rep_pkg_find_archives () # +{ + IFS=$' \t\n' bpkg_rep_pkg_find_archives_impl "$@" +} + +function bpkg_rep_pkg_find_archives_impl () +{ + local nam="$1" + local ver="$2" + local dir="$3" + + local r="" + + if [ -d "$dir" ]; then + local vr # Version with the revision stripped, if search for revisions. + local np # File name pattern for archives search. + + if [ "$ver" != "*" -a "${ver: -1}" == "*" ]; then # * + vr="$(sed -n -re 's%^(\+?[^+]+)(\+[0-9]+)?\*$%\1%p' <<<"$ver")" + np="$nam-$vr*.*" # foo-1.0*.*, etc. + else # * or + np="$nam-$ver.*" # foo-*.*, foo-1.0.*, etc. + fi + + # Go through the potentially matching archives (for example, for 'foo' + # '1.2.3+2*': foo-1.2.3.tar.gz, foo-1.2.3+1.tar.gz, foo-1.2.30.tar.gz, + # etc) and return those which package name and version match properly. + # + local f + while read f; do + local p + p=($(bpkg_rep_pkg_verify_archive "$f")) + + local n="${p[0]}" + local v="${p[1]}" + + if [[ "$n" == "$nam" && + ( "$ver" == "*" || \ + "$v" == "$ver" || \ + ( -n "$vr" && "$v" =~ ^"$vr"(\+[0-9]+)?$ )) ]]; then + + if [ -n "$r" ]; then + r="$r"$'\n'"$f" + else + r="$f" + fi + fi + done < <(find "$dir" -type f -name "$np") + fi + + if [ -n "$r" ]; then + echo -n "$r" + fi +} + +# Search for a package archive in a directory using a file name pattern. If +# the archive is found, then print the package information in the +# '\n\n\n' form, where the project field is +# empty if the project value is not specified in the manifest. +# +# Note that if there are multiple archives matching the pattern, then it is +# unspecified which one is picked. +# +# NOTE: this function can be called with overriden IFS. +# +function bpkg_rep_pkg_find_archive () # +{ + IFS=$' \t\n' bpkg_rep_pkg_find_archive_impl "$@" +} + +function bpkg_rep_pkg_find_archive_impl () +{ + local pat="$1" + local dir="$2" + + if [ -d "$dir" ]; then + local f + + # We could probably use -print -quit but this is not portable (NetBSD + # needs -exit instead of -quit). + # + f="$(find "$dir" -type f -name "$pat" | head -n 1)" + + if [ -n "$f" ]; then + + local p + p=($(bpkg_rep_pkg_verify_archive "$f")) + + printf "${p[0]}\n${p[1]}\n${p[2]}\n$f" + return + fi + fi +} diff --git a/build/export.build b/build/export.build index 6c09e3e..1ac1b48 100644 --- a/build/export.build +++ b/build/export.build @@ -9,5 +9,6 @@ $out_root/ switch $import.target { case exe{bpkg-rep-publish} - export $out_root/bpkg-rep/exe{bpkg-rep-publish} + case bash{package-archive} + export $out_root/bpkg-rep/$import.target } diff --git a/manifest b/manifest index f06deed..496e8bf 100644 --- a/manifest +++ b/manifest @@ -19,3 +19,4 @@ requires: bash >= 4.3 depends: * build2 >= 0.13.0 depends: * bpkg >= 0.13.0 depends: bpkg [0.14.0-a.0.1 0.14.0-a.1) +depends: libbutl.bash [0.14.0-a.0.1 0.14.0-a.1) diff --git a/repositories.manifest b/repositories.manifest index b78709d..6fc6dd2 100644 --- a/repositories.manifest +++ b/repositories.manifest @@ -4,3 +4,7 @@ summary: bpkg repository management utilities repository : role: prerequisite location: ../bpkg.git##HEAD + +: +role: prerequisite +location: ../libbutl.bash.git##HEAD diff --git a/tests/.gitignore b/tests/.gitignore index 35ec43f..2e508a9 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -1,2 +1,3 @@ +driver test/ test-*/ diff --git a/tests/build/root.build b/tests/build/root.build index 1472384..c3d4252 100644 --- a/tests/build/root.build +++ b/tests/build/root.build @@ -1,6 +1,8 @@ # file : tests/build/root.build # license : MIT; see accompanying LICENSE file -# Setup the utilities that we are testing. +using bash + +# Every exe{} in this subproject is by default a test. # -import publish = bpkg-rep%exe{bpkg-rep-publish} +exe{*}: test = true diff --git a/tests/buildfile b/tests/buildfile index 0beb40f..556ed55 100644 --- a/tests/buildfile +++ b/tests/buildfile @@ -1,6 +1,4 @@ # file : tests/buildfile # license : MIT; see accompanying LICENSE file -./: testscript{*} $publish - -testscript{publish}@./: test = $publish +./: {*/ -build/} diff --git a/tests/package-archive/buildfile b/tests/package-archive/buildfile new file mode 100644 index 0000000..a8ff42b --- /dev/null +++ b/tests/package-archive/buildfile @@ -0,0 +1,8 @@ +# file : tests/package-archive/buildfile +# license : MIT; see accompanying LICENSE file + +import mods = bpkg-rep%bash{package-archive} + +./: exe{driver} file{*.tar.gz} + +exe{driver}: in{driver} $mods testscript diff --git a/tests/package-archive/driver.in b/tests/package-archive/driver.in new file mode 100644 index 0000000..d760a1e --- /dev/null +++ b/tests/package-archive/driver.in @@ -0,0 +1,20 @@ +#!/usr/bin/env bash + +# file : tests/package-archive/driver.in +# license : MIT; see accompanying LICENSE file + +# bpkg utility path. +# +bpkg_rep_bpkg=bpkg + +trap "{ exit 1; }" ERR +set -o errtrace # Trap ERR in functions. + +@import bpkg-rep/package-archive@ + +# Call the function passed on the command line. +# +# Note that we reset IFS to make sure that the function being tested is not +# affected by its value set by the caller. +# +IFS= "$@" diff --git a/tests/package-archive/libhello-0.1.0+1.tar.gz b/tests/package-archive/libhello-0.1.0+1.tar.gz new file mode 100644 index 0000000..8d6bde9 Binary files /dev/null and b/tests/package-archive/libhello-0.1.0+1.tar.gz differ diff --git a/tests/package-archive/libhello-0.1.0.tar.gz b/tests/package-archive/libhello-0.1.0.tar.gz new file mode 100644 index 0000000..604a536 Binary files /dev/null and b/tests/package-archive/libhello-0.1.0.tar.gz differ diff --git a/tests/package-archive/testscript b/tests/package-archive/testscript new file mode 100644 index 0000000..6124d4a --- /dev/null +++ b/tests/package-archive/testscript @@ -0,0 +1,127 @@ +# file : tests/package-archive/testscript +# license : MIT; see accompanying LICENSE file + +# Note that searching for packages directly in $src_base is a bad idea, since +# removing testscript working directories while testing in source makes the +# find utility to fail with the 'no such file or directory' error. Thus, we +# clone the archives into the test working directories and search there. +# +clone_arcs = \ + cp $src_base/libhello-0.1.0.tar.gz $src_base/libhello-0.1.0+1.tar.gz ./ + +: pkg-verify-archive +: +{ + test.arguments += bpkg_rep_pkg_verify_archive + + : non-existing-archive + : + $* libhello-0.1.0.tar.gz 2>>EOE != 0 + error: archive file 'libhello-0.1.0.tar.gz' does not exist + EOE + + : success + : + $* $src_base/libhello-0.1.0.tar.gz >:'libhello 0.1.0 hello' +} + +: pkg-find-archives +: +{ + test.arguments += bpkg_rep_pkg_find_archives + + : none + : + { + $clone_arcs; + + $* 'libhello' '0.2.0' $~ + } + + : package + : + { + $clone_arcs; + + $* 'libhello' '*' $~ >>:/~"%EOO%" + %\( + $~/libhello-0.1.0.tar.gz + $~/libhello-0.1.0+1.tar.gz + %| + $~/libhello-0.1.0+1.tar.gz + $~/libhello-0.1.0.tar.gz + %\) + EOO + } + + : package-version + : + { + $clone_arcs; + + $* 'libhello' '0.1.0' $~ >:/"$~/libhello-0.1.0.tar.gz" + } + + : package-revision + : + { + $clone_arcs; + + $* 'libhello' '0.1.0+1' $~ >:/"$~/libhello-0.1.0+1.tar.gz" + } + + : package-revisions1 + : + { + $clone_arcs; + + $* 'libhello' '0.1.0*' $~ >>:/~"%EOO%" + %\( + $~/libhello-0.1.0.tar.gz + $~/libhello-0.1.0+1.tar.gz + %| + $~/libhello-0.1.0+1.tar.gz + $~/libhello-0.1.0.tar.gz + %\) + EOO + } + + : package-revisions2 + : + { + $clone_arcs; + + $* 'libhello' '0.1.0+2*' $~ >>:/~"%EOO%" + %\( + $~/libhello-0.1.0.tar.gz + $~/libhello-0.1.0+1.tar.gz + %| + $~/libhello-0.1.0+1.tar.gz + $~/libhello-0.1.0.tar.gz + %\) + EOO + } +} + +: pkg-find-archive +: +{ + test.arguments += bpkg_rep_pkg_find_archive + + : non-existent + : + $* 'libhello-0.1.0.*' $~ + + : existing + : + { + $clone_arcs; + + $* 'libhello-0.1.0.*' $~ >>:/"EOO" + libhello + 0.1.0 + hello + $~/libhello-0.1.0.tar.gz + EOO + } +} diff --git a/tests/publish.testscript b/tests/publish.testscript deleted file mode 100644 index f11a171..0000000 --- a/tests/publish.testscript +++ /dev/null @@ -1,8 +0,0 @@ -# file : tests/publish.testscript -# license : MIT; see accompanying LICENSE file - -: args -: -{ - $* 2>~"/usage: .+/" != 0 : no-args -} diff --git a/tests/publish/buildfile b/tests/publish/buildfile new file mode 100644 index 0000000..4cc1bd0 --- /dev/null +++ b/tests/publish/buildfile @@ -0,0 +1,8 @@ +# file : tests/publish/buildfile +# license : MIT; see accompanying LICENSE file + +import publish = bpkg-rep%exe{bpkg-rep-publish} + +./: testscript $publish + +testscript@./: test = $publish diff --git a/tests/publish/testscript b/tests/publish/testscript new file mode 100644 index 0000000..2d3fb4c --- /dev/null +++ b/tests/publish/testscript @@ -0,0 +1,8 @@ +# file : tests/publish/testscript +# license : MIT; see accompanying LICENSE file + +: args +: +{ + $* 2>~"/usage: .+/" != 0 : no-args +} -- cgit v1.1