396 lines
9.7 KiB
Bash
Executable File
396 lines
9.7 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
|
|
set -u
|
|
|
|
ROOT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
VENV_DIR="${HOME}/.local/share/ffx.venv"
|
|
VENV_BIN_DIR="${VENV_DIR}/bin"
|
|
VENV_PYTHON="${VENV_BIN_DIR}/python"
|
|
VENV_PIP="${VENV_BIN_DIR}/pip"
|
|
VENV_FFX="${VENV_BIN_DIR}/ffx"
|
|
BASHRC_FILE="${HOME}/.bashrc"
|
|
ALIAS_BLOCK_BEGIN="# >>> ffx alias >>>"
|
|
ALIAS_BLOCK_END="# <<< ffx alias <<<"
|
|
ALIAS_LINE="alias ffx=\"${VENV_FFX}\""
|
|
|
|
CHECK_ONLY=0
|
|
WITH_TESTS=0
|
|
READINESS_FAILURES=0
|
|
INSTALL_FAILURES=0
|
|
|
|
COLOR_RESET=""
|
|
COLOR_GREEN=""
|
|
COLOR_YELLOW=""
|
|
COLOR_RED=""
|
|
|
|
if [ -t 1 ]; then
|
|
COLOR_RESET="$(printf '\033[0m')"
|
|
COLOR_GREEN="$(printf '\033[32m')"
|
|
COLOR_YELLOW="$(printf '\033[33m')"
|
|
COLOR_RED="$(printf '\033[31m')"
|
|
fi
|
|
|
|
usage() {
|
|
cat <<EOF
|
|
Usage: $(basename "$0") [--check] [--with-tests] [--help]
|
|
|
|
Prepare the persistent FFX bundle installation at:
|
|
${VENV_DIR}
|
|
|
|
Actions:
|
|
- create or reuse ${VENV_DIR}
|
|
- install this repository into the venv with pip --editable
|
|
- ensure ${BASHRC_FILE} exposes alias ffx -> ${VENV_FFX}
|
|
- optionally install Python packages required for modern tests
|
|
|
|
Options:
|
|
--check Report readiness only. Do not create or modify anything.
|
|
--with-tests Also install and verify Python packages required for modern tests.
|
|
--help Show this help text.
|
|
|
|
Notes:
|
|
- This is the first installation step.
|
|
- tools/configure_workstation.sh is the second step and configures system dependencies plus local user files.
|
|
EOF
|
|
}
|
|
|
|
status_ok() {
|
|
printf '%sok%s' "${COLOR_GREEN}" "${COLOR_RESET}"
|
|
}
|
|
|
|
status_warn() {
|
|
printf '%swarn%s' "${COLOR_YELLOW}" "${COLOR_RESET}"
|
|
}
|
|
|
|
status_fail() {
|
|
printf '%sfailed%s' "${COLOR_RED}" "${COLOR_RESET}"
|
|
}
|
|
|
|
report_component() {
|
|
local level="$1"
|
|
local label="$2"
|
|
local detail="$3"
|
|
local rendered_status=""
|
|
|
|
case "${level}" in
|
|
ok)
|
|
rendered_status="$(status_ok)"
|
|
;;
|
|
warn)
|
|
rendered_status="$(status_warn)"
|
|
;;
|
|
*)
|
|
rendered_status="$(status_fail)"
|
|
;;
|
|
esac
|
|
|
|
printf '[%s] %s%s\n' "${rendered_status}" "${label}" "${detail:+: $detail}"
|
|
}
|
|
|
|
command_exists() {
|
|
command -v "$1" >/dev/null 2>&1
|
|
}
|
|
|
|
check_python3() {
|
|
command_exists python3
|
|
}
|
|
|
|
check_venv_dir() {
|
|
[ -x "${VENV_PYTHON}" ]
|
|
}
|
|
|
|
check_venv_pip() {
|
|
check_venv_dir && "${VENV_PIP}" --version >/dev/null 2>&1
|
|
}
|
|
|
|
check_venv_ffx() {
|
|
[ -x "${VENV_FFX}" ]
|
|
}
|
|
|
|
check_venv_pytest() {
|
|
check_venv_dir && "${VENV_PYTHON}" -m pytest --version >/dev/null 2>&1
|
|
}
|
|
|
|
check_bashrc_file() {
|
|
[ -f "${BASHRC_FILE}" ]
|
|
}
|
|
|
|
check_bashrc_alias() {
|
|
check_bashrc_file && grep -Fqx "${ALIAS_LINE}" "${BASHRC_FILE}"
|
|
}
|
|
|
|
detail_python3() {
|
|
command -v python3 || printf "command 'python3' not found"
|
|
}
|
|
|
|
detail_venv_dir() {
|
|
if check_venv_dir; then
|
|
printf '%s' "${VENV_DIR}"
|
|
else
|
|
printf 'missing %s' "${VENV_DIR}"
|
|
fi
|
|
}
|
|
|
|
detail_venv_pip() {
|
|
if check_venv_pip; then
|
|
"${VENV_PIP}" --version
|
|
else
|
|
printf 'missing pip in %s' "${VENV_DIR}"
|
|
fi
|
|
}
|
|
|
|
detail_venv_ffx() {
|
|
if check_venv_ffx; then
|
|
printf '%s' "${VENV_FFX}"
|
|
else
|
|
printf 'missing %s' "${VENV_FFX}"
|
|
fi
|
|
}
|
|
|
|
detail_venv_pytest() {
|
|
if check_venv_pytest; then
|
|
"${VENV_PYTHON}" -m pytest --version 2>/dev/null | head -n 1
|
|
else
|
|
printf 'missing pytest in %s' "${VENV_DIR}"
|
|
fi
|
|
}
|
|
|
|
detail_bashrc_file() {
|
|
if check_bashrc_file; then
|
|
printf '%s' "${BASHRC_FILE}"
|
|
else
|
|
printf 'missing %s; prep can create it' "${BASHRC_FILE}"
|
|
fi
|
|
}
|
|
|
|
detail_bashrc_alias() {
|
|
if check_bashrc_alias; then
|
|
printf '%s' "${ALIAS_LINE}"
|
|
else
|
|
printf 'missing alias line for %s' "${VENV_FFX}"
|
|
fi
|
|
}
|
|
|
|
print_status_report() {
|
|
READINESS_FAILURES=0
|
|
|
|
echo "Dependency status:"
|
|
if check_python3; then
|
|
report_component ok "python3" "$(detail_python3)"
|
|
else
|
|
report_component failed "python3" "$(detail_python3)"
|
|
READINESS_FAILURES=$((READINESS_FAILURES + 1))
|
|
fi
|
|
|
|
echo
|
|
echo "Bundle venv status:"
|
|
if check_venv_dir; then
|
|
report_component ok "bundle virtualenv" "$(detail_venv_dir)"
|
|
else
|
|
report_component failed "bundle virtualenv" "$(detail_venv_dir)"
|
|
READINESS_FAILURES=$((READINESS_FAILURES + 1))
|
|
fi
|
|
|
|
if check_venv_pip; then
|
|
report_component ok "bundle pip" "$(detail_venv_pip)"
|
|
else
|
|
report_component failed "bundle pip" "$(detail_venv_pip)"
|
|
READINESS_FAILURES=$((READINESS_FAILURES + 1))
|
|
fi
|
|
|
|
if check_venv_ffx; then
|
|
report_component ok "bundle ffx" "$(detail_venv_ffx)"
|
|
else
|
|
report_component failed "bundle ffx" "$(detail_venv_ffx)"
|
|
READINESS_FAILURES=$((READINESS_FAILURES + 1))
|
|
fi
|
|
|
|
if [ "${WITH_TESTS}" -eq 1 ]; then
|
|
echo
|
|
echo "Bundle test package status:"
|
|
if check_venv_pytest; then
|
|
report_component ok "bundle pytest" "$(detail_venv_pytest)"
|
|
else
|
|
report_component failed "bundle pytest" "$(detail_venv_pytest)"
|
|
READINESS_FAILURES=$((READINESS_FAILURES + 1))
|
|
fi
|
|
fi
|
|
|
|
echo
|
|
echo "Shell exposure status:"
|
|
if check_bashrc_file; then
|
|
report_component ok ".bashrc" "$(detail_bashrc_file)"
|
|
else
|
|
report_component warn ".bashrc" "$(detail_bashrc_file)"
|
|
fi
|
|
|
|
if check_bashrc_alias; then
|
|
report_component ok "ffx alias" "$(detail_bashrc_alias)"
|
|
else
|
|
report_component failed "ffx alias" "$(detail_bashrc_alias)"
|
|
READINESS_FAILURES=$((READINESS_FAILURES + 1))
|
|
fi
|
|
}
|
|
|
|
ensure_bundle_venv() {
|
|
mkdir -p "${HOME}/.local/share"
|
|
|
|
if ! check_venv_dir; then
|
|
printf 'Creating bundle virtualenv at %s...\n' "${VENV_DIR}"
|
|
if ! python3 -m venv "${VENV_DIR}"; then
|
|
printf 'Failed to create virtualenv at %s.\n' "${VENV_DIR}" >&2
|
|
INSTALL_FAILURES=$((INSTALL_FAILURES + 1))
|
|
return 1
|
|
fi
|
|
fi
|
|
|
|
if ! check_venv_pip; then
|
|
printf 'Missing pip in %s.\n' "${VENV_DIR}" >&2
|
|
INSTALL_FAILURES=$((INSTALL_FAILURES + 1))
|
|
return 1
|
|
fi
|
|
|
|
if [ "${WITH_TESTS}" -eq 1 ]; then
|
|
printf 'Installing FFX package and test extras into %s...\n' "${VENV_DIR}"
|
|
if ! (
|
|
cd "${ROOT_DIR}" &&
|
|
"${VENV_PIP}" install --editable '.[test]'
|
|
); then
|
|
printf 'Failed to install FFX package and test extras into %s.\n' "${VENV_DIR}" >&2
|
|
INSTALL_FAILURES=$((INSTALL_FAILURES + 1))
|
|
return 1
|
|
fi
|
|
else
|
|
printf 'Installing FFX package into %s...\n' "${VENV_DIR}"
|
|
if ! "${VENV_PIP}" install --editable "${ROOT_DIR}"; then
|
|
printf 'Failed to install FFX package into %s.\n' "${VENV_DIR}" >&2
|
|
INSTALL_FAILURES=$((INSTALL_FAILURES + 1))
|
|
return 1
|
|
fi
|
|
fi
|
|
|
|
return 0
|
|
}
|
|
|
|
write_alias_block() {
|
|
local bashrc_dir
|
|
bashrc_dir="$(dirname "${BASHRC_FILE}")"
|
|
mkdir -p "${bashrc_dir}"
|
|
touch "${BASHRC_FILE}"
|
|
|
|
if grep -Fq "${ALIAS_BLOCK_BEGIN}" "${BASHRC_FILE}" || grep -Fq "${ALIAS_BLOCK_END}" "${BASHRC_FILE}"; then
|
|
if ! python3 - "${BASHRC_FILE}" "${ALIAS_BLOCK_BEGIN}" "${ALIAS_BLOCK_END}" "${ALIAS_LINE}" <<'PY'
|
|
import pathlib
|
|
import sys
|
|
|
|
path = pathlib.Path(sys.argv[1])
|
|
begin = sys.argv[2]
|
|
end = sys.argv[3]
|
|
alias_line = sys.argv[4]
|
|
|
|
content = path.read_text()
|
|
block = f"{begin}\n{alias_line}\n{end}\n"
|
|
|
|
start = content.find(begin)
|
|
stop = content.find(end)
|
|
|
|
if start != -1 and stop != -1 and stop >= start:
|
|
stop += len(end)
|
|
if stop < len(content) and content[stop] == "\n":
|
|
stop += 1
|
|
content = content[:start] + block + content[stop:]
|
|
else:
|
|
if content and not content.endswith("\n"):
|
|
content += "\n"
|
|
content += block
|
|
|
|
path.write_text(content)
|
|
PY
|
|
then
|
|
printf 'Failed to update managed alias block in %s.\n' "${BASHRC_FILE}" >&2
|
|
INSTALL_FAILURES=$((INSTALL_FAILURES + 1))
|
|
return 1
|
|
fi
|
|
elif check_bashrc_alias; then
|
|
:
|
|
else
|
|
{
|
|
if [ -s "${BASHRC_FILE}" ] && [ "$(tail -c 1 "${BASHRC_FILE}" 2>/dev/null || true)" != "" ]; then
|
|
printf '\n'
|
|
fi
|
|
printf '%s\n' "${ALIAS_BLOCK_BEGIN}"
|
|
printf '%s\n' "${ALIAS_LINE}"
|
|
printf '%s\n' "${ALIAS_BLOCK_END}"
|
|
} >>"${BASHRC_FILE}" || {
|
|
printf 'Failed to append alias block to %s.\n' "${BASHRC_FILE}" >&2
|
|
INSTALL_FAILURES=$((INSTALL_FAILURES + 1))
|
|
return 1
|
|
}
|
|
fi
|
|
|
|
return 0
|
|
}
|
|
|
|
ensure_bashrc_alias() {
|
|
printf 'Ensuring ffx alias in %s...\n' "${BASHRC_FILE}"
|
|
write_alias_block
|
|
}
|
|
|
|
parse_args() {
|
|
while [ "$#" -gt 0 ]; do
|
|
case "$1" in
|
|
--check)
|
|
CHECK_ONLY=1
|
|
;;
|
|
--with-tests)
|
|
WITH_TESTS=1
|
|
;;
|
|
--help|-h)
|
|
usage
|
|
exit 0
|
|
;;
|
|
*)
|
|
printf 'Unknown option: %s\n\n' "$1" >&2
|
|
usage >&2
|
|
exit 2
|
|
;;
|
|
esac
|
|
shift
|
|
done
|
|
}
|
|
|
|
main() {
|
|
parse_args "$@"
|
|
|
|
print_status_report
|
|
|
|
if [ "${CHECK_ONLY}" -eq 0 ]; then
|
|
if ! check_python3; then
|
|
printf '\npython3 is required before the bundle venv can be prepared.\n' >&2
|
|
exit 1
|
|
fi
|
|
|
|
echo
|
|
ensure_bundle_venv
|
|
ensure_bashrc_alias
|
|
|
|
echo
|
|
print_status_report
|
|
fi
|
|
|
|
echo
|
|
if [ "${INSTALL_FAILURES}" -gt 0 ]; then
|
|
echo "One or more bundle preparation steps failed; see the status checks above." >&2
|
|
exit 1
|
|
fi
|
|
|
|
if [ "${READINESS_FAILURES}" -gt 0 ]; then
|
|
echo "The FFX bundle virtualenv and/or alias setup is incomplete." >&2
|
|
exit 1
|
|
fi
|
|
|
|
echo "The FFX bundle virtualenv is ready."
|
|
}
|
|
|
|
main "$@"
|