# Copyright 2021 Gentoo Authors
# Distributed under the terms of the GNU General Public License v2

EAPI=8

# Maintainer notes:
# - This ebuild uses Bundler to download and install all gems in deployment mode
#   (i.e. into isolated directory inside application). That's not Gentoo way how
#   it should be done, but GitLab has too many dependencies that it will be too
#   difficult to maintain them via ebuilds.

USE_RUBY="ruby31 ruby32"

EGIT_REPO_URI="https://gitlab.com/gitlab-org/gitlab-foss.git"
EGIT_COMMIT="v${PV}"

inherit git-r3 ruby-single systemd tmpfiles

DESCRIPTION="The gitlab and gitaly parts of the GitLab DevOps platform"
HOMEPAGE="https://gitlab.com/gitlab-org/gitlab-foss"

LICENSE="MIT"
RESTRICT="network-sandbox splitdebug strip"
SLOT="0"
KEYWORDS="~amd64 ~x86"
IUSE="favicon +gitaly_git -gitlab-config kerberos -mail_room -pages -prometheus -relative_url systemd"
# Current (2021-11-24) groups in gitlab Gemfile:
# puma development test danger coverage omnibus ed25519 kerberos
# Current (2021-11-24) groups in gitlab-gitaly Gemfile:
# development test omnibus
# USE flags that affect the "--local without" option below
WITHOUTflags="kerberos"
PGSlot=14

## Gems dependencies:
#   gpgme				app-crypt/gpgme
#   rugged				dev-libs/libgit2
#   nokogiri			dev-libs/libxml2, dev-libs/libxslt
#   charlock_holmes		dev-libs/icu
#   yajl-ruby			dev-libs/yajl
#   pg					dev-db/postgresql-base
#   gitlab-markup		dev-python/docutils
#
GEMS_DEPEND="
	app-crypt/gpgme
	dev-libs/icu
	dev-libs/libxml2
	dev-libs/libxslt
	dev-util/ragel
	dev-libs/yajl
	dev-db/postgresql:${PGSlot}
	net-libs/http-parser
	dev-python/docutils"
GITALY_DEPEND="
	>=dev-lang/go-1.20
	dev-build/cmake"
WORKHORSE_DEPEND="
	dev-lang/go
	media-libs/exiftool"
DEPEND="
	${GEMS_DEPEND}
	${GITALY_DEPEND}
	${WORKHORSE_DEPEND}
	${RUBY_DEPS}
	>=acct-user/git-0-r4[gitlab]
	acct-group/git
	>=net-libs/nodejs-20.13.0
	>=dev-lang/ruby-3.1.4:3.1[ssl]
	>=dev-vcs/gitlab-shell-14.39.0[relative_url=]
	pages? ( ~www-apps/gitlab-pages-${PV} )
	!gitaly_git? ( >=dev-vcs/git-2.46.0[pcre] dev-libs/libpcre2[jit] )
	net-misc/curl
	virtual/ssh
	=sys-apps/yarn-1.22*
	dev-libs/re2"
RDEPEND="${DEPEND}
	!www-servers/gitlab-workhorse
	>=dev-db/redis-6.2.14
	virtual/mta
	kerberos? ( app-crypt/mit-krb5 )
	favicon? ( media-gfx/graphicsmagick )"
BDEPEND="
	=dev-ruby/rubygems-3.5*
	>=dev-ruby/bundler-2:2"

GIT_USER="git"
GIT_GROUP="git"
GIT_HOME="/var/lib/gitlab"
BASE_DIR="/opt/gitlab"
GITLAB="${BASE_DIR}/${PN}"
CONF_DIR="/etc/${PN}"
GITLAB_CONFIG="${GITLAB}/config"
CONF_DIR_GITALY="/etc/gitlab-gitaly"
LOG_DIR="/var/log/${PN}"
TMP_DIR="/var/tmp/${PN}"
WORKHORSE="${BASE_DIR}/gitlab-workhorse"
WORKHORSE_BIN="${WORKHORSE}/bin"
vSYS=4 # version of SYStemd service files used by this ebuild
vORC=2 # version of OpenRC init files used by this ebuild

GIT_REPOS="${GIT_HOME}/repositories"
GITLAB_SHELL="${BASE_DIR}/gitlab-shell"
GITLAB_SOCKETS="${GITLAB}/tmp/sockets"
GITLAB_GITALY="${BASE_DIR}/gitlab-gitaly"
GITALY_CONF="/etc/gitlab-gitaly"

RAILS_ENV=${RAILS_ENV:-production}
NODE_ENV=${RAILS_ENV:-production}
BUNDLE="ruby /usr/bin/bundle"

MODUS='' # [new|rebuild|patch|minor|major]

