aboutsummaryrefslogtreecommitdiff
path: root/brep/handler/submit
diff options
context:
space:
mode:
Diffstat (limited to 'brep/handler/submit')
-rw-r--r--brep/handler/submit/.gitignore1
-rw-r--r--brep/handler/submit/buildfile5
-rw-r--r--brep/handler/submit/submit-dir.in10
-rw-r--r--brep/handler/submit/submit-git.bash.in102
-rw-r--r--brep/handler/submit/submit-git.in50
-rw-r--r--brep/handler/submit/submit-pub.in435
-rw-r--r--brep/handler/submit/submit.bash.in57
7 files changed, 596 insertions, 64 deletions
diff --git a/brep/handler/submit/.gitignore b/brep/handler/submit/.gitignore
index cbbd541..098bf75 100644
--- a/brep/handler/submit/.gitignore
+++ b/brep/handler/submit/.gitignore
@@ -1,2 +1,3 @@
brep-submit-dir
brep-submit-git
+brep-submit-pub
diff --git a/brep/handler/submit/buildfile b/brep/handler/submit/buildfile
index 7951a46..1747c64 100644
--- a/brep/handler/submit/buildfile
+++ b/brep/handler/submit/buildfile
@@ -1,8 +1,7 @@
# file : brep/handler/submit/buildfile
-# copyright : Copyright (c) 2014-2019 Code Synthesis Ltd
# license : MIT; see accompanying LICENSE file
-./: exe{brep-submit-dir} exe{brep-submit-git}
+./: exe{brep-submit-dir} exe{brep-submit-git} exe{brep-submit-pub}
include ../
@@ -11,5 +10,7 @@ exe{brep-submit-dir}: in{submit-dir} bash{submit} ../bash{handler}
exe{brep-submit-git}: in{submit-git} \
bash{submit-git} bash{submit} ../bash{handler}
+exe{brep-submit-pub}: in{submit-pub} bash{submit} ../bash{handler}
+
bash{submit}: in{submit} ../bash{handler}
bash{submit-git}: in{submit-git} bash{submit} ../bash{handler}
diff --git a/brep/handler/submit/submit-dir.in b/brep/handler/submit/submit-dir.in
index ae0dcbd..b28ab38 100644
--- a/brep/handler/submit/submit-dir.in
+++ b/brep/handler/submit/submit-dir.in
@@ -1,7 +1,6 @@
#!/usr/bin/env bash
# file : brep/handler/submit/submit-dir.in
-# copyright : Copyright (c) 2014-2019 Code Synthesis Ltd
# license : MIT; see accompanying LICENSE file
# Simple package submission handler with directory storage.
@@ -67,20 +66,17 @@ fi
m="$data_dir/package.manifest"
extract_package_manifest "$data_dir/$archive" "$m"
-# Parse the package manifest and obtain the package name, version, and
-# project.
+# Parse the package manifest and obtain the package name and version.
#
manifest_parser_start "$m"
name=
version=
-project=
while IFS=: read -ru "$manifest_parser_ofd" -d '' n v; do
case "$n" in
name) name="$v" ;;
version) version="$v" ;;
- project) project="$v" ;;
esac
done
@@ -94,10 +90,6 @@ if [ -z "$version" ]; then
error "version manifest value expected"
fi
-if [ -z "$project" ]; then
- project="$name"
-fi
-
if [ -n "$simulate" ]; then
run rm -r "$data_dir"
trace "package submission is simulated: $name/$version"
diff --git a/brep/handler/submit/submit-git.bash.in b/brep/handler/submit/submit-git.bash.in
index 56cce33..cf7300d 100644
--- a/brep/handler/submit/submit-git.bash.in
+++ b/brep/handler/submit/submit-git.bash.in
@@ -1,5 +1,4 @@
# file : brep/handler/submit/submit-git.bash.in
-# copyright : Copyright (c) 2014-2019 Code Synthesis Ltd
# license : MIT; see accompanying LICENSE file
# Utility functions for the submit-git handler.
@@ -60,6 +59,10 @@ function owners_dir () # <repo-dir>
# Check if a repository already contains the package. Respond with the
# 'duplicate submission' result manifest and exit if that's the case.
#
+# Also check if the repository contains newer revision of this package
+# version. Respond with the 'newer revision is present' result manifest and
+# exit if that's the case.
+#
function check_package_duplicate () # <name> <version> <repo-dir>
{
trace_func "$@"
@@ -73,22 +76,54 @@ function check_package_duplicate () # <name> <version> <repo-dir>
run source "$rep/submit.config.bash"
- # Check for duplicate package in all sections. Use <name>-<version>.*
- # without .tar.gz in case we want to support more archive types later.
+ local rev
+ rev="$(version_revision "$ver")"
+
+ # Check for duplicate package and its newer revisions in all sections. Use
+ # <name>-<version>.* without .tar.gz in case we want to support more archive
+ # types later.
#
local s
for s in "${!sections[@]}"; do
local d="$rep/${sections[$s]}"
- if [ -d "$d" ]; then
- local f
- f="$(run find "$d" -name "$nam-$ver.*")"
+ # Check for duplicate.
+ #
+ local p
+ run pkg_find_archive "$nam-$ver.*" "$d" | readarray -t p
+
+ if [ "${#p[@]}" -ne 0 ]; then
+ local n="${p[1]}"
+ local v="${p[2]}"
- if [ -n "$f" ]; then
- trace "found: $f"
+ trace "found: $n/$v in ${p[0]}"
+
+ if [ "$n" == "$nam" ]; then
exit_with_manifest 422 "duplicate submission"
+ else
+ exit_with_manifest 422 "submission conflicts with $n/$v"
fi
fi
+
+ # Check for newer revision.
+ #
+ local arcs
+ run pkg_find_archives "$nam" "$ver*" "$d" | readarray -t arcs
+
+ local f
+ for f in "${arcs[@]}"; do
+ local p
+ pkg_verify_archive "$f" | readarray -t p
+
+ local v="${p[1]}"
+
+ local rv
+ rv="$(version_revision "$v")"
+
+ if [ "$rv" -gt "$rev" ]; then
+ exit_with_manifest 422 "newer revision $nam/$v is present"
+ fi
+ done
done
}
@@ -164,6 +199,7 @@ function auth_project () # <project> <control> <repo-dir>
local r="unknown"
local m="$d/$prj/project-owner.manifest"
+ local info=
# If the project owner manifest exists then parse it and try to authenticate
# the submitter as the project owner.
@@ -176,16 +212,31 @@ function auth_project () # <project> <control> <repo-dir>
local n v
while IFS=: read -ru "$manifest_parser_ofd" -d '' n v; do
- if [[ "$n" == "control" && "$ctl" == "$v"* ]]; then
- r="project"
- break
+ if [[ "$n" == "control" ]]; then
+ if [[ "$ctl" == "$v"* ]]; then
+ r="project"
+ break
+ fi
+
+ # If the control URLs don't match, then compare them case-
+ # insensitively, converting them to the lower case. If they match
+ # case-insensitively, then still fail the authentication but provide
+ # additional information in the manifest message value.
+ #
+ if [[ "${ctl,,}" == "${v,,}"* ]]; then
+ info="
+ info: control repository URL differs only in character case
+ info: submitted URL: $ctl
+ info: project owner's URL: $v
+ info: consider using --control to specify exact URL"
+ fi
fi
done
manifest_parser_finish
if [ "$r" != "project" ]; then
- exit_with_manifest 401 "project owner authentication failed"
+ exit_with_manifest 401 "project owner authentication failed$info"
fi
fi
@@ -211,7 +262,8 @@ function auth_package () # <project> <package> <control> <repo-dir>
local prj="$1"
local pkg="$2"
- local ctl="${3%.git}" # Strip the potential .git extension.
+ local ctl="${3%.git}" # For comparison strip the potential .git extension.
+ local ctl_orig="$3" # For diagnostics use the original URL.
local rep="$4"
local d
@@ -228,6 +280,7 @@ function auth_package () # <project> <package> <control> <repo-dir>
local r="unknown"
local m="$d/$prj/$pkg/package-owner.manifest"
+ local info=
# If the package owner manifest exists then parse it and try to authenticate
# the submitter as the package owner.
@@ -242,16 +295,31 @@ function auth_package () # <project> <package> <control> <repo-dir>
#
local n v
while IFS=: read -ru "$manifest_parser_ofd" -d '' n v; do
- if [ "$n" == "control" -a "${v%.git}" == "$ctl" ]; then
- r="package"
- break
+ if [ "$n" == "control" ]; then
+ local u="${v%.git}"
+
+ if [ "$u" == "$ctl" ]; then
+ r="package"
+ break
+ fi
+
+ # If the control URLs don't match, then compare them case-
+ # insensitively (see auth_project() for details).
+ #
+ if [ "${u,,}" == "${ctl,,}" ]; then
+ info="
+ info: control repository URL differs only in character case
+ info: submitted URL: $ctl_orig
+ info: package owner's URL: $v
+ info: consider using --control to specify exact URL"
+ fi
fi
done
manifest_parser_finish
if [ "$r" != "package" ]; then
- exit_with_manifest 401 "package owner authentication failed"
+ exit_with_manifest 401 "package owner authentication failed$info"
fi
fi
diff --git a/brep/handler/submit/submit-git.in b/brep/handler/submit/submit-git.in
index 8263efe..c882b84 100644
--- a/brep/handler/submit/submit-git.in
+++ b/brep/handler/submit/submit-git.in
@@ -1,7 +1,6 @@
#!/usr/bin/env bash
# file : brep/handler/submit/submit-git.in
-# copyright : Copyright (c) 2014-2019 Code Synthesis Ltd
# license : MIT; see accompanying LICENSE file
# Package submission handler with git repository storage.
@@ -187,8 +186,10 @@ git_timeout=10
ref_lock_timeout=30
trap "{ exit 1; }" ERR
-set -o errtrace # Trap ERR in functions.
-set -o pipefail # Return the rightmost non-zero exit status in a pipeline.
+set -o errtrace # Trap in functions and subshells.
+set -o pipefail # Fail if any pipeline command fails.
+shopt -s lastpipe # Execute last pipeline command in the current shell.
+shopt -s nullglob # Expand no-match globs to nothing rather than themselves.
@import brep/handler/handler@
@import brep/handler/submit/submit@
@@ -404,7 +405,7 @@ function git_add () # <repo-dir> <path>...
local d="$1"
shift
- run git -C "$d" add $gvo "$@" >&2
+ run git -C "$d" add --force $gvo "$@" >&2
}
# For now we make 10 re-tries to add the package and push to target. Push can
@@ -440,8 +441,12 @@ for i in {1..11}; do
trace "+ exec {fd}<$l"
exec {fd}<"$l"
+ # Note that on the locking failure we don't suggest the user to try again,
+ # since the client program may suggest to re-try later for all server
+ # errors (as bdep-publish(1) does).
+ #
if ! run flock -w "$ref_lock_timeout" "$fd"; then
- exit_with_manifest 503 "submission service temporarily unavailable"
+ exit_with_manifest 503 "submission service is busy"
fi
# Pull the reference repository.
@@ -558,7 +563,7 @@ for i in {1..11}; do
#
prj_man="$d/project-owner.manifest"
- if [ ! -f "$prj_man" ]; then
+ if [ ! -f "$prj_man" -a "$ref_auth" != "project" ]; then
run mkdir -p "$d" # Also creates the owners directory if not exist.
ctl="$(repository_base "$control")"
@@ -575,7 +580,7 @@ for i in {1..11}; do
# Now the package name.
#
d="$d/$name"
- run mkdir "$d"
+ run mkdir -p "$d" # Also creates the project directory if not exist.
pkg_man="$d/package-owner.manifest"
@@ -636,34 +641,21 @@ for i in {1..11}; do
exit_with_manifest 400 "unrecognized section '$section'"
fi
- # Strips the version revision part, if present.
- #
- v="$(sed -n -re 's%^(\+?[^+]+)(\+[0-9]+)?$%\1%p' <<<"$version")"
-
- # Make sure the section directory exists before we run find in it.
- #
- d="$tgt_dir/$s/$project"
- run mkdir -p "$d" # Create all the parent directories as well.
+ run pkg_find_archives "$name" "$version*" "$tgt_dir/$s" | readarray -t arcs
- # 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 remove those that match exactly.
- #
- # Change CWD to the section directory to make sure that the found archive
- # paths don't contain spaces.
- #
- fs=($(run cd "$tgt_dir/$s" && run find -name "$name-$v*"))
-
- for f in "${fs[@]}"; do
- if [[ "$f" =~ ^\./[^/]+/"$name-$v"(\+[0-9]+)?\.[^/]+$ ]]; then
- run git -C "$tgt_dir" rm $gqo "$s/$f" >&2
- fi
+ for f in "${arcs[@]}"; do
+ run git -C "$tgt_dir" rm $gqo "${f#$tgt_dir/}" >&2
done
# Finally, add the package archive to the target repository.
#
- # We copy the archive rather than move it since we may need it for a re-try.
+ # Copy the archive rather than move it since we may need it for a re-try.
+ # Make sure the project directory exists before we copy the archive into it.
+ # Note that it was removed by git-rm if it became empty.
#
+ d="$tgt_dir/$s/$project"
+ run mkdir -p "$d" # Create all the parent directories as well.
+
a="$d/$archive"
run cp "$data_dir/$archive" "$a"
diff --git a/brep/handler/submit/submit-pub.in b/brep/handler/submit/submit-pub.in
new file mode 100644
index 0000000..42d478d
--- /dev/null
+++ b/brep/handler/submit/submit-pub.in
@@ -0,0 +1,435 @@
+#!/usr/bin/env bash
+
+# file : brep/handler/submit/submit-pub.in
+# license : MIT; see accompanying LICENSE file
+
+# Package submission handler with direct repository publishing.
+#
+# The overall idea behind this handler is to directly add the package to a
+# private/trusted (unsigned) pkg repository with a simple structure (no
+# sections). Upon successful execution of this handler no additional steps are
+# required.
+#
+# Specifically, the handler performs the following steps:
+#
+# - Lock the repository directory for the duration of the package submission.
+#
+# - Check for the package duplicate.
+#
+# - Create the new repository as a hardlink-copy of the current one.
+#
+# - Remove any package revisions, if present.
+#
+# - Validate and add the package archive to the new repository (with project
+# subdirectory).
+#
+# - Re-generate the new repository without signing.
+#
+# - Verify that the new repository is loadable into the brep package database.
+#
+# - Atomically switch the repository symlink to refer to the new repository.
+#
+# - Release the lock and remove the old repository.
+#
+# The repository argument (<repo>) should be an absolute path to a symbolic
+# link to the pkg repository directory, with the archive and manifest files
+# residing in its 1/ subdirectory. The base name of the <repo> path is used
+# as a base for new repository directories.
+#
+# Unless the handler is called for testing, the loader program's absolute path
+# and options should be specified so that the handler can verify that the
+# package is loadable into the brep package database (this makes sure the
+# package dependencies are resolvable, etc).
+#
+# Notes:
+#
+# - Filesystem entries that exist or are created in the data directory:
+#
+# <pkg>-<ver>.tar.gz saved by brep (could be other archives in the future)
+# request.manifest created by brep
+# package.manifest extracted by the handler
+# loadtab created by the handler
+# result.manifest saved by brep
+#
+# Options:
+#
+# --user <name>
+#
+# Re-execute itself under the specified user.
+#
+# Note that the repository can also be modified manually (e.g., to remove
+# packages). This option is normally specified to make sure that all the
+# repository filesystem entries belong to a single user, which, in
+# particular, can simplify their permissions handling (avoid extra ACLs,
+# etc).
+#
+# Note that if this option is specified, then current user (normally the
+# user under which Apache2 is running) must be allowed to execute sudo
+# without a password, which is only recommended in private/trusted
+# environments.
+#
+# --result-url <url>
+#
+# Result URL base for the response. If specified, the handler appends the
+# <package>/<version> to this value and includes the resulting URL in the
+# response message.
+#
+usage="usage: $0 [<options>] [<loader-path> <loader-options>] <repo> <dir>"
+
+# Diagnostics.
+#
+verbose= #true
+
+# The repository lock timeout (seconds).
+#
+rep_lock_timeout=60
+
+trap "{ exit 1; }" ERR
+set -o errtrace # Trap in functions and subshells.
+set -o pipefail # Fail if any pipeline command fails.
+shopt -s lastpipe # Execute last pipeline command in the current shell.
+shopt -s nullglob # Expand no-match globs to nothing rather than themselves.
+
+@import brep/handler/handler@
+@import brep/handler/submit/submit@
+
+# Parse the command line options and, while at it, compose the arguments array
+# for potential re-execution under a different user.
+#
+user=
+result_url=
+
+scr_exe="$(realpath "${BASH_SOURCE[0]}")"
+scr_dir="$(dirname "$scr_exe")"
+
+args=("$scr_exe")
+
+while [ "$#" -gt 0 ]; do
+ case $1 in
+ --user)
+ shift
+ user="$1"
+ shift
+ ;;
+ --result-url)
+ args+=("$1")
+ shift
+ result_url="${1%/}"
+ args+=("$1")
+ shift
+ ;;
+ *)
+ break; # The end of options is encountered.
+ ;;
+ esac
+done
+
+loader_args=() # The loader path and options.
+
+# Assume all the remaining arguments except for the last two (repository
+# symlink and data directory) as the loader program path and arguments.
+#
+while [ "$#" -gt 2 ]; do
+ loader_args+=("$1")
+ args+=("$1")
+ shift
+done
+
+if [ "$#" -ne 2 ]; then
+ error "$usage"
+fi
+
+# pkg repository symlink.
+#
+repo="${1%/}"
+shift
+
+if [ -z "$repo" ]; then
+ error "$usage"
+fi
+
+# Submission data directory.
+#
+data_dir="${1%/}"
+shift
+
+if [ -z "$data_dir" ]; then
+ error "$usage"
+fi
+
+# Re-execute itself under a different user, if requested.
+#
+if [ -n "$user" ]; then
+ args+=("$repo" "$data_dir")
+
+ # Compose the arguments string to pass to the su program, quoting empty
+ # arguments as well as those that contain spaces. Note that here, for
+ # simplicity, we assume that the arguments may not contain '"'.
+ #
+ as=
+ for a in "${args[@]}"; do
+ if [ -z "$a" -o -z "${a##* *}" ]; then
+ a="\"$a\""
+ fi
+ if [ -n "$as" ]; then
+ a=" $a"
+ fi
+ as="$as$a"
+ done
+
+ run exec sudo --non-interactive su -l "$user" -c "$as"
+fi
+
+# Check path presence (do it after user switch for permissions).
+#
+if [ ! -L "$repo" ]; then
+ error "'$repo' does not exist or is not a symlink"
+fi
+
+if [ ! -d "$data_dir" ]; then
+ error "'$data_dir' does not exist or is not a directory"
+fi
+
+reference="$(basename "$data_dir")"
+
+# Parse the submission request manifest and obtain the archive path as well as
+# the simulate value.
+#
+manifest_parser_start "$data_dir/request.manifest"
+
+archive=
+simulate=
+
+while IFS=: read -ru "$manifest_parser_ofd" -d '' n v; do
+ case "$n" in
+ archive) archive="$v" ;;
+ simulate) simulate="$v" ;;
+ esac
+done
+
+manifest_parser_finish
+
+if [ -z "$archive" ]; then
+ error "archive manifest value expected"
+fi
+
+if [ -n "$simulate" -a "$simulate" != "success" ]; then
+ exit_with_manifest 400 "unrecognized simulation outcome '$simulate'"
+fi
+
+m="$data_dir/package.manifest"
+extract_package_manifest "$data_dir/$archive" "$m"
+
+# Parse the package manifest and obtain the package name, version, and
+# project.
+#
+manifest_parser_start "$m"
+
+name=
+version=
+project=
+
+while IFS=: read -ru "$manifest_parser_ofd" -d '' n v; do
+ case "$n" in
+ name) name="$v" ;;
+ version) version="$v" ;;
+ project) project="$v" ;;
+ esac
+done
+
+manifest_parser_finish
+
+if [ -z "$name" ]; then
+ error "name manifest value expected"
+fi
+
+if [ -z "$version" ]; then
+ error "version manifest value expected"
+fi
+
+if [ -z "$project" ]; then
+ project="$name"
+fi
+
+if [ -n "$result_url" ]; then
+ message_suffix=": $result_url/$name/$version"
+else
+ message_suffix=": $name/$version"
+fi
+
+revision="$(version_revision "$version")"
+
+# Open the reading file descriptor and lock the repository. Fail if unable to
+# lock before timeout.
+#
+l="$repo.lock"
+run touch "$l"
+trace "+ exec {lfd}<$l"
+exec {lfd}<"$l"
+
+# Note that on the locking failure we don't suggest the user to try again,
+# since the client program may suggest to re-try later for all server errors
+# (as bdep-publish(1) does).
+#
+if ! run flock -w "$rep_lock_timeout" "$lfd"; then
+ exit_with_manifest 503 "submission service is busy"
+fi
+
+repo_old="$(realpath "$repo")" # Old repo path.
+repo_name="$(basename "$repo")-$(date "+%Y%m%d-%H%M%S-%N")" # New repo name.
+repo_new="$(dirname "$repo_old")/$repo_name" # New repo path.
+repo_link="$repo_new.link" # New repo symlink.
+
+# On exit, remove the new repository symlink and directory, unless the link
+# doesn't exist or the directory removal is canceled (for example, the new
+# repository is made current).
+#
+function exit_trap ()
+{
+ if [ -L "$repo_link" ]; then
+ run rm -r -f "$repo_link"
+ fi
+
+ if [ -n "$repo_new" -a -d "$repo_new" ]; then
+ run rm -r -f "$repo_new"
+ fi
+}
+
+trap exit_trap EXIT
+
+# Check for the package duplicate (in all projects).
+#
+# Use <name>-<version>.* without .tar.gz in case we want to support more
+# archive types later.
+#
+run pkg_find_archive "$name-$version.*" "$repo_old/1" | readarray -t p
+
+if [ "${#p[@]}" -ne 0 ]; then
+ n="${p[1]}"
+ v="${p[2]}"
+
+ trace "found: $n/$v in ${p[0]}"
+
+ if [ "$n" == "$name" ]; then
+ exit_with_manifest 422 "duplicate submission"
+ else
+ exit_with_manifest 422 "submission conflicts with $n/$v"
+ fi
+fi
+
+# Copy the current repository using hardlinks.
+#
+# -r (recursive)
+# -t (preserve timestamps)
+# -O (omit dir timestamps)
+# --link-dest (hardlink files instead of copying)
+#
+# We also exclude the packages.manifest file that will be re-generated anyway.
+#
+run rsync -rtO --exclude 'packages.manifest' --link-dest="$repo_old" \
+ "$repo_old/" "$repo_new"
+
+# Remove the package version revision archives that may exist in the
+# repository.
+#
+# But first check if the repository contains newer revision of this package
+# version. Respond with the 'newer revision is present' result manifest and
+# exit if that's the case.
+#
+run pkg_find_archives "$name" "$version*" "$repo_new/1" | readarray -t arcs
+
+for f in "${arcs[@]}"; do
+ pkg_verify_archive "$f" | readarray -t p
+
+ v="${p[1]}"
+ rv="$(version_revision "$v")"
+
+ if [ "$rv" -gt "$revision" ]; then
+ exit_with_manifest 422 "newer revision $name/$v is present"
+ fi
+done
+
+for f in "${arcs[@]}"; do
+ run rm "$f"
+done
+
+# Copy the archive rather than moving it since we may need it for
+# troubleshooting. Note: the data and repository directories can be on
+# different filesystems and so hardlinking could fail.
+#
+run mkdir -p "$repo_new/1/$project"
+run cp "$data_dir/$archive" "$repo_new/1/$project"
+
+# Create the new repository.
+#
+# Note that if bpkg-rep-create fails, we can't reliably distinguish if this is
+# a user or internal error (broken package archive vs broken repository).
+# Thus, we always treat is as a user error, providing the full error
+# description in the response and assuming that the submitter can either fix
+# the issue or report it to the repository maintainers. This again assumes
+# private/trusted environment.
+#
+trace "+ bpkg rep-create '$repo_new/1' 2>&1"
+
+if ! e="$(bpkg rep-create "$repo_new/1" 2>&1)"; then
+ exit_with_manifest 400 "submitted archive is not a valid package
+$e"
+fi
+
+# If requested, verify that the new repository is loadable into the package
+# database and, as in the above case, treat the potential error as a user
+# error.
+#
+if [ "${#loader_args[@]}" -ne 0 ]; then
+ f="$data_dir/loadtab"
+ echo "http://testrepo/1 private cache:$repo_new/1" >"$f"
+
+ trace "+ ${loader_args[@]} '$f' 2>&1"
+
+ if ! e="$("${loader_args[@]}" "$f" 2>&1)"; then
+
+ # Sanitize the error message, removing the confusing lines.
+ #
+ e="$(run sed -re '/testrepo/d' <<<"$e")"
+ exit_with_manifest 400 "unable to add package to repository
+$e"
+ fi
+fi
+
+# Finally, create the new repository symlink and replace the current symlink
+# with it, unless we are simulating.
+#
+run ln -sf "$repo_name" "$repo_link"
+
+if [ -z "$simulate" ]; then
+ run mv -T "$repo_link" "$repo" # Switch the repository symlink atomically.
+
+ # Now, when the repository link is switched, disable the new repository
+ # removal.
+ #
+ # Note that we still can respond with an error status. However, the
+ # remaining operations are all cleanups and thus unlikely to fail.
+ #
+ repo_new=
+fi
+
+trace "+ exec {lfd}<&-"
+exec {lfd}<&- # Close the file descriptor and unlock the repository.
+
+# Remove the old repository, unless we are simulating.
+#
+# Note that if simulating, we leave the new repository directory/symlink
+# removal to the exit trap (see above).
+#
+if [ -z "$simulate" ]; then
+ run rm -r "$repo_old"
+
+ what="published"
+else
+ what="simulated"
+fi
+
+run rm -r "$data_dir"
+
+trace "package is $what$message_suffix"
+exit_with_manifest 200 "package is published$message_suffix"
diff --git a/brep/handler/submit/submit.bash.in b/brep/handler/submit/submit.bash.in
index d1d0634..7826809 100644
--- a/brep/handler/submit/submit.bash.in
+++ b/brep/handler/submit/submit.bash.in
@@ -1,5 +1,4 @@
# file : brep/handler/submit/submit.bash.in
-# copyright : Copyright (c) 2014-2019 Code Synthesis Ltd
# license : MIT; see accompanying LICENSE file
# Utility functions useful for implementing package submission handlers.
@@ -48,12 +47,29 @@ function extract_package_manifest () # <archive> <manifest>
local arc="$1"
local man="$2"
- # Pass the --deep option to make sure that the *-file manifest values are
- # resolvable, so rep-create will not fail due to this package down the road.
- # Note that we also make sure that all the manifest values are known (see
- # bpkg-pkg-verify for details).
+ # Pass the --deep option to make sure that the bootstrap buildfile is
+ # present and the *-file manifest values are resolvable, so rep-create will
+ # not fail due to this package down the road. Note that we also make sure
+ # that all the manifest values are known (see bpkg-pkg-verify for details).
#
- if ! run_silent bpkg pkg-verify --deep --manifest "$arc" >"$man"; then
+ local cmd=(bpkg pkg-verify --deep --manifest "$arc")
+ trace_cmd "${cmd[@]}"
+
+ # Note that we used to just advise the user to run bpkg-pkg-verify locally
+ # for the details on the potential failure. That, however, may not always be
+ # helpful since the user can use a different version of the toolchain and so
+ # may observe a different behavior. Thus, we add the bpkg-pkg-verify error
+ # message to the response, turning it into an info. This way the user may
+ # potentially see the following bdep-publish diagnostics:
+ #
+ # error: package archive is not valid
+ # info: unable to satisfy constraint (build2 >= 0.17.0-) for package libhello-1.0.0.tar.gz
+ # info: available build2 version is 0.16.0
+ # info: run bpkg pkg-verify for details
+ # info: reference: 308e155764c8
+ #
+ local e
+ if ! e="$("${cmd[@]}" 2>&1 >"$man")"; then
# Perform the sanity check to make sure that bpkg is runnable.
#
@@ -61,6 +77,33 @@ function extract_package_manifest () # <archive> <manifest>
error "unable to run bpkg"
fi
- exit_with_manifest 400 "archive is not a valid package (run bpkg pkg-verify for details)"
+ # Note that bpkg-pkg-verify diagnostics may potentially contain the
+ # archive absolute path. Let's sanitize this diagnostics by stripping the
+ # archive directory path, if present. Also note that to use sed for that
+ # we first need to escape the special regex characters and slashes in the
+ # archive directory path (see sed's basic regular expressions for
+ # details).
+ #
+ local d="$(sed 's/[[\.*^$/]/\\&/g' <<<"$(dirname "$arc")/")"
+
+ e="$(sed -e "s/$d//g" -e 's/^error:/ info:/' <<<"$e")"
+ e=$'package archive is not valid\n'"$e"$'\n info: run bpkg pkg-verify for details'
+
+ exit_with_manifest 400 "$e"
fi
}
+
+# Extract the revision part from the package version. Return 0 if the version
+# doesn't contain revision.
+#
+function version_revision () # version
+{
+ local r
+ r="$(sed -n -re 's%^(\+?[^+]+)(\+([0-9]+))?$%\3%p' <<<"$1")"
+
+ if [ -z "$r" ]; then
+ r="0"
+ fi
+
+ echo "$r"
+}