Jump to content

/home/alex/dev/setup-venv.sh (1)

From RaySoft
#!/bin/bash -
# ------------------------------------------------------------------------------
# setup-venv.sh
# =============
#
# Scope     Native
# Copyright (C) 2025 by RaySoft, Zurich, Switzerland
# License   GNU General Public License (GPL) 2.0
#           https://www.gnu.org/licenses/gpl2.txt
#
# ------------------------------------------------------------------------------

set -o 'noglob' -o 'nounset' -o 'pipefail' # -o 'errexit' -o 'xtrace'

# ------------------------------------------------------------------------------

# Path to the global libraries
SV_DE_GLOBAL_LIB="${HOME}/dev/lib"

# Global libraries to be loaded
SV_GLOBAL_LIBS=('nf' 'os')

# Path to the development environment
SV_DE_DEV_PATH="${HOME}/dev"

# Path to the configuration file
SV_FE_CONFIG_PATH="${SV_DE_DEV_PATH}/setup-venv.yaml"

# Path to the 'venv-runner.sh' file
SV_FE_RUNNER_PATH="${SV_DE_DEV_PATH}/venv-runner.sh"

# ------------------------------------------------------------------------------

SV_X_LN=('/opt/homebrew/bin/gln' '--force')
SV_X_PYTHON=('/opt/homebrew/bin/python3')
SV_X_RM=('/opt/homebrew/bin/grm' '--recursive')
SV_X_YQ=('/opt/homebrew/bin/yq')

# ------------------------------------------------------------------------------

# Test if library path is available
if [[ ! -d "${SV_DE_GLOBAL_LIB}" ]]; then
  echo "Error finding directory: ${SV_DE_GLOBAL_LIB}!"
  exit 1
fi

# Load libraries
for lib in "${SV_GLOBAL_LIBS[@]}"; do
  lib="${SV_DE_GLOBAL_LIB}/${lib}.sh"

  if [[ ! -f "${lib}" ]]; then
    echo "Error finding library: ${lib}!"
    exit 1
  fi

  if ! source "${lib}"; then
    exit 1
  fi
done

# Test the defined variables
if ! os::test_env 'SV'; then
  exit 1
fi

# Get the list of all projects from the configuration file
if ! mapfile -t 'projects' < <( \
       "${SV_X_YQ[@]}" 'keys[]' <"${SV_FE_CONFIG_PATH}" 2>'/dev/null' \
     )
then
  nf::carp -e 'Error getting the list of projects!'
fi

