2
0
mirror of https://github.com/bpkg/bpkg synced 2024-11-19 15:25:36 +00:00
bpkg/lib/install/install.sh

495 lines
12 KiB
Bash
Raw Normal View History

2014-05-22 19:52:55 +00:00
#!/bin/bash
# Include config rc file if found
CONFIG_FILE="$HOME/.bpkgrc"
2021-01-09 19:08:53 +00:00
# shellcheck disable=SC1090
[[ -f "$CONFIG_FILE" ]] && source "$CONFIG_FILE"
## set defaults
if [[ ${#BPKG_REMOTES[@]} -eq 0 ]]; then
BPKG_REMOTES[0]=${BPKG_REMOTE-https://raw.githubusercontent.com}
BPKG_GIT_REMOTES[0]=${BPKG_GIT_REMOTE-https://github.com}
fi
BPKG_USER="${BPKG_USER:-bpkg}"
2014-05-22 19:52:55 +00:00
## check parameter consistency
validate_parameters () {
if [[ ${#BPKG_GIT_REMOTES[@]} -ne ${#BPKG_REMOTES[@]} ]]; then
2021-01-09 20:24:34 +00:00
error "$(printf 'BPKG_GIT_REMOTES[%d] differs in size from BPKG_REMOTES[%d] array' "${#BPKG_GIT_REMOTES[@]}" "${#BPKG_REMOTES[@]}")"
return 1
fi
return 0
}
2014-05-22 19:52:55 +00:00
## outut usage
usage () {
echo 'usage: bpkg-install [-h|--help]'
echo ' or: bpkg-install [-g|--global] <package>'
echo ' or: bpkg-install [-g|--global] <user>/<package>'
2014-05-22 19:52:55 +00:00
}
## format and output message
2014-05-24 19:29:07 +00:00
message () {
if type -f bpkg-term > /dev/null 2>&1; then
2014-05-26 14:16:06 +00:00
bpkg-term color "${1}"
2014-05-24 19:29:07 +00:00
fi
shift
2021-01-09 20:24:34 +00:00
echo -n " ${1}"
2014-05-24 19:29:07 +00:00
shift
if type -f bpkg-term > /dev/null 2>&1; then
2014-05-26 14:16:06 +00:00
bpkg-term reset
2014-05-24 19:29:07 +00:00
fi
printf ': '
2014-05-24 19:29:07 +00:00
if type -f bpkg-term > /dev/null 2>&1; then
2014-05-26 14:16:06 +00:00
bpkg-term reset
bpkg-term bright
2014-05-24 19:29:07 +00:00
fi
printf "%s\n" "${@}"
if type -f bpkg-term > /dev/null 2>&1; then
2014-05-26 14:16:06 +00:00
bpkg-term reset
2014-05-24 19:29:07 +00:00
fi
}
## output error
error () {
{
message 'red' 'error' "${@}"
2014-05-24 19:29:07 +00:00
} >&2
}
## output warning
warn () {
{
message 'yellow' 'warn' "${@}"
2014-05-24 19:29:07 +00:00
} >&2
}
## output info
info () {
local title='info'
2014-05-24 19:29:07 +00:00
if (( "${#}" > 1 )); then
title="${1}"
shift
fi
message 'cyan' "${title}" "${@}"
}
save_remote_file () {
local auth_param path url
url="${1}"
path="${2}"
auth_param="${3:-}"
local dirname="$(dirname "${path}")"
# Make sure directory exists
if [[ ! -d "${dirname}" ]];then
mkdir -p "${dirname}"
fi
if [[ "${auth_param}" ]];then
curl --silent -L -o "${path}" -u "${auth_param}" "${url}"
else
curl --silent -L -o "${path}" "${url}"
fi
}
url_exists () {
local auth_param exists url
url="${1}"
auth_param="${2:-}"
exists=0
if [[ "${auth_param}" ]];then
status=$(curl --silent -L -w '%{http_code}' -o '/dev/null' -u "${auth_param}" "${url}")
result="$?"
else
status=$(curl --silent -L -w '%{http_code}' -o '/dev/null' "${url}")
result="$?"
fi
# In some rare cases, curl will return CURLE_WRITE_ERROR (23) when writing
# to `/dev/null`. In such a case we do not care that such an error occured.
# We are only interested in the status, which *will* be available regardless.
if [[ '0' != "${result}" && '23' != "${result}" ]] || (( status >= 400 )); then
exists=1
fi
return "${exists}"
}
read_package_json () {
local auth_param url
url="${1}"
auth_param="${2:-}"
if [[ "${auth_param}" ]];then
curl --silent -L -u "${auth_param}" "${url}"
else
curl --silent -L "${url}"
fi
2014-05-24 19:29:07 +00:00
}
2014-05-22 19:52:55 +00:00
## Install a bash package
bpkg_install () {
local pkg=''
2014-05-22 20:34:28 +00:00
local let needs_global=0
2014-05-22 19:52:55 +00:00
for opt in "${@}"; do
if [[ '-' = "${opt:0:1}" ]]; then
continue
fi
pkg="${opt}"
break
done
for opt in "${@}"; do
case "${opt}" in
-h|--help)
usage
return 0
;;
-g|--global)
shift
needs_global=1
;;
*)
if [[ '-' = "${opt:0:1}" ]]; then
echo 2>&1 "error: Unknown argument \`${1}'"
usage
return 1
fi
;;
esac
done
2014-05-22 19:52:55 +00:00
## ensure there is a package to install
if [[ -z "${pkg}" ]]; then
2014-05-23 00:07:51 +00:00
usage
2014-05-22 19:52:55 +00:00
return 1
fi
2014-05-24 19:29:07 +00:00
echo
## Check each remote in order
local let i=0
for remote in "${BPKG_REMOTES[@]}"; do
local git_remote=${BPKG_GIT_REMOTES[$i]}
bpkg_install_from_remote "$pkg" "$remote" "$git_remote" $needs_global
if [[ "$?" == '0' ]]; then
return 0
elif [[ "$?" == '2' ]]; then
error 'fatal error occurred during install'
2014-05-22 19:52:55 +00:00
return 1
fi
i=$((i+1))
done
error 'package not found on any remote'
return 1
}
## try to install a package from a specific remote
## returns values:
## 0: success
## 1: the package was not found on the remote
## 2: a fatal error occurred
bpkg_install_from_remote () {
local pkg=$1
local remote=$2
local git_remote=$3
local let needs_global=$4
local cwd=$(pwd)
local url=''
local uri=''
local version=''
local status=''
local json=''
local user=''
local name=''
local version=''
local auth_param=''
local let has_pkg_json=0
declare -a local pkg_parts=()
declare -a local remote_parts=()
declare -a local scripts=()
declare -a local files=()
2014-05-22 19:52:55 +00:00
## get version if available
{
OLDIFS="${IFS}"
IFS="@"
pkg_parts=(${pkg})
2014-05-22 19:52:55 +00:00
IFS="${OLDIFS}"
}
if [[ ${#pkg_parts[@]} -eq 1 ]]; then
version='master'
#info "Using latest (master)"
elif [[ ${#pkg_parts[@]} -eq 2 ]]; then
name="${pkg_parts[0]}"
version="${pkg_parts[1]}"
2014-05-22 19:52:55 +00:00
else
error 'Error parsing package version'
2014-05-22 19:52:55 +00:00
return 1
fi
## split by user name and repo
{
OLDIFS="${IFS}"
IFS='/'
pkg_parts=(${pkg})
2014-05-22 19:52:55 +00:00
IFS="${OLDIFS}"
}
if [[ ${#pkg_parts[@]} -eq 1 ]]; then
2014-05-22 19:52:55 +00:00
user="${BPKG_USER}"
name="${pkg_parts[0]}"
elif [[ ${#pkg_parts[@]} -eq 2 ]]; then
user="${pkg_parts[0]}"
name="${pkg_parts[1]}"
2014-05-22 19:52:55 +00:00
else
error 'Unable to determine package name'
2014-05-22 19:52:55 +00:00
return 1
fi
2014-05-23 00:07:51 +00:00
## clean up name of weird trailing
## versions and slashes
2014-05-22 19:52:55 +00:00
name=${name/@*//}
2014-05-23 00:07:51 +00:00
name=${name////}
2014-05-22 19:52:55 +00:00
## check to see if remote is raw with oauth (GHE)
if [[ "${remote:0:10}" == "raw-oauth|" ]]; then
info 'Using OAUTH basic with content requests'
OLDIFS="${IFS}"
IFS="'|'"
local remote_parts=($remote)
IFS="${OLDIFS}"
local token=${remote_parts[1]}
remote=${remote_parts[2]}
auth_param="$token:x-oauth-basic"
uri="/${user}/${name}/raw/${version}"
## If git remote is a URL, and doesn't contain token information, we
## inject it into the <user>@host field
if [[ "$git_remote" == https://* ]] && [[ "$git_remote" != *x-oauth-basic* ]] && [[ "$git_remote" != *${token}* ]]; then
git_remote=${git_remote/https:\/\//https:\/\/$token:x-oauth-basic@}
fi
else
uri="/${user}/${name}/${version}"
fi
2014-05-22 19:52:55 +00:00
## clean up extra slashes in uri
uri=${uri/\/\///}
info "Install $uri from remote $remote [$git_remote]"
## Ensure remote is reachable
## If a remote is totally down, this will be considered a fatal
## error since the user may have intended to install the package
## from the broken remote.
{
if ! url_exists "${remote}" "${auth_param}"; then
error "Remote unreachable: ${remote}"
return 2
fi
}
2014-05-22 19:52:55 +00:00
## build url
url="${remote}${uri}"
repo_url="${git_remote}/${user}/${name}.git"
2014-05-22 19:52:55 +00:00
## determine if `bpkg.json` or 'package.json' exists at url
2014-05-22 19:52:55 +00:00
{
if url_exists "${url}/bpkg.json?$(date +%s)" "${auth_param}"; then
has_pkg_json=2
else
warn 'bpkg.json doesn`t exist'
if url_exists "${url}/package.json?$(date +%s)" "${auth_param}"; then
has_pkg_json=1
else
warn 'package.json doesn`t exist'
has_pkg_json=0
# check to see if there's a Makefile. If not, this is not a valid package
if ! url_exists "${url}/Makefile?$(date +%s)" "${auth_param}"; then
warn "Makefile not found, skipping remote: $url"
return 1
fi
fi
2014-05-22 19:52:55 +00:00
fi
}
## read bpkg.json or package.json
if (( 2 == has_pkg_json )); then
json=$(read_package_json "${url}/bpkg.json?$(date +%s)" "${auth_param}")
elif (( 1 == has_pkg_json )); then
json=$(read_package_json "${url}/package.json?$(date +%s)" "${auth_param}")
fi
2014-05-22 19:52:55 +00:00
if (( has_pkg_json > 0 )); then
## get package name from 'bpkg.json' or 'package.json'
name="$(
echo -n "${json}" |
bpkg-json -b |
2018-08-01 20:06:25 +00:00
grep -m 1 '"name"' |
awk '{ $1=""; print $0 }' |
tr -d '\"' |
tr -d ' '
)"
## check if forced global
if [[ "$(echo -n "${json}" | bpkg-json -b | grep '\["global"\]' | awk '{ print $2 }' | tr -d '"')" == 'true' ]]; then
needs_global=1
fi
2014-05-24 19:33:24 +00:00
## construct scripts array
{
scripts=$(echo -n "${json}" | bpkg-json -b | grep '\["scripts' | awk '{ print $2 }' | tr -d '"')
2014-05-25 23:22:12 +00:00
## create array by splitting on newline
OLDIFS="${IFS}"
IFS=$'\n'
scripts=(${scripts[@]})
IFS="${OLDIFS}"
}
## construct files array
{
files=$(echo -n "${json}" | bpkg-json -b | grep '\["files' | awk '{ print $2 }' | tr -d '"')
## create array by splitting on newline
OLDIFS="${IFS}"
IFS=$'\n'
files=(${files[@]})
IFS="${OLDIFS}"
}
fi
2014-05-22 19:52:55 +00:00
2014-05-24 19:29:07 +00:00
## build global if needed
if (( 1 == needs_global )); then
if (( has_pkg_json > 0 )); then
## install bin if needed
build="$(echo -n "${json}" | bpkg-json -b | grep '\["install"\]' | awk '{$1=""; print $0 }' | tr -d '\"')"
build="$(echo -n "${build}" | sed -e 's/^ *//' -e 's/ *$//')"
fi
if [[ -z "${build}" ]]; then
warn 'Missing build script'
warn 'Trying `make install`...'
build='make install'
fi
2019-12-05 14:20:06 +00:00
if [ -z "$PREFIX" ]; then
if [ "$USER" == "root" ]; then
2019-12-05 14:20:06 +00:00
PREFIX="/usr/local"
else
PREFIX="$HOME/.local"
fi
build="env PREFIX=$PREFIX $build"
fi
{ (
## go to tmp dir
2021-01-09 20:53:07 +00:00
cd "$( [[ -n "${TMPDIR}" ]] && echo "${TMPDIR}" || echo /tmp)" &&
2014-05-22 20:49:04 +00:00
## prune existing
rm -rf "${name}-${version}" &&
2014-05-22 20:49:04 +00:00
## shallow clone
info "Cloning ${repo_url} to ${name}-${version}"
git clone "${repo_url}" "${name}-${version}" &&
2014-05-24 19:29:07 +00:00
(
## move into directory
cd "${name}-${version}" &&
(
## checkout to branch version
git checkout ${version} ||
## checkout into branch 'main' just in case 'master' was renamed
[ "${version}" = "master" ] && git checkout main
) &&
## build
info "Performing install: \`${build}'"
build_output=$(eval "${build}")
echo "${build_output}"
) &&
2014-05-22 20:49:04 +00:00
## clean up
rm -rf "${name}-${version}"
) }
## perform local install otherwise
else
## copy bpkg.json or package.json over
if (( 2 == has_pkg_json )); then
save_remote_file "${url}/bpkg.json" "${cwd}/deps/${name}/bpkg.json" "${auth_param}"
elif (( 1 == has_pkg_json )); then
save_remote_file "${url}/package.json" "${cwd}/deps/${name}/package.json" "${auth_param}"
fi
## make 'deps/' directory if possible
mkdir -p "${cwd}/deps/${name}"
## make 'deps/bin' directory if possible
mkdir -p "${cwd}/deps/bin"
# install package dependencies
info "Install dependencies for ${name}"
(cd "${cwd}/deps/${name}" && bpkg getdeps)
## grab each script and place in deps directory
for script in "${scripts[@]}"; do
(
if [[ "${script}" ]];then
local scriptname="$(echo "${script}" | xargs basename )"
info "fetch" "${url}/${script}"
info "write" "${cwd}/deps/${name}/${script}"
save_remote_file "${url}/${script}" "${cwd}/deps/${name}/${script}" "${auth_param}"
scriptname="${scriptname%.*}"
info "${scriptname} to PATH" "${cwd}/deps/bin/${scriptname}"
ln -si "${cwd}/deps/${name}/${script}" "${cwd}/deps/bin/${scriptname}"
chmod u+x "${cwd}/deps/bin/${scriptname}"
fi
)
done
if [[ "${#files[@]}" -gt '0' ]]; then
## grab each file and place in correct directory
for file in "${files[@]}"; do
(
if [[ "${file}" ]];then
local filename="$(echo "${file}" | xargs basename )"
info "fetch" "${url}/${file}"
info "write" "${cwd}/deps/${name}/${file}"
save_remote_file "${url}/${file}" "${cwd}/deps/${name}/${file}" "${auth_param}"
fi
)
done
fi
fi
2014-05-22 19:52:55 +00:00
return 0
}
## Use as lib or perform install
2021-01-09 19:15:19 +00:00
if [[ ${BASH_SOURCE[0]} != "$0" ]]; then
2014-05-22 19:52:55 +00:00
export -f bpkg_install
elif validate_parameters; then
2014-05-22 19:52:55 +00:00
bpkg_install "${@}"
exit $?
else
#param validation failed
exit $?
2014-05-22 19:52:55 +00:00
fi