pkg_setup() {
	# get the installed version
	vINST=$(best_version www-apps/gitlab)
	if [ -z "$vINST" ]; then
		vINST=$(best_version www-apps/gitlabhq)
		[ -n "$vINST" ] && die "The migration from a www-apps/gitlabhq installation to "\
							   ">=www-apps/gitlab-14.0.0 isn't supported."\
							   "Upgrade to 13.12.15 first."
	fi
	vINST=${vINST%-r*}
	vINST=${vINST##*-}
	if [ -n "$vINST" ] && ver_test "$PV" -lt "$vINST"; then
		# do downgrades on explicit user request only
		ewarn "Going to downgrade from $vINST to $PV."
		ewarn "Note that the maintainer of the GitLab overlay never tested this."
		ewarn "Extra actions may be neccessary, like the ones described in"
		ewarn "https://docs.gitlab.com/ee/administration/backup_restore/restore_gitlab.html"
		if [ "$GITLAB_DOWNGRADE" != "true" ]; then
			die "Set GITLAB_DOWNGRADE=\"true\" to really do the downgrade."
		fi
	else
		local eM eM1 em em1 em2 ep
		eM=$(ver_cut 1); eM1=$(($eM - 1))
		em=$(ver_cut 2); em1=$(($em - 1)); em2=$(($em - 2))
		ep=$(ver_cut 3)
		# check if upgrade path is supported and qualified for upgrading without downtime
		case "$vINST" in
			"")					MODUS="new"
								elog "This is a new installation.";;
			${PV})				MODUS="rebuild"
								elog "This is a rebuild of $PV.";;
			${eM}.${em}.*)		MODUS="patch"
								elog "This is a patch upgrade from $vINST to $PV.";;
			${eM}.${em1}.*)		MODUS="minor"
								elog "This is a minor upgrade from $vINST to $PV.";;
			${eM}.[0-${em2}].*) die "It's recommended to do minor upgrades step by step.";;
			16.11.4)			if [ "${PV}" = "17.0.0" ]; then
									MODUS="major"
									elog "This is a major upgrade from $vINST to $PV."
								else
									die "It's recommended to upgrade to 17.0.0 first."
								fi;;
			15.11.13)			if [ "${PV}" = "16.0.0" ]; then
									MODUS="major"
									elog "This is a major upgrade from $vINST to $PV."
								else
									die "It's recommended to upgrade to 16.0.0 first."
								fi;;
			14.10.5)			if [ "${PV}" = "15.0.0" ]; then
									MODUS="major"
									elog "This is a major upgrade from $vINST to $PV."
								else
									die "It's recommended to upgrade to 15.0.0 first."
								fi;;
			13.12.15)			die "It's recommended to upgrade to 14.0.0 first.";;
			12.10.14)			die "It's recommended to upgrade to 13.1.0 first.";;
			12.*.*)				die "It's recommended to upgrade to 12.10.14 first.";;
			${eM1}.*.*)			die "It's recommended to upgrade to latest ${eM1}.x.x version"\
									"first and then to the ${eM}.0.0 version.";;
			*)					if ver_test $vINST -lt 12.0.0 ; then
									die "Upgrading from $vINST isn't supported. Do it manual."
								else
									die "Do step by step upgrades to latest minor version in"\
										" each major version until ${eM}.${em}.x is reached."
								fi;;
		esac
	fi

	if [ "$MODUS" = "patch" ] || [ "$MODUS" = "minor" ] || [ "$MODUS" = "major" ]; then
		# ensure that any background migrations have been fully completed
		# see /opt/gitlab/gitlab/doc/update/README.md
		elog "Checking for background migrations ..."
		local bm gitlab_dir rails_cmd="'puts Gitlab::BackgroundMigration.remaining'"
		gitlab_dir="${BASE_DIR}/${PN}"
		local rubyVinst=$(ruby --version) # version string is "ruby N.N.N[pNNN] (...)"
		rubyVinst=${rubyVinst##ruby }     # remove leading "ruby "
		rubyVinst=${rubyVinst%%.? *}      # remove from ".N " on if there is no patch number
		rubyVinst=${rubyVinst%%.?p*}      # remove from ".NpNNN" on if there is a patch number
		rubyVinst=${rubyVinst/./}         # convert "N.N" to "NN"
		local rubyV=$(ls ${gitlab_dir}/vendor/bundle/ruby 2>/dev/null)
		rubyV=${rubyV%.?}
		rubyV=${rubyV/./}
		if [ "$rubyVinst" != "$rubyV" ]; then
			elog "Temporary switch to ruby${rubyV}"
			eselect ruby set ruby${rubyV}
		fi
		bm=$(su -l ${GIT_USER} -s /bin/sh -c "
			cd ${gitlab_dir}
			${BUNDLE} exec rails runner -e ${RAILS_ENV} ${rails_cmd}" \
				|| die "failed to check for background migrations")
		if [ "${bm}" != "0" ]; then
			elog "The new version may require a set of background migrations to be finished."
			elog "For more information see:"
			elog "https://gitlab.com/gitlab-org/gitlab-foss/-/blob/master/doc/update/README.md#checking-for-background-migrations-before-upgrading"
			eerror "Number of remaining background migrations is ${bm}"
			eerror "Try again later."
			die "Background migrations from previous upgrade not finished yet."
		else
			elog "OK: No remaining background migrations found."
		fi
		if [ "$rubyVinst" != "$rubyV" ]; then
			elog "Switching back to ruby${rubyVinst}"
			eselect ruby set ruby${rubyVinst}
		fi
	fi

	if [ "$MODUS" = "rebuild" ] || \
		 [ "$MODUS" = "patch" ] || [ "$MODUS" = "minor" ] || [ "$MODUS" = "major" ]; then
		elog  "Saving current configuration"
		cp -a ${CONF_DIR} ${T}/etc-config
	fi
	if use gitlab-config; then
		if [ ! -f /etc/env.d/42${PN} ]; then
			cat > /etc/env.d/99${PN}_temp <<-EOF
				CONFIG_PROTECT="${GITLAB_CONFIG}"
			EOF
			env-update
			# will be removed again in pkg_postinst()
		fi
	fi
	if [ -f /etc/env.d/42${PN} ]; then
		if ! use gitlab-config; then
			rm -f /etc/env.d/42${PN}
			env-update
		fi
	fi

	if [ "$(eselect postgresql show)" != "${PGSlot}" ]; then
		case "$(equery u postgresql | grep server)" in
			-server) ewarn "Switching to new PostgreSQL Slot ${PGSlot}"
					 eselect postgresql set ${PGSlot}
					 ewarn "Please switch to PosgreSQL ${PGSlot} on your DB server, too!";;
			+server) die "You have to upgrade PosgreSQL to Slot ${PGSlot} before " \
						 "the GitLab emerge!";;
		esac
	fi
}

src_unpack_gitaly() {
	EGIT_REPO_URI="https://gitlab.com/gitlab-org/gitaly.git"
	EGIT_COMMIT="v${PV}"
	EGIT_CHECKOUT_DIR="${WORKDIR}/gitlab-gitaly-${PV}"
	git-r3_src_unpack
}

src_unpack() {
	git-r3_src_unpack # default src_unpack() for the gitlab part

	src_unpack_gitaly
}

