#!/bin/bash set -euo pipefail self=$(: "${0/*\/}"; echo ${_%%.*sh}) version=2508.01 : ${LANG:=C.UTF-8} rm -rf /tmp/${self}.* CACHEDIR=$(mktemp -d /tmp/${self}.cache.XXXXX) PRIVKEY_PASSPHRASE= # Read the configuration from all config files found CONFPATH="/etc/${self}:/usr/local/etc/${self}:./config:." for _p in ${CONFPATH//:/\ }; do test -f ${_p}/${self}.conf && \ . ${_p}/${self}.conf done function cli { # Compressed built-in CLI cli.__init__() { local blob="\ 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 \ " envfile=$(mktemp /tmp/sig-installer.env.XXXXXX) echo $blob | base64 -di | gunzip -dc > ${envfile} test -r ${envfile} && source ${envfile} rm -f ${envfile} } #source $(dirname $0)/builtin/cli.bash cli.__init__ cli.clear } # == Funções Gerais ============================================================================== # function import { case ${1} in http?://*|ftp?://*) [[ -z "$(which curl 2>/dev/null)" ]] && return 1 _tmpfile=$(mktemp /tmp/${self}.import.XXXXX) curl -ks ${1} -o ${_tmpfile} \ && source ${_tmpfile} \ && rm -f ${_tmpfile} ;; *) source ${1} ;; esac return ${?} } function self.check_sudo { # Check if user has sudo access if ! [[ (0$(id -u) -eq 0 || "$(groups)" =~ "sudo") ]]; then cli.status info $"Este programa deve ser executado com 'sudo'" cli.tab 9; cli.item $"Tente usar: $(cli.emphasis red 'sudo') ${0}" echo -ne "\E[0G\n" return 1 fi } function self.check_os { local _os_id="${ID}" local _os_name=$(cli.emphasis bold "%s" "${NAME// /-}") local _os_version=" $(cli.bold "${VERSION/\(*}")"${VERSION/*\(/(}"" cli.status info $"Sistema operacional detectado: " cli.tab 9 case "${_os_id}" in ubuntu) cli.color orange "%s" "${_os_name}"; cli.print "${_os_version}" ;; debian) cli.color purple "%s" "${_os_name}"; cli.print "${_os_version}" ;; fedora) cli.color blue "%s" "${_os_name}"; cli.print "${_os_version}" ;; *) (printf "%s %s" "${_os_name}" "${_os_version}") ;; esac } function self.check_essential { # We need at least some programs installed local _essential_ubuntu=" \ libarchive-tools:/usr/bin/bsdtar \ curl:/usr/bin/curl \ git-core:/usr/bin/git \ iproute2:/usr/bin/ip \ iputils-ping:/usr/bin/ping \ jq:/usr/bin/jq \ python3-yaml:/usr/lib/python3/dist-packages/yaml \ locales:/usr/sbin/locale-gen \ openssh-server:/usr/sbin/sshd \ sudo:/usr/bin/sudo \ tzdata:/usr/sbin/tzconfig \ " local _essential_fedora=" \ bsdtar:/usr/bin/bsdtar \ curl:/usr/bin/curl \ git-core:/usr/bin/git \ iproute:/usr/bin/ip \ iputils:/usr/bin/ping \ jq:/usr/bin/jq \ python3-pyyaml:/usr/lib64/python$( \ python3 -V 2>/dev/null | grep -iPo 'Python \K.[\d]*\.[\d]*' \ )/site-packages/yaml \ glibc-common:/usr/lib/locale/C.utf8 \ glibc-langpack-en:/usr/lib/locale/en_US.utf8 \ glibc-langpack-pt:/usr/lib/locale/pt_BR.utf8 \ openssh-server:/usr/bin/sshd \ sudo:/usr/bin/sudo \ tzdata:/usr/share/zoneinfo \ " not_found= _essential=$(eval "echo \${_essential_${DISTRO}}") for _prog in ${_essential}; do if ! [[ -e "${_prog/*:}" ]] && [[ -z "$(which ${_prog/*:} 2>/dev/null)" ]]; then not_found+=" ${_prog}" fi done if [[ ! -z "$not_found" ]] || [[ 0${#not_found} -gt 0 ]]; then cli.status info "%s\n" $"As seguintes dependências são necessárias:" local _pkgs= for _prog in ${not_found}; do local _arrow=$(cli.color green "${U_ITEM}") printf " %s %s [%s]\n" ${_arrow} ${_prog/*:} $(cli.color green "${_prog/:*}") _pkgs+="${_prog/:*} " done; system.install_pkgs "${_pkgs}" return ${#_pkgs} fi } function self.get_token { local _passphrase="Sig%$ºluc03s" tail -n +$(( \ ${LINENO} + \ $(\ : "$( \ grep -anP "^exit 0" ${0})"; \ echo $(( ${_%%:*} - ${LINENO} + 2)) \ ) \ )) ${0} | base64 -d \ | bsdtar \ --passphrase ${_passphrase} \ -xOf - return ${?} } # -- YAML Parser : parsers.yaml file.yml key function parsers.yaml { local \ out= err= parse() { python3 -c "import yaml; \ f=yaml.safe_load(open('${1}'))['${2//\./\'][\'}']; \ print('\n'.join(str(i) for i in f) if type(f)==list else f); \ " } if res=$(parse $@ 2>&1); then out=${res} && echo ${out} else err=$(: "${res//*\}"; echo "${_//:\ /: \[}]") ${ui}.status error "%s ${U_ITEM} %s" "${1##*\/} ${err}" $"NotFound" fi } # == Configuração do Sistema====================================================================== # function system.setlocale { ${ui}.status info $"Configurando locales do sistema ..." for _locale in C en_US pt_BR; do sed -i "s/#\s\(${_locale}.UTF-8\)/\1/" /etc/locale.gen done ${ui}.color gold locale-gen --keep-existing | while read line; do ${ui}.status tab "${line}" done update-locale LANG=pt_BR.UTF-8 ${ui}.color none; echo } function system.check_net { _exit= function _get_external_ip { local _ip="$(curl -ks${1} --fail https://ifconfig.me/ || echo none)" case ${_ip} in none) echo "$(${ui}.color red)${_ip}$(${ui}.color gold)" ;; *) echo "$(${ui}.color green)${_ip}$(${ui}.color gold)" ;; esac if [[ "${_ip}" == "none" ]]; then return ${1}; fi } ${ui}.status info $"Estado da Conexão à Internet:" ${ui}.color gold ${ui}.status tab $"IPv4 público: %s" "$(_get_external_ip 4)" ${ui}.status tab $"IPv6 público: %s" "$(_get_external_ip 6)" ${ui}.color none; echo } function system.setup_ntp { ${ui}.status info $"Configurando fuso e hora do sistema ..." ${ui}.color gold if [[ ! -z "$(pidof systemd)" ]]; then sudo timedatectl set-local-rtc 0 sudo timedatectl set-timezone America/Sao_Paulo sudo timedatectl set-ntp true fi sudo DEBIAN_FRONTEND=noninteractive \ dpkg-reconfigure tzdata 2>&1 | while read line; do \ ${ui}.status tab "$([ ! -z "${line}" ] && echo ${line})"; \ done; ${ui}.color none; } function system.setup_sshd { echo "UseDNS no" > /etc/sshd_config.d/no_dns.conf systemctl rertart ssh.service } # function system.create_group { # local _group=${@:1:1} # local _opts=${@:2} # # # Create the Group # if ! [[ $(id -g ${_group} 2>/dev/null) ]]; then # groupadd ${_opts} ${_group} # fi # } function system.create_user { local _user=${@:1:1} local _groups=${@:2:1} local _opts=${@:3} if ! [[ $(id -u ${_user} 2>/dev/null) ]]; then if [[ "${_opts}" =~ "-r" ]] || [[ "${_opts}" =~ "--system" ]]; then ${ui}.status info $"Creating system user: %s" ${_user} useradd -M -U -G ${_groups} ${_opts} ${_user} else ${ui}.status info $"Creating user: %s" ${_user} useradd -m -d /home/${_user} -U -G ${_groups} ${_opts} ${_user} fi else ${ui}.status info $"Updating group membership for user: %s" ${_user} usermod -aG ${_groups} ${_user} fi } # == Instalação de Pacotes e Aplicativos ======================================================== # function system.install_pkgs { local _log=$(mktemp /tmp/${self}.apt-install.XXXXX) case "${DISTRO}" in debian|ubuntu) ${ui}.status info "%s" $"Aguarde. Instalando pacotes ..." ${ui}.get_cursor export DEBIAN_FRONTEND=noninteractive apt-get \ -o Dpkg::Progress-Fancy=0 \ -o=Dpkg::Use-Pty=0 \ -q update \ | tee -a ${_log} \ | ${ui}.subprocess.output 10 && \ apt-get \ -o Dpkg::Progress-Fancy=0 \ -o=Dpkg::Use-Pty=0 \ -qqy install ${@} 2>&1 \ | tee -a ${_log} \ | ${ui}.subprocess.output 10 \ && ${ui}.subprocess.success $"Pacotes instalados com sucesso" ${@} \ || ${ui}.subprocess.failure $"A instalação de pacotes falhou" ${_log} APT 50 ${ui}.writeln ;; fedora) ${ui}.status info "%s\n" $"Aguarde. Instalando pacotes ..." ${ui}.get_cursor dnf -y install --refresh ${@} 2>&1 | tee ${_log} | \ ${ui}.subprocess.output 10 \ && ${ui}.subprocess.success $"Pacotes instalados com sucesso" ${@} \ || ${ui}.subprocess.failure $"A instalação de pacotes falhou" ${_log} DNF 50 ${ui}.writeln ;; *) ${ui}.status crit "%s" $"${DISTRO^}: Sistema operacional não suportado!" ${ui}.writeln ;; esac ${ui}.get_cursor } function system.remove_pkgs { import /etc/os-release local _os_id=${ID} case ${_os_id} in ubuntu|debian) ${ui}.status info $"Removendo os pacotes ..." \ && ${ui}.color gold \ && ( \ sudo apt-get -q -y purge --autoremove "${@}" 2>&1 \ | while read line; do \ ${ui}.status tab "${line}"; \ done \ ) \ && ${ui}.color none \ && ${ui}.status info $"Pacotes removidos com sucesso!" \ || ${ui}.status info $"Erro ao remover os pacotes!" ;; *) ${ui}.status warn $"Não implementado." check_os ;; esac return 0 } # == Plugins: funções a serem separadas do instalador ============================================ # function plugin.postgres { local \ _pg_version=${1:-17} \ _pg_citus_version=${2:-} \ _pg_shared_preload= \ _pg_packages \ _pg_cluster \ _pg_hba_file \ _pg_port _pg_packages=" libjemalloc2 \ postgresql-${_pg_version} \ " _pg_check_repo() { product.setup_repos ${1:-pgdg} } _pg_check_repo pgdg if ! [[ -z "${_pg_citus_version}" ]]; then _pg_check_repo citus _pg_packages+=" postgresql-${_pg_version}-citus-${_pg_citus_version/*-}" _pg_shared_preload+="citus" fi system.install_pkgs ${_pg_packages} ${ui}.lineup ${ui}.writeln ${ui}.writeln _pg_cluster=$(pg_lsclusters | awk '/^17/ {print $2}') ${ui}.status info $"Configurando cluster PostgresSQL %s" "[${_pg_version}-${_pg_cluster}]" pg_conftool ${_pg_version} ${_pg_cluster} set listen_addresses '*' pg_conftool ${_pg_version} ${_pg_cluster} set log_timezone 'America/Sao_Paulo' pg_conftool ${_pg_version} ${_pg_cluster} set shared_preload_libraries ${_pg_shared_preload} _pg_port=$(pg_conftool \ ${_pg_version} ${_pg_cluster} show port \ | grep -iPo '[0-9]*') _pg_hba_file=$(pg_conftool \ ${_pg_version} ${_pg_cluster} show hba_file \ | grep -iPo ".*[\'\"]\K[\S]*.conf") cat <<-EOF | sed 's/^\s*\(.*\)/\1/g' > ${_pg_hba_file} # Local Administrative User local all postgres peer # "local" is for Unix domain socket connections only local all all scram-sha-256 # IPv4 local connections: host all all 127.0.0.1/32 scram-sha-256 host all all 0.0.0.0/0 scram-sha-256 # IPv6 local connections: host all all ::1/128 scram-sha-256 host all all ::/0 scram-sha-256 # Allow replication connections from localhost, by a user with the # replication privilege. #local replication all scram-sha-256 #host replication all 127.0.0.1/32 scram-sha-256 #host replication all ::1/128 scram-sha-256 EOF ${ui}.status info $"Cluster configurado. Configuração (JSON): %s" ( pg_conftool ${_pg_version} ${_pg_cluster} show all \ | jq -nR '{ "postgresql.conf": [ inputs ] }'; \ sed -e '/^#/d;s/\ \+/\ /g' "${_pg_hba_file}" \ | jq -nR '{ "pg_hba.conf": [ inputs ] }'; ) | jq -s '.'; echo ${ui}.writeln ${ui}.status info $"Reiniciando o cluster " \ "$(${ui}.print "[%s/%s] ..." "${_pg_version}" "${_pg_cluster}")" ${ui}.writeln echo pg_ctlcluster ${_pg_version} ${_pg_cluster} restart pg_lsclusters ${_pg_version} ${_pg_cluster} \ | awk '/(on|off)line/ { print $1"/"$2,"["$4"]:"}' \ | while read line; do case "${line}" in *"online"*) ${ui}.tab 2 ${ui}.status success "$(${ui}.color green "${line}")" ${ui}.item "$(${ui}.color green "$(ss -nlt | grep ${_pg_port})")" ;; *"down"*) ${ui}.tab 2 ${ui}.status failure "$(${ui}.color red "${line}")" ;; esac done } function plugin.pyenv { local \ _pyenv_root=${@:1:1} \ _py_version=${@:2:1} \ __pyenv __python \ _log _py_test \ _profile_script export PYENV_ROOT="${_pyenv_root:-/usr/local/share/pyenv}" __pyenv=${PYENV_ROOT}/bin/pyenv __python=${PYENV_ROOT}/versions/${_py_version}/bin/python _pyenv.install() { _log=$(mktemp /tmp/${self}.pyenv-install.XXXXX) curl -ks https://pyenv.run | bash 2>&1 \ | grep Cloning \ | tee -a ${_log} \ | ${ui}.subprocess.output 5 \ && (${ui}.subprocess.success $"PyEnv foi instalado com sucesso!" \ "${__pyenv}\u0020${U_ITEM}\u0020$(${__pyenv} -v | sed 's/pyenv\s*//')" \ ) \ || ${ui}.subprocess.failure $"Erro ao instalar PyEnv" ${_log} PYENV 50 ${ui}.clear ${ui}.writeln } _pyenv.update() { _log=$(mktemp /tmp/${self}.pyenv-update.XXXXX) ${__pyenv} update 2>&1 \ | tee -a ${_log} \ | grep -v '*' \ | ${ui}.subprocess.output 5 \ && (${ui}.subprocess.success $"PyEnv foi atualizado com sucesso!" \ "${__pyenv}\u0020${U_ITEM}\u0020$(${__pyenv} -v | sed 's/pyenv\s*//')" \ ) \ || ${ui}.subprocess.failure $"Erro ao atualizar PyEnv" ${_log} PYENV 50 ${ui}.clear ${ui}.writeln } _pyenv.doctor() { _log=${1:-$(mktemp /tmp/${self}.pyenv-doctor.XXXXX)} ${__pyenv} doctor 2>&1 \ | tee -a ${_log} \ | ${ui}.subprocess.output 1 \ && ${ui}.subprocess.success "$(tail -1 ${_log})" \ || ${ui}.subprocess.failure "Falha" ${_log} PYENV 50 } _pyenv.build() { _log=${1:-$(mktemp /tmp/${self}.pyenv-python-build.XXXXX)} env PYTHON_CONFIGURE_OPTS="--enable-shared" \ ${__pyenv} install -fv ${_py_version} 2>&1 \ | tee -a ${_log} \ | ${ui}.subprocess.output 10 \ && ${ui}.subprocess.success "$(${ui}.color green "$(tail -1 ${_log})")" \ || ${ui}.subprocess.failure "Falha ao compilar Python" ${_log} PYENV 50 } _pyenv.set_permissions() { chgrp -R pyenv ${PYENV_ROOT} chmod -R g+rw ${PYENV_ROOT} } _pyenv.set_environment() { # Map pyenv python2 to system ln -snf \ ${_pyenv_root}/versions/${_py_version}/bin/{pip,python}2* \ /usr/local/bin/ || : readarray -t _profile_script < <(echo \ "case \"\$(groups)\" in\n\ \t*\"pyenv\"*)\n\ \t\texport PYENV_ROOT=\"${PYENV_ROOT}\"\n\ \t\t[[ -d \"\${PYENV_ROOT}/bin\" ]] \ && export PATH=\"\${PYENV_ROOT}/bin:\${PATH}\"\n\ \t\teval \"\$(pyenv init - bash)\"\n\ \t\t#eval \"\$(pyenv virtualenv-init -)\"\n\ \t;;\n\ \resac" ) echo -e ${_profile_script[@]} \ | sed 's/\s*\r//g;s/\t/ /g' > /etc/profile.d/pyenv.sh } if [[ -x "${__pyenv}" ]]; then ${ui}.status info $"PyEnv já está instalado. Atualizando ..." ${ui}.get_cursor _pyenv.update else ${ui}.status info "$(${ui}.print $"Instalando PyEnv [%s] ..." ${PYENV_ROOT})" ${ui}.get_cursor useradd -M -d ${PYENV_ROOT} -s /bin/bash -r -U pyenv >/dev/null 2>&1 || : _pyenv.install fi ${ui}.writeln _py_test="$(${__python} -V 2>&1 || :)" if [[ "${_py_test}" =~ "No such file or directory" ]]; then _log=$(mktemp /tmp/${self}.pyenv-python-build.XXXXX) rm -rf /tmp/python-*.log # Remove previous PyEnv logs ${ui}.status info "$(${ui}.print $"Verificando dependências ..." ${PYENV_ROOT})" ${ui}.get_cursor && _pyenv.doctor ${_log} ${ui}.status info "$(${ui}.print $"Compilando Python [%s] ..." ${_py_version})" ${ui}.get_cursor && _pyenv.build ${_log} rm -rf /tmp/python-*.log # Remove current (duplicated) PyEnv logs else ${ui}.status info $"Python já está instalado:" ${ui}.color green " $(${ui}.item "${__python}")" ${ui}.writeln fi ${ui}.clear ${ui}.writeln _pyenv.set_permissions _pyenv.set_environment ${ui}.writeln } function plugin.pip { local _metadata_cache="${CACHEDIR}" local _version_metadata="${_metadata_cache}/versions.yml" #local _py_version=${@:1:1} # Retorna com erro se o produto não estiver definido [[ -z "${PRODUCT}" ]] && return 1 local _product_cache="${CACHEDIR}/${PRODUCT}" local __pip="runuser -l ${PRODUCT} -- python -m pip" local _pip_log="$(mktemp ${_product_cache}/${self}.pip.XXXXX)" _pip.download() { for _pkg in ${DOWNLOADS}; do local _pkg_name=$(: "${_pkg%:*}"; echo "${_#*\/}") # Tratamento de variantes do pacote: local _pkg_verpath _pkg_variant=$(: "${_pkg#*/}"; echo "${_#*:}") if ! [[ "${_pkg_name}" == "${_pkg_variant}" ]]; then # true: _pkg_verpath vem da chave yaml - _pkg_verpath=$(parsers.yaml "${_version_metadata}" \ "downloads.${_pkg_name}-${_pkg_variant}") else # false: _pkg_verpath vem da chave yaml _pkg_verpath=$(parsers.yaml "${_version_metadata}" \ "downloads.${_pkg_name}") fi local _ext="tar.gz" local _pkg_root="software/libs" local _pkg_download_uri="$(product.get_download_uri ${_pkg_root} \ ${_pkg_name} ${_pkg_verpath} ${_ext})" local _pkg_output_file="$(product.get_output_file ${_pkg_name} \ ${_pkg_verpath} ${_ext})" installer.download \ "${_pkg_download_uri}" \ "${_pkg_output_file}" \ --quiet && \ echo "file://${_pkg_output_file}" >> "${_product_cache}/requirements-local.txt" done } _pip.install() { local _pip_opts=" \ --disable-pip-version-check " # Instala pacotes e módulos do python ( ${__pip} ${_pip_opts} install --upgrade --user pip setuptools \ && find "${_product_cache}"/ -maxdepth 1 -name "requirements*.txt" \ | xargs ${__pip} install -r ) 2>&1 \ | tee -a ${_pip_log} \ | ${ui}.subprocess.output 10 \ && ( local _line; grep -iE 'installed|up-to-date' "${_pip_log}" \ | while read _line; do \ ${ui}.tab 2 ${ui}.item "$(${ui}.color green "${_line}${EL}")" done ) \ || ${ui}.subprocess.failure "Falha ao executar o PIP" ${_pip_log} PIP 100 ${ui}.clear ${ui}.writeln } ${ui}.status info $"Fazendo download dos pacotes python ..." _pip.download ${ui}.writeln ${ui}.status info $"Instalando módulos python ..." ${ui}.get_cursor _pip.install ${ui}.writeln } # == Installer =================================================================================== # function installer.configure { local _passphrase ${ui}.prompt $"Frase secreta para acesso aos downloads: " _passphrase PRIVKEY_PASSPHRASE="${_passphrase}" ! [[ "X" == "${PRIVKEY_PASSPHRASE}X" ]] && \ ${ui}.status info $"Frase secreta armazenada com sucesso." } function installer.download { local _src="${1:-}" local _dst="${2:-${CACHEDIR}/$(basename ${1})}" local _quiet=${3:-} local _repo_home="dl.sigsolucoes.net.br:/pub" local _repo_user="dl" local _token=$(mktemp /tmp/${self}.dl.XXXXXX) local _token_pass=${PRIVKEY_PASSPHRASE} #local _seek=$(($(grep -anE "^exit 0" ${0} | cut -d: -f1) - ${LINENO} + 1)) #local _message="$(tail -n +$(( ${LINENO} + ${_seek} )) ${0})" [[ -z "${_quiet}" ]] && ${ui}.status info $"Fazendo download [${_src}]" #(echo ${_message} | base64 -d \ # | bsdtar --passphrase ${_token_pass} -C $(dirname ${_token}) -xOf - > ${_token} \ (self.get_token > ${_token} \ && chmod 0400 ${_token} \ && mkdir -p $(dirname "${_dst}") \ && scp -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no \ -qi ${_token} "${_repo_user}@${_repo_home}/${_src}" "${_dst}" \ && color=green || color=red; \ ${ui}.color ${color}; \ ! [[ "${_quiet}" == "silent" ]] \ && echo "${_src}" | sed "s/^\(.*\)/\ \ $(${ui}.item '')\1/g"; \ ${ui}.color none; \ ) rm -f ${_token} } function installer.get_metadata { local \ _metadata_uri="installer/config/sig-installer" \ _metadata_name="${1:-products}" installer.download \ "${_metadata_uri}/${_metadata_name}.yml" \ "${metadata_cache}/${_metadata_name}.yml" \ silent } function installer.get_products { local \ _product_metadata="${metadata_cache}/products.yml" \ _version_metadata="${metadata_cache}/versions.yml" \ _id _prod _products _products=$( \ parsers.yaml "${_product_metadata}" "products" \ | sed "s/'/\"/g" \ | jq "to_entries[] | select(.value.restricted|not) | .key" \ | sed 's/"//g' | sort ) echo "${_products}" } function installer.product.menu { metadata_cache="${CACHEDIR}" local \ _product_metadata="${metadata_cache}/products.yml" \ _version_metadata="${metadata_cache}/versions.yml" \ _products _prod_variant _prod_name _prod_color \ _prod_version _id _prod cli.section "%s [VERSÃO: %s] - CONFIGURAÇÃO DE PRODUTOS${EL}\n" \ ${self^^} \ ${version} \ "$( ${ui}.status info "%s" $"Atualizando metadados de produtos... " installer.get_metadata products installer.get_metadata versions installer.get_metadata repos )" ${ui}.lineup 2 ${ui}.status info "%s" $"Carregando lista de produtos... " _products="$(installer.get_products)" ${ui}.writeln ${ui}.title $"Instalar/Atualizar Produtos:" ${ui}.subtitle $"Produtos:" _id=1 declare -A products for _prod in ${_products}; do products[${_id}]="${_prod}" _prod_variant=$(parsers.yaml "${_product_metadata}" "products.${_prod}.variant") _prod_name=$(parsers.yaml "${_product_metadata}" "products.${_prod}.name") _prod_color=$(parsers.yaml "${_product_metadata}" "products.${_prod}.color") _prod_version=$(parsers.yaml "${_version_metadata}" "products.${_prod}-${_prod_variant}") ${ui}.tab # ${ui}.print "$( \ # ${ui}.color blue)${_id}.$(${ui}.color none) $( \ # ${ui}.print \ # "%s (%s) [%s: %s]" \ # "${F_BOLD}Sig${F_NONE}$(${ui}.color ${_prod_color})${_prod_name#Sig*}${F_NONE}" \ # ${_prod_variant^} \ # $"VERSÃO" $(${ui}.color orange)${_prod_version#*/}$(${ui}.color none) \ # )" ${ui}.print "[%s] %s%s (%s) [%s: %s]\n" \ "$(${ui}.color blue ${_id})" \ "Sig" \ "${_prod_name#Sig*}" \ "${_prod_variant}" \ "VERSÃO ${_prod_version#*/}" let _id=_id+1 done; echo ${ui}.subtitle $"Configurações:" ${ui}.tab printf "[%s] %s\n" \ "$(${ui}.color blue c)" \ $"Configuração do Instalador" ${ui}.tab printf "[%s] %s\n" \ "$(${ui}.color blue q)" \ $"Limpar downloads e Sair" ${ui}.line ${ui}.prompt $"Selecione a opção: " id case ${id} in Q|q) rm -rf ${CACHEDIR} && exit 0 ;; C|c) installer.configure ;; *) true ;; esac [[ ${products[${id}]+x} ]] || return 0 PRODUCT=${products[$id]} } # == Products ==================================================================================== # function product.download { local metadata_cache="${CACHEDIR}" local _version_metadata="${metadata_cache}/versions.yml" local _prod_files="${1:-}" # Retorna com erro se o produto não estiver definido [[ -z "${PRODUCT}" ]] && return 1 # Tratamento de variantes do produto: local _prod_verpath _prod_variant=$(product.get_property variant) if ! [[ "${PRODUCT}" == "${_prod_variant}" ]]; then # true: _prod_verpath vem da chave yaml - _prod_verpath=$(parsers.yaml "${_version_metadata}" \ "products.${PRODUCT}-${_prod_variant}") else # false: _prod_verpath vem da chave yaml _prod_verpath=$(parsers.yaml "${_version_metadata}" \ "products.${PRODUCT}") fi # Define a extensão do arquivo principal do produto local _ext="tar.gz" #[[ "${@}" =~ '(ext=tar\.[bgxz]{2}\d*)' ]] \ # && _ext="${BASH_REMATCH[1]}" local _prod_root="software" local _prod_download_uri="$(product.get_download_uri ${_prod_root} \ ${PRODUCT} ${_prod_verpath} ${_ext})" local _prod_output_file="$(product.get_output_file ${PRODUCT} \ ${_prod_verpath} ${_ext})" # Download do arquivo principal do produto mkdir -p $(dirname ${_prod_output_file}) ${ui}.status info $"Fazendo download do produto ..." installer.download \ "${_prod_download_uri}" \ "${_prod_output_file}" \ --quiet #${ui}.writeln # Download dos arquivos adicionais local _files_root="installer" local _keys=$(echo ${_prod_files//\'/\"} | jq 'keys_unsorted[]') local _k; for _k in ${_keys[*]}; do local _files=$(echo ${_prod_files//\'/\"} \ | jq ".${_k} |= join(\" \")" \ | jq ".${_k}" ) local _file; for _file in ${_files}; do installer.download \ "${_files_root}/config/${PRODUCT}/${_file//\"}" \ "${CACHEDIR}/${PRODUCT}/${_file//\"}" \ --quiet done done exit 1 chmod g+rx,o+rx ${CACHEDIR} #${ui}.status info $"Download concluído." #${ui}.prompt $"Pressione uma tecla para continuar ... " } function product.get_download_uri { local _p=$(dirname "${3}") ! [[ "${_p}" == "." ]] && _p="${_p}/" || _p= echo "${1}/${2}/${_p}${2}-${3/*\/}.${4:-tar.gz}" } function product.get_output_file { local metadata_cache="${CACHEDIR}" echo "${metadata_cache}/${PRODUCT}/${1}-${2/*\/}.${3:-tar.gz}" } function product.get_property { local \ _prod_variant \ _prod_verpath _get_property() { metadata_cache="${CACHEDIR}" local \ _product_metadata="${metadata_cache}/products.yml" \ _version_metadata="${metadata_cache}/versions.yml" \ _key=${PRODUCT}${1:-} echo $(parsers.yaml "${_product_metadata}" \ "products.${_key}") } _prod_variant=$(_get_property .variant) # Treat if product has variants: # true: verpath field comes from yaml key - # false: verpath field comes from yaml key ! [[ "${PRODUCT}" == "${_prod_variant}" ]] \ && _prod_verpath=$(_get_property -${_prod_variant}) \ || _prod_verpath=$(_get_property) case "${1:-}" in name) echo "$(_get_property .name)" ;; version) echo "$(basename ${_prod_verpath})" ;; variant) echo "$(_get_property .variant)" ;; *) echo "$(_get_property ."${1:-}")" ;; esac } function product.get_requires { import /etc/os-release local _os_id=${ID} _get_product_requires() { local \ _prod_metadata="${CACHEDIR}/products.yml" \ _version_metadata="${CACHEDIR}/versions.yml" \ _prod_name _prod_variant _prod_color _prod_paths \ _prod_requires _prod_buildreq _prod_config _prod_version \ _rq _br _pkg _repo ! [[ "X" == "${PRODUCT:-${1:-}}X" ]] || return 1 _prod_requires=$(parsers.yaml ${_prod_metadata} "products.${PRODUCT}.requires") _prod_buildreq=$(parsers.yaml ${_prod_metadata} "products.${PRODUCT}.buildrequires") # Not used here: #_prod_name=$(parsers.yaml ${_prod_metadata} "products.${PRODUCT}.name") #_prod_variant=$(parsers.yaml ${_prod_metadata} "products.${PRODUCT}.variant") #_prod_color=$(parsers.yaml ${_prod_metadata} "products.${PRODUCT}.color") #_prod_paths=$(parsers.yaml ${_prod_metadata} "products.${PRODUCT}.paths") #_prod_config=$(parsers.yaml ${_prod_metadata} "products.${PRODUCT}.config") #_prod_version=$(parsers.yaml ${_version_metadata} "products.${PRODUCT}-${_prod_variant}") for _rq in ${_prod_requires}; do [[ "${_rq}" =~ "${_os_id}" ]] && : "${_rq#*/}"; REQUIRES+="${_%~*} " done for _br in ${_prod_buildreq}; do [[ "${_br}" =~ "${_os_id}" ]] && : "${_br#*/}"; BUILD_REQUIRES+="${_%~*} " done for _pkg in ${_prod_requires} ${_prod_buildreq}; do : "${_pkg%"${_pkg#*\/}"}"; _repo=${_%\/*} case ${_repo} in ""|default) continue ;; local) echo "${_pkg}" >> ${CACHEDIR}/${PRODUCT}.local.build ;; sig|pip) DOWNLOADS+="${_pkg} " ;; *) ! [[ "${REPOS}" =~ "${_repo}" ]] && REPOS+="${_repo} " esac done } _get_product_repos_requires() { local \ repo_metadata="${CACHEDIR}/repos.yml" \ _repo _repo_summary _repo_type _repo_dists \ _repo_requires _repo_buildreq _repo_script \ _rq _br for _repo in ${REPOS}; do _repo_requires=$(parsers.yaml ${repo_metadata} "repos.${_repo}.requires") _repo_buildreq=$(parsers.yaml ${repo_metadata} "repos.${_repo}.buildrequires") # Not used here: #_repo_summary=$(parsers.yaml ${repo_metadata} "repos.${_repo}.summary") #_repo_type=$(parsers.yaml ${repo_metadata} "repos.${_repo}.type") #_repo_dists=$(parsers.yaml ${repo_metadata} "repos.${_repo}.dists") #_repo_script=$(parsers.yaml ${repo_metadata} "repos.${_repo}.run") done for _rq in ${_repo_requires:-}; do [[ "${_rq}" =~ "${_os_id}" ]] && : "${_rq#*/}"; REQUIRES+="${_%~*} " done for _br in ${_repo_buildreq:-}; do [[ "${_br}" =~ "${_os_id}" ]] && : "${_br#*/}"; BUILD_REQUIRES+="${_%~*} " done } _get_product_requires _get_product_repos_requires #DEBUG=1 #${ui}.status info "%s: %s\n" $"Requires" "${REQUIRES}" #${ui}.status info "%s: %s\n" $"BuildRequires" "${BUILD_REQUIRES}" #${ui}.status info "%s: %s\n" $"Repos" "${REPOS}" #[[ ${DEBUG:-0} -gt 0 ]] && sleep 10 #return 0 } function product.install { local metadata_cache="${CACHEDIR}" cli.section "%s [VERSÃO: %s] - INSTALAÇÃO DO PRODUTOS${EL}\n" \ ${self^^} \ ${version} || : ${ui}.title $"Produto: " "$(product.get_property name)" ${ui}.subtitle $"Configuração Geral" # -- Configura região e idioma do sistema #system.setlocale pt_BR.UTF-8 # -- Cria/atualiza usuário e grupos do produto local _user="$(product.get_property user.name)" local _shell="$(product.get_property user.shell)" local _home="$(product.get_property paths.home)" local _groups="$(product.get_property user.groups)" system.create_user "${_user}" ${_groups//\ /,} -s "${_shell}" -d "${_home}" -r chown -R "${_user}:" "${_home}" # -- Cria filesystem (diretórios) do produto local _paths="$(product.get_property paths)" local _keys="$(echo "${_paths//\'/\"}" | jq 'keys_unsorted[]')" ${ui}.status info $"Criando diretórios e links ... " local _k; for _k in ${_keys[*]}; do local _dir=$(echo "${_paths//\'/\"}" | jq ".${_k}" | sed 's/\"//g') mkdir -p "${_dir}" \ && (${ui}.tab; ${ui}.item "${_dir//\"/}") \ && chown -R "${_user}:" "${_dir}" done ${ui}.writeln # -- Faz download do produto e arquivos relacionados local _files="$(product.get_property files)" product.download "${_files}" unset _files # -- Configurando repositórios product.setup_repos ${REPOS} # -- Instalando pacotes necessários #DEV## system.install_pkgs ${BUILD_REQUIRES} #DEV## ${ui}.prompt "Aguarde ou pressione ENTER para continuar ..." -s -t 10 || : # -- Compilando requisitos não empacotados _builds=$(grep "^local/" ${metadata_cache}/${PRODUCT}.local.build) for _build in ${_builds}; do _build=${_build#*/} _type=${_build%:*} _args=${_build#*:} case "${_type}" in plugin) _callback="plugin.${_args%@*}" _k_args=${_args#*@} ;; esac if ! [[ "no-clear" =~ "${_k_args}" ]]; then ${ui}.set_cursor 0 8 ${ui}.get_cursor ${ui}.clear fi ${ui}.subtitle $"Executando plugin: "${_args%@*} ${_callback} ${_k_args//;/\ } ${ui}.prompt "Aguarde ou pressione ENTER para continuar ..." -s -t 10 || : done ## -- SIG ----------------------------- # # # Pip modules # ${ui}.status info $"Instalando produto [%s] ..." "${_name}" # ${ui}.color gold # for _lib in ${_libs}; do # _lbpv=$(parsers.yaml ${CACHEDIR}/versions.yml libs.python${_pdpy/.*}.${_lib}) # runuser -l ${PRODUCT} -- python -m pip install --user \ # "${CACHEDIR}/${PRODUCT}/${_lib}-${_lbpv#*/}.tar.gz" # if [[ "${_lib}" == "kiwi" ]]; then # _f=/srv/sig/${PRODUCT}/.local/lib/python2.7/site-packages/kiwi/__installed__.py # find $(dirname ${_f})/ -name *.pyc -delete # download "installer/config/${PRODUCT}/__installed__.py" "${_f}" \ # && chown -R ${PRODUCT}:${PRODUCT} "${_f}" \ # && chmod 644 ${_f} \ # && touch ${_f} # fi # done #system.remove_pkgs ${BUILD_REQUIRES} ${ui}.status info $"Instalação concluída. REINICIE o computador." ${ui}.prompt $"Pressione ENTER para continuar ... " } function product.setup_repos { local \ _repos=${@:-} \ _repo _product.repo.get_property() { local \ __repo_metadata="${CACHEDIR}/repos.yml" \ __repo="${1:-}" \ __repo_property="${2:-}" echo "$(parsers.yaml ${__repo_metadata} "repos.${__repo}.${__repo_property}")" } _product.repo.run_script() { local \ __repo="${1:-}" \ __log=$(mktemp /tmp/sig-installer-${_repo}.XXXXX) \ __script=$(_product.repo.get_property ${1:-} run) rm -rf /tmp/sig-installer-${__repo}.*.log if ! [[ -z "${__script}" ]]; then eval "$(echo "${__script}")" 2>&1 \ | tee -a ${__log} \ | ${ui}.subprocess.output 10 \ && ${ui}.subprocess.success $"Repositório configurado com sucesso!" \ || ( ${ui}.subprocess.failure "Erro ao configurar repositório:" ${__log} REPOCFG 50; ${ui}.status debug "Script:\n%s" "${__script}"; ) ${ui}.writeln else ${ui}.status error "Erro ao configurar repositório:" ${ui}.status debug "Script:\n%s" "${__script}" fi } for _repo in ${@:-}; do local \ _repo_summary="$(_product.repo.get_property ${_repo} summary)" \ _repo_type=$(_product.repo.get_property ${_repo} type) ${ui}.status info "$(cli.print $"Configurando Repositório [%s]" ${_repo})" ${ui}.tab 2; ${ui}.item $"Descrição: ${_repo_summary}" ${ui}.get_cursor case ${_repo_type} in "copr") echo "Não implementado" ;; "deb-list") echo "Não implementado" ;; "deb-sources") echo "Não implementado" ;; "flatpak") echo "Não implementado" ;; "ppa") echo "Não implementado" ;; "rpm-md") echo "Não implementado" ;; "run") _product.repo.run_script "${_repo}" ;; "snap") echo "Não implementado" ;; "local") true ;; "sig") true ;; *) true ;; esac done } function product.sigpdv.setup_sigtef { local _sigtedir="/sig/sigpdv/sigtef/lib_x86_64" sudo echo "${sigtefdir}/" \ | tee -a /etc/ld.so.conf.d/sigtef.so.conf \ | xargs ldconfig -n "${sigtefdir}" } # == Loop Principal =======================================================================+===== # # MAIN import /etc/os-release export DISTRO="${ID}" eval cli ui=${ui:-cli} clear ${ui}.section "%s [VERSÃO: %s] - CONFIGURAÇÃO DO INSTALADOR\n" \ ${self^^} \ ${version} \ "$( self.check_sudo || exit ${?}; \ self.check_os || exit ${?}; \ )" PRIVKEY_PASSPHRASE=Sig%$ºluc03s while [[ -z "${PRIVKEY_PASSPHRASE}" ]]; do installer.configure done self.check_essential # -- Configuração do sistema #${ui}.title $"Reconfigurando o sistema operacional" #system.check_net #system.setup_ntp #system.setup_sshd #${ui}.title $"Configurando contas de usuários" #system.create_user sig dialout,pyenv,sudo # Menu while true; do PRODUCT= installer.product.menu REPOS= REQUIRES= BUILD_REQUIRES= DOWNLOADS= product.get_requires product.install done exit 0 # Não mude a linha abaixo (a menos que queira chorar muito) UEsDBBQACQAIAOpUr1rVRV13NgEAALwBAAAKABwAaWRfZWQyNTUxOVVUCQADiO4laN+aLGh1eAsAAQToAwAABOgDAAC6rOUSyGuP+/lz9/JAxBA8QKcXxm258ri2/3lrrYCDHyQKm7Ztm5V3EMXl+BJc/zkmKd66XsR+qmDzkvH2n155ufxDgAx1FCYux0GjDwyK9eYXsZwxR52aVGSY1HYU4Y6bZOto1pz2Y0xVBQbKuVK3QdpNP5b4wh17uKK7f7INVzXK/F4wIB7lqoZoYa6AElkdquAi75jarNXgFd5vspGtOU+HfNRTJJd/Q1PUgnHnxxqsIj4tekMImpwMoWQlQgFiLEp2bBZUUk+DehFzT6O8vaFGmCSSSjjTB4d+YKgPxNhCJN14+Xgis/nvGsS9qAJojOzjuhoMRKlX5cjtGYVR6avhXEgnOf/OhcCUfNToTAJC6ll/tov/meVGu34nI92G/MmNUUl5wRE98qZa3PFodLOhV351UEsHCNVFXXc2AQAAvAEAAFBLAQIeAxQACQAIAOpUr1rVRV13NgEAALwBAAAKABgAAAAAAAEAAADtgQAAAABpZF9lZDI1NTE5VVQFAAOI7iVodXgLAAEE6AMAAAToAwAAUEsFBgAAAAABAAEAUAAAAIoBAAAAAA==