#! /usr/bin/env sh # ╭───────────────╮ # │ __ = internal │ # ╰───────────────╯ # __SPCD_BANNER_TOP # __SPCD_BANNER_MIDDLE # __SPCD_BANNER_BOTTOM # __SPCD_STEP_LEVEL # __SPCD_STEP_level_INDEX # __SPCD_STEP_level_LABEL # ╭─────────────╮ # │ _ = private │ # ╰─────────────╯ _SPCD_BOX_DOWN_AND_HORIZONTAL="┬" _SPCD_BOX_DOWN_AND_LEFT="╮" _SPCD_BOX_DOWN_AND_RIGHT="╭" _SPCD_BOX_HORIZONTAL="─" _SPCD_BOX_LEFT="╴" _SPCD_BOX_UP_AND_HORIZONTAL="┴" _SPCD_BOX_UP_AND_LEFT="╯" _SPCD_BOX_UP_AND_RIGHT="╰" _SPCD_BOX_VERTICAL="│" _SPCD_CMD_SUM="sha512sum" _spcd_fill() { local index="${1}" while [ "${index}" -gt 0 ]; do printf "%s" "${2}" index=$((index - 1)) done } # ╭───┬────────╮ # │ _ │ banner │ # ╰───┴────────╯ _SPCD_BANNER_BOTTOM_LEFT="${_SPCD_BOX_UP_AND_RIGHT}" _SPCD_BANNER_BOTTOM_MIDDLE="${_SPCD_BOX_UP_AND_HORIZONTAL}" _SPCD_BANNER_BOTTOM_RIGHT="${_SPCD_BOX_UP_AND_LEFT}" _SPCD_BANNER_HORIZONTAL="${_SPCD_BOX_HORIZONTAL}" _SPCD_BANNER_TOP_LEFT="${_SPCD_BOX_DOWN_AND_RIGHT}" _SPCD_BANNER_TOP_MIDDLE="${_SPCD_BOX_DOWN_AND_HORIZONTAL}" _SPCD_BANNER_TOP_RIGHT="${_SPCD_BOX_DOWN_AND_LEFT}" _SPCD_BANNER_VERTICAL="${_SPCD_BOX_VERTICAL}" _spcd_banner_add() { if [ -n "${1}" ]; then local text length filler text=" ${1} " length=${#text} filler="$(_spcd_fill "${length}" "${_SPCD_BANNER_HORIZONTAL}")" if [ -n "${__SPCD_BANNER_MIDDLE}" ]; then _spcd_banner_split else _spcd_banner_open fi _spcd_banner_append "${filler}" "${text}" "${filler}" fi } _spcd_banner_add_index() { local level value label while true; do level=$((level + 1)) eval "value=\"\${__SPCD_STEP_${level}_INDEX}\"" [ -n "${value}" ] || break if [ -n "${label}" ]; then label="${label}.${value}" else label="${value}" fi done _spcd_banner_add "${label}" } _spcd_banner_append() { if [ -n "${3}" ]; then __SPCD_BANNER_TOP="${__SPCD_BANNER_TOP}${1}" __SPCD_BANNER_MIDDLE="${__SPCD_BANNER_MIDDLE}${2}" __SPCD_BANNER_BOTTOM="${__SPCD_BANNER_BOTTOM}${3}" fi } _spcd_banner_echo() { echo "\ ${__SPCD_BANNER_TOP} ${__SPCD_BANNER_MIDDLE} ${__SPCD_BANNER_BOTTOM}" } _spcd_banner_open() { _spcd_banner_append \ "${_SPCD_BANNER_TOP_LEFT}" \ "${_SPCD_BANNER_VERTICAL}" \ "${_SPCD_BANNER_BOTTOM_LEFT}" } _spcd_banner_render() { _spcd_banner_wipe _spcd_banner_add "S" _spcd_banner_add_index # local level value while true; do level=$((level + 1)) eval "value=\"\${__SPCD_STEP_${level}_LABEL}\"" [ -n "${value}" ] || break _spcd_banner_add "${value}" done # _spcd_banner_shut _spcd_banner_echo _spcd_banner_wipe } _spcd_banner_shut() { _spcd_banner_append \ "${_SPCD_BANNER_TOP_RIGHT}" \ "${_SPCD_BANNER_VERTICAL}" \ "${_SPCD_BANNER_BOTTOM_RIGHT}" } _spcd_banner_split() { _spcd_banner_append \ "${_SPCD_BANNER_TOP_MIDDLE}" \ "${_SPCD_BANNER_VERTICAL}" \ "${_SPCD_BANNER_BOTTOM_MIDDLE}" } _spcd_banner_wipe() { unset \ __SPCD_BANNER_TOP \ __SPCD_BANNER_MIDDLE \ __SPCD_BANNER_BOTTOM } # ╭───┬───────╮ # │ _ │ error │ # ╰───┴───────╯ _SPCD_ERROR_CI=1 _SPCD_ERROR_OS=2 # ╭───┬───────╮ # │ _ │ frame │ # ╰───┴───────╯ _SPCD_FRAME_TOP="${_SPCD_BOX_DOWN_AND_RIGHT}${_SPCD_BOX_LEFT}" _SPCD_FRAME_BOTTOM="${_SPCD_BOX_UP_AND_RIGHT}${_SPCD_BOX_LEFT}" _spcd_frame_open() { echo "${_SPCD_FRAME_TOP}${*}" } _spcd_frame_shut() { echo "${_SPCD_FRAME_BOTTOM}${*}" } # ╭───┬────╮ # │ _ │ os │ # ╰───┴────╯ _spcd_os_cat() { if [ -n "${1}" ]; then _spcd_frame_open "${1}" cat "${1}" _spcd_frame_shut "${1}" fi } # ╭───┬──────╮ # │ _ │ rule │ # ╰───┴──────╯ _SPCD_RULE_LEFT="╶" _SPCD_RULE_MIDDLE="─" _SPCD_RULE_RIGHT="╴" # ╭───┬──────╮ # │ _ │ step │ # ╰───┴──────╯ _spcd_step_wipe() { unset \ "__SPCD_STEP_${__SPCD_STEP_LEVEL}_INDEX" \ "__SPCD_STEP_${__SPCD_STEP_LEVEL}_LABEL" } # ╭───┬─────╮ # │ _ │ txt │ # ╰───┴─────╯ _SPCD_TXT_CHARSET="UTF-8" _SPCD_TXT_LOCALE_DEFAULT="C" _spcd_txt_pick() { case "${SPCD_TXT_LOCALE}" in "${SPCD_TXT_LOCALE_ENGLISH}") echo "${1}" ;; "${SPCD_TXT_LOCALE_FRENCH}") echo "${2}" ;; *) ;; esac } # ╭────────────╮ # │ p = public │ # ╰────────────╯ spcd_ca() { local grep="\(After\|Before\|Issuer\|Signature Algorithm\|Subject:\)" local index name value local root="${2}" while true; do index=$((index + 1)) name="SPCD_CA_${index}" eval "value=\"\${${name}}\"" [ -n "${value}" ] || break case "${1}" in "list") echo "${name} =" echo "${value}" | openssl x509 -noout -text | grep "${grep}" ;; "write") spcd_os_write "${root}/${index}.crt" "${value}" ;; *) ;; esac done } spcd_dns() { local index name text value while true; do index=$((index + 1)) name="SPCD_DNS_${index}" eval "value=\"\${${name}}\"" [ -n "${value}" ] || break case "${1}" in "list") spcd_os_printenv "${name}" ;; "write") text="${text}\ nameserver ${value} " ;; *) ;; esac done if [ "${1}" = "write" ]; then spcd_os_write "/etc/resolv.conf" "${text}" fi } spcd_run() { if [ -n "${1}" ]; then _spcd_frame_open "${@}" "${@}" _spcd_frame_shut "${@}" fi } # ╭───┬───────╮ # │ p │ error │ # ╰───┴───────╯ spcd_error_ci() { echo "× CI: ${*}" exit "${_SPCD_ERROR_CI}" } spcd_error_os() { printf "× OS: " spcd_os_printenv "${1}" exit "${_SPCD_ERROR_OS}" } # ╭───┬────╮ # │ p │ os │ # ╰───┴────╯ spcd_os_grep() { local variable="${1}" [ -n "${variable}" ] && grep "^${variable}=" "/etc/os-release" | sed "s|^${variable}=||" | sed "s|^\"\(.*\)\"$|\1|" } spcd_os_ls() { if [ -n "${1}" ]; then _spcd_frame_open "${1}" ls -a -l "${1}" _spcd_frame_shut "${1}" fi } spcd_os_mkdir() { echo "→ ${1}" mkdir --parents "${1}" } spcd_os_printenv() { if [ -n "${1}" ]; then local name text for name in "${@}"; do eval "text=\"\${${name}}\"" echo "${name} = \"${text}\"" done fi } spcd_os_rm() { if [ -e "${1}" ]; then echo "← ${1}" rm -r "${1}" fi } spcd_os_sed() { local file="${1}" shift if [ -f "${file}" ]; then local regex _spcd_os_cat "${file}" for regex in "${@}"; do sed --in-place "s${regex}g" "${file}" && _spcd_os_cat "${file}" done fi } spcd_os_write() { local file="${1}" local text="${2}" if [ -n "${file}" ]; then [ -f "${file}" ] && _spcd_os_cat "${file}" echo "→ ${file}" printf "%s" "${text}" >"${file}" _spcd_os_cat "${file}" fi } # ╭───┬───────╮ # │ p │ split │ # ╰───┴───────╯ spcd_split() { echo "${_SPCD_RULE_LEFT}${_SPCD_RULE_MIDDLE}${_SPCD_RULE_RIGHT}" } # ╭───┬──────╮ # │ p │ step │ # ╰───┴──────╯ spcd_step() { if [ -n "${1}" ]; then [ -n "${__SPCD_STEP_LEVEL}" ] || __SPCD_STEP_LEVEL=1 local prefix="__SPCD_STEP_${__SPCD_STEP_LEVEL}" local index="${prefix}_INDEX" eval "${index}=\$((${index} + 1))" eval "${prefix}_LABEL=\"${1}\"" _spcd_banner_render fi } spcd_step_in() { [ -n "${1}" ] && spcd_step "${1}" __SPCD_STEP_LEVEL=$((__SPCD_STEP_LEVEL + 1)) _spcd_step_wipe } spcd_step_out() { _spcd_step_wipe __SPCD_STEP_LEVEL=$((__SPCD_STEP_LEVEL - 1)) } # ╭───┬─────╮ # │ p │ txt │ # ╰───┴─────╯ SPCD_TXT_LOCALE_ENGLISH="en_US" SPCD_TXT_LOCALE_FRENCH="fr_FR" spcd_txt_get() { case "${1}" in "set") _spcd_txt_pick "Set" "Définir" ;; *) ;; esac } # ╭──────────╮ # │ s = step │ # ╰──────────╯ # ╭───┬───────────╮ # │ s │ constants │ # ╰───┴───────────╯ SPCD_OS_ALMA="alma" SPCD_OS_ALPINE="alpine" SPCD_OS_ARCH="arch" SPCD_OS_DEBIAN="debian" SPCD_OS_FEDORA="fedora" SPCD_OS_OPENSUSE="opensuse" SPCD_OS_ROCKY="rocky" SPCD_OS_UBUNTU="ubuntu" SPCD_PM_APK="apk" SPCD_PM_APT="apt" SPCD_PM_DNF="dnf" SPCD_PM_PACMAN="pacman" SPCD_PM_ZYPPER="zypper" # ╭───┬─────────────╮ # │ s │ environment │ # ╰───┴─────────────╯ spcd_step__environment_print() { spcd_ca "list" spcd_dns "list" set -- \ "GIT_RWX" \ "GIT_SPCD" \ "GIT_SHUNIT" \ "REF_FEATURE" \ "REF_RELEASE" \ "REF_STAGING" \ "TXT_LOCALE" \ "URL_ALMA" \ "URL_ALPINE" \ "URL_ARCH" \ "URL_DEBIAN" \ "URL_EPEL" \ "URL_FEDORA" \ "URL_OPENSUSE" \ "URL_PYTHON" \ "URL_ROCKY" \ "URL_UBUNTU" local item name value for item in "${@}"; do name="SPCD_${item}" eval "value=\"\${${name}}\"" if [ -n "${value}" ]; then spcd_os_printenv "${name}" fi done } spcd_step__environment_defaults_set() { spcd_step "$(spcd_txt_get "set")" [ -n "${SPCD_DNS_1}" ] || SPCD_DNS_1="9.9.9.9" [ -n "${SPCD_GIT_RWX}" ] || SPCD_GIT_RWX="rwx" [ -n "${SPCD_GIT_SPCD}" ] || SPCD_GIT_SPCD="spcd" [ -n "${SPCD_GIT_SHUNIT}" ] || SPCD_GIT_SHUNIT="shunit2" [ -n "${SPCD_REF_FEATURE}" ] || SPCD_REF_FEATURE="f" [ -n "${SPCD_REF_RELEASE}" ] || SPCD_REF_RELEASE="main" [ -n "${SPCD_REF_STAGING}" ] || SPCD_REF_STAGING="dev" [ -n "${SPCD_TXT_LOCALE}" ] || SPCD_TXT_LOCALE="${SPCD_TXT_LOCALE_ENGLISH}" [ -n "${SPCD_URL_ALMA}" ] || # /almalinux/$releasever/… # /vault/$releasever/… SPCD_URL_ALMA="https://repo.almalinux.org" [ -n "${SPCD_URL_ALPINE}" ] || # /v?.??/community # /v?.??/main SPCD_URL_ALPINE="https://dl-cdn.alpinelinux.org/alpine" [ -n "${SPCD_URL_ARCH}" ] || # /$repo/os/$arch SPCD_URL_ARCH="https://geo.mirror.pkgbuild.com" [ -n "${SPCD_URL_DEBIAN}" ] || SPCD_URL_DEBIAN="https://deb.debian.org/debian" [ -n "${SPCD_URL_EPEL}" ] || # /$releasever/Everything/… # /testing/$releasever/Everything/… SPCD_URL_EPEL="https://dl.fedoraproject.org/pub/epel" [ -n "${SPCD_URL_FEDORA}" ] || # /releases/$releasever/Everything/… # /updates/$releasever/Everything/… # /updates/testing/$releasever/Everything/… SPCD_URL_FEDORA="https://rpmfind.net/linux/fedora/linux" [ -n "${SPCD_URL_OPENSUSE}" ] || # /debug/… # /distribution/… # /source/… # /update/… SPCD_URL_OPENSUSE="https://download.opensuse.org" [ -n "${SPCD_URL_PYTHON}" ] || # /simple SPCD_URL_PYTHON="https://pypi.org" [ -n "${SPCD_URL_ROCKY}" ] || # /$contentdir/$releasever/… SPCD_URL_ROCKY="https://dl.rockylinux.org" [ -n "${SPCD_URL_UBUNTU}" ] || SPCD_URL_UBUNTU="https://ubuntu.mirrors.ovh.net/ubuntu" } spcd_step__environment_set_variables() { spcd_step "Set variables" # check script first [ -n "${SPCD}" ] || spcd_error_ci "SPCD" # continuous integration / github → gitea → forgejo if [ -n "${GITHUB_ACTIONS}" ]; then # project branch if [ -n "${GITHUB_REF_NAME}" ]; then SPCD_PROJECT_BRANCH="${GITHUB_REF_NAME}" else spcd_error_ci "GITHUB_REF_NAME" fi # project root if [ -n "${GITHUB_SERVER_URL}" ]; then SPCD_PROJECT_ROOT="$(dirname "${GITHUB_SERVER_URL}")//" [ -n "${GITHUB_TOKEN}" ] && SPCD_PROJECT_ROOT="${SPCD_PROJECT_ROOT}${GITHUB_TOKEN}@" SPCD_PROJECT_ROOT="\ ${SPCD_PROJECT_ROOT}$(basename "${GITHUB_SERVER_URL}")" else spcd_error_ci "GITHUB_SERVER_URL" fi # project path & name if [ -n "${GITHUB_REPOSITORY}" ]; then # project path SPCD_PROJECT_PATH="$(dirname "${GITHUB_REPOSITORY}")" # project name SPCD_PROJECT_NAME="$(basename "${GITHUB_REPOSITORY}")" else spcd_error_ci "GITHUB_REPOSITORY" fi # continuous integration / gitlab elif [ -n "${GITLAB_CI}" ]; then # project branch if [ -n "${CI_COMMIT_BRANCH}" ]; then SPCD_PROJECT_BRANCH="${CI_COMMIT_BRANCH}" else spcd_error_ci "CI_COMMIT_BRANCH" fi # project root / protocol if [ -n "${CI_SERVER_PROTOCOL}" ]; then # project root / login if [ -n "${CI_REGISTRY_USER}" ]; then # project root / password if [ -n "${CI_REGISTRY_PASSWORD}" ]; then # project root / host if [ -n "${CI_SERVER_FQDN}" ]; then # project root SPCD_PROJECT_ROOT="${CI_SERVER_PROTOCOL}\ ://${CI_REGISTRY_USER}:${CI_REGISTRY_PASSWORD}@${CI_SERVER_FQDN}" # project path if [ -n "${CI_PROJECT_NAMESPACE}" ]; then SPCD_PROJECT_PATH="${CI_PROJECT_NAMESPACE}" # project name if [ -n "${CI_PROJECT_NAME}" ]; then SPCD_PROJECT_NAME="${CI_PROJECT_NAME}" else spcd_error_ci "CI_PROJECT_NAME" fi else spcd_error_ci "CI_PROJECT_NAMESPACE" fi else spcd_error_ci "CI_SERVER_FQDN" fi else spcd_error_ci "CI_REGISTRY_PASSWORD" fi else spcd_error_ci "CI_REGISTRY_USER" fi else spcd_error_ci "CI_SERVER_PROTOCOL" fi # continuous integration / unsupported else spcd_error_ci "ø" fi # check project variables case "${SPCD_PROJECT_BRANCH}" in "${SPCD_REF_RELEASE}" | \ "${SPCD_REF_STAGING}" | \ "${SPCD_REF_FEATURE}") ;; *) spcd_error_ci "SPCD_PROJECT_BRANCH" ;; esac [ -n "${SPCD_PROJECT_ROOT}" ] || spcd_error_ci "SPCD_PROJECT_ROOT" [ -n "${SPCD_PROJECT_PATH}" ] || spcd_error_ci "SPCD_PROJECT_PATH" [ -n "${SPCD_PROJECT_NAME}" ] || spcd_error_ci "SPCD_PROJECT_NAME" # spcd_os_printenv "SPCD_PROJECT_BRANCH" \ "SPCD_PROJECT_ROOT" "SPCD_PROJECT_PATH" "SPCD_PROJECT_NAME" # set operating system id SPCD_OS_ID="$(spcd_os_grep ID)" case "${SPCD_OS_ID}" in "almalinux") SPCD_OS_ID="${SPCD_OS_ALMA}" ;; "alpine") SPCD_OS_ID="${SPCD_OS_ALPINE}" ;; "arch") SPCD_OS_ID="${SPCD_OS_ARCH}" ;; "debian") SPCD_OS_ID="${SPCD_OS_DEBIAN}" ;; "fedora") SPCD_OS_ID="${SPCD_OS_FEDORA}" ;; "opensuse-leap") SPCD_OS_ID="${SPCD_OS_OPENSUSE}" ;; "rocky") SPCD_OS_ID="${SPCD_OS_ROCKY}" ;; "ubuntu") SPCD_OS_ID="${SPCD_OS_UBUNTU}" ;; *) spcd_error_os "SPCD_OS_ID" ;; esac # set operating system version case "${SPCD_OS_ID}" in "${SPCD_OS_ALMA}" | "${SPCD_OS_FEDORA}" | "${SPCD_OS_ROCKY}" | \ "${SPCD_OS_ARCH}") SPCD_OS_VERSION=$(spcd_os_grep VERSION_ID | sed "s|^\([0-9]\+\)\..*|\1|") ;; "${SPCD_OS_ALPINE}" | "${SPCD_OS_OPENSUSE}") SPCD_OS_VERSION=$(spcd_os_grep VERSION_ID | sed "s|^\([0-9]\+\.[0-9]\+\)\..*|\1|") ;; "${SPCD_OS_DEBIAN}" | "${SPCD_OS_UBUNTU}") SPCD_OS_VERSION="$(spcd_os_grep VERSION_CODENAME)" ;; *) ;; esac # check operating system version case "${SPCD_OS_ID}" in "${SPCD_OS_ALMA}" | "${SPCD_OS_ROCKY}") case "${SPCD_OS_VERSION}" in "9" | "8") ;; *) spcd_error_os "SPCD_OS_VERSION" ;; esac ;; "${SPCD_OS_ALPINE}") case "${SPCD_OS_VERSION}" in "3.20" | "3.19") ;; *) spcd_error_os "SPCD_OS_VERSION" ;; esac ;; "${SPCD_OS_ARCH}") case "${SPCD_OS_VERSION}" in "20240818" | "20240101") ;; *) spcd_error_os "SPCD_OS_VERSION" ;; esac ;; "${SPCD_OS_DEBIAN}") case "${SPCD_OS_VERSION}" in "bookworm" | "bullseye") ;; *) spcd_error_os "SPCD_OS_VERSION" ;; esac ;; "${SPCD_OS_FEDORA}") case "${SPCD_OS_VERSION}" in "40" | "39") ;; *) spcd_error_os "SPCD_OS_VERSION" ;; esac ;; "${SPCD_OS_OPENSUSE}") case "${SPCD_OS_VERSION}" in "15.6" | "15.5") ;; *) spcd_error_os "SPCD_OS_VERSION" ;; esac ;; "${SPCD_OS_UBUNTU}") case "${SPCD_OS_VERSION}" in "noble" | "jammy") ;; *) spcd_error_os "SPCD_OS_VERSION" ;; esac ;; *) ;; esac spcd_split spcd_os_printenv "SPCD_OS_ID" "SPCD_OS_VERSION" # set package manager case "${SPCD_OS_ID}" in "${SPCD_OS_ALPINE}") SPCD_PM="${SPCD_PM_APK}" ;; "${SPCD_OS_DEBIAN}" | "${SPCD_OS_UBUNTU}") SPCD_PM="${SPCD_PM_APT}" ;; "${SPCD_OS_ALMA}" | "${SPCD_OS_FEDORA}" | "${SPCD_OS_ROCKY}") SPCD_PM="${SPCD_PM_DNF}" ;; "${SPCD_OS_ARCH}") SPCD_PM="${SPCD_PM_PACMAN}" ;; "${SPCD_OS_OPENSUSE}") SPCD_PM="${SPCD_PM_ZYPPER}" ;; *) ;; esac spcd_split spcd_os_printenv "SPCD_PM" # set python alias SPCD_PYTHON_ALIAS="python3" spcd_split spcd_os_printenv "SPCD_PYTHON_ALIAS" # set python command & package case "${SPCD_OS_ID}" in "${SPCD_OS_ALMA}" | "${SPCD_OS_ROCKY}") SPCD_PYTHON_COMMAND="python3.12" SPCD_PYTHON_PACKAGE="python3.12" ;; "${SPCD_OS_ALPINE}") case "${SPCD_OS_VERSION}" in "3.20") SPCD_PYTHON_COMMAND="python3.12" ;; "3.19") SPCD_PYTHON_COMMAND="python3.11" ;; *) ;; esac SPCD_PYTHON_PACKAGE="python3" ;; "${SPCD_OS_ARCH}") SPCD_PYTHON_COMMAND="python3.12" SPCD_PYTHON_PACKAGE="python" ;; "${SPCD_OS_DEBIAN}") case "${SPCD_OS_VERSION}" in "bookworm") SPCD_PYTHON_COMMAND="python3.11" ;; "bullseye") SPCD_PYTHON_COMMAND="python3.9" ;; *) ;; esac SPCD_PYTHON_PACKAGE="python3" ;; "${SPCD_OS_FEDORA}") SPCD_PYTHON_COMMAND="python3.12" SPCD_PYTHON_PACKAGE="python3" ;; "${SPCD_OS_OPENSUSE}") case "${SPCD_OS_VERSION}" in "15.6") SPCD_PYTHON_COMMAND="python3.12" SPCD_PYTHON_PACKAGE="python312" ;; "15.5") SPCD_PYTHON_COMMAND="python3.11" SPCD_PYTHON_PACKAGE="python311" ;; *) ;; esac ;; "${SPCD_OS_UBUNTU}") case "${SPCD_OS_VERSION}" in "noble") SPCD_PYTHON_COMMAND="python3.12" ;; "jammy") SPCD_PYTHON_COMMAND="python3.10" ;; *) ;; esac SPCD_PYTHON_PACKAGE="python3" ;; *) ;; esac spcd_split spcd_os_printenv "SPCD_PYTHON_COMMAND" "SPCD_PYTHON_PACKAGE" # set python packages case "${SPCD_OS_ID}" in "${SPCD_OS_ALMA}" | "${SPCD_OS_FEDORA}" | "${SPCD_OS_ROCKY}" | \ "${SPCD_OS_OPENSUSE}") SPCD_PYTHON_PACKAGES="/usr/lib64/${SPCD_PYTHON_COMMAND}/site-packages" ;; "${SPCD_OS_ALPINE}" | "${SPCD_OS_ARCH}") SPCD_PYTHON_PACKAGES="/usr/lib/${SPCD_PYTHON_COMMAND}/site-packages" ;; "${SPCD_OS_DEBIAN}" | "${SPCD_OS_UBUNTU}") SPCD_PYTHON_PACKAGES="/usr/lib/${SPCD_PYTHON_ALIAS}/dist-packages" ;; *) ;; esac # set python virtual environment SPCD_PYTHON_VENV="/opt/venv" SPCD_PYTHON_VENV_BINARIES="${SPCD_PYTHON_VENV}/bin" SPCD_PYTHON_VENV_PACKAGES="\ ${SPCD_PYTHON_VENV}/lib/${SPCD_PYTHON_COMMAND}/site-packages" spcd_split spcd_os_printenv "SPCD_PYTHON_PACKAGES" "SPCD_PYTHON_VENV_PACKAGES" # downloads SPCD_DL="$(mktemp --directory)" spcd_split spcd_os_printenv "SPCD_DL" } spcd_step__environment_list_workspace() { spcd_step "List workspace" spcd_os_ls "$(realpath .)" } # ╭──────────────╮ # │ f = function │ # ╰──────────────╯ spcd_git_clone() { if [ -n "${1}" ]; then local name path url name="$(basename "${1}")" path="${SPCD_DL}/${name}" url="$(spcd_git_url "${1}")" git clone "${url}" "${path}" echo "${path}" fi } spcd_git_url() { if [ -n "${1}" ]; then case "${1}" in http*) echo "${1}" ;; */*) echo "${SPCD_PROJECT_ROOT}/${1}" ;; *) echo "${SPCD_PROJECT_ROOT}/${SPCD_PROJECT_PATH}/${1}" ;; esac fi } spcd_python_ln() { local command="${1}" if [ -n "${command}" ]; then echo "→ ${SPCD_PYTHON_ALIAS} → ${command}" ln -f -s "${command}" \ "/usr/bin/${SPCD_PYTHON_ALIAS}" fi } spcd_python_pip() { if [ -n "${1}" ]; then local name path target name="$(basename "${1}")" path="$(spcd_git_clone "${1}")" path="${path}/${name}" target="${SPCD_PYTHON_VENV_PACKAGES}" cp --recursive "${path}" "${target}" echo "${target}/${name}" fi } spcd_txt_locale() { local action="${1}" local chosen="${2}" set -- \ "LANG" \ "LC_CTYPE" \ "LC_NUMERIC" \ "LC_TIME" \ "LC_COLLATE" \ "LC_MONETARY" \ "LC_MESSAGES" case "${SPCD_OS_ID}" in "${SPCD_OS_ALPINE}") ;; *) set -- \ "${@}" \ "LANGUAGE" \ "LC_PAPER" \ "LC_NAME" \ "LC_ADDRESS" \ "LC_TELEPHONE" \ "LC_MEASUREMENT" \ "LC_IDENTIFICATION" ;; esac local name case "${action}" in "list") if ! locale; then for name in "${@}"; do spcd_os_printenv "${name}" done fi ;; "set") spcd_txt_locale "list" spcd_split local locale long if [ -n "${chosen}" ]; then locale="${chosen}" else locale="${_SPCD_TXT_LOCALE_DEFAULT}" fi long="${locale}.${_SPCD_TXT_CHARSET}" for name in "${@}"; do if [ "${name}" != "LANGUAGE" ]; then export "${name}=${long}" else export "${name}=$(spcd_txt_locales language "${chosen}")" fi done spcd_split spcd_txt_locale "list" ;; "show") local regex for name in "${@}"; do unset regex case "${name}" in # LC_CTYPE "LC_NUMERIC") regex="^\(decimal\|thousands\|grouping\)" ;; "LC_TIME") regex="^\(day\|mon\)" ;; # LC_COLLATE "LC_MONETARY") case "${SPCD_OS_ID}" in "${SPCD_OS_ALPINE}") ;; *) regex="^\(int_curr\|currency\|mon_\)" ;; esac ;; "LC_MESSAGES") regex="^\(yes\|no\)" ;; "LC_PAPER") regex="^\(height\|width\)" ;; "LC_NAME") regex="^name_m" ;; "LC_ADDRESS") regex="^\(country\|lang\)_name" ;; "LC_TELEPHONE") regex="^int_" ;; "LC_MEASUREMENT") regex="^measurement=" ;; "LC_IDENTIFICATION") regex="^\(language\|territory\|title\)" ;; *) ;; esac spcd_split spcd_os_printenv "${name}" if [ -n "${regex}" ]; then locale --keyword-name "${name}" | grep "${regex}" fi done ;; *) ;; esac } spcd_txt_locales() { local action="${1}" local chosen="${2}" set -- \ "${SPCD_TXT_LOCALE_ENGLISH}" \ "${SPCD_TXT_LOCALE_FRENCH}" local name case "${action}" in "install") spcd_txt_locales "list" spcd_split case "${SPCD_PM}" in "${SPCD_PM_APK}") spcd_f_pm_pkg_install "musl-locales" export MUSL_LOCPATH="/usr/share/i18n/locales/musl" ;; "${SPCD_PM_APT}") local text for name in "${@}"; do text="${text}\ ${name}.${_SPCD_TXT_CHARSET} ${_SPCD_TXT_CHARSET} " done spcd_os_write "/etc/locale.gen" "${text}" spcd_f_pm_pkg_install "locales" ;; "${SPCD_PM_DNF}") local language for name in "${@}"; do language="$(echo "${name}" | cut -d _ -f 1)" spcd_f_pm_pkg_install "glibc-langpack-${language}" done ;; "${SPCD_PM_PACMAN}") spcd_f_pm_pkg_install "glibc-locales" ;; "${SPCD_PM_ZYPPER}") spcd_f_pm_pkg_install "glibc-locale" ;; *) ;; esac spcd_split spcd_txt_locales "list" ;; "language") local text if [ -n "${chosen}" ]; then text="${chosen}" fi for name in "${@}"; do if [ "${name}" != "${chosen}" ]; then if [ -n "${text}" ]; then text="${text}:${name}" else text="${name}" fi fi done ;; "list") if ! spcd_run locale --all-locales; then echo "No locales yet!" fi ;; *) ;; esac } # ╭───┬──────────────────────╮ # │ f │ pm = package manager │ # ╰───┴──────────────────────╯ spcd_f_pm_query() { case "${SPCD_PM}" in "${SPCD_PM_APK}") apk info "${1}" ;; "${SPCD_PM_APT}") dpkg-query --show "${1}" ;; "${SPCD_PM_DNF}") rpm --query "${1}" ;; "${SPCD_PM_PACMAN}") pacman --query "${1}" ;; "${SPCD_PM_ZYPPER}") rpm --query "${1}" ;; *) ;; esac } spcd_f_pm_upgrade() { spcd_step "Upgrade" case "${SPCD_PM}" in "${SPCD_PM_APK}") spcd_run apk upgrade ;; "${SPCD_PM_APT}") spcd_run apt-get upgrade --assume-yes ;; "${SPCD_PM_DNF}") spcd_run dnf upgrade --assumeyes ;; "${SPCD_PM_PACMAN}") spcd_run pacman --sync --sysupgrade --noconfirm ;; "${SPCD_PM_ZYPPER}") spcd_run zypper --non-interactive update ;; *) ;; esac } # ╭───┬────┬───────╮ # │ f │ pm │ https │ # ╰───┴────┴───────╯ spcd_f_pm_https_path() { case "${SPCD_PM}" in "${SPCD_PM_APK}") echo "/etc/apk/repositories.d/https" ;; "${SPCD_PM_APT}") echo "/etc/apt/apt.conf.d/https" ;; "${SPCD_PM_DNF}") echo "/etc/dnf/dnf.conf.d/https.conf" ;; "${SPCD_PM_PACMAN}") echo "/etc/pacman.d/https.conf" ;; "${SPCD_PM_ZYPPER}") ;; *) ;; esac } spcd_f_pm_https_trust() { spcd_step "Trust" local path text if [ -n "${SPCD_CA_1}" ] || [ "${SPCD_PM}" = "${SPCD_PM_APT}" ]; then path="$(spcd_f_pm_https_path)" if [ -n "${path}" ]; then spcd_os_mkdir "$(dirname "${path}")" case "${SPCD_PM}" in "${SPCD_PM_APK}") text="\ --no-verify " ;; "${SPCD_PM_APT}") text="\ Acquire::https::Verify-Peer False; " ;; "${SPCD_PM_DNF}") text="\ sslverify=False " ;; "${SPCD_PM_PACMAN}") text="\ SSLVerify = No " ;; "${SPCD_PM_ZYPPER}") ;; *) ;; esac [ -n "${text}" ] && spcd_os_write "${path}" "${text}" fi fi } spcd_f_pm_https_verify() { spcd_step "Verify" local path path="$(spcd_f_pm_https_path)" if [ -n "${path}" ]; then spcd_os_rm "${path}" fi } # ╭───┬────┬─────╮ # │ f │ pm │ pkg │ # ╰───┴────┴─────╯ spcd_f_pm_pkg_clean() { case "${SPCD_PM}" in "${SPCD_PM_APK}") spcd_run apk cache purge ;; "${SPCD_PM_APT}") spcd_run apt-get clean ;; "${SPCD_PM_DNF}") spcd_run dnf clean all ;; "${SPCD_PM_PACMAN}") spcd_run pacman --sync --clean --noconfirm ;; "${SPCD_PM_ZYPPER}") spcd_run zypper clean ;; *) ;; esac } spcd_f_pm_pkg_install() { case "${SPCD_PM}" in "${SPCD_PM_APK}") spcd_run apk add "${1}" ;; "${SPCD_PM_APT}") spcd_run apt-get install --assume-yes "${1}" ;; "${SPCD_PM_DNF}") spcd_run dnf install --assumeyes "${1}" ;; "${SPCD_PM_PACMAN}") spcd_run pacman --sync --noconfirm "${1}" ;; "${SPCD_PM_ZYPPER}") spcd_run zypper --non-interactive install "${1}" ;; *) ;; esac spcd_f_pm_pkg_clean } spcd_f_pm_pkg_update() { spcd_step "Update" case "${SPCD_PM}" in "${SPCD_PM_APK}") spcd_run apk update ;; "${SPCD_PM_APT}") spcd_run apt-get update ;; "${SPCD_PM_DNF}") spcd_run dnf makecache ;; "${SPCD_PM_PACMAN}") spcd_run pacman --sync --refresh ;; "${SPCD_PM_ZYPPER}") spcd_run zypper refresh ;; *) ;; esac } # ╭───┬─────╮ # │ s │ dns │ # ╰───┴─────╯ spcd_step__dns() { spcd_step "DNS" spcd_dns "write" } # ╭───┬──────────╮ # │ s │ packages │ # ╰───┴──────────╯ spcd_step__packages_set_repositories() { spcd_step "Set repositories" local file case "${SPCD_OS_ID}" in "${SPCD_OS_ALMA}") case "${SPCD_OS_VERSION}" in "9") set -- \ "-appstream" \ "-baseos" \ "-crb" \ "-extras" \ "-highavailability" \ "-nfv" \ "-plus" \ "-resilientstorage" \ "-rt" \ "-sap" \ "-saphana" ;; "8") set -- \ "-ha" \ "-nfv" \ "-plus" \ "-powertools" \ "-resilientstorage" \ "-rt" \ "-sap" \ "-saphana" \ "" ;; *) ;; esac for file in "${@}"; do spcd_os_sed "/etc/yum.repos.d/almalinux${file}.repo" \ "|^mirrorlist|#mirrorlist|" \ "|https://repo.almalinux.org|${SPCD_URL_ALMA}|" \ "|^# baseurl|baseurl|" done ;; "${SPCD_OS_ALPINE}") spcd_os_write "/etc/apk/repositories" "\ ${SPCD_URL_ALPINE}/v${SPCD_OS_VERSION}/main ${SPCD_URL_ALPINE}/v${SPCD_OS_VERSION}/community " ;; "${SPCD_OS_ARCH}") spcd_os_write "/etc/pacman.d/mirrorlist" "\ Server = ${SPCD_URL_ARCH}/\$repo/os/\$arch " ;; "${SPCD_OS_DEBIAN}") spcd_os_write "/etc/apt/sources.list" "\ deb ${SPCD_URL_DEBIAN} ${SPCD_OS_VERSION} main deb ${SPCD_URL_DEBIAN} ${SPCD_OS_VERSION}-backports main deb ${SPCD_URL_DEBIAN} ${SPCD_OS_VERSION}-updates main deb ${SPCD_URL_DEBIAN}-security ${SPCD_OS_VERSION}-security main " ;; "${SPCD_OS_FEDORA}") set -- \ "-updates-testing" \ "-updates" \ "" for file in "${@}"; do spcd_os_sed "/etc/yum.repos.d/fedora${file}.repo" \ "|^metalink|#metalink|" "\ |http://download.example/pub/fedora/linux|${SPCD_URL_FEDORA}|" \ "|^#baseurl|baseurl|" done spcd_os_sed "/etc/yum.repos.d/fedora-cisco-openh264.repo" \ "|^enabled=1|enabled=0|" ;; "${SPCD_OS_OPENSUSE}") set -- \ "backports-debug-update" \ "backports-update" \ "debug-non-oss" \ "debug-update-non-oss" \ "debug-update" \ "debug" \ "non-oss" \ "oss" \ "sle-debug-update" \ "sle-update" \ "source" \ "update-non-oss" \ "update" for file in "${@}"; do spcd_os_sed "/etc/zypp/repos.d/repo-${file}.repo" \ "|http://download.opensuse.org|${SPCD_URL_OPENSUSE}|" done spcd_os_sed "/etc/zypp/repos.d/repo-openh264.repo" \ "|^enabled=1|enabled=0|" ;; "${SPCD_OS_ROCKY}") case "${SPCD_OS_VERSION}" in "9") set -- \ "rocky-addons" \ "rocky-devel" \ "rocky-extras" \ "rocky" ;; "8") # not Rocky-Media set -- \ "Rocky-AppStream" \ "Rocky-BaseOS" \ "Rocky-Debuginfo" \ "Rocky-Devel" \ "Rocky-Extras" \ "Rocky-HighAvailability" \ "Rocky-NFV" \ "Rocky-Plus" \ "Rocky-PowerTools" \ "Rocky-RT" \ "Rocky-ResilientStorage" \ "Rocky-Sources" ;; *) ;; esac for file in "${@}"; do spcd_os_sed "/etc/yum.repos.d/${file}.repo" \ "|^mirrorlist|#mirrorlist|" \ "|http://dl.rockylinux.org|${SPCD_URL_ROCKY}|" \ "|^#baseurl|baseurl|" done ;; "${SPCD_OS_UBUNTU}") spcd_os_write "/etc/apt/sources.list" "\ deb ${SPCD_URL_UBUNTU} ${SPCD_OS_VERSION} main universe deb ${SPCD_URL_UBUNTU} ${SPCD_OS_VERSION}-backports main universe deb ${SPCD_URL_UBUNTU} ${SPCD_OS_VERSION}-updates main universe deb ${SPCD_URL_UBUNTU} ${SPCD_OS_VERSION}-security main universe " ;; *) ;; esac } spcd_step__packages_set_configuration() { spcd_step "Set configuration" case "${SPCD_PM}" in "${SPCD_PM_DNF}") spcd_os_write "/etc/dnf/dnf.conf" "\ [main] best=True clean_requirements_on_remove=True gpgcheck=1 installonly_limit=3 skip_if_unavailable=False " ;; "${SPCD_PM_APK}") ;; "${SPCD_PM_PACMAN}") ;; "${SPCD_PM_APT}") spcd_os_write "/etc/apt/apt.conf.d/apt.conf" "\ Acquire::Check-Valid-Until True; APT::Get::Show-Versions True; APT::Install-Recommends False; APT::Install-Suggests False; Dir::Etc::SourceParts \"\"; " ;; "${SPCD_PM_ZYPPER}") ;; *) ;; esac case "${SPCD_OS_ID}" in "${SPCD_OS_ARCH}") spcd_run pacman-key --init ;; "${SPCD_OS_DEBIAN}" | "${SPCD_OS_UBUNTU}") export DEBIAN_FRONTEND="noninteractive" spcd_os_printenv DEBIAN_FRONTEND ;; *) ;; esac } spcd_step__packages_install_tools() { spcd_step "Install tools" case "${SPCD_PM}" in "${SPCD_PM_APT}") spcd_f_pm_pkg_install "apt-utils" ;; *) ;; esac } spcd_step__packages_install_locales() { spcd_step_in "Locales" spcd_step "Install" spcd_txt_locales "install" spcd_step "Set" spcd_txt_locale "set" "${SPCD_TXT_LOCALE}" spcd_step "Show" spcd_txt_locale "show" spcd_step_out } # ╭───┬────╮ # │ s │ ca │ # ╰───┴────╯ spcd_step__ca_install_package() { spcd_step "Install package" spcd_f_pm_pkg_install "ca-certificates" } spcd_step__ca_write_certificates() { spcd_step "Write certificates" local root case "${SPCD_OS_ID}" in "${SPCD_OS_ALMA}" | "${SPCD_OS_FEDORA}" | "${SPCD_OS_ROCKY}") root="/etc/pki/ca-trust/source/anchors" ;; "${SPCD_OS_ALPINE}" | "${SPCD_OS_DEBIAN}" | "${SPCD_OS_UBUNTU}") root="/usr/local/share/ca-certificates" ;; "${SPCD_OS_ARCH}") root="/etc/ca-certificates/trust-source/anchors" ;; "${SPCD_OS_OPENSUSE}") root="/etc/pki/trust/anchors" ;; *) ;; esac spcd_os_mkdir "${root}" spcd_ca "write" "${root}" } spcd_step__ca_update_certificates() { spcd_step "Update certificates" case "${SPCD_OS_ID}" in "${SPCD_OS_ARCH}" | \ "${SPCD_OS_ALMA}" | \ "${SPCD_OS_FEDORA}" | \ "${SPCD_OS_ROCKY}") spcd_run update-ca-trust ;; "${SPCD_OS_ALPINE}" | \ "${SPCD_OS_DEBIAN}" | \ "${SPCD_OS_OPENSUSE}" | \ "${SPCD_OS_UBUNTU}") spcd_run update-ca-certificates ;; *) ;; esac } # ╭───┬──────────╮ # │ s │ packages │ # ╰───┴──────────╯ spcd_step__packages_install_dos2unix() { spcd_step "Install dos2unix" spcd_f_pm_pkg_install "dos2unix" } spcd_step__packages_install_git() { spcd_step "Install Git" spcd_f_pm_pkg_install "git" } # ╭───┬────────╮ # │ s │ python │ # ╰───┴────────╯ spcd_step__python_install() { spcd_step "Install package" spcd_f_pm_pkg_install "${SPCD_PYTHON_PACKAGE}" spcd_step "Link alias to command" spcd_python_ln "${SPCD_PYTHON_COMMAND}" # venv spcd_step_in "Virtual environment" spcd_step "Install package" case "${SPCD_OS_ID}" in "${SPCD_OS_DEBIAN}" | "${SPCD_OS_UBUNTU}") spcd_f_pm_pkg_install "python3-venv" ;; *) ;; esac spcd_step "Configure PIP" spcd_os_write "/etc/pip.conf" "\ [global] index-url = ${SPCD_URL_PYTHON}/simple " spcd_step "Create" "${SPCD_PYTHON_ALIAS}" -m "venv" "${SPCD_PYTHON_VENV}" spcd_step "Activate" export PATH="${SPCD_PYTHON_VENV_BINARIES}:${PATH}" export VIRTUAL_ENV="${SPCD_PYTHON_VENV}" spcd_step_out } spcd_step__python_modules() { spcd_step_in "Modules" local target path script check target="${SPCD_PYTHON_VENV_PACKAGES}" spcd_step "List" spcd_os_ls "${target}" spcd_step "Main" path="$(spcd_python_pip "${SPCD_GIT_SPCD}")" # check matching of file and variable spcd_step "Check" script="${path}/bootstrap.sh" ls -l "${script}" check="${script}.${_SPCD_CMD_SUM}" "${_SPCD_CMD_SUM}" "${script}" >"${check}" printf "%s" "${SPCD}" >"${script}" ls -l "${script}" dos2unix "${script}" ls -l "${script}" "${_SPCD_CMD_SUM}" -c "${check}" || spcd_error_ci "SPCD" spcd_step "Root" spcd_python_pip "${SPCD_GIT_RWX}" spcd_step "List" spcd_os_ls "${target}" spcd_step_out } spcd_step__install_packages() { spcd_step_in "OS packages" local file # epel spcd_step "EPEL" case "${SPCD_OS_ID}" in "${SPCD_OS_ALMA}" | "${SPCD_OS_ROCKY}") spcd_f_pm_pkg_install "epel-release" case "${SPCD_OS_VERSION}" in "9") set -- \ "-testing" \ "" ;; "8") set -- \ "-modular" \ "-testing-modular" \ "-testing" \ "" ;; *) ;; esac for file in "${@}"; do spcd_os_sed "/etc/yum.repos.d/epel${file}.repo" \ "|^metalink|#metalink|" \ "|https://download.example/pub/epel|${SPCD_URL_EPEL}|" \ "|^#baseurl|baseurl|" done file="/etc/yum.repos.d/epel-cisco-openh264.repo" if [ -f "${file}" ]; then spcd_os_sed "${file}" \ "|^enabled=1|enabled=0|" fi ;; *) ;; esac # graphviz spcd_step "GraphViz" spcd_f_pm_pkg_install "graphviz" # openssh spcd_step "OpenSSH" case "${SPCD_PM}" in "${SPCD_PM_APK}" | "${SPCD_PM_APT}") spcd_f_pm_pkg_install "openssh-client" ;; "${SPCD_PM_DNF}" | "${SPCD_PM_ZYPPER}") spcd_f_pm_pkg_install "openssh-clients" ;; "${SPCD_PM_PACMAN}") spcd_f_pm_pkg_install "openssh" ;; *) ;; esac # plantuml spcd_step "PlantUML" spcd_f_pm_pkg_install "plantuml" # rsync spcd_step "Rsync" spcd_f_pm_pkg_install "rsync" # shell check spcd_step "ShellCheck" case "${SPCD_PM}" in "${SPCD_PM_DNF}" | "${SPCD_PM_ZYPPER}") spcd_f_pm_pkg_install "ShellCheck" ;; *) spcd_f_pm_pkg_install "shellcheck" ;; esac # shfmt spcd_step "ShellFormat" case "${SPCD_OS_ID}" in "${SPCD_OS_ALMA}" | "${SPCD_OS_ROCKY}") ;; "${SPCD_OS_DEBIAN}") case "${SPCD_OS_VERSION}" in "bullseye") ;; *) spcd_f_pm_pkg_install "shfmt" ;; esac ;; *) spcd_f_pm_pkg_install "shfmt" ;; esac spcd_step_out } spcd_step__python_write_module() { spcd_step "Write module" local variable value text index root for variable in \ OS_ID OS_VERSION \ PROJECT_ROOT PROJECT_PATH PROJECT_NAME \ PYTHON_VENV_BINARIES PYTHON_VENV_PACKAGES \ OPEN DOWN VERT SPLT __UP SHUT; do value="$(spcd_os_printenv "SPCD_${variable}")" text="${text}${value} " done index=$((__SPCD_STEP_1_INDEX + 1)) for root in \ "${SPCD_PYTHON_PACKAGES}" "${SPCD_PYTHON_VENV_PACKAGES}"; do spcd_os_write "${root}/env.py" "${text} SPCD_STEP = ${index} " done } spcd_step__python_switch() { spcd_step "Switch" local name path name="$(basename "${SPCD_GIT_SPCD}")" path="$(realpath "${0}")" echo "\ ${path} ↓ ${SPCD_PYTHON_ALIAS} / ${name}" "${SPCD_PYTHON_ALIAS}" -m "${name}" } # ╭──────╮ # │ main │ # ╰──────╯ spcd_main() { # environment spcd_step_in "Environment" spcd_step_in "Defaults" spcd_step "Print defined" spcd_step__environment_print spcd_step__environment_defaults_set spcd_step "Print effective" spcd_step__environment_print spcd_step_out spcd_step__environment_set_variables spcd_step__environment_list_workspace spcd_step_out # dns spcd_step__dns # packages spcd_step_in "Packages" spcd_step__packages_set_repositories spcd_step__packages_set_configuration spcd_f_pm_https_trust spcd_f_pm_pkg_update spcd_step__packages_install_tools spcd_step__packages_install_locales spcd_step_out # ca spcd_step_in "CA" spcd_step__ca_install_package spcd_step__ca_write_certificates spcd_step__ca_update_certificates spcd_step_out # packages spcd_step_in "Packages" spcd_f_pm_https_verify spcd_f_pm_pkg_update spcd_f_pm_upgrade spcd_step__packages_install_dos2unix spcd_step__packages_install_git spcd_step_out # python spcd_step_in "Python" spcd_step__python_install spcd_step__python_modules spcd_step__install_packages spcd_step__python_write_module spcd_step__python_switch } spcd_main