src_prepare_gitaly() {
	cd ${WORKDIR}/gitlab-gitaly-${PV}
	# Update paths for gitlab
	# Note: Order of -e expressions is important here
	local gitlab_urlenc=$(echo "${GITLAB}/" | sed -e "s|/|%2F|g")
	sed -i \
		-e "s|^bin_dir = \".*\"|bin_dir = \"${GITLAB_GITALY}/bin\"|" \
		-e "s|/home/git/gitaly|${GITLAB_GITALY}|g" \
		-e "s|/home/git/gitlab-shell|${GITLAB_SHELL}|g" \
		-e "s|/home/git/gitlab/log|${GITLAB}/log|g" \
		-e "s|http+unix://%2Fhome%2Fgit%2Fgitlab%2F|http+unix://${gitlab_urlenc}|" \
		-e "s|/home/git/gitlab/tmp/sockets/private|${GITLAB_SOCKETS}|g" \
		-e "s|/home/git/|${GIT_HOME}/|g" \
		-e "s|^# \[logging\]|\[logging\]|" \
		-e "s|^# level = .*|level = \"warn\"|" \
		-e "s|^# internal_socket_dir = |internal_socket_dir = |" \
		config.toml.example || die "failed to filter config.toml.example"
	if use gitaly_git ; then
		sed -i \
			-e "s|bin_path = .*|bin_path = \"/opt/gitlab/gitlab-gitaly/bin/git\"|" \
			config.toml.example || die "failed to filter config.toml.example"
	fi
	if use relative_url ; then
		sed -i \
			-e "s|^# relative_url_root = '/'|relative_url_root = '/gitlab'|" \
			config.toml.example || die "failed to filter config.toml.example"
	fi
}

src_prepare() {
	eapply -p0 "${FILESDIR}/${PN}-fix-gitlab-shell-check-name.patch"
	eapply -p0 "${FILESDIR}/${PN}-fix-checks-gentoo-r1.patch"
	eapply -p0 "${FILESDIR}/${PN}-fix-sendmail-param.patch"
	eapply -p0 "${FILESDIR}/${PN}-pod-markup.patch"

	eapply_user
	# Update paths for gitlab
	# Note: Order of -e expressions is important here.
	sed -i \
		-e "s|/sockets/private/|/sockets/|g" \
		-e "s|/home/git/gitlab-shell|${GITLAB_SHELL}|g" \
		-e "s|/home/git/gitlab/|${GITLAB}/|g" \
		-e "s|/home/git/gitaly|${GITLAB_GITALY}|g" \
		-e "s|/home/git|${GIT_HOME}|g" \
		config/gitlab.yml.example || die "failed to filter gitlab.yml.example"
	if ! use prometheus; then
		sed -i \
			-e '/ *sidekiq_exporter:/{n;s| *# *enabled: true|      enabled: false|}' \
			-e '/ *web_exporter:/{n;s| *# *enabled: true|      enabled: false|}' \
			-e '/ *prometheus:/{n;s| *# *enabled: true|    enabled: false|}' \
		config/gitlab.yml.example || die "failed to filter gitlab.yml.example"
	fi
	if use gitaly_git && \
		[ "$MODUS" != "new" ] && \
		has_version "www-apps/gitlab[gitaly_git]"
	then
		sed -i \
			-e "s|bin_path: /usr/bin/git|bin_path: /opt/gitlab/gitlab-gitaly/bin/git|" \
			config/gitlab.yml.example || die "failed to filter gitlab.yml.example"
	fi
	if use relative_url; then
		sed -i \
			-e "s|# relative_url_root|relative_url_root|g" \
			config/gitlab.yml.example || die "failed to filter gitlab.yml.example"
	fi
	# Update paths for puma
	sed -i \
		-e "s|/home/git/gitlab|${GITLAB}|g" \
		config/puma.rb.example \
		|| die "failed to modify puma.rb.example"
	if use relative_url; then
		echo "ENV['RAILS_RELATIVE_URL_ROOT'] = \"/gitlab\"" >> config/puma.rb.example \
			|| die "failed to modify puma.rb.example"
	fi

	cp config/resque.yml.example config/resque.yml
	cp config/cable.yml.example config/cable.yml
	cp config/puma.rb.example config/puma.rb

	# remove needless files
	rm .gitignore

	# Remove Geo database setting as in gitlab-foss Geo is not available and
	# GitLab will do a "Only main: and ci: database names are supported." check. 
	sed -i '/geo:/,/^$/d' config/database.yml.postgresql

	# "Compiling GetText PO files" wants to read these configs
	cp config/database.yml.postgresql config/database.yml
	cp config/gitlab.yml.example config/gitlab.yml

	# With version 13.7.1 we moved the "yarn install" call
	# from   pkg_config() - i.e. outside sandbox - to
	#       src_install() - i.e. inside  sandbox.
	# But yarn still wants to create/read /usr/local/share/.yarnrc
	addwrite /usr/local/share/

	# With version 17.1.0 external-diffs was removed but even without enabling
	# external_diffs in /etc/gitlab/gitlab.yml the existance of the default
	# storage_path: shared/external-diffs is checked by
	# /opt/gitlab/gitlab/lib/backup/targets/files.rb which is used by the
	# gitlab:backup:create task.
	mkdir shared/external-diffs

	if [ "$MODUS" = "new" ]; then
		# initialize our source for ${CONF_DIR}
		mkdir -p ${T}/etc-config
		cp config/database.yml.postgresql ${T}/etc-config/database.yml
		cp config/gitlab.yml.example ${T}/etc-config/gitlab.yml
		cp config/puma.rb.example ${T}/etc-config/puma.rb
		if use relative_url; then
			mkdir -p ${T}/etc-config/initializers
			cp config/initializers/relative_url.rb.sample \
				${T}/etc-config/initializers/relative_url.rb
		fi
	fi

	src_prepare_gitaly
}

find_files() {
	local f t="${1}"
	for f in $(find ${ED}${2} -type ${t}); do
		echo $f | sed "s|${ED}||"
	done
}

continue_or_skip() {
	local answer=""
	while true
	do
		read -r answer
		if   [[ $answer =~ ^(s|S)$ ]]; then answer="" && break
		elif [[ $answer =~ ^(c|C)$ ]]; then answer=1  && break
		else echo "Please type either \"c\" to continue or \"s\" to skip ... " >&2
		fi
	done
	echo $answer
}

