Index: pkg/mac/codesign-binaries.sh
===================================================================
--- pkg/mac/codesign-binaries.sh	(revision 12a7ce54d93346e49efe1b14d1ad61d949adbbcc)
+++ pkg/mac/codesign-binaries.sh	(revision 12a7ce54d93346e49efe1b14d1ad61d949adbbcc)
@@ -1,36 +0,0 @@
-#!/bin/sh
-BUNDLE="$1"
-
-if ! test -d "${BUNDLE}" ; then
-	echo "${BUNDLE} is no bundle!" >&2
-	exit 1
-fi
-
-# Get the config
-source codesign.conf
-
-if [ -z "${DEVELOPER_ID}" ] ; then
-	echo "Developer ID Application not found in codesign.conf" >&2
-	exit 1
-fi
-
-if [ -z "${DEVELOPER_BUNDLE_ID}" ]; then
-	echo "Developer Bundle Identifier not found in codesign.conf" >&2
-fi
-
-echo Signing ${BUNDLE} binaries
-IFS=$'\n'
-for i in $(find "${BUNDLE}" -type f)
-do
-	file "${i}" | grep -E "Mach-O executable|Mach-O 64-bit executable|Mach-O 64-bit bundle"
-	if [ $? -eq 0 ] ; then
-		codesign --deep --force --verify --verbose --timestamp --options runtime -i "${DEVELOPER_BUNDLE_ID}" --sign "${DEVELOPER_ID}" "$i"
-	fi
-done
-
-echo Signing ${BUNDLE} libraries
-for i in $(find "${BUNDLE}" -type f -name "*.dylib*")
-do
-	codesign --deep --force --verify --verbose --timestamp --options runtime -i "${DEVELOPER_BUNDLE_ID}" --sign "${DEVELOPER_ID}" "$i"
-done
-
Index: pkg/mac/codesign-bundle.sh
===================================================================
--- pkg/mac/codesign-bundle.sh	(revision 12a7ce54d93346e49efe1b14d1ad61d949adbbcc)
+++ pkg/mac/codesign-bundle.sh	(revision 12a7ce54d93346e49efe1b14d1ad61d949adbbcc)
@@ -1,26 +0,0 @@
-#!/bin/sh
-
-BUNDLE="$1"
-
-if ! test -d "${BUNDLE}" ; then
-	echo "${BUNDLE} is no bundle!" >&2
-	exit 1
-fi
-
-# Get the config
-source codesign.conf
-
-# Sign the .app
-echo Signing ${BUNDLE}
-codesign --deep --force --verify --verbose --timestamp --options runtime -i "${DEVELOPER_BUNDLE_ID}" --sign "${DEVELOPER_ID}" "${BUNDLE}"
-
-# Verify it worked
-echo Verifying the signature
-codesign --verify --verbose --deep --force "${BUNDLE}"
-RETURN_STATUS=$?
-if [ ${RETURN_STATUS} -ne 0 ]; then
-  echo Code signing did not work, check the log
-  exit 1
-else
-  echo ${BUNDLE} successfully signed
-fi
Index: pkg/mac/codesign-dmg.sh
===================================================================
--- pkg/mac/codesign-dmg.sh	(revision 12a7ce54d93346e49efe1b14d1ad61d949adbbcc)
+++ pkg/mac/codesign-dmg.sh	(revision 12a7ce54d93346e49efe1b14d1ad61d949adbbcc)
@@ -1,30 +0,0 @@
-#!/bin/bash
-
-DMG_VOLUME_NAME=${APP_NAME}
-DMG_NAME=`echo ${DMG_VOLUME_NAME} | sed 's/ //g' | awk '{print tolower($0)}'`
-DMG_IMAGE=${DISTROOT}/${DMG_NAME}-${APP_LONG_VERSION}.dmg
-
-if ! test -f "${DMG_IMAGE}" ; then
-	echo "${DMG_IMAGE} is no disk image!" >&2
-	exit 1
-fi
-
-# Get the config
-source codesign.conf
-
-SCRIPT_DIR=`pwd`
-
-# Sign the .app
-echo Signing ${DMG_IMAGE}
-codesign --deep --force --verify --verbose --timestamp --options runtime -i "${DEVELOPER_BUNDLE_ID}" --sign "${DEVELOPER_ID}" "${DMG_IMAGE}"
-
-# Verify it worked
-echo Verifying the signature
-codesign --verify --verbose --force "${DMG_IMAGE}"
-RETURN_STATUS=$?
-if [ ${RETURN_STATUS} -ne 0 ]; then
-  echo ERROR: Code signing did not work
-  exit 1
-else
-  echo ${DMG_IMAGE} successfully signed
-fi
Index: pkg/mac/create-dmg.sh
===================================================================
--- pkg/mac/create-dmg.sh	(revision 12a7ce54d93346e49efe1b14d1ad61d949adbbcc)
+++ pkg/mac/create-dmg.sh	(revision 12a7ce54d93346e49efe1b14d1ad61d949adbbcc)
@@ -1,36 +0,0 @@
-#!/bin/sh
-
-# move to the directory where we want to create the DMG
-test -d ${DISTROOT} || mkdir ${DISTROOT}
-cd ${DISTROOT}
-
-DMG_SOURCES="./../mac-build/${APP_BUNDLE_NAME}"
-DMG_LICENCE=./../pkg/mac/licence.rtf
-DMG_VOLUME_NAME=${APP_NAME}
-DMG_NAME=`echo ${DMG_VOLUME_NAME} | sed 's/ //g' | awk '{print tolower($0)}'`
-DMG_IMAGE=${DMG_NAME}-${APP_LONG_VERSION}.dmg
-HDIUTIL=/usr/bin/hdiutil
-REZ="/usr/bin/Rez"
-
-DMG_DIR=./${DMG_IMAGE}.src
-
-if test -e "${DMG_DIR}"; then
-	echo "Directory ${DMG_DIR} already exists. Please delete it manually." >&2
-	exit 1
-fi
-
-echo "Cleaning up"
-rm -f "${DMG_IMAGE}" || exit 1
-mkdir "${DMG_DIR}" || exit 1
-
-echo "Copying data into temporary directory"
-for src in "${DMG_SOURCES}"; do
-	cp -R "${src}" "${DMG_DIR}" || exit 1
-done
-
-echo "Creating image"
-${HDIUTIL} create -quiet -srcfolder "$DMG_DIR" -fs HFS+ -format UDZO -volname "${DMG_VOLUME_NAME}" -ov "${DMG_IMAGE}" || exit 1
-rm -rf "${DMG_DIR}" || exit 1
-
-echo "Attaching License to image"
-python ./../pkg/mac/dmg-license.py "${DMG_IMAGE}" "${DMG_LICENCE}" -c bz2
Index: pkg/mac/framework-config.sh
===================================================================
--- pkg/mac/framework-config.sh	(revision 12a7ce54d93346e49efe1b14d1ad61d949adbbcc)
+++ pkg/mac/framework-config.sh	(revision 12a7ce54d93346e49efe1b14d1ad61d949adbbcc)
@@ -1,71 +0,0 @@
-#!/bin/sh
-
-BUNDLE="$1"
-
-if ! test -d "${BUNDLE}" ; then
-	echo "${BUNDLE} is no bundle!" >&2
-	exit 1
-fi
-
-# Get the config
-source framework.conf
-
-SCRIPT_DIR=`pwd`
-
-echo Reorganising the framework structure
-
-# Create "Current" and "Current/Resources" inside each of the framework dirs
-MYDIR=`pwd`
-find "${BUNDLE}/Contents/Frameworks"/*framework -type d -name "Versions" | while read -r framework_dir; do
-  cd "${framework_dir}"
-
-  # Create framework 'Current' soft link
-  VERSION_NUMBER=`ls -1`
-  ln -s ${VERSION_NUMBER} Current || { echo "link creation in framework-config.sh failed"; exit 1; }
-
-  # Create "Resources" subdirectory
-  if [ ! -d Current/Resources ]; then
-    mkdir Current/Resources
-  fi
-
-  cd "${MYDIR}"
-done
-
-# Stuff for Qt framework files only
-find "${BUNDLE}/Contents/Frameworks" -type d -name "Qt*framework" | while read -r framework_dir; do
-  cd "${framework_dir}"
-
-  # Create soft link to the framework binary
-  ln -s Versions/Current/Qt* || { echo "link creation in framework-config.sh failed"; exit 1; }
-
-  # Create soft link to the framework Resources dir
-  ln -s Versions/Current/Resources || { echo "link creation in framework-config.sh failed"; exit 1; }
-
-  # Create the Info.plist files
-  MYNAME=`ls -1 Qt*`
-  if [ -f Resources/Info.plist ]; then
-    chmod +w Resources/Info.plist
-  fi
-  sed 's/__SHORT_VERSION__/${QT_SHORT_VERSION}/' "${SCRIPT_DIR}/Info.plist-template_Qt5" | sed 's/__FULL_VERSION__/${QT_FULL_VERSION}/' | sed "s/__FRAMEWORK_NAME__/${MYNAME}/" > "Resources/Info.plist" || { echo "sed replacement in framework-config.sh failed"; exit 1; }
-
-  cd "${MYDIR}"
-done
-
-# Same thing, but specific to the Python framework dir
-find "${BUNDLE}/Contents/Frameworks" -type d -name "P*framework" | while read -r framework_dir; do
-  cd "${framework_dir}"
-
-  # Create soft link to the framework binary
-  ln -s Versions/Current/Py* || { echo "link creation in framework-config.sh failed"; exit 1; }
-
-  # Create soft link to the framework Resources dir
-  ln -s Versions/Current/Resources || { echo "link creation in framework-config.sh failed"; exit 1; }
-
-  # Create the Info.plist file
-  MYNAME=`ls -1 Py*`
-  sed 's/__SHORT_VERSION__/${PYTHON_SHORT_VERSION}/' "${SCRIPT_DIR}/Info.plist-template_Python" | sed 's/__FULL_VERSION__/${PYTHON_FULL_VERSION}/' | sed "s/__FRAMEWORK_NAME__/${MYNAME}/" > "Resources/Info.plist" || { echo "sed replacement in framework-config.sh failed"; exit 1; }
-
-  cd "${MYDIR}"
-done
-
-echo ${BUNDLE} framework config finished
Index: pkg/mac/build-functions.sh
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- pkg/mac/build-functions.sh	(date 1589445174016)
+++ pkg/mac/build-functions.sh	(date 1589445174016)
@@ -0,0 +1,387 @@
+_setup_env() {
+    APP_RELEASE=`grep "^APP_RELEASE" web/config.py | cut -d"=" -f2 | sed 's/ //g'`
+    APP_REVISION=`grep "^APP_REVISION" web/config.py | cut -d"=" -f2 | sed 's/ //g'`
+    APP_NAME=`grep "^APP_NAME" web/config.py | cut -d"=" -f2 | sed "s/'//g" | sed 's/^ //'`
+    APP_LONG_VERSION=${APP_RELEASE}.${APP_REVISION}
+    APP_SHORT_VERSION=`echo ${APP_LONG_VERSION} | cut -d . -f1,2`
+    APP_SUFFIX=`grep "^APP_SUFFIX" web/config.py | cut -d"=" -f2 | sed 's/ //g' | sed "s/'//g"`
+    if [ ! -z ${APP_SUFFIX} ]; then
+        APP_LONG_VERSION=${APP_LONG_VERSION}-${APP_SUFFIX}
+    fi
+    BUNDLE_DIR="${BUILD_ROOT}/${APP_NAME}.app"
+}
+
+_cleanup() {
+    echo Cleaning up the old environment and app bundle...
+    rm -rf ${SOURCE_DIR}/runtime/*.app
+    rm -rf ${BUILD_ROOT}
+    rm -f ${DIST_ROOT}/*.dmg
+}
+
+_create_venv() {
+    PATH=${PGDIR}/bin:${PATH}
+    LD_LIBRARY_PATH=${PGDIR}/lib:${LD_LIBRARY_PATH}
+
+    test -d ${BUILD_ROOT} || mkdir ${BUILD_ROOT}
+    cd ${BUILD_ROOT}
+
+    ${PYTHON_EXE} -m venv --copies venv
+
+    source venv/bin/activate
+    pip install --no-cache-dir --no-binary psycopg2 -r ${SOURCE_DIR}/requirements.txt
+
+    # Figure the source path for use when completing the venv
+    SOURCE_PYMODULES_PATH=$(dirname $("${PYTHON_EXE}" -c "from distutils.sysconfig import get_python_lib; print(get_python_lib())"))
+
+    # Figure the target path for use when completing the venv
+    # Use "python" here as we want the venv path
+    TARGET_PYMODULES_PATH=$(dirname $(python -c "from distutils.sysconfig import get_python_lib; print(get_python_lib())"))
+
+    # Copy in the additional system python modules
+    cp -R ${SOURCE_PYMODULES_PATH}/* "${TARGET_PYMODULES_PATH}/"
+
+    # Link the python<version> directory to python so that the private environment path is found by the application.
+    ln -s "$(basename ${TARGET_PYMODULES_PATH})" "${TARGET_PYMODULES_PATH}/../python"
+
+    # Remove tests
+    find venv -name "test" -type d -print0 | xargs -0 rm -rf
+    find venv -name "tests" -type d -print0 | xargs -0 rm -rf
+}
+
+_build_runtime() {
+    cd ${SOURCE_DIR}/runtime
+    make clean
+    ${QMAKE}
+    make
+    cp -r pgAdmin4.app "${BUNDLE_DIR}"
+}
+
+_build_docs() {
+    cd ${SOURCE_DIR}/docs/en_US
+    test -d "${BUNDLE_DIR}/Contents/Resources/docs/en_US" || mkdir -p "${BUNDLE_DIR}/Contents/Resources/docs/en_US"
+    cp -r _build/html "${BUNDLE_DIR}/Contents/Resources/docs/en_US/"
+}
+
+_complete_bundle() {
+    cd ${SCRIPT_DIR}
+
+    # Copy the binary utilities into place
+    mkdir -p "${BUNDLE_DIR}/Contents/SharedSupport/"
+    cp "${PGDIR}/bin/pg_dump" "${BUNDLE_DIR}/Contents/SharedSupport/"
+    cp "${PGDIR}/bin/pg_dumpall" "${BUNDLE_DIR}/Contents/SharedSupport/"
+    cp "${PGDIR}/bin/pg_restore" "${BUNDLE_DIR}/Contents/SharedSupport/"
+    cp "${PGDIR}/bin/psql" "${BUNDLE_DIR}/Contents/SharedSupport/"
+    
+    # Replace the place holders with the current version
+    sed -e "s/PGADMIN_LONG_VERSION/${APP_LONG_VERSION}/g" -e "s/PGADMIN_SHORT_VERSION/${APP_SHORT_VERSION}/g" pgadmin.Info.plist.in > pgadmin.Info.plist
+
+    # copy Python private environment to app bundle
+    cp -PR ${BUILD_ROOT}/venv "${BUNDLE_DIR}/Contents/Resources/"
+
+    # Remove any TCL-related files that may cause us problems
+    find "${BUNDLE_DIR}/Contents/Resources/venv/" -name "_tkinter*" -print0 | xargs -0 rm -f
+
+    test -d "${BUNDLE_DIR}/Contents/Resources" || mkdir -p "${BUNDLE_DIR}/Contents/Resources"
+    # Create qt.conf so that app knows where the Plugins are present
+    cat >> "${BUNDLE_DIR}/Contents/Resources/qt.conf" << EOF
+[Paths]
+Plugins = PlugIns
+EOF
+
+    test -d "${BUNDLE_DIR}/Contents/Frameworks" || mkdir -p "${BUNDLE_DIR}/Contents/Frameworks"
+    test -d "${BUNDLE_DIR}/Contents/PlugIns/platforms" || mkdir -p "${BUNDLE_DIR}/Contents/PlugIns/platforms"
+    test -d "${BUNDLE_DIR}/Contents/PlugIns/imageformats" || mkdir -p "${BUNDLE_DIR}/Contents/PlugIns/imageformats"
+    cp -f ${QTDIR}/plugins/platforms/libqcocoa.dylib "${BUNDLE_DIR}/Contents/PlugIns/platforms"
+    cp -f ${QTDIR}/plugins/imageformats/libqsvg.dylib "${BUNDLE_DIR}/Contents/PlugIns/imageformats"
+    cp -f ${PGDIR}/lib/libpq.5.dylib "${BUNDLE_DIR}/Contents/Frameworks"
+
+	local bundle=$1 todo todo_old fw_relpath lib lib_bn nested_app na_relpath
+
+	pushd "${BUNDLE_DIR}" > /dev/null
+
+	# We skip nested apps here - those are treated specially
+	todo=$(file `find ./ -perm +0111 ! -type d ! -path "*.app/*" ! -name "*.app"` | grep -E "Mach-O 64-bit" | awk -F ':| ' '{ORS=" "; print $1}')
+
+	echo "Found executables: ${todo}"
+	while test "${todo}" != ""; do
+		todo_old=${todo} ;
+		todo="" ;
+		for todo_obj in ${todo_old}; do
+			echo "Post-processing: ${todo_obj}"
+
+			# Figure out the relative path from todo_obj to Contents/Frameworks
+			fw_relpath=$(echo "${todo_obj}" | sed -n 's|^\(\.//*\)\(\([^/][^/]*/\)*\)[^/][^/]*$|\2|gp' | sed -n 's|[^/][^/]*/|../|gp')"Contents/Frameworks"
+			fw_relpath_old=${fw_relpath}
+
+			fw_loc="Contents/Frameworks"
+
+			# Find all libraries $todo_obj depends on, but skip system libraries
+			for lib in $(otool -L ${todo_obj} | grep "Qt\|dylib\|Frameworks\|PlugIns" | grep -v ":" | sed 's/(.*//' | egrep -v '(/usr/lib)|(/System)|@executable_path@'); do
+				if echo ${lib} | grep "PlugIns\|libqcocoa"  > /dev/null; then
+					lib_loc="Contents/PlugIns/platforms"
+				elif echo ${lib} | grep "PlugIns\|libqsvg"  > /dev/null; then
+					lib_loc="Contents/PlugIns/imageformats"
+				elif echo ${lib} | grep "Qt" > /dev/null; then
+					qtfw_path="$(dirname ${lib} | sed 's|.*\(Qt.*framework\)|\1|')"
+					lib_loc="Contents/Frameworks/${qtfw_path}"
+					if [ "$(basename ${todo_obj})" = "${lib}" ]; then
+						lib_loc="$(dirname ${todo_obj})"
+						qtfw_path=$(echo ${lib_loc} | sed 's/Contents\/Frameworks\///')
+					fi
+				elif echo ${lib} | grep "Python" > /dev/null; then
+					pyfw_path="$(dirname ${lib} | sed 's|.*\(Python.*framework\)|\1|')"
+					lib_loc="Contents/Frameworks/${pyfw_path}"
+					if [ "$(basename ${todo_obj})" = "${lib}" ]; then
+						lib_loc="$(dirname ${todo_obj})"
+						pyfw_path=$(echo ${lib_loc} | sed 's/Contents\/Frameworks\///')
+					fi
+				else
+					lib_loc="Contents/Frameworks"
+				fi
+				lib_bn="$(basename "${lib}")" ;
+				if ! test -f "${lib_loc}/${lib_bn}"; then
+                    target_file=""
+					target_path=""
+					echo "Adding symlink: ${lib_bn} (because of: ${todo_obj})"
+
+					# Copy the QT and Python framework
+					if echo ${lib} | grep Qt > /dev/null ; then
+						test -d ${lib_loc} || mkdir -p ${lib_loc}
+						echo Copying -R ${QTDIR}/lib/${qtfw_path}/${lib_bn} to ${lib_loc}/
+						cp ${QTDIR}/lib/${qtfw_path}/${lib_bn} ${lib_loc}/
+					elif echo ${lib} | grep Python > /dev/null ; then
+						test -d ${lib_loc} || mkdir -p ${lib_loc}
+						cp -R "${lib}" "${lib_loc}/${lib_bn}"
+					else
+						cp -R "${lib}" "${lib_loc}/${lib_bn}"
+					fi
+					if ! test -L "${lib_loc}/${lib_bn}"; then
+						chmod 755 "${lib_loc}/${lib_bn}"
+					else
+						target_file=$(readlink "${lib}")
+						target_path=$(dirname "${lib}")/${target_file}
+
+					    echo "Adding symlink target: ${target_path}"
+						cp "${target_path}" "${lib_loc}/${target_file}"
+						chmod 755 "${lib_loc}/${target_file}"
+					fi
+					echo "Rewriting ID in ${lib_loc}/${lib_bn} to ${lib_bn}"
+                    install_name_tool -id "${lib_bn}" "${lib_loc}/${lib_bn}"
+
+					todo="${todo} ./${lib_loc}/${lib_bn}"
+				fi
+				if echo ${lib} | grep Qt > /dev/null ; then
+					fw_relpath="${fw_relpath}/${qtfw_path}"
+				fi
+				if echo ${lib} | grep Python > /dev/null ; then
+					fw_relpath="${fw_relpath}/${pyfw_path}"
+				fi
+				chmod +w ${todo_obj}
+				echo "Rewriting library ${lib} to @loader_path/${fw_relpath}/${lib_bn} in ${todo_obj}"
+
+				install_name_tool -change "${lib}" "@loader_path/${fw_relpath}/${lib_bn}" "${todo_obj}"
+                install_name_tool -change "${target_path}" "@loader_path/${fw_relpath}/${target_file}" "${todo_obj}"
+
+				fw_relpath="${fw_relpath_old}"
+			done
+		done
+	done
+
+	# Fix the rpaths for psycopg module
+	find "${BUNDLE_DIR}/Contents/Resources/venv/" -name _psycopg.so -print0 | xargs -0 install_name_tool -change libpq.5.dylib @loader_path/../../../../../../Frameworks/libpq.5.dylib
+	find "${BUNDLE_DIR}/Contents/Resources/venv/" -name _psycopg.so -print0 | xargs -0 install_name_tool -change libssl.1.0.0.dylib @loader_path/../../../../../../Frameworks/libssl.1.0.0.dylib
+	find "${BUNDLE_DIR}/Contents/Resources/venv/" -name _psycopg.so -print0 | xargs -0 install_name_tool -change libcrypto.1.0.0.dylib @loader_path/../../../../../../Frameworks/libcrypto.1.0.0.dylib
+
+	echo "App completed: ${BUNDLE_DIR}"
+	popd > /dev/null
+
+    pushd ${SOURCE_DIR}/web > /dev/null
+        yarn install
+        yarn run bundle
+
+        curl https://curl.haxx.se/ca/cacert.pem -o cacert.pem -s
+    popd > /dev/null
+
+    # copy the web directory to the bundle as it is required by runtime
+    cp -r ${SOURCE_DIR}/web "${BUNDLE_DIR}/Contents/Resources/"
+    cd "${BUNDLE_DIR}/Contents/Resources/web"
+    rm -f pgadmin4.db config_local.*
+    rm -rf karma.conf.js package.json node_modules/ regression/ tools/ pgadmin/static/js/generated/.cache
+    find . -name "tests" -type d -print0 | xargs -0 rm -rf
+    find . -name "feature_tests" -type d -print0 | xargs -0 rm -rf
+    find . -name ".DS_Store" -print0 | xargs -0 rm -f
+
+    echo "SERVER_MODE = False" > config_distro.py
+    echo "HELP_PATH = '../../../docs/en_US/html/'" >> config_distro.py
+    echo "DEFAULT_BINARY_PATHS = {" >> config_distro.py
+    echo "    'pg':   '\$DIR/../../SharedSupport'," >> config_distro.py
+    echo "    'ppas': ''" >> config_distro.py
+    echo "}" >> config_distro.py
+
+    # Remove the .pyc files if any
+    find "${BUNDLE_DIR}" -name *.pyc -print0 | xargs -0 rm -f
+}
+
+_framework_config() {
+    # Get the config
+    source ${SCRIPT_DIR}/framework.conf
+
+    echo Reorganising the framework structure...
+
+    # Create "Current" and "Current/Resources" inside each of the framework dirs
+    find "${BUNDLE_DIR}/Contents/Frameworks"/*framework -type d -name "Versions" | while read -r framework_dir; do
+      pushd "${framework_dir}" > /dev/null
+
+      # Create framework 'Current' soft link
+      VERSION_NUMBER=`ls -1`
+      ln -s ${VERSION_NUMBER} Current
+
+      # Create "Resources" subdirectory
+      if [ ! -d Current/Resources ]; then
+        mkdir Current/Resources
+      fi
+
+      popd > /dev/null
+    done
+
+    # Stuff for Qt framework files only
+    find "${BUNDLE_DIR}/Contents/Frameworks" -type d -name "Qt*framework" | while read -r framework_dir; do
+      pushd "${framework_dir}" > /dev/null
+
+      # Create soft link to the framework binary
+      ln -s Versions/Current/Qt*
+
+      # Create soft link to the framework Resources dir
+      ln -s Versions/Current/Resources
+
+      # Create the Info.plist files
+      MYNAME=`ls -1 Qt*`
+      if [ -f Resources/Info.plist ]; then
+        chmod +w Resources/Info.plist
+      fi
+      sed 's/__SHORT_VERSION__/${QT_SHORT_VERSION}/' "${SCRIPT_DIR}/Info.plist-template_Qt5" | sed 's/__FULL_VERSION__/${QT_FULL_VERSION}/' | sed "s/__FRAMEWORK_NAME__/${MYNAME}/" > "Resources/Info.plist"
+
+      popd > /dev/null
+    done
+
+    # Same thing, but specific to the Python framework dir
+    find "${BUNDLE_DIR}/Contents/Frameworks" -type d -name "P*framework" | while read -r framework_dir; do
+      pushd "${framework_dir}" > /dev/null
+
+      # Create soft link to the framework binary
+      ln -s Versions/Current/Py*
+
+      # Create soft link to the framework Resources dir
+      ln -s Versions/Current/Resources
+
+      # Create the Info.plist file
+      MYNAME=`ls -1 Py*`
+      sed 's/__SHORT_VERSION__/${PYTHON_SHORT_VERSION}/' "${SCRIPT_DIR}/Info.plist-template_Python" | sed 's/__FULL_VERSION__/${PYTHON_FULL_VERSION}/' | sed "s/__FRAMEWORK_NAME__/${MYNAME}/" > "Resources/Info.plist"
+
+      popd > /dev/null
+    done
+}
+
+_codesign_binaries() {
+    if [ ${CODESIGN} -eq 0 ]; then
+        return
+    fi
+
+    if [ -z "${DEVELOPER_ID}" ] ; then
+        echo "Developer ID Application not found in codesign.conf" >&2
+        exit 1
+    fi
+
+    if [ -z "${DEVELOPER_BUNDLE_ID}" ]; then
+        echo "Developer Bundle Identifier not found in codesign.conf" >&2
+    fi
+
+    echo Signing ${BUNDLE_DIR} binaries...
+    IFS=$'\n'
+    for i in $(find "${BUNDLE_DIR}" -type f -perm +111 -exec file "{}" \; | grep -E "Mach-O executable|Mach-O 64-bit executable|Mach-O 64-bit bundle" | awk -F":| \\\(" '{print $1}' | uniq)
+    do
+        codesign --deep --force --verify --verbose --timestamp --options runtime -i "${DEVELOPER_BUNDLE_ID}" --sign "${DEVELOPER_ID}" "$i"
+    done
+
+    echo Signing ${BUNDLE_DIR} libraries...
+    for i in $(find "${BUNDLE_DIR}" -type f -name "*.dylib*")
+    do
+        codesign --deep --force --verify --verbose --timestamp --options runtime -i "${DEVELOPER_BUNDLE_ID}" --sign "${DEVELOPER_ID}" "$i"
+    done
+}
+
+_codesign_bundle() {
+    if [ ${CODESIGN} -eq 0 ]; then
+        return
+    fi
+
+    # Sign the .app
+    echo Signing ${BUNDLE_DIR}...
+    codesign --deep --force --verify --verbose --timestamp --options runtime -i "${DEVELOPER_BUNDLE_ID}" --sign "${DEVELOPER_ID}" "${BUNDLE_DIR}"
+
+    # Verify it worked
+    echo Verifying the signature...
+    codesign --verify --verbose --deep --force "${BUNDLE_DIR}"
+    echo ${BUNDLE_DIR} successfully signed.
+}
+
+_create_dmg() {
+    # move to the directory where we want to create the DMG
+    test -d ${DIST_ROOT} || mkdir ${DIST_ROOT}
+    cd ${DIST_ROOT}
+
+    DMG_SOURCES="./../mac-build/${APP_NAME}.app"
+    DMG_LICENCE=./../pkg/mac/licence.rtf
+    DMG_VOLUME_NAME=${APP_NAME}
+    DMG_NAME=`echo ${DMG_VOLUME_NAME} | sed 's/ //g' | awk '{print tolower($0)}'`
+    DMG_IMAGE=${DMG_NAME}-${APP_LONG_VERSION}.dmg
+    HDIUTIL=/usr/bin/hdiutil
+    REZ="/usr/bin/Rez"
+
+    DMG_DIR=./${DMG_IMAGE}.src
+
+    if test -e "${DMG_DIR}"; then
+        echo "Directory ${DMG_DIR} already exists. Please delete it manually." >&2
+        exit 1
+    fi
+
+    echo "Cleaning up"
+    rm -f "${DMG_IMAGE}"
+    mkdir "${DMG_DIR}"
+
+    echo "Copying data into temporary directory"
+    for src in "${DMG_SOURCES}"; do
+        cp -R "${src}" "${DMG_DIR}"
+    done
+
+    echo "Creating image"
+    ${HDIUTIL} create -quiet -srcfolder "$DMG_DIR" -fs HFS+ -format UDZO -volname "${DMG_VOLUME_NAME}" -ov "${DMG_IMAGE}"
+    rm -rf "${DMG_DIR}"
+
+    echo Attaching License to image...
+    python ${SCRIPT_DIR}/dmg-license.py "${DMG_IMAGE}" "${DMG_LICENCE}" -c bz2
+}
+
+_codesign_dmg() {
+    if [ ${CODESIGN} -eq 0 ]; then
+        return
+    fi
+
+    DMG_VOLUME_NAME=${APP_NAME}
+    DMG_NAME=`echo ${DMG_VOLUME_NAME} | sed 's/ //g' | awk '{print tolower($0)}'`
+    DMG_IMAGE=${DIST_ROOT}/${DMG_NAME}-${APP_LONG_VERSION}.dmg
+
+    if ! test -f "${DMG_IMAGE}" ; then
+        echo "${DMG_IMAGE} is no disk image!" >&2
+        exit 1
+    fi
+
+    # Sign the .app
+    echo Signing ${DMG_IMAGE}...
+    codesign --deep --force --verify --verbose --timestamp --options runtime -i "${DEVELOPER_BUNDLE_ID}" --sign "${DEVELOPER_ID}" "${DMG_IMAGE}"
+
+    # Verify it worked
+    echo Verifying the signature...
+    codesign --verify --verbose --force "${DMG_IMAGE}"
+    echo ${DMG_IMAGE} successfully signed.
+}
\ No newline at end of file
Index: docs/en_US/release_notes_4_22.rst
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- docs/en_US/release_notes_4_22.rst	(revision 12a7ce54d93346e49efe1b14d1ad61d949adbbcc)
+++ docs/en_US/release_notes_4_22.rst	(date 1589462888768)
@@ -18,6 +18,7 @@
 | `Issue #5443 <https://redmine.postgresql.org/issues/5443>`_ -  Remove support for Python 2.
 | `Issue #5444 <https://redmine.postgresql.org/issues/5444>`_ -  Cleanup Python detection in the runtime project file.
 | `Issue #5455 <https://redmine.postgresql.org/issues/5455>`_ -  Refactor pgAdmin4.py so it can be imported and is a lot more readable.
+| `Issue #5525 <https://redmine.postgresql.org/issues/5525>`_ -  Cleanup and refactor the macOS build scripts.
 
 Bug fixes
 *********
Index: pkg/mac/build.sh
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- pkg/mac/build.sh	(revision 12a7ce54d93346e49efe1b14d1ad61d949adbbcc)
+++ pkg/mac/build.sh	(date 1589206120250)
@@ -2,13 +2,19 @@
 
 # Build script to create Mac App Bundle and DMG for pgAdmin4 runtime
 
-export WD=$(cd `dirname $0` && pwd)
-export SOURCEDIR=${WD}/../..
-export BUILDROOT=${WD}/../../mac-build
-export DISTROOT=${WD}/../../dist
-export VIRTUALENV=venv
+# Exit when any command fails
+set -e -E
+
+# Debugging shizz
+trap 'last_command=$current_command; current_command=$BASH_COMMAND' DEBUG
+trap 'if [ $? -ne 0 ]; then echo "\"${last_command}\" command filed with exit code $?."; fi' EXIT
 
-if [ ! -f ${SOURCEDIR}/pkg/mac/framework.conf ]; then
+SCRIPT_DIR=$(cd `dirname $0` && pwd)
+SOURCE_DIR=$(realpath ${SCRIPT_DIR}/../..)
+BUILD_ROOT=$(realpath ${SCRIPT_DIR}/../..)/mac-build
+DIST_ROOT=$(realpath ${SCRIPT_DIR}/../..)/dist
+
+if [ ! -f ${SCRIPT_DIR}/framework.conf ]; then
     echo
     echo "Error: pkg/mac/framework.conf not found!"
     echo "Copy pkg/mac/framework.conf.in to pkg/mac/framework.conf and edit as required for the current system."
@@ -16,14 +22,28 @@
     exit 1
 fi
 
-if [ "x${PYTHON_HOME}" == "x" ]; then
-    echo "PYTHON_HOME not set. It must be set, and pointing to a Python 3 installation."
+CODESIGN=1
+if [ ! -f ${SCRIPT_DIR}/codesign.conf ]; then
+    echo
+    echo "******************************************************************"
+    echo "* ${SCRIPT_DIR}/codesign.conf not found. NOT signing the binaries."
+    echo "******************************************************************"
+    echo
+    CODESIGN=0
+    sleep 5
+else
+    source ${SCRIPT_DIR}/codesign.conf
+fi
+
+if [ "x${PGADMIN_PYTHON_DIR}" == "x" ]; then
+    echo "PGADMIN_PYTHON_DIR not set. It must be set, and pointing to a Python 3 installation."
     exit 1
 fi
+PYTHON_EXE=${PGADMIN_PYTHON_DIR}/bin/python3
 
 # Check if Python is working and calculate PYTHON_VERSION
-if ${PYTHON_HOME}/bin/python3 -V > /dev/null 2>&1; then
-    export PYTHON_VERSION=`${PYTHON_HOME}/bin/python3 -V 2>&1 | awk '{print $2}' | cut -d"." -f1-2 | sed 's/\.//'`
+if ${PYTHON_EXE} -V > /dev/null 2>&1; then
+    PYTHON_VERSION=`${PYTHON_EXE} -V 2>&1 | awk '{print $2}' | cut -d"." -f1-2 | sed 's/\.//'`
 else
     echo "Error: Python installation missing!"
     exit 1
@@ -34,14 +54,12 @@
     exit 1
 fi
 
-export PYTHON=${PYTHON_HOME}/bin/python3
-export PIP=pip3
-
 if [ "x${QTDIR}" == "x" ]; then
     echo "QTDIR not set. Setting it to default."
-    export QTDIR=~/Qt/5.8/clang_64
+    export QTDIR=~/Qt/5.13.2/clang_64
 fi
-export QMAKE=${QTDIR}/bin/qmake
+
+QMAKE=${QTDIR}/bin/qmake
 if ! ${QMAKE} --version > /dev/null 2>&1; then
     echo "Error: qmake not found. QT installation is not present or incomplete."
     exit 1
@@ -52,208 +70,13 @@
     export PGDIR=/usr/local/pgsql
 fi
 
-_get_version() {
-    export APP_RELEASE=`grep "^APP_RELEASE" web/config.py | cut -d"=" -f2 | sed 's/ //g'`
-    export APP_REVISION=`grep "^APP_REVISION" web/config.py | cut -d"=" -f2 | sed 's/ //g'`
-    export APP_NAME=`grep "^APP_NAME" web/config.py | cut -d"=" -f2 | sed "s/'//g" | sed 's/^ //'`
-    export APP_BUNDLE_NAME=${APP_NAME}.app
-    export APP_LONG_VERSION=${APP_RELEASE}.${APP_REVISION}
-    export APP_SHORT_VERSION=`echo ${APP_LONG_VERSION} | cut -d . -f1,2`
-    export APP_SUFFIX=`grep "^APP_SUFFIX" web/config.py | cut -d"=" -f2 | sed 's/ //g' | sed "s/'//g"`
-    if [ ! -z ${APP_SUFFIX} ]; then
-        export APP_LONG_VERSION=${APP_LONG_VERSION}-${APP_SUFFIX}
-    fi
-}
-
-_cleanup() {
-    echo "Cleaning up the old environment and app bundle"
-    rm -rf ${SOURCEDIR}/runtime/pgAdmin4.app
-    rm -rf ${BUILDROOT}
-    rm -f ${DISTROOT}/pgadmin4*.dmg
-}
-
-_create_venv() {
-    export PATH=${PGDIR}/bin:${PATH}
-    export LD_LIBRARY_PATH=${PGDIR}/lib:${LD_LIBRARY_PATH}
-    test -d ${BUILDROOT} || mkdir ${BUILDROOT} || exit 1
-    cd ${BUILDROOT}
-
-    test -d ${VIRTUALENV} || virtualenv -p ${PYTHON} --always-copy ${VIRTUALENV} || exit 1
-
-    source ${VIRTUALENV}/bin/activate
-    ${PIP} install --no-cache-dir --no-binary psycopg2 -r ${SOURCEDIR}/requirements.txt || { echo PIP install failed. Please resolve the issue and rerun the script; exit 1; }
-
-    # Figure out some paths for use when completing the venv
-    # Use "python" here as we want the venv path
-    export PYMODULES_PATH=`python -c "from distutils.sysconfig import get_python_lib; print(get_python_lib())"`
-    export DIR_PYMODULES_PATH=`dirname ${PYMODULES_PATH}`
-    
-    # Use $PYTHON here as we want the system path
-    export PYSYSLIB_PATH=`${PYTHON} -c "import sys; print('%s/lib/python%d.%.d' % (sys.prefix, sys.version_info.major, sys.version_info.minor))"`
-    
-    # Symlink in the rest of the Python libs. This is required because the runtime
-    # will clear PYTHONHOME for safety, which has the side-effect of preventing
-    # it from finding modules that are not explicitly included in the venv
-    cd ${DIR_PYMODULES_PATH}
-
-    # Files
-    for FULLPATH in ${PYSYSLIB_PATH}/*.py; do
-        FILE=${FULLPATH##*/}
-        if [ ! -e ${FILE} ]; then
-           cp ${FULLPATH} ${FILE}
-        fi
-    done
-
-    # Paths
-    for FULLPATH in ${PYSYSLIB_PATH}/*/; do
-        FULLPATH=${FULLPATH%*/}
-        FILE=${FULLPATH##*/}
-        if [ ! -e ${FILE} ]; then
-            cp -R ${FULLPATH} ${FILE}
-        fi
-    done
-
-    # Remove tests
-    cd site-packages
-    find . -name "test" -type d -print0 | xargs -0 rm -rf
-    find . -name "tests" -type d -print0 | xargs -0 rm -rf
+source ${SCRIPT_DIR}/build-functions.sh
 
-    # Link the python<version> directory to python so that the private environment path is found by the application.
-    if test -d ${DIR_PYMODULES_PATH}; then
-        ln -s $(basename ${DIR_PYMODULES_PATH}) ${DIR_PYMODULES_PATH}/../python
-    fi
-}
-
-_build_runtime() {
-    cd ${SOURCEDIR}/runtime
-    make clean
-    PGADMIN_PYTHON_DIR=${PYTHON_HOME} ${QMAKE} || { echo qmake failed; exit 1; }
-    make || { echo make failed; exit 1; }
-    cp -r pgAdmin4.app "${BUILDROOT}/${APP_BUNDLE_NAME}"
-}
-
-_build_doc() {
-    cd ${SOURCEDIR}/docs/en_US
-    test -d "${BUILDROOT}/${APP_BUNDLE_NAME}/Contents/Resources" || "mkdir -p ${BUILDROOT}/${APP_BUNDLE_NAME}/Contents/Resources"
-    test -d "${BUILDROOT}/${APP_BUNDLE_NAME}/Contents/Resources/docs/en_US" || mkdir -p "${BUILDROOT}/${APP_BUNDLE_NAME}/Contents/Resources/docs/en_US"
-    cp -r _build/html "${BUILDROOT}/${APP_BUNDLE_NAME}/Contents/Resources/docs/en_US/" || exit 1
-}
-
-_complete_bundle() {
-    cd ${SOURCEDIR}/pkg/mac
-
-    # Copy the binary utilities into place
-    mkdir -p "${BUILDROOT}/${APP_BUNDLE_NAME}/Contents/SharedSupport/" || exit 1
-    cp "${PGDIR}/bin/pg_dump" "${BUILDROOT}/${APP_BUNDLE_NAME}/Contents/SharedSupport/" || exit 1
-    cp "${PGDIR}/bin/pg_dumpall" "${BUILDROOT}/${APP_BUNDLE_NAME}/Contents/SharedSupport/" || exit 1
-    cp "${PGDIR}/bin/pg_restore" "${BUILDROOT}/${APP_BUNDLE_NAME}/Contents/SharedSupport/" || exit 1
-    cp "${PGDIR}/bin/psql" "${BUILDROOT}/${APP_BUNDLE_NAME}/Contents/SharedSupport/" || exit 1
-    
-    # Replace the place holders with the current version
-    sed -e "s/PGADMIN_LONG_VERSION/${APP_LONG_VERSION}/g" -e "s/PGADMIN_SHORT_VERSION/${APP_SHORT_VERSION}/g" pgadmin.Info.plist.in > pgadmin.Info.plist
-
-    # copy Python private environment to app bundle
-    cp -PR ${BUILDROOT}/${VIRTUALENV} "${BUILDROOT}/${APP_BUNDLE_NAME}/Contents/Resources/" || exit 1
-
-    # Remove any TCL-related files that may cause us problems
-    find "${BUILDROOT}/${APP_BUNDLE_NAME}/Contents/Resources/${VIRTUALENV}/" -name "_tkinter*" -print0 | xargs -0 rm -f
-
-    # run complete-bundle to copy the dependent libraries and frameworks and fix the rpaths
-    ./complete-bundle.sh "${BUILDROOT}/${APP_BUNDLE_NAME}" || { echo complete-bundle.sh failed; exit 1; }
-
-    pushd ${SOURCEDIR}/web
-        yarn install || exit 1
-        yarn run bundle || exit 1
-
-        curl https://curl.haxx.se/ca/cacert.pem -o cacert.pem -s || { echo Failed to download cacert.pem; exit 1; }
-    popd
-
-    # copy the web directory to the bundle as it is required by runtime
-    cp -r ${SOURCEDIR}/web "${BUILDROOT}/${APP_BUNDLE_NAME}/Contents/Resources/" || exit 1
-    cd "${BUILDROOT}/${APP_BUNDLE_NAME}/Contents/Resources/web"
-    rm -f pgadmin4.db config_local.*
-    rm -rf karma.conf.js package.json node_modules/ regression/ tools/ pgadmin/static/js/generated/.cache
-    find . -name "tests" -type d -print0 | xargs -0 rm -rf
-    find . -name "feature_tests" -type d -print0 | xargs -0 rm -rf
-    find . -name ".DS_Store" -print0 | xargs -0 rm -f
-
-    echo "SERVER_MODE = False" > config_distro.py
-    echo "HELP_PATH = '../../../docs/en_US/html/'" >> config_distro.py
-    echo "DEFAULT_BINARY_PATHS = {" >> config_distro.py
-    echo "    'pg':   '\$DIR/../../SharedSupport'," >> config_distro.py
-    echo "    'ppas': ''" >> config_distro.py
-    echo "}" >> config_distro.py
-
-    # Remove the .pyc files if any
-    cd "${BUILDROOT}/${APP_BUNDLE_NAME}"
-    find . -name *.pyc -print0 | xargs -0 rm -f
-}
-
-_framework_config() {
-    cd ${SOURCEDIR}/pkg/mac
-    ./framework-config.sh "${BUILDROOT}/${APP_BUNDLE_NAME}" || { echo "framework-config.sh failed"; exit 1; }
-}
-
-_codesign_binaries() {
-    cd ${SOURCEDIR}/pkg/mac
-
-    if [ ! -f codesign.conf ]; then
-        echo
-        echo "******************************************************************"
-        echo "* codesign.conf not found. NOT signing the binaries."
-        echo "******************************************************************"
-        echo
-        sleep 5
-        return
-    fi
-
-    ./codesign-binaries.sh "${BUILDROOT}/${APP_BUNDLE_NAME}" || { echo codesign-binaries.sh failed; exit 1; }
-}
-
-_codesign_bundle() {
-    cd ${SOURCEDIR}/pkg/mac
-
-    if [ ! -f codesign.conf ]; then
-        echo
-        echo "******************************************************************"
-        echo "* codesign.conf not found. NOT signing the bundle."
-        echo "******************************************************************"
-        echo
-        sleep 5
-        return
-    fi
-
-    ./codesign-bundle.sh "${BUILDROOT}/${APP_BUNDLE_NAME}" || { echo codesign-bundle.sh failed; exit 1; }
-}
-
-_create_dmg() {
-    cd ${SOURCEDIR}
-    ./pkg/mac/create-dmg.sh || { echo create-dmg.sh failed; exit 1; }
-    # Clean the mac-build/ on successful build
-    rm -rf ${BUILDROOT}/*
-}
-
-_codesign_dmg() {
-    cd ${SOURCEDIR}/pkg/mac
-    
-    if [ ! -f codesign.conf ]; then
-        echo
-        echo "******************************************************************"
-        echo "* codesign.conf not found. NOT signing the disk image."
-        echo "******************************************************************"
-        echo
-        sleep 5
-        return
-    fi
-
-    ./codesign-dmg.sh || { echo codesign-bundle.sh failed; exit 1; }    
-}
-
-_get_version || { echo Could not get versioning; exit 1; }
+_setup_env
 _cleanup
-_create_venv || { echo venv creation failed; exit 1; }
-_build_runtime || { echo Runtime build failed; exit 1; }
-_build_doc
+_create_venv
+_build_runtime
+_build_docs
 _complete_bundle
 _framework_config
 _codesign_binaries
