diff --git a/oca-repos-manager.sh b/oca-repos-manager.sh new file mode 100644 index 0000000..715d2bd --- /dev/null +++ b/oca-repos-manager.sh @@ -0,0 +1,453 @@ +#!/bin/bash + +# Copyright 2024 Fabrizio Forlini + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# Version: 1.0.0 + +set -euo pipefail + +readonly orm_file="${HOME}/.oca-repos-manager/orm.conf" + +readonly color_success="\e[32m" +readonly color_warning="\e[33m" +readonly color_danger="\e[31m" +readonly color_link="\e[36m" +readonly color_end="\e[0m" +readonly bold="\e[1m" +readonly bold_end="\e[0m" + + +show_installed_repos_path() { + local content=( $( find ${oca_addons_install_path} -type d -mindepth 1 -maxdepth 1 ) ) + if [[ -z ${content[@]} ]]; then + printf "\n\n\t\t${bold}%b${bold_end}\n" "No repositories found!" + else + printf "\n\n%s\n\n" "Please, ENSURE 'addons_path' variable in your Odoo configuration file contains:" + printf "${color_success}\t%b${color_end},\n" "${content[@]}" + fi +} + +get_local_repos() { + local local_repos=( $( find ${oca_addons_install_path} -type d -mindepth 1 -maxdepth 1 -exec basename {} \; ) ) + echo "${local_repos[@]}" +} + +update() { + local repos=( $(get_local_repos) ) + local selected_repos + + select_repos "update_cmd" "${repos[@]}" +} + +update_cmd() { + local exit_code + + printf "\n%s\n" "Start updating..." + + for repo in $@; do + echo "Entering: ${oca_addons_install_path}/${repo}" >&2 + cd ${oca_addons_install_path}/${repo} && git pull + exit_code=$? + if [[ "${exit_code}" -ne 0 ]]; then + printf "${color_danger}%s${color_end},\n" "Error! Something was wrong updating ${repo} repository..." + fi + done + if [[ "${exit_code}" -eq 0 ]]; then + printf "\n${bold}%b${bold_end}\n" "Update complete!" + else + printf "${color_danger}%s${color_end}\n" "Error! Something was wrong during the update process." + exit 1 + fi +} + +clone() { + local repos + + while true; do + if [ -z "${gh_token:-}" ]; then + set_github_token + fi + if [[ $(verify_github_token "${gh_token}") == false ]]; then + printf "\n${color_danger}%b${color_end}" "Token not valid!" + set_github_token + continue + fi + break + done + + repos=( $(get_repos_names) ) + + select_repos "clone_cmd" "${repos[@]}" + show_installed_repos_path +} + +clone_cmd() { + local exit_code + + printf "\n%s\n" "Start cloning..." + + for repo in $@; do + if [[ -d "${oca_addons_install_path}/${repo}" ]]; then + printf "${color_warning}%b${color_end},\n" "Repo ${repo} already exist! Skipped..." + continue + fi + cd ${oca_addons_install_path} && git clone https://github.com/OCA/${repo}.git --branch ${odoo_version} --single-branch + exit_code=$? + if [[ "${exit_code}" -ne 0 ]]; then + printf "${color_danger}%s${color_end},\n" "Error! Something was wrong cloning ${repo} repository..." + fi + done + if [[ "${exit_code}" -ne 0 ]]; then + printf "${color_danger}%s${color_end}\n" "Error! Something was wrong during the cloning process." + exit 1 + fi + + printf "\n${bold}%b${bold_end}\n" "Clone complete!" +} + +select_repos() { + local cmd=$1 + shift + local repos_names=("${@}") + local selected=() + local item + local nums=() + local num + + COLUMNS=$(tput cols) + + if [[ ! -z "${repos_names[@]}" ]]; then + while true; do + selected=() + nums=() + + echo -e "\n" + + PS3=$'\nPlease, select the repositories : (eg: "1 2 3", "1-3", repo name, or "A" for ALL)\n==> ' + select _ in "${repos_names[@]}" ; do + for item in ${REPLY}; do + if [[ "${item}" =~ ^(A)+$ ]]; then + selected+=( "${repos_names[@]}" ) + break + fi + if [[ "${item}" =~ ^([0-9]*)-([0-9]*)+$ ]]; then + nums+=( $( seq ${item//[!0-9]/ } ) ) + fi + if [[ "${item}" =~ ^[0-9]+$ ]]; then + nums+=( ${item} ) + fi + if [[ " ${repos_names[*]} " = *" ${item} "* ]]; then + selected+=( ${item} ) + fi + done + for num in "${nums[@]}"; do + if [[ -n "${num}" && "${num}" -gt 0 && "${num}" -le "${#repos_names[@]}" ]]; then + selected+=(${repos_names[num - 1]}) + fi + done + [[ ${#selected[@]} -gt 0 ]] && break + done + + echo -e "\n" + echo "--------------------------------------------------------" + echo " SELECTED REPOS " + echo "--------------------------------------------------------" + printf "%b\n" "${selected[@]}" + echo "--------------------------------------------------------" + echo + echo "Do you want to proceed? [y/N]" + echo -n "==> " + read -sn 1 + + case "${REPLY}" in + Y|y) + ${cmd} "${selected[@]}" + break + ;; + *) + continue + ;; + esac + done + else + printf "\n\n\t\t${bold}%b${bold_end}\n" "No repositories found!" + fi +} + +sort_array() { + sorted=() + while IFS= read -rd '' item; do + sorted+=("$item") + done < <(printf '%s\0' "$@" | sort -z) + + echo "${sorted[@]}" +} + +get_repos_names() { + local repos_names + local repo_name + local branch_content + local content_is_present + local all_repos_names=( $(get_all_repos_names) ) + + echo -en "\nFetching OCA repositories for version ${odoo_version}..." >&2 + + for repo_name in "${all_repos_names[@]}"; do + echo -n "." >&2 + branch_content=$(curl -s -H "Accept: application/vnd.github+json" -H "Authorization: Bearer ${gh_token}" -H "X-GitHub-Api-Version: 2022-11-28" https://api.github.com/repos/OCA/"${repo_name}"/contents\?ref\="${odoo_version}") + + content_is_present=$(echo "${branch_content}" | jq -e 'any(arrays[]; .name | . == "README.md" or . == "LICENSE" or startswith(".") | not)') + + if [[ "${content_is_present}" == true ]]; then + repos_names+=("${repo_name}") + fi + done + + echo >&2 + echo "Found ${#all_repos_names[@]} OCA repositories!" >&2 + echo "Found ${#repos_names[@]} OCA repositories for Odoo ${odoo_version}!" >&2 + + repos_names=( $(sort_array "${repos_names[@]}") ) + + echo "${repos_names[@]}" +} + +get_all_repos_names() { + local page + local repos_in_the_page + local repos_names_in_the_page + local all_repos_names + local ignore_repos_list=(".github" "OCB") + + printf "\n\n%b" "Fetching OCA repositories..." >&2 + + page=1 + + while true; do + echo -n "." >&2 + repos_in_the_page=$(curl -s -H "Accept: application/vnd.github+json" -H "Authorization: Bearer ${gh_token}" -H "X-GitHub-Api-Version: 2022-11-28" https://api.github.com/orgs/OCA/repos?per_page=100\&page=${page}\&sort=updated) + repos_names_in_the_page=( + $( echo "${repos_in_the_page}" | jq -r '.[].name' | while read -r name; do + for ignored_repo in "${ignore_repos_list[@]}"; do + if [[ "${name}" == "${ignored_repo}" ]]; then + continue 2 + fi + done + echo "${name}"; + done + ) + ) + + if [[ "${#repos_names_in_the_page[@]}" == 0 ]]; then + break + fi + + ((page++)) + + all_repos_names+=( "${repos_names_in_the_page[@]}" ) + done + + echo "${all_repos_names[@]}" +} + +verify_github_token() { + local token=$1 + local status_code=$(curl --write-out '%{http_code}' --silent --output /dev/null -H "Accept: application/vnd.github+json" -H "Authorization: Bearer ${gh_token}" -H "X-GitHub-Api-Version: 2022-11-28" https://api.github.com/user) + + if [[ "${status_code}" -ne 200 ]]; then + echo false + else + echo true + fi +} + +set_github_token() { + local token + + printf "\n\n${bold}%b${bold_end} %b\n" "Github Personal Access Token is required." "Don't you have one?" + printf "%b ${bold}${color_link}%b${color_end}${bold_end}\n\n" "Go here and create it:" "https://github.com/settings/tokens" + echo "Please. enter your Github Personal Access Token:" + echo -n "==> " + read -e token + + save_in_config_file "gh_token" "${token}" +} + +set_oca_addons_install_path() { + local path_selected + + while true; do + printf "\n\n%s\n" "Please, insert the OCA addons install path." + echo "[Enter for default: /opt/odoo/${odoo_version}/addons/OCA/ ]:" + echo -n "==> " + read -e path_selected + + if [[ -z "${path_selected:-}" ]]; then + path_selected="/opt/odoo/${odoo_version}/addons/OCA/" + fi + + if [[ ! -d "${path_selected}" ]]; then + printf "\n\n${color_danger}%s${color_end}" "Path doesn't exist! Please, insert an existing one." + continue + fi + + break + done + + # Remove trailing slash if present + path_selected="${path_selected%/}" + + save_in_config_file "oca_addons_install_path" "${path_selected}" +} + +set_odoo_version() { + local odoo_version_selected + + while true; do + printf "\n\n%b\n" "Please, select the Odoo version of your interest." + echo "[E.g. 14 -> for Odoo v14.0, 16 -> for Odoo v16.0, etc.]:" + echo -n "==> " + read -e odoo_version_selected + [[ "${odoo_version_selected}" =~ ^[[:digit:]]+$ ]] || continue + (( ( "${odoo_version_selected}" >= 1 ) && ( "${odoo_version_selected}" <= 30 ) )) || continue + odoo_version_selected="${odoo_version_selected}.0" + + save_in_config_file "odoo_version" "${odoo_version_selected}" + break + done +} + +save_in_config_file() { + local key=$1 + local value=$2 + + if ! grep -q "${key}=" "${orm_file}"; then + echo "${key}=${value}" >> ${orm_file}; + get_config_file + return + fi + sed -i.bak "s:${key}.*:${key}=${value}:g" "${orm_file}" + get_config_file +} + +get_config_file() { + mkdir -p $(dirname "${orm_file}") + touch "${orm_file}" + + source "${orm_file}" 2>/dev/null +} + +main_menu() { + while true; do + echo + if [[ ! -z "${odoo_version+x}" ]]; then + echo "********************************************************" + printf "${bold}%s ${color_success}%b${color_end}\n" " > Odoo target version:" "${odoo_version}" + fi + if [[ ! -z "${oca_addons_install_path+x:-}" ]]; then + printf "${bold}%s ${color_success}%b${color_end}\n" " > OCA addons install path:" "${oca_addons_install_path}" + fi + echo "********************************************************" + echo + echo "1) Clone OCA repositories" + echo "2) Update OCA repositories already cloned" + echo "3) Show installed repositories path for Odoo config file" + echo "---" + echo "4) Change Odoo target version" + echo "5) Change OCA addons install path" + echo "---" + echo "q) Exit" + echo + echo -n "==> " + read -sn 1 + + case "${REPLY}" in + 1) + clone + read -p $'\nPress any key to continue...' + continue + ;; + 2) + update + read -p $'\nPress any key to continue...' + continue + ;; + 3) + show_installed_repos_path + read -p $'\nPress any key to continue...' + continue + ;; + 4) + set_odoo_version + continue + ;; + 5) + set_oca_addons_install_path + continue + ;; + q|Q) + exit 0 + ;; + *) + echo "Choice not valid!" + continue + ;; + esac + done +} + +check_dependencies() { + for cmd in "${@}"; do + command -v "${cmd}" 2&>/dev/null + local exitcode=$? + if [[ "${exitcode}" -ne "0" ]]; then + echo -e "\n${cmd} is missing. Please, install it. E.g.: apt install ${cmd}" + echo -e "\nQuitting..." + exit "${exitcode}" + fi + done +} + +intro_banner() { + echo + echo + echo " ╔═╗╔═╗╔═╗ " + echo " ║ ║║ ╠═╣ " + echo " ╚═╝╚═╝╩ ╩ " + echo " ┬─┐┌─┐┌─┐┌─┐┌─┐ ┌┬┐┌─┐┌┐┌┌─┐┌─┐┌─┐┬─┐ " + echo " ├┬┘├┤ ├─┘│ │└─┐ │││├─┤│││├─┤│ ┬├┤ ├┬┘ " + echo " ┴└─└─┘┴ └─┘└─┘ ┴ ┴┴ ┴┘└┘┴ ┴└─┘└─┘┴└─ " + echo +} + +main() { + intro_banner + check_dependencies jq curl git + + get_config_file + + if [[ -z "${odoo_version+x:-}" ]]; then + set_odoo_version + fi + + if [[ -z "${oca_addons_install_path+x:-}" ]]; then + set_oca_addons_install_path + fi + + main_menu +} + +main