#!/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 -uo 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" update_all() { local repos=( $(get_local_repos) ) update_oca_repos "${repos[@]}" update_ocb } update_ocb() { local exit_code printf "\n%s\n" "Start updating OCB..." echo "Entering: ${ocb_install_path}" >&2 cd ${ocb_install_path} && git pull exit_code=$? if [[ "${exit_code}" -ne 0 ]]; then printf "${color_danger}%s${color_end},\n" "Error! Something was wrong updating OCB..." fi 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 } show_installed_repos_path() { local content=( $( find ${oca_addons_install_path} -mindepth 1 -maxdepth 1 -type d ) ) 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} -mindepth 1 -maxdepth 1 -type d -exec basename {} \; ) ) echo "${local_repos[@]}" } select_oca_repos_to_update() { local repos=( $(get_local_repos) ) local selected_repos select_repos "update_oca_repos" "${repos[@]}" } update_oca_repos() { 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 } select_repos_to_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_repos" "${repos[@]}" show_installed_repos_path } clone_repos() { 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 if [[ "${?}" -ne 0 ]]; then printf "${color_danger}%s${color_end},\n" "Error! Something was wrong cloning ${repo} repository..." fi done 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 . == "setup" 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_ocb_install_path() { local path_selected while true; do printf "\n\n%s\n" "Please, insert the OCB install path." echo "[Enter for default: /opt/odoo/${odoo_version}/OCB/ ]:" echo -n "==> " read -e path_selected if [[ -z "${path_selected:-}" ]]; then path_selected="/opt/odoo/${odoo_version}/OCB/" 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 "ocb_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 } settings_menu() { while true; do echo echo "********************************************************" echo " SETTINGS " echo "********************************************************" printf "${bold}%s ${color_success}%b${color_end}\n" " > Odoo target version:" "${odoo_version}" printf "${bold}%s ${color_success}%b${color_end}\n" " > OCB install path:" "${ocb_install_path}" printf "${bold}%s ${color_success}%b${color_end}\n" " > OCA addons install path:" "${oca_addons_install_path}" echo "********************************************************" echo echo "1) Change Odoo target version" echo "2) Change OCB install path" echo "3) Change OCA addons install path" echo "---" echo "4) Back" echo echo -n "==> " read -sn 1 case "${REPLY}" in 1) set_odoo_version continue ;; 2) set_ocb_install_path continue ;; 3) set_oca_addons_install_path continue ;; 4) main_menu break ;; *) echo "Choice not valid!" continue ;; esac done } main_menu() { while true; do echo echo "********************************************************" printf "\t\t\t${bold}%s${bold_end}\n" "MAIN MENU" echo "********************************************************" echo echo "1) Clone OCA repositories" echo "2) Show installed repositories path for Odoo config file" echo "3) Update OCA repositories already cloned" echo "4) Update OCB" echo "5) Update All" echo "---" echo "6) Settings" echo "---" echo "q) Exit" echo echo -n "==> " read -sn 1 case "${REPLY}" in 1) select_repos_to_clone read -p $'\nPress any key to continue...' continue ;; 2) show_installed_repos_path read -p $'\nPress any key to continue...' continue ;; 3) select_oca_repos_to_update read -p $'\nPress any key to continue...' continue ;; 4) update_ocb read -p $'\nPress any key to continue...' continue ;; 5) update_all read -p $'\nPress any key to continue...' continue ;; 6) settings_menu continue ;; q|Q) exit 0 ;; *) echo "Choice not valid!" continue ;; esac done } check_dependencies() { for cmd in "${@}"; do command -v "${cmd}" > /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 "${ocb_install_path+x:-}" ]]; then set_ocb_install_path fi if [[ -z "${oca_addons_install_path+x:-}" ]]; then set_oca_addons_install_path fi main_menu } main