saphyr-serde/.bpan/run-or-docker.bash
Ethiraric e303cbe543 Squashed 'parser/tests/yaml-test-suite/' content from commit ccfa74e5
git-subtree-dir: parser/tests/yaml-test-suite
git-subtree-split: ccfa74e56afb53da960847ff6e6976c0a0825709
2024-10-12 16:15:38 +02:00

351 lines
6.7 KiB
Bash

#!/bash
# shellcheck disable=2030,2031,2154
set -e -u -o pipefail
# shellcheck disable=2206
declare -a docker_run_options=(${RUN_OR_DOCKER_OPTIONS-})
RUN_OR_DOCKER_PULL=${RUN_OR_DOCKER_PULL:-false}
RUN_OR_DOCKER_PUSH=${RUN_OR_DOCKER_PUSH:-false}
run() (
verbose=${RUN_OR_DOCKER_VERBOSE:-false}
bin=$(dirname "${BASH_SOURCE[1]}")
self=$(basename "${BASH_SOURCE[1]}")
root=${ROOT:-$PWD}
prog=$(cd "$bin" && echo "$self".*)
version=${version:-$(calculate-version)}
image=yamlio/$self:$version
if [[ $prog == *'.*' ]]; then
prog=$self
lang=bash
else
case $prog in
*.sh) lang=bash ;;
*.bash) lang=bash ;;
*.pl) lang=perl ;;
*.py) lang=python3 ;;
*.rb) lang=ruby ;;
*) die "Don't recognize language of '$prog'" ;;
esac
fi
if $RUN_OR_DOCKER_PUSH; then
build-docker-image
exit
fi
if [[ -e /.dockerenv || ${RUN_OR_DOCKER-} == local ]]; then
if [[ $self == "$prog" ]]; then
main "$@"
else
run-local "$@"
fi
return
fi
if [[ ${RUN_OR_DOCKER-} == force* || ${GITHUB_ACTIONS-} ]]; then
run-docker "$@"
return
fi
out=$(check 2>/tmp/out) && rc=0 || rc=$?
err=$(< /tmp/out)
[[ $rc == 0 || $err == FAIL:* ]] || die "Error: $err"
if [[ $rc -eq 0 ]]; then
run-local "$@"
else
$verbose &&
echo "Can't run '$self' locally: ${err#FAIL:\ }" >&2
echo "Running '$self' with docker..." >&2
run-docker "$@"
fi
)
run-local() (
export RUN_OR_DOCKER=local
"$lang" "$bin/$prog" "$@"
)
run-docker() (
if [[ ${RUN_OR_DOCKER-} == force-build ]]; then
build-docker-image
else
docker inspect --type=image "$image" &>/dev/null &&
ok=true || ok=false
if ! $ok; then
if $RUN_OR_DOCKER_PULL; then
(
$verbose && set -x
docker pull "$image"
)
else
build-docker-image
fi
fi
fi
args=()
docker_run_options+=(
--env ROOT=/home/host
)
for arg; do
if [[ $arg == "$root"/* ]]; then
arg=/home/host/${arg#$root/}
fi
args+=("$arg")
done
flags=()
[[ -t 0 ]] && flags+=('--tty')
workdir=/home/host
[[ ${RUN_OR_DOCKER_WORKDIR-} ]] &&
workdir=$workdir/$RUN_OR_DOCKER_WORKDIR
uid=$(id -u)
gid=$(id -g)
$verbose && set -x
docker run "${flags[@]}" --interactive --rm \
--volume "$root":/home/host \
--workdir "$workdir" \
--user "$uid:$gid" \
--entrypoint '' \
"${docker_run_options[@]}" \
"$image" \
"$self" "${args[@]}"
)
force() {
fail 'docker is forced here'
}
need() {
cmd=$1
[[ $(command -v "$cmd") ]] ||
fail "requires command '$cmd'"
[[ ${2-} ]] || return 0
if [[ ${2-} =~ ^[0-9]+(\.|$) ]]; then
check=need-version
else
check=need-modules
fi
"$check" "$@"
}
need-version() {
cmd=$1 ver=$2
if [[ $("$cmd" --version) =~ ([0-9]+)\.([0-9]+)(\.[0-9]+)? ]]; then
set -- "${BASH_REMATCH[1]}" "${BASH_REMATCH[2]}" "${BASH_REMATCH[3]#.}"
else
die "Could not get version from '$cmd --version'"
fi
vers=$ver
while [[ $vers && $* ]]; do
v=${vers%%.*}
if [[ $1 -gt $v ]]; then
return
elif [[ $1 -lt $v ]]; then
fail "requires '$cmd' version '$ver' or higher"
fi
if [[ $vers != *.* ]]; then
return
fi
vers=${vers#*.}
shift
done
fail "requires '$cmd' version '$ver' or higher"
}
need-modules() {
cmd=$1; shift
for module; do
case $cmd in
perl)
if [[ $module == *=* ]]; then
want=$module
version=${module#*=}
module=${module%=*}
perl -M"$module"\ "$version" -e1 &>/dev/null ||
fail "'$cmd' requires Perl module '$want'"
else
want=$module
perl -M"$module" -e1 &>/dev/null ||
fail "'$cmd' requires Perl module '$module'"
fi
;;
node)
node -e "require('$module');" &>/dev/null ||
fail "'$cmd' requires NodeJS module '$module'"
;;
python)
python3 -c "import $module" &>/dev/null ||
fail "'$cmd' requires Python(3) module '$module'"
;;
ruby)
list=$(gem list)
if [[ $module == *=* ]]; then
version=${module#*=}
module=${module%=*}
else
version=0
fi
(set -x; ruby -e "gem '$module', '>=$version'") &>/dev/null ||
fail "'$cmd' requires Ruby module '$module' >= v$version"
;;
*) die "Can't check module '$module' for '$cmd'" ;;
esac
done
}
build-docker-image() (
build=$(mktemp -d)
build-fail() { fail "docker-build failed: $*"; }
cmd() (
_args=${1//\ \+\ /\ &&\ }
echo "$_args"
echo
)
run() (
cmd "RUN $*"
)
from() {
_from=$1
case $_from in
alpine)
cmd 'FROM alpine'
cmd 'WORKDIR /home'
cmd 'RUN apk update && apk add bash build-base coreutils'
;;
ubuntu)
cmd 'FROM ubuntu:20.04'
cmd 'WORKDIR /home'
cmd 'RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y build-essential'
;;
*) build-fail "from $*"
esac
}
pkg() (
case $_from in
alpine)
cmd "RUN apk add $*"
;;
ubuntu)
cmd "RUN DEBIAN_FRONTEND=noninteractive apt-get install -y $*"
;;
*) build-fail "pkg $*"
esac
)
cpan() (
case $_from in
alpine)
pkg perl perl-dev perl-app-cpanminus wget
;;
ubuntu)
pkg cpanminus
;;
*) build-fail "cpan $*"
esac
cmd "RUN cpanm -n $*"
)
npm() (
case $_from in
alpine)
pkg nodejs npm
;;
*) build-fail "npm $*"
esac
cmd "RUN mkdir node_modules && npm install $*"
)
gem() (
case $_from in
alpine)
pkg ruby
;;
*) build-fail "npm $*"
esac
for module; do
if [[ $module == *=* ]]; then
module="${module%=*} -v ${module#*=}"
fi
cmd "RUN gem install $module"
done
)
pip() (
case $_from in
alpine)
pkg python3 py3-pip
;;
*) build-fail "npm $*"
esac
cmd "RUN pip3 install $*"
)
(
dockerfile
bin=$(dirname "$0")
bin=${bin#$root/}
if [[ $bin == bin ]]; then
cmd "ENV PATH=/home/host/bin:\$PATH"
else
cmd "ENV PATH=/home/host/$bin:/home/host/bin:\$PATH"
fi
) > "$build/Dockerfile"
(
set -x
cd "$build"
docker build -t "$image" .
)
rm -fr "$build"
if $RUN_OR_DOCKER_PUSH; then
(
set -x
docker push "$image"
)
fi
)
calculate-version() (
[[ ${uses-} ]] ||
die "'$0' requires either '\$version' variable or '\$uses' array"
cd "$(dirname "$0")" || exit
cat "${uses[@]}" |
md5sum |
cut -d' ' -f1
)
fail() { echo "FAIL: $*" >&2; exit 1; }
die() { echo "$*" >&2; exit 1; }