src_compile() {
	# Nothing to do for gitlab
	einfo "Nothing to do for gitlab."

	# Compile workhorse
	cd workhorse
	einfo "Compiling source in $PWD ..."
	emake || die "Compiling workhorse failed"

	# Compile gitaly
	cd ${WORKDIR}/gitlab-gitaly-${PV}
	export RUBYOPT=--disable-did_you_mean
	einfo "Compiling source in $PWD ..."
	MAKEOPTS="${MAKEOPTS} -j1" emake WITH_BUNDLED_GIT=$(usex gitaly_git) \
		|| die "Compiling gitaly failed"
}

src_install_gitaly() {
	cd ${WORKDIR}/gitlab-gitaly-${PV}
	# cleanup candidates: a.out *.bin

	# Will install binaries to ${GITLAB_GITALY}/bin. Don't specify the "bin"!
	into "${GITLAB_GITALY}"
	dobin _build/bin/*

	if use gitaly_git ; then
		sed -i \
			-e "s|\${GIT_PREFIX}/bin/git|\${GIT_DEFAULT_PREFIX}/bin/git|" \
			-e '/${Q}env.*${GIT_BUILD_OPTIONS} install/{n;s|\${Q}touch \$@||}' \
			Makefile || die "failed to fix gitaly Makefile"
		emake git DESTDIR="${D}" GIT_PREFIX="${GITLAB_GITALY}"
	fi

	insinto "${CONF_DIR_GITALY}"
	newins "config.toml.example" "config.toml"
}

src_install() {
	## Prepare directories ##
	local uploads="${GITLAB}/public/uploads"
	diropts -m700
	dodir "${uploads}"

	diropts -m750
	keepdir "${LOG_DIR}"
	# is created at runtime by /usr/lib/tmpfiles.d/gitlab.conf
	#keepdir "${TMP_DIR}"

	diropts -m700
	keepdir "${GIT_REPOS}"
	dodir "${GITLAB}"

	## Install the config ##
	if use gitlab-config; then
		# env file to protect configs in $GITLAB/config
		cat > ${T}/42${PN} <<-EOF
			CONFIG_PROTECT="${GITLAB_CONFIG}"
		EOF
		doenvd ${T}/42${PN}
		insinto "${CONF_DIR}"
		cat > ${T}/README_GENTOO <<-EOF
			The gitlab-config USE flag is on.
			Configs are installed to ${GITLAB_CONFIG} only.
			See news 2021-02-22-etc-gitlab for details.
		EOF
		doins ${T}/README_GENTOO
	else
		insinto "${CONF_DIR}"
		local cfile cfiles
		# pkg_preinst() prepared config in ${T}/etc-config
		# we just want the folder structure; most files will be overwritten in for loop
		cp -a ${T}/etc-config ${T}/config
		for cfile in $(find ${T}/etc-config -type f); do
			cfile=${cfile/${T}\/etc-config\//}
			if [ -f config/${cfile} ]; then
				cp -f config/${cfile} ${T}/config/${cfile}
			fi
			cp -f ${T}/etc-config/${cfile} config/${cfile}
		done
		chown -R ${GIT_USER}:${GIT_GROUP} ${T}/config
		doins -r ${T}/config/.
		cat > ${T}/README_GENTOO <<-EOF
			The gitlab-config USE flag is off.
			Configs are installed to ${CONF_DIR} and automatically
			synced to ${GITLAB_CONFIG} on (re)start of GitLab.
			See news 2021-02-22-etc-gitlab for details.
		EOF
		doins ${T}/README_GENTOO
	fi

	## Install workhorse ##

	local exe all_exe=$(grep "EXE_ALL *:= *" workhorse/Makefile)
	into "${WORKHORSE}"
	for exe in ${all_exe#EXE_ALL *:= *}; do
		dobin workhorse/${exe}
	done
	# Remove workhorse/ dir because of the "doins -r ./" below!
	rm -rf workhorse

	## Install all others ##

	# keep upstream permissions in scripts/ folder
	cp -dR --preserve=mode scripts "${ED}/${GITLAB}"
	rm -rf scripts
	# now copy all the rest
	insinto "${GITLAB}"
	doins -r ./

	# make binaries executable
	exeinto "${GITLAB}/bin"
	doexe bin/*
	exeinto "${GITLAB}/qa/bin"
	doexe qa/bin/*

	## Install logrotate config ##

	dodir /etc/logrotate.d
	sed -e "s|@LOG_DIR@|${LOG_DIR}|g" \
		"${FILESDIR}"/gitlab.logrotate > "${ED}"/etc/logrotate.d/${PN} \
		|| die "failed to filter gitlab.logrotate"

	## Install gems via bundler ##

	cd "${ED}/${GITLAB}"

	local gitlab_dir="${BASE_DIR}/${PN}"

	local without="development test omnibus"
	local flag; for flag in ${WITHOUTflags}; do
		without+="$(use $flag || echo ' '$flag)"
	done
	${BUNDLE} config set --local deployment 'true'
	${BUNDLE} config set --local without "${without}"
	${BUNDLE} config set --local build.gpgme --use-system-libraries
	${BUNDLE} config set --local build.nokogiri --use-system-libraries
	${BUNDLE} config set --local build.ruby-magic --use-system-libraries

	#einfo "Current ruby version is \"$(ruby --version)\""

	einfo "Install Gems ..."
	# Cleanup args to extract only JOBS.
	# Because bundler does not know anything else.
	local jobs=1
	grep -Eo '(-j|--jobs)(=?|[[:space:]]*)[[:digit:]]+' <<< "${MAKEOPTS}" > /dev/null
	if [[ $? -eq 0 ]] ; then
		jobs=$(grep -Eo '(-j|--jobs)(=?|[[:space:]]*)[[:digit:]]+' <<< "${MAKEOPTS}" \
			| tail -n1 | grep -Eo '[[:digit:]]+')
	fi
	${BUNDLE} install --jobs=${jobs} || die "Install Gems failed"

	## Install GetText PO files, yarn, assets via bundler ##

	dodir ${GITLAB_SHELL}
	local vGS=$(best_version dev-vcs/gitlab-shell)
	vGS=$(echo ${vGS#dev-vcs/gitlab-shell-})
	echo ${vGS%-*} > ${ED}/${GITLAB_SHELL}/VERSION
	# Let lib/gitlab/shell.rb set the .gitlab_shell_secret symlink
	# inside the sandbox. The real symlink will be set in pkg_config().
	# Note: The gitlab-shell path "${ED}/${GITLAB_SHELL}" is set
	#       here to prevent lib/gitlab/shell.rb creating the
	#       gitlab_shell.secret symlink outside the sandbox.
	sed -i \
		-e "s|${GITLAB_SHELL}|${ED}${GITLAB_SHELL}|g" \
		config/gitlab.yml || die "failed to fake the gitlab-shell path"
	einfo "Updating node dependencies ..."
	/bin/sh -c "yarn install --${RAILS_ENV} --pure-lockfile" \
		|| die "failed to update node dependencies"
	einfo "Compiling assets ..."
	# On machines with few CPUs and/or without swap the webpack part of
	# gitlab:assets:compile may either stall or fail with the error
	# "SyntaxError: Bad control character in string literal in JSON".
	# Mitigation is to reduce the poolParallelJobs number from 200 to
	# a lower value (1 seems to work in any case). Supply the new value
	# through the WEBPACK_POLL_PARALLEL_JOBS environment variable.
	if [ "$WEBPACK_POLL_PARALLEL_JOBS" ]; then
		sed -i \
			-e "s|poolParallelJobs: .*,|poolParallelJobs: ${WEBPACK_POLL_PARALLEL_JOBS},|" \
			${ED}/${GITLAB_CONFIG}/webpack.config.js
	fi
	local rdoc_libs
	rdoc_libs="$(find /usr/lib64/ruby/ -regextype egrep -iregex '.*rdoc-.*/lib')"
	/bin/sh -c "
		export RUBYLIB=\"$(echo "$rdoc_libs" | head -c -1 | tr '\n' ':')\"
		${BUNDLE} exec rake gitlab:assets:compile \
		RAILS_ENV=${RAILS_ENV} NODE_ENV=${NODE_ENV}" \
		|| die "failed to compile assets"
	# Correct the gitlab-shell path we fooled lib/gitlab/shell.rb with.
	sed -i \
		-e "s|${ED}${GITLAB_SHELL}|${GITLAB_SHELL}|g" \
		${ED}/${GITLAB_CONFIG}/gitlab.yml || die "failed to change back gitlab-shell path"
	if [ "$MODUS" != "new" ]; then
		# Use the .gitlab_shell_secret file of the installed GitLab
		cp -f ${gitlab_dir}/.gitlab_shell_secret ${ED}${GITLAB}/.gitlab_shell_secret
	fi
	# Correct the link
	ln -sf ${GITLAB}/.gitlab_shell_secret ${ED}${GITLAB_SHELL}/.gitlab_shell_secret
	# Remove ${ED}/${GITLAB_SHELL}/VERSION to avoid file collision with dev-vcs/gitlab-shell
	rm -f ${ED}/${GITLAB_SHELL}/VERSION

	## Clean ##

	local rubyV=$(ls vendor/bundle/ruby)
	local ruby_vpath=vendor/bundle/ruby/${rubyV}

	# remove gems cache
	rm -Rf ${ruby_vpath}/cache

	# fix QA Security Notice: world writable file(s)
	elog "Fixing permissions of world writable files"
	local gemsdir="${ruby_vpath}/gems"
	local file gem wwfgems="devfile gitlab-dangerfiles gitlab-labkit gitlab-sdk \
							graphql-client licensee os rack-cors semver_dialects \
							tanuki_emoji toml-rb unleash"
	# If we are using wildcards, the shell fills them without prefixing ${ED}. Thus
	# we would target a file list in the real system instead of in the sandbox.
	for gem in ${wwfgems}; do
		for file in $(find_files "d,f" "${GITLAB}/${gemsdir}/${gem}-*"); do
			fperms go-w $file
		done
	done

	# remove tmp and log dir of the build process
	rm -Rf tmp log
	dosym "${TMP_DIR}" "${GITLAB}/tmp"
	dosym "${LOG_DIR}" "${GITLAB}/log"

	# systemd/openrc files
	use relative_url && relative_url="/gitlab" || relative_url=""

	if use systemd; then
		## Systemd files ##
		elog "Installing systemd unit files"
		local service services="gitaly sidekiq workhorse puma" unit unitfile
		use mail_room && services+=" mailroom"
		use gitlab-config || services+=" update-config"
		for service in ${services}; do
			unitfile="${FILESDIR}/${PN}-${service}.service.${vSYS}"
			unit="${PN}-${service}.service"
			sed -e "s|@BASE_DIR@|${BASE_DIR}|g" \
				-e "s|@GITLAB@|${GITLAB}|g" \
				-e "s|@GIT_USER@|${GIT_USER}|g" \
				-e "s|@CONF_DIR@|${CONF_DIR}|g" \
				-e "s|@GITLAB_CONFIG@|${GITLAB_CONFIG}|g" \
				-e "s|@TMP_DIR@|${TMP_DIR}|g" \
				-e "s|@WORKHORSE_BIN@|${WORKHORSE_BIN}|g" \
				-e "s|@RELATIVE_URL@|${relative_url}|g" \
				"${unitfile}" > "${T}/${unit}" || die "failed to configure: $unit"
			systemd_dounit "${T}/${unit}"
		done

		local optional_wants="" optional_requires="" optional_after=""
		use mail_room && optional_wants+="Wants=gitlab-mailroom.service"
		use gitlab-config || optional_requires+="Requires=gitlab-update-config.service"
		use gitlab-config || optional_after+="After=gitlab-update-config.service"
		sed -e "s|@OPTIONAL_REQUIRES@|${optional_requires}|" \
			-e "s|@OPTIONAL_AFTER@|${optional_after}|" \
			-e "s|@OPTIONAL_WANTS@|${optional_wants}|" \
			"${FILESDIR}/${PN}.target.${vSYS}" > "${T}/${PN}.target" \
			|| die "failed to configure: ${PN}.target"
		systemd_dounit "${T}/${PN}.target"
		cp "${FILESDIR}/${PN}.slice.${vSYS}" "${T}/${PN}.slice"
		systemd_dounit "${T}/${PN}.slice"
	else
		## OpenRC init scripts ##
		elog "Installing OpenRC init.d files"
		local service services="${PN} gitlab-gitaly" rc rcfile update_config puma_start
		local mailroom_vars='' mailroom_start='' mailroom_stop='' mailroom_status=''

		rcfile="${FILESDIR}/${PN}.init.${vORC}"
		# The sed command will replace the newline(s) with the string "\n".
		# Note: We use this below to replace a matching line of the rcfile by
		# the contents of another file whose newlines would break the outer sed.
		# Note: Continuation characters '\' in inserted files have to be escaped!
		puma_start="$(sed -z 's/\n/\\n/g' ${rcfile}.puma_start | head -c -2)"
		if use mail_room; then
			mailroom_vars="\n$(sed -z 's/\n/\\n/g' ${rcfile}.mailroom_vars)"
			mailroom_start="\n$(sed -z 's/\n/\\n/g' ${rcfile}.mailroom_start)"
			mailroom_stop="\n$(sed -z 's/\n/\\n/g' ${rcfile}.mailroom_stop)"
			mailroom_status="\n$(sed -z 's/\n/\\n/g' ${rcfile}.mailroom_status | head -c -2)"
		fi 
		if use gitlab-config; then
			update_config=""
		else
			update_config="su -l ${GIT_USER} -c \"rsync -aHAX ${CONF_DIR}/ ${GITLAB_CONFIG}/\""
		fi
		use relative_url && relative_url="/gitlab" || relative_url=""
		sed -e "s|@WEBSERVER_START@|${puma_start}|" \
			-e "s|@MAILROOM_VARS@|${mailroom_vars}|" \
			-e "s|@UPDATE_CONFIG@|${update_config}|" \
			-e "s|@MAILROOM_START@|${mailroom_start}|" \
			-e "s|@MAILROOM_STOP@|${mailroom_stop}|" \
			-e "s|@MAILROOM_STATUS@|${mailroom_status}|" \
			-e "s|@RELATIVE_URL@|${relative_url}|" \
			${rcfile} > ${T}/${PN}.init.${vORC} || die "failed to prepare ${rcfile}"
		cp "${FILESDIR}/gitlab-gitaly.init.${vORC}" ${T}/

		for service in ${services}; do
			rcfile="${T}/${service}.init.${vORC}"
			rc="${service}.init"
			sed -e "s|@RAILS_ENV@|${RAILS_ENV}|g" \
				-e "s|@GIT_USER@|${GIT_USER}|g" \
				-e "s|@GIT_GROUP@|${GIT_GROUP}|g" \
				-e "s|@GITLAB@|${GITLAB}|g" \
				-e "s|@LOG_DIR@|${GITLAB}/log|g" \
				-e "s|@WORKHORSE_BIN@|${WORKHORSE_BIN}|g" \
				-e "s|@GITLAB_GITALY@|${GITLAB_GITALY}|g" \
				-e "s|@GITALY_CONF@|${GITALY_CONF}|g" \
				"${rcfile}" > "${T}/${rc}" || die "failed to configure: ${rc}"
			newinitd "${T}/${rc}" "${service}"
		done
		newconfd "${FILESDIR}/gitlab.confd" gitlab
	fi

	newtmpfiles "${FILESDIR}/${PN}-tmpfiles.conf" ${PN}.conf

	# fix permissions

	fowners -R ${GIT_USER}:${GIT_GROUP} $GITLAB $CONF_DIR $LOG_DIR $GIT_REPOS
	[ -f "${ED}/${CONF_DIR}/secrets.yml" ]      && fperms 600 "${CONF_DIR}/secrets.yml"
	[ -f "${ED}/${GITLAB_CONFIG}/secrets.yml" ] && fperms 600 "${GITLAB_CONFIG}/secrets.yml"

	src_install_gitaly
}

