Files
telegram-groupfactory-bot/scripts/mirror_repo.sh
Corrado Mulas 3e90eecedf .
2026-06-19 18:10:26 +02:00

295 lines
8.4 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
DEFAULT_SOURCE_URL="https://git.mulas.me/corrado/telegram-groupfactory-bot.git"
DEFAULT_DESTINATION_URL="https://git.devops.lnxc.it/dev/IMS/telegram-groupfactory-bot.git"
SOURCE_URL="${SOURCE_URL:-$DEFAULT_SOURCE_URL}"
DESTINATION_URL="${DESTINATION_URL:-$DEFAULT_DESTINATION_URL}"
SOURCE_BRANCH="${SOURCE_BRANCH:-master}"
TARGET_BRANCH="${TARGET_BRANCH:-master}"
MR_BRANCH="${MR_BRANCH:-feat}"
MR_TITLE="${MR_TITLE:-Mirror telegram-groupfactory-bot}"
MR_DESCRIPTION="${MR_DESCRIPTION:-Mirror from ${SOURCE_URL} into ${DESTINATION_URL}.}"
COMMIT_MESSAGE="${COMMIT_MESSAGE:-}"
WORKDIR=""
KEEP_WORKDIR=0
ASSUME_YES=0
FORCE_WITH_LEASE=1
SRC_ONLY="${SRC_ONLY:-0}"
usage() {
cat <<'EOF'
Mirror telegram-groupfactory-bot through a GitLab merge request.
This script:
1. Clones DESTINATION_URL / TARGET_BRANCH into a temporary workdir.
2. Fetches SOURCE_URL / SOURCE_BRANCH.
3. Creates MR_BRANCH from TARGET_BRANCH.
4. Replaces the MR_BRANCH tree with the source branch tree.
5. Pushes MR_BRANCH and asks GitLab to create a merge request.
It does not push directly to the protected target branch.
Because the MR branch starts from the destination target branch, it avoids
"source branch is behind target" merge conflicts caused by protected master.
Use --src-only to mirror the source repository while preserving destination
deployment/example folders: argo/, argocd/, examples/, and k8s/.
Usage:
scripts/mirror_repo.sh [options]
Options:
-s, --source URL Source repository URL.
-d, --destination URL Destination repository URL.
--source-branch NAME Source branch to copy. Default: master.
--target-branch NAME MR target branch. Default: master.
--mr-branch NAME Destination feature branch. Default: feat.
--title TEXT Merge request title.
--description TEXT Merge request description.
--commit-message TEXT Commit message for the source tree update.
--src-only Mirror everything except argo/, argocd/, examples/, and k8s/.
--no-force-with-lease Use plain --force instead of --force-with-lease.
-w, --workdir DIR Directory used for the temporary clone.
-k, --keep-workdir Keep the temporary clone after finishing.
-y, --yes Skip the confirmation prompt.
-h, --help Show this help.
Environment overrides:
SOURCE_URL, DESTINATION_URL, SOURCE_BRANCH, TARGET_BRANCH, MR_BRANCH,
MR_TITLE, MR_DESCRIPTION, COMMIT_MESSAGE, SRC_ONLY
Defaults:
source: https://git.mulas.me/corrado/telegram-groupfactory-bot.git
destination: https://git.devops.lnxc.it/dev/IMS/telegram-groupfactory-bot.git
source ref: master
MR branch: feat
MR target: master
EOF
}
while [[ $# -gt 0 ]]; do
case "$1" in
-s|--source)
SOURCE_URL="$2"
shift 2
;;
-d|--destination)
DESTINATION_URL="$2"
shift 2
;;
--source-branch)
SOURCE_BRANCH="$2"
shift 2
;;
--target-branch)
TARGET_BRANCH="$2"
shift 2
;;
--mr-branch)
MR_BRANCH="$2"
shift 2
;;
--title)
MR_TITLE="$2"
shift 2
;;
--description)
MR_DESCRIPTION="$2"
shift 2
;;
--commit-message)
COMMIT_MESSAGE="$2"
shift 2
;;
--src-only)
SRC_ONLY=1
shift
;;
--no-force-with-lease)
FORCE_WITH_LEASE=0
shift
;;
-w|--workdir)
WORKDIR="$2"
shift 2
;;
-k|--keep-workdir)
KEEP_WORKDIR=1
shift
;;
-y|--yes)
ASSUME_YES=1
shift
;;
-h|--help)
usage
exit 0
;;
*)
echo "Unknown option: $1" >&2
usage >&2
exit 2
;;
esac
done
case "$SRC_ONLY" in
1|true|TRUE|yes|YES)
SRC_ONLY=1
;;
0|false|FALSE|no|NO|"")
SRC_ONLY=0
;;
*)
echo "SRC_ONLY must be 1/0, true/false, or yes/no." >&2
exit 2
;;
esac
if [[ -z "$COMMIT_MESSAGE" ]]; then
if [[ "$SRC_ONLY" -eq 1 ]]; then
COMMIT_MESSAGE="Mirror source tree excluding deployment folders"
else
COMMIT_MESSAGE="Mirror source repository tree"
fi
fi
if [[ -z "$SOURCE_URL" || -z "$DESTINATION_URL" ]]; then
echo "Both source and destination URLs are required." >&2
exit 2
fi
if [[ -z "$SOURCE_BRANCH" || -z "$TARGET_BRANCH" || -z "$MR_BRANCH" ]]; then
echo "Source branch, target branch, and MR branch are required." >&2
exit 2
fi
if ! command -v git >/dev/null 2>&1; then
echo "git is required but was not found in PATH." >&2
exit 1
fi
if [[ -z "$WORKDIR" ]]; then
WORKDIR="$(mktemp -d "${TMPDIR:-/tmp}/telegram-groupfactory-bot-mr.XXXXXX")"
else
mkdir -p "$WORKDIR"
fi
cleanup() {
if [[ "$KEEP_WORKDIR" -eq 0 && -n "$WORKDIR" && -d "$WORKDIR" ]]; then
rm -rf "$WORKDIR"
fi
}
trap cleanup EXIT
echo "Source: $SOURCE_URL"
echo "Destination: $DESTINATION_URL"
echo "Source ref: $SOURCE_BRANCH"
echo "MR branch: $MR_BRANCH"
echo "MR target: $TARGET_BRANCH"
if [[ "$SRC_ONLY" -eq 1 ]]; then
echo "Mirror mode: full tree excluding argo/, argocd/, examples/, k8s/"
else
echo "Mirror mode: full tree"
fi
echo "Workdir: $WORKDIR"
echo
if [[ "$ASSUME_YES" -eq 0 ]]; then
if [[ "$SRC_ONLY" -eq 1 ]]; then
prompt="Create ${MR_BRANCH} from destination/${TARGET_BRANCH}, apply source/${SOURCE_BRANCH} excluding argo/, argocd/, examples/, k8s/, and create MR? [y/N] "
else
prompt="Create ${MR_BRANCH} from destination/${TARGET_BRANCH}, apply source/${SOURCE_BRANCH}, and create MR? [y/N] "
fi
read -r -p "$prompt" reply
case "$reply" in
y|Y|yes|YES)
;;
*)
echo "Aborted."
exit 1
;;
esac
fi
echo "Cloning destination target branch..."
git clone --single-branch --branch "$TARGET_BRANCH" "$DESTINATION_URL" "$WORKDIR/repo"
cd "$WORKDIR/repo"
git remote add source "$SOURCE_URL"
echo "Fetching source branch..."
git fetch source "refs/heads/${SOURCE_BRANCH}:refs/remotes/source/${SOURCE_BRANCH}"
echo "Fetching destination MR branch for lease..."
if git fetch origin "refs/heads/${MR_BRANCH}:refs/remotes/origin/${MR_BRANCH}"; then
MR_REMOTE_COMMIT="$(git rev-parse "refs/remotes/origin/${MR_BRANCH}")"
else
MR_REMOTE_COMMIT=""
fi
SOURCE_REF="refs/remotes/source/${SOURCE_BRANCH}"
TARGET_REF="refs/remotes/origin/${TARGET_BRANCH}"
SOURCE_COMMIT="$(git rev-parse "$SOURCE_REF")"
TARGET_COMMIT="$(git rev-parse "$TARGET_REF")"
echo "Creating MR branch from destination target..."
git checkout -B "$MR_BRANCH" "$TARGET_REF"
if [[ "$SRC_ONLY" -eq 1 ]]; then
echo "Applying source tree from ${SOURCE_COMMIT}, preserving deployment/example folders from destination..."
git read-tree --reset -u "$SOURCE_REF"
preserved_paths=(argo argocd examples k8s)
for preserved_path in "${preserved_paths[@]}"; do
if git cat-file -e "${TARGET_REF}:${preserved_path}" 2>/dev/null; then
echo "Preserving ${preserved_path}/ from destination ${TARGET_BRANCH}..."
git checkout "$TARGET_REF" -- "$preserved_path"
else
echo "Removing ${preserved_path}/ because it is not present in destination ${TARGET_BRANCH}..."
git rm -r --ignore-unmatch -- "$preserved_path"
fi
done
else
echo "Applying source tree from ${SOURCE_COMMIT}..."
git read-tree --reset -u "$SOURCE_REF"
fi
if git diff --cached --quiet; then
if [[ "$SRC_ONLY" -eq 1 ]]; then
echo "Destination ${TARGET_BRANCH} already has the same source tree as ${SOURCE_BRANCH}, excluding preserved folders."
else
echo "Destination ${TARGET_BRANCH} already has the same tree as source ${SOURCE_BRANCH}."
fi
echo "Nothing to push."
exit 0
fi
git commit \
-m "$COMMIT_MESSAGE" \
-m "Source: $SOURCE_URL" \
-m "Source branch: $SOURCE_BRANCH" \
-m "Source commit: $SOURCE_COMMIT" \
-m "Mirror mode: $([[ "$SRC_ONLY" -eq 1 ]] && echo "full tree excluding argo/, argocd/, examples/, k8s/" || echo "full tree")" \
-m "Destination base: $DESTINATION_URL" \
-m "Destination target: $TARGET_BRANCH" \
-m "Destination base commit: $TARGET_COMMIT"
push_args=()
if [[ "$FORCE_WITH_LEASE" -eq 1 ]]; then
push_args+=(--force-with-lease="refs/heads/${MR_BRANCH}:${MR_REMOTE_COMMIT}")
else
push_args+=(--force)
fi
echo "Pushing MR branch and asking GitLab to create the merge request..."
git push "${push_args[@]}" \
-o merge_request.create \
-o merge_request.target="$TARGET_BRANCH" \
-o merge_request.title="$MR_TITLE" \
-o merge_request.description="$MR_DESCRIPTION" \
origin "HEAD:refs/heads/${MR_BRANCH}"
echo "Merge request push complete."