#!/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