pkg_postinst_gitaly() {
	if use gitaly_git; then
		local conf_dir="${CONF_DIR}"
		use gitlab-config && conf_dir="${GITLAB_CONFIG}"
		elog  ""
		ewarn "Note: With gitaly_git USE flag enabled the included git was installed to"
		ewarn "      ${GITLAB_GITALY}/bin/. In order to use it set the"
		ewarn "      [git] \"bin_path\" variable in \"${CONF_DIR_GITALY}/config.toml\" and in"
		ewarn "      \"${conf_dir}/gitlab.yml\" to \"${GITLAB_GITALY}/bin/git\""
	fi
}

pkg_postinst() {
	if [ -f /etc/env.d/99${PN}_temp ]; then
		rm -f /etc/env.d/99${PN}_temp
		env-update
	fi
	tmpfiles_process "${PN}.conf"
	if [ ! -e "${GIT_HOME}/.gitconfig" ]; then
		einfo "Setting git user/email in ${GIT_HOME}/.gitconfig,"
		einfo "feel free to modify this file as needed!"
		su -l ${GIT_USER} -s /bin/sh -c "
			git config --global user.email 'gitlab@localhost';
			git config --global user.name 'GitLab'" \
			|| die "failed to setup git user/email"
	fi
	einfo "Cleaning Git global settings for git user"
	su -l ${GIT_USER} -s /bin/sh -c "
		git config --global --remove-section core 2>/dev/null;
		git config --global --remove-section gc 2>/dev/null;
		git config --global --remove-section repack 2>/dev/null;
		git config --global --remove-section receive 2>/dev/null;"

	if [ "$MODUS" = "new" ]; then
		local conf_dir="${CONF_DIR}"
		use gitlab-config && conf_dir="${GITLAB_CONFIG}"
		elog
		elog "For this new installation, proceed with the following steps:"
		elog
		elog "  1. Create a database user for GitLab."
		elog "     On the database server (local ore remote) become user postgres:"
		elog "       su -l postgres"
		elog "     GitLab needs three PostgreSQL extensions: pg_trgm, btree_gist, plpgsql."
		elog "     To create the extensions if they are missing do:"
		elog "       psql -d template1 -c \"CREATE EXTENSION IF NOT EXISTS pg_trgm;\""
		elog "       psql -d template1 -c \"CREATE EXTENSION IF NOT EXISTS btree_gist;\""
		elog "       psql -d template1 -c \"CREATE EXTENSION IF NOT EXISTS plpgsql;\""
		elog "     Create the database user:"
		elog "       psql -c \"CREATE USER gitlab CREATEDB PASSWORD 'gitlab'\""
		elog "     Note: Change the PASSWORD to something more random ..."
		elog "     It may be needed to add configs for the new 'gitlab' user to the"
		elog "     pg_hba.conf and pg_ident.conf files of the database server."
		elog
		elog "     This ebuild assumes that the Postgres server runs on a"
		elog "     different machine. If it runs here add the dependency"
		if use systemd; then
			elog "       systemctl edit gitlab-puma.service"
			elog "     In the editor that opens, add the following and save the file:"
			elog "       [Unit]"
			elog "       Wants=postgresql.service"
			elog "       After=postgresql.service"
			elog
			elog "       systemctl edit gitlab-sidekiq.service"
			elog "     In the editor that opens, add the following and save the file:"
			elog "       [Unit]"
			elog "       Wants=postgresql.service"
			elog "       After=postgresql.service"
		else
			elog "       in /etc/init.d/gitlab (see the comment there)."
		fi
		elog
		elog "  2. Edit ${conf_dir}/database.yml in order to configure"
		elog "     database settings for \"${RAILS_ENV}\" environment."
		elog
		elog "  3. Edit ${conf_dir}/gitlab.yml"
		elog "     in order to configure the GitLab settings."
		elog
		if use gitaly_git; then
			elog "     With gitaly_git USE flag enabled the included git was installed to"
			elog "     ${GITLAB_GITALY}/bin/. In order to use it one has to set the"
			elog "     [git] \"bin_path\" variable in \"${CONF_DIR_GITALY}/config.toml\" and in"
			elog "     \"${conf_dir}/gitlab.yml\" to \"${GITLAB_GITALY}/bin/git\""
			elog
		fi
		if use gitlab-config; then
			elog "     With the \"gitlab-config\" USE flag on edit the"
			elog "     config files in the /opt/gitlab/gitlab/config/ folder!"
			elog
		else
			elog "     GitLab expects the parent directory of the config files to"
			elog "     be its base directory, so we have to sync changes made in"
			elog "     /etc/gitlab/ back to /opt/gitlab/gitlab/config/."
			elog "     This is done automatically on start/restart of gitlab"
			elog "     but sometimes it is neccessary to do it manually by"
			elog "       rsync -aHAX /etc/gitlab/ /opt/gitlab/gitlab/config/"
			elog
		fi
		elog "  4. Configure redis to use a UNIX socket and maybe adjust the maxmemory"
		elog "     settings as appropriate. Change /etc/redis/redis.conf to"
		elog "       unixsocket /var/run/redis/redis.sock"
		elog "       unixsocketperm 770"
		elog "       maxmemory 1024MB"
		elog "       maxmemory-policy volatile-lru"
		if use systemd; then
			elog "     Supervise Redis with systemd: Change /etc/redis/redis.conf to"
			elog "       daemonize no"
			elog "       supervised systemd"
			elog "       #pidfile /run/redis/redis.pid"
			elog "     Make matching changes to the systemd unit file:"
			elog "     Create /etc/systemd/system/redis.service.d/10fix_type.conf"
			elog "     and insert the following lines"
			elog "       [Service]"
			elog "       Type=notify"
			elog "       PIDFile="
			elog "     Then run"
			elog "       systemctl daemon-reload"
			elog "       systemctl restart redis.service"
		fi
		elog
		elog "  5. Gitaly must be running for the \"emerge --config\". Execute"
		if use systemd; then
			elog "     systemctl start gitlab-update-config.service"
			elog "     systemctl --job-mode=ignore-dependencies start ${PN}-gitaly.service"
		else
			elog "     rsync -aHAX /etc/gitlab/ /opt/gitlab/gitlab/config/"
			elog "     rc-service ${PN}-gitaly start"
		fi
		elog "     Make sure the Redis server is running and execute:"
		elog "         emerge --config \"=${CATEGORY}/${PF}\""
	elif [ "$MODUS" = "rebuild" ]; then
		elog "Update the config in /etc/gitlab and then run"
		if use systemd; then
			elog "     systemctl restart gitlab.target"
		else
			elog "     rc-service gitlab restart"
		fi
	elif [ "$MODUS" = "patch" ] || [ "$MODUS" = "minor" ] || [ "$MODUS" = "major" ]; then
		elog
		elog "Migrating database without post deployment migrations ..."
		su -l ${GIT_USER} -s /bin/sh -c "
			cd ${GITLAB}
			SKIP_POST_DEPLOYMENT_MIGRATIONS=true \
			${BUNDLE} exec rake db:migrate RAILS_ENV=${RAILS_ENV}" \
				|| die "failed to migrate database."
		elog
		elog "Update the config in /etc/gitlab and then run"
		if use systemd; then
			elog "     systemctl restart gitlab.target"
		else
			elog "     rc-service gitlab restart"
		fi
		elog
		elog "To complete the upgrade of this GitLab instance, run:"
		elog "    emerge --config \"=${CATEGORY}/${PF}\""
		elog
	fi
	pkg_postinst_gitaly
}