for project in "${projects[@]}"; do
  nf::print_heading '=' 'b' "${project}"

  # Get the path to the project from the configuration file
  if ! project_path="$( \
         "${SV_X_YQ[@]}" ".\"${project}\".path" <"${SV_FE_CONFIG_PATH}" \
           2>'/dev/null' \
       )"
  then
    nf::carp -e "Error getting the project path: ${project}!"
  fi

  project_path+="/${project}"

  # Get the 'requirements' boolean from the configuration file that defines
  # whether the 'requirements.txt' file is written or not
  if ! requ_bool="$( \
         "${SV_X_YQ[@]}" ".\"${project}\".requirements" <"${SV_FE_CONFIG_PATH}" \
           2>'/dev/null' \
       )"
  then
    nf::carp -e "Error getting the 'requirements' boolean: ${project}!"
  fi

  requ_path="${project_path}/requirements.txt"

  # Remove the 'requirements.txt' file if it is defined in the configuration
  # file and the file already exists
  if [[ "${requ_bool}" == 'true' && -f "${requ_path}" ]]; then
    if ! "${SV_X_RM[@]}" "${requ_path}" >'/dev/null' 2>&1; then
      nf::carp "Error removing file: ${requ_path}!"
    fi
  fi

  venv_path="${project_path}/venv"

  # Remove the virtual environment if it already exists
  if [[ -d "${venv_path}" ]]; then
    if ! "${SV_X_RM[@]}" "${venv_path}" >'/dev/null' 2>&1; then
      nf::carp "Error removing directory: ${venv_path}!"

      continue
    fi
  fi

  # Make the 'venv' directory
  if ! os::mkdir_ifn "${project_path}/venv"; then
    continue
  fi

  # Get the 'runner' boolean from the configuration file that defines whether
  # the ‘venv-runner.sh’ script is written or not
  if ! runner_bool="$( \
         "${SV_X_YQ[@]}" ".\"${project}\".runner" <"${SV_FE_CONFIG_PATH}" \
           2>'/dev/null' \
       )"
  then
    nf::carp -e "Error getting the 'runner' boolean: ${project}!"
  fi

  runner_file="${project_path}/src/${SV_FE_RUNNER_PATH##*/}"

  # Make the 'src' directory and hard link the 'venv-runner.sh' script into the
  # directory if it is defined in the configuration file
  if [[ "${runner_bool}" == 'true' ]]; then
    if ! os::mkdir_ifn "${project_path}/src"; then
      continue
    fi

    if ! "${SV_X_LN[@]}" "${SV_FE_RUNNER_PATH}" "${runner_file}" \
           >'/dev/null' 2>&1
    then
      nf::carp "Error linking file: ${runner_file}!"
    fi
  # Remove the 'venv-runner.sh' script from the project if it isn't defined in
  # the configuration file and the script already exists
  elif [[ "${runner_bool}" != 'true' && -f "${runner_file}" ]]; then
    if ! "${SV_X_RM[@]}" "${runner_file}" >'/dev/null' 2>&1; then
      nf::carp "Error removing file: ${runner_file}!"
    fi
  fi

  # Create the virtual environment
  if ! "${SV_X_PYTHON[@]}" -m 'venv' --prompt "venv '${project}'" \
         --system-site-packages "${venv_path}" >'/dev/null' 2>&1
  then
    nf::carp "Error creating virtual environment: ${project}!"

    continue
  fi

  # Activate the virtual environment
  if ! source "${venv_path}/bin/activate"; then
    nf::carp "Error activating virtual environment: ${project}!"

    continue
  fi

  # NOTE: In the further part of this script, the binary 'python3' is not
  #       called with an absolute path, as it is to be used from the virtual
  #       environment.

  # Get the list of project-related libraries from the configuration file
  if ! mapfile -t 'libraries' < <( \
         "${SV_X_YQ[@]}" ".\"${project}\".libraries[]" <"${SV_FE_CONFIG_PATH}" \
           2>'/dev/null' \
       )
  then
    # Deactivate the virtual environment
    deactivate

    nf::carp -e "Error getting the list of libraries: ${project}!"
  fi

  # Install the project-related libraries
  for library in "${libraries[@]}"; do
    nf::print_heading '-' 'b' "${library##*/}"

    if ! python3 -m 'pip' install --upgrade "${library}"; then
      # Deactivate the virtual environment
      deactivate

      nf::carp "Error installing library: ${library}!"

      continue 2
    fi
  done

  echo

  # Verify the dependencies of the installed libraries
  python3 -m 'pip' check

  # Write the 'requirements.txt' file if it is defined in the configuration file
  if [[ "${requ_bool}" == 'true' ]]; then
    if ! python3 -m 'pip' freeze >"${requ_path}" 2>'/dev/null'; then
      nf::carp "Error creating file: ${requ_path}!"
    fi
  fi

  # Deactivate the virtual environment
  deactivate
done

# ------------------------------------------------------------------------------

exit 0

Usage

Create a configuration file
vi "${HOME}/dev/setup-venv.yaml"
atlassian-api:
  path: /home/alex/dev
  runner: true
  requirements: true
  libraries:
    - atlassian-python-api

bw2kp:
  path: /home/alex/dev
  runner: false
  requirements: false
  libraries:
    - git+https://codeberg.org/k3karthic/bitwarden-to-keepass

deploy_certs:
  path: /home/alex/dev
  runner: true
  requirements: true
  libraries:
    - certbot
    - certbot-dns-nsone
Change the script's permissions
chmod 0700 "${HOME}/dev/setup-venv.sh"
Run the script
"${HOME}/dev/setup-venv.sh"