pkg_config_do_upgrade_migrate_data() {
	elog  "-- Migrating data --"
	elog "Found the latest gitlabhq instance at \"${BASE_DIR}/gitlabhq-${vINST}\"."

	elog  "1. This will move the public/uploads/ folder from"
	elog  "   \"${BASE_DIR}/gitlabhq-${vINST}\" to \"${GITLAB}\"."
	einfon "   (C)ontinue or (s)kip? "
	local migrate_uploads=$(continue_or_skip)
	if [[ $migrate_uploads ]]; then
		elog "   Moving the public/uploads/ folder ..."
		su -l ${GIT_USER} -s /bin/sh -c "
			rm -rf ${GITLAB}/public/uploads && \
			mv ${BASE_DIR}/gitlabhq-${vINST}/public/uploads ${GITLAB}/public/uploads" \
			|| die "failed to move the public/uploads/ folder."

		# Fix permissions
		find "${GITLAB}/public/uploads/" -type d -exec chmod 0700 {} \;
		elog "   ... finished."
	fi

	elog  "2. This will move the shared/ data folder from"
	elog  "   \"${BASE_DIR}/gitlabhq-${vINST}\" to \"${GITLAB}\"."
	einfon "   (C)ontinue or (s)kip? "
	local migrate_shared=$(continue_or_skip)
	if [[ $migrate_shared ]]; then
		elog "   Moving the shared/ data folder ..."
		su -l ${GIT_USER} -s /bin/sh -c "
			rm -rf ${GITLAB}/shared && \
			mv ${BASE_DIR}/gitlabhq-${vINST}/shared ${GITLAB}/shared" \
			|| die "failed to move the shared/ data folder."

		# Fix permissions
		find "${GITLAB}/shared/" -type d -exec chmod 0700 {} \;
		elog "   ... finished."
	fi
}

pkg_config_do_upgrade_migrate_database() {
	elog "Migrating database ..."
	su -l ${GIT_USER} -s /bin/sh -c "
		cd ${GITLAB}
		${BUNDLE} exec rake db:migrate RAILS_ENV=${RAILS_ENV}" \
			|| die "failed to migrate database."
}

pkg_config_do_upgrade_clear_redis_cache() {
	elog "Clean up cache ..."
	su -l ${GIT_USER} -s /bin/sh -c "
		cd ${GITLAB}
		${BUNDLE} exec rake cache:clear RAILS_ENV=${RAILS_ENV}" \
			|| die "failed to run cache:clear"
}

pkg_config_do_upgrade() {
	# do the upgrade
	pkg_config_do_upgrade_migrate_database

	pkg_config_do_upgrade_clear_redis_cache
}

pkg_config_initialize() {
	# check config and initialize database
	local conf_dir="${CONF_DIR}"
	use gitlab-config && conf_dir="${GITLAB_CONFIG}"

	## Check config files existence ##
	elog "Checking configuration files ..."
	if [ ! -r "${conf_dir}/database.yml" ]; then
		eerror "Copy \"${GITLAB_CONFIG}/database.yml.postgresql\" to \"${conf_dir}/database.yml\""
		eerror "and edit this file in order to configure the database settings for"
		eerror "\"${RAILS_ENV}\" environment."
		die
	fi
	if [ ! -r "${conf_dir}/gitlab.yml" ]; then
		eerror "Copy \"${GITLAB_CONFIG}/gitlab.yml.example\" to \"${conf_dir}/gitlab.yml\""
		eerror "and edit this file in order to configure the GitLab settings"
		eerror "for \"${RAILS_ENV}\" environment."
		die
	fi

	local pw email
	einfon "Set the Administrator/root password: "
	read -sr pw
	einfo
	einfon "Set the Administrator/root email: "
	read -r email
	elog "Initializing database ..."
	su -l ${GIT_USER} -s /bin/sh -c "
		cd ${GITLAB}
		${BUNDLE} exec rake gitlab:setup RAILS_ENV=${RAILS_ENV} \
			GITLAB_ROOT_PASSWORD=\"${pw}\" GITLAB_ROOT_EMAIL=\"${email}\"" \
			|| die "failed to run rake gitlab:setup"
}

pkg_config() {
#	## (Re-)Link gitlab_shell_secret into gitlab-shell
#	if [ -L "${GITLAB_SHELL}/.gitlab_shell_secret" ]; then
#		rm "${GITLAB_SHELL}/.gitlab_shell_secret"
#	fi
#	ln -s "${GITLAB}/.gitlab_shell_secret" "${GITLAB_SHELL}/.gitlab_shell_secret"

	if [ "$MODUS" = "new" ]; then
		pkg_config_initialize
	elif [ "$MODUS" = "rebuild" ]; then
		elog "No need to run \"emerge --config\" after a rebuild."
	elif [ "$MODUS" = "patch" ] || [ "$MODUS" = "minor" ] || [ "$MODUS" = "major" ]; then
		pkg_config_do_upgrade
		local ret=$?
		if [ $ret -ne 0 ]; then return $ret; fi
	fi

	if [ "$MODUS" = "new" ]; then
		elog
		elog "Now start ${PN} with"
		if use systemd; then
			elog "\$ systemctl start ${PN}.target"
		else
			elog "\$ rc-service ${PN} start"
		fi
	fi

	elog
	elog "To check the application status run this:"
	elog "\$ cd ${GITLAB}"
	elog "\$ sudo -i -u ${GIT_USER} ${BUNDLE} exec rake gitlab:check RAILS_ENV=${RAILS_ENV}"
	elog
	elog "GitLab is prepared now."
	if [ "$MODUS" = "patch" ] || [ "$MODUS" = "minor" ] || [ "$MODUS" = "major" ]; then
		elog "Ensure beeing still up-to-date with the latest NGINX configuration changes:"
		elog "\$ cd /opt/gitlab/gitlab"
		elog "\$ git -P diff v${vINST}:lib/support/nginx/ v${PV}:lib/support/nginx/"
	elif [ "$MODUS" = "new" ]; then
		elog "To configure the nginx site have a look at the examples configurations"
		elog "in the ${GITLAB}/lib/support/nginx/ folder."
		if use relative_url; then
			elog "For a relative URL installation several modifications must be made to nginx"
			elog "\t Move everything in the top-level 'server' block to top-level nginx.conf"
			elog "\t Remove the top-level 'server' block"
			elog "\t Add a 'location /gitlab at the top where the server block was"
			elog "\t Change 'location /' to 'location /gitlab/'"
			elog "\t Symlink <htdocs>/gitlab to ${GITLAB}/public"
			elog "In order for the Background Jobs page to work, add"
			elog "\t 'location ~ ^/gitlab/admin/sidekiq/* {"
			elog "\t proxy_pass http://gitlab-workhorse;"
			elog "\t }"
			elog "under the main gitlab location block"
		fi
	fi
}