Skip to content

Commit 02cdabe

Browse files
committed
public - new file: .rsync-ignore
modified: sync-public-branch.sh
1 parent 0745fa5 commit 02cdabe

2 files changed

Lines changed: 169 additions & 98 deletions

File tree

.rsync-ignore

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
private/***
2+
vault/***
3+
archive/***
4+
save/***
5+
vault.yml
6+
*vault.yml
7+
secrets.yml
8+
*secrets.yml
9+
.vault_pass
10+
#*vault*
11+
vault.yml
12+
integration_config.yml
13+
integration_config.vault.yml
14+
*.log

sync-public-branch.sh

Lines changed: 155 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#!/usr/bin/env bash
22

3-
VERSION="2025.7.1"
3+
VERSION="2025.9.5"
44

55
GIT_DEFAULT_BRANCH=main
66
GIT_PUBLIC_BRANCH=public
@@ -17,61 +17,35 @@ SCRIPT_NAME="$(basename "$0")"
1717
CONFIRM=0
1818

1919
## PURPOSE RELATED VARS
20-
#PROJECT_DIR=$( git rev-parse --show-toplevel )
21-
PROJECT_DIR="$(cd "${SCRIPT_DIR}" && git rev-parse --show-toplevel)"
20+
#REPO_DIR=$( git rev-parse --show-toplevel )
21+
REPO_DIR="$(cd "${SCRIPT_DIR}" && git rev-parse --show-toplevel)"
2222

2323
#PUBLIC_GITIGNORE=files/git/pub.gitignore
2424

25-
## ref: https://stackoverflow.com/questions/53839253/how-can-i-convert-an-array-into-a-comma-separated-string
26-
declare -a PRIVATE_CONTENT_ARRAY
27-
PRIVATE_CONTENT_ARRAY+=('**/private/***')
28-
PRIVATE_CONTENT_ARRAY+=('**/vault/***')
29-
PRIVATE_CONTENT_ARRAY+=('**/archive/***')
30-
PRIVATE_CONTENT_ARRAY+=('**/save/***')
31-
PRIVATE_CONTENT_ARRAY+=('**/vault.yml')
32-
PRIVATE_CONTENT_ARRAY+=('**/*vault.yml')
33-
PRIVATE_CONTENT_ARRAY+=('**/secrets.yml')
34-
PRIVATE_CONTENT_ARRAY+=('**/*secrets.yml')
35-
PRIVATE_CONTENT_ARRAY+=('.vault_pass')
36-
#PRIVATE_CONTENT_ARRAY+=('***/*vault*')
37-
PRIVATE_CONTENT_ARRAY+=('***/vault.yml')
38-
PRIVATE_CONTENT_ARRAY+=('**/integration_config.yml')
39-
PRIVATE_CONTENT_ARRAY+=('**/integration_config.vault.yml')
40-
PRIVATE_CONTENT_ARRAY+=('*.log')
41-
42-
printf -v PRIVATE_CONTENT_LIST '%s,' "${PRIVATE_CONTENT_ARRAY[@]}"
43-
PRIVATE_CONTENT_LIST="${PRIVATE_CONTENT_LIST%,}"
44-
4525
## ref: https://stackoverflow.com/questions/53839253/how-can-i-convert-an-array-into-a-comma-separated-string
4626
declare -a EXCLUDES_ARRAY
4727
EXCLUDES_ARRAY+=('.git')
4828
EXCLUDES_ARRAY+=('.gitmodule')
49-
EXCLUDES_ARRAY+=('.idea')
50-
EXCLUDES_ARRAY+=('.vscode')
51-
EXCLUDES_ARRAY+=('venv')
52-
EXCLUDES_ARRAY+=('**/.DS_Store')
53-
EXCLUDES_ARRAY+=('*.log')
5429

55-
printf -v EXCLUDES_LIST '%s,' "${EXCLUDES_ARRAY[@]}"
56-
EXCLUDES_LIST="${EXCLUDES_LIST%,}"
30+
# Read .gitignore and populate excludes array
31+
while read -r line; do
32+
line=$(echo "$line" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
33+
[[ -z "$line" || "$line" =~ ^#.* ]] && continue
34+
EXCLUDES_ARRAY+=("$line")
35+
done < "${REPO_DIR}/.gitignore"
5736

58-
## https://serverfault.com/questions/219013/showing-total-progress-in-rsync-is-it-possible
59-
## https://www.studytonight.com/linux-guide/how-to-exclude-files-and-directory-using-rsync
60-
RSYNC_OPTS_GIT_MIRROR=()
61-
RSYNC_OPTS_GIT_MIRROR+=("-dar")
62-
RSYNC_OPTS_GIT_MIRROR+=("--links")
63-
RSYNC_OPTS_GIT_MIRROR+=("--delete-excluded")
64-
RSYNC_OPTS_GIT_MIRROR+=("--exclude={${EXCLUDES_LIST},${PRIVATE_CONTENT_LIST}}")
37+
# Read .rsync-ignore and populate excludes array
38+
while read -r line; do
39+
line=$(echo "$line" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
40+
[[ -z "$line" || "$line" =~ ^#.* ]] && continue
41+
EXCLUDES_ARRAY+=("$line")
42+
done < "${REPO_DIR}/.rsync-ignore"
6543

66-
RSYNC_OPTS_GIT_UPDATE=()
67-
RSYNC_OPTS_GIT_UPDATE+=("-ari")
68-
RSYNC_OPTS_GIT_UPDATE+=("--links")
44+
printf -v EXCLUDES_LIST '%s,' "${EXCLUDES_ARRAY[@]}"
45+
EXCLUDES_LIST="${EXCLUDES_LIST%,}"
6946

70-
## https://www.pixelstech.net/article/1577768087-Create-temp-file-in-Bash-using-mktemp-and-trap
7147
TEMP_DIR=$(mktemp -d -p ~)
7248

73-
trap 'rm -fr "$TEMP_DIR"' EXIT
74-
7549
#### LOGGING RELATED
7650
LOG_ERROR=0
7751
LOG_WARN=1
@@ -119,6 +93,8 @@ reverse_array LOGLEVEL_TO_STR LOGLEVELSTR_TO_LEVEL
11993
#LOG_LEVEL=${LOG_DEBUG}
12094
LOG_LEVEL=${LOG_INFO}
12195

96+
# --- Logging Functions ---
97+
12298
function log_error() {
12399
if [ "$LOG_LEVEL" -ge "$LOG_ERROR" ]; then
124100
log_message "${LOG_ERROR}" "${1}"
@@ -264,6 +240,8 @@ function set_log_level() {
264240

265241
}
266242

243+
# --- Helper Functions ---
244+
267245
function execute() {
268246
log_info "${*}"
269247
if ! "$@"
@@ -314,13 +292,13 @@ function git_commit_push() {
314292
local REMOTE_AND_BRANCH
315293
LOCAL_BRANCH="$(git symbolic-ref --short HEAD)" && \
316294
REMOTE_AND_BRANCH=$(git rev-parse --abbrev-ref "${LOCAL_BRANCH}@{upstream}") && \
317-
IFS=/ read -r REMOTE REMOTE_BRANCH <<< "${REMOTE_AND_BRANCH}" && \
295+
IFS=/ read -r REMOTE_NAME REMOTE_BRANCH <<< "${REMOTE_AND_BRANCH}" && \
318296
echo "Staging changes:" && \
319297
(git add -A || true) && \
320298
echo "Committing changes:" && \
321-
(git commit -am "group updates to public branch" || true) && \
322-
echo "Pushing branch '${LOCAL_BRANCH}' to remote '${REMOTE}' branch '${REMOTE_BRANCH}':" && \
323-
(git push -f -u "${REMOTE}" "${LOCAL_BRANCH}:${REMOTE_BRANCH}" || true)
299+
(git commit -am "Sync: Automated sync from main to public branch." || true) && \
300+
echo "Pushing branch '${LOCAL_BRANCH}' to remote '${REMOTE_NAME}' branch '${REMOTE_BRANCH}':" && \
301+
(git push -f -u "${REMOTE_NAME}" "${LOCAL_BRANCH}:${REMOTE_BRANCH}" || true)
324302
}
325303

326304
function search_repo_keywords () {
@@ -365,7 +343,7 @@ function search_repo_keywords () {
365343
## ref: https://stackoverflow.com/questions/6565471/how-can-i-exclude-directories-from-grep-r#8692318
366344
## ref: https://unix.stackexchange.com/questions/342008/find-and-echo-file-names-only-with-pattern-found
367345
## ref: https://www.baeldung.com/linux/find-exclude-paths
368-
local FIND_CMD="find ${PROJECT_DIR}/ \( ${FIND_EXCLUDE_DIRS} \) -o -exec ${GREP_COMMAND} {} 2>/dev/null +"
346+
local FIND_CMD="find ${REPO_DIR}/ \( ${FIND_EXCLUDE_DIRS} \) -o -exec ${GREP_COMMAND} {} 2>/dev/null +"
369347
log_info "${FIND_CMD}"
370348

371349
local EXCEPTION_COUNT
@@ -380,68 +358,139 @@ function search_repo_keywords () {
380358
return "${EXCEPTION_COUNT}"
381359
}
382360

383-
function sync_public_branch() {
384-
local RSYNC_MIRROR_OPTS="${RSYNC_OPTS_GIT_MIRROR[*]}"
385-
local RSYNC_UPDATE_OPTS="${RSYNC_OPTS_GIT_UPDATE[*]}"
361+
# --- Core Functions ---
386362

387-
log_debug "RSYNC_MIRROR_OPTS=${RSYNC_MIRROR_OPTS}"
388-
log_debug "RSYNC_UPDATE_OPTS=${RSYNC_UPDATE_OPTS}"
363+
# Function to clean up the temporary directory
364+
cleanup() {
365+
if [[ -d "${TEMP_DIR}" ]]; then
366+
log_info "Cleaning up temporary directory: ${TEMP_DIR}"
367+
rm -rf "${TEMP_DIR}"
368+
fi
369+
}
389370

390-
git fetch --all
391-
git checkout ${GIT_DEFAULT_BRANCH}
371+
# Function to handle errors
372+
on_error() {
373+
local exit_code="$?"
374+
if [[ "$exit_code" -ne 0 ]]; then
375+
log_error "Script failed with error code $exit_code."
376+
cleanup
377+
fi
378+
}
392379

393-
#RSYNC_OPTS=${RSYNC_OPTS_GIT_MIRROR[@]}
394-
log_debug "copy project to temporary dir $TEMP_DIR"
395-
local RSYNC_CMD
396-
RSYNC_CMD="rsync ${RSYNC_MIRROR_OPTS} ${PROJECT_DIR}/ ${TEMP_DIR}/"
397-
execute_eval_command "${RSYNC_CMD}"
380+
# Function to copy the project to a temporary directory
381+
copy_project_to_temp_dir() {
382+
local REPO_DIR="$1"
383+
TEMP_DIR=$(mktemp -d /tmp/sync-repo.XXXXXXXXXX)
384+
log_info "Copying project to temporary directory: ${TEMP_DIR}"
385+
386+
#local RSYNC_CMD="rsync -av --exclude={'${EXCLUDES_LIST}'} --exclude='.git/' '${REPO_DIR}/' '${TEMP_DIR}/'"
387+
local RSYNC_CMD="rsync -dar --links --exclude={${EXCLUDES_LIST}} '${REPO_DIR}/' '${TEMP_DIR}/'"
388+
389+
if [[ "${DRY_RUN}" == "true" ]]; then
390+
log_info "Dry run: Would have executed: ${RSYNC_CMD}"
391+
# Since it's a dry run, we don't actually execute the rsync
392+
else
393+
log_debug "Executing: ${RSYNC_CMD}"
394+
execute_eval_command "${RSYNC_CMD}"
395+
fi
396+
}
398397

399-
log_info "Checkout public branch"
400-
git checkout ${GIT_PUBLIC_BRANCH}
398+
# Function to update the public branch
399+
sync_public_branch() {
400+
local REPO_DIR="$1"
401+
local PUBLIC_BRANCH="$2"
401402

402-
if [ $GIT_REMOVE_CACHED_FILES -eq 1 ]; then
403-
log_info "Removing files cached in git"
404-
git rm -r --cached .
405-
fi
403+
log_info "Stashing any local changes on the current branch."
404+
if ! git -C "${REPO_DIR}" stash push -u -m "Stash before sync to ${PUBLIC_BRANCH}"; then
405+
log_error "Failed to stash local changes."
406+
fi
406407

407-
log_info "Copy ${TEMP_DIR} to project dir $PROJECT_DIR"
408-
RSYNC_CMD="rsync ${RSYNC_UPDATE_OPTS} ${TEMP_DIR}/ ${PROJECT_DIR}/"
409-
execute_eval_command "${RSYNC_CMD}"
408+
git fetch --all
410409

411-
printf -v TO_REMOVE '%s ' "${PRIVATE_CONTENT_ARRAY[@]}"
412-
TO_REMOVE="${TO_REMOVE% }"
413-
log_info "TO_REMOVE=${TO_REMOVE}"
414-
CLEANUP_CMD="rm -fr ${TO_REMOVE}"
415-
execute_eval_command "${CLEANUP_CMD}"
410+
log_info "Checking out public branch: ${PUBLIC_BRANCH}"
411+
if ! git -C "${REPO_DIR}" checkout "${PUBLIC_BRANCH}"; then
412+
log_error "Failed to checkout branch: ${PUBLIC_BRANCH}"
413+
fi
416414

417-
if [ -n "${PUBLIC_GITIGNORE}" ]; then
418-
if [ -e $PUBLIC_GITIGNORE ]; then
419-
log_info "Update public files:"
420-
cp -p $PUBLIC_GITIGNORE .gitignore
415+
log_info "Pulling latest changes from the public branch."
416+
# local REMOTE_BRANCH=$(git -C "${REPO_DIR}" rev-parse --abbrev-ref --symbolic-full-name @{u} 2>/dev/null)
417+
local REMOTE_AND_BRANCH=$(git rev-parse --abbrev-ref "${PUBLIC_BRANCH}@{upstream}") && \
418+
IFS=/ read -r REMOTE_NAME REMOTE_BRANCH <<< "${REMOTE_AND_BRANCH}" && \
419+
420+
if [[ -z "${REMOTE_BRANCH}" ]]; then
421+
log_warn "No upstream branch found for ${PUBLIC_BRANCH}. Skipping pull."
422+
else
423+
log_info "Pulling from REMOTE_BRANCH remote: ${REMOTE_NAME}"
424+
if ! git -C "${REPO_DIR}" pull "${REMOTE_NAME}" "${REMOTE_BRANCH}:${PUBLIC_BRANCH}"; then
425+
log_warn "Failed to pull from ${REMOTE_NAME}/${REMOTE_BRANCH}:${PUBLIC_BRANCH}. Continuing anyway."
426+
fi
427+
fi
428+
429+
log_info "Syncing temporary directory to public branch."
430+
431+
if [ "${GIT_REMOVE_CACHED_FILES}" -eq 1 ]; then
432+
log_info "Removing files cached in git"
433+
git rm -r --cached .
421434
fi
422-
fi
423435

424-
log_info "Show changes before push:"
425-
git status
436+
log_info "Copy ${TEMP_DIR} to project dir ${REPO_DIR}"
437+
# Added --delete and --exclude '.git/'
438+
local RSYNC_CMD="rsync -dar --links --delete --exclude '.git/' '${TEMP_DIR}/' '${REPO_DIR}/'"
439+
# local RSYNC_CMD="rsync -av --delete --exclude '.git/' '${TEMP_DIR}/' '${REPO_DIR}/'"
440+
# local RSYNC_CMD="rsync ${RSYNC_UPDATE_OPTS} ${TEMP_DIR}/ ${REPO_DIR}/"
441+
442+
if [[ "${DRY_RUN}" == "true" ]]; then
443+
log_info "Dry run: Would have executed: ${RSYNC_CMD}"
444+
else
445+
log_debug "Executing: ${RSYNC_CMD}"
446+
if ! eval "${RSYNC_CMD}"; then
447+
log_error "rsync failed during sync to public branch."
448+
fi
449+
fi
426450

427-
## https://stackoverflow.com/questions/5989592/git-cannot-checkout-branch-error-pathspec-did-not-match-any-files-kn
428-
## git diff --name-only ${GIT_PUBLIC_BRANCH} ${GIT_DEFAULT_BRANCH} --
451+
if [ -n "${PUBLIC_GITIGNORE}" ]; then
452+
if [ -e "${PUBLIC_GITIGNORE}" ]; then
453+
log_info "Update public files:"
454+
cp -p "${PUBLIC_GITIGNORE}" .gitignore
455+
fi
456+
fi
429457

430-
if [ $CONFIRM -eq 0 ]; then
431-
## https://www.shellhacks.com/yes-no-bash-script-prompt-confirmation/
432-
read -p "Are you sure you want to merge the changes above to public branch ${TARGET_BRANCH}? " -n 1 -r
433-
echo # (optional) move to a new line
434-
if [[ ! $REPLY =~ ^[Yy]$ ]]
435-
then
436-
exit 1
458+
log_info "Show changes before push:"
459+
git status
460+
461+
## https://stackoverflow.com/questions/5989592/git-cannot-checkout-branch-error-pathspec-did-not-match-any-files-kn
462+
## git diff --name-only ${GIT_PUBLIC_BRANCH} ${GIT_DEFAULT_BRANCH} --
463+
464+
if [ $CONFIRM -eq 0 ]; then
465+
## https://www.shellhacks.com/yes-no-bash-script-prompt-confirmation/
466+
read -p "Are you sure you want to merge the changes above to public branch ${TARGET_BRANCH}? " -n 1 -r
467+
echo # (optional) move to a new line
468+
if [[ ! $REPLY =~ ^[Yy]$ ]]
469+
then
470+
exit 1
471+
fi
437472
fi
438-
fi
473+
474+
## https://stackoverflow.com/questions/5738797/how-can-i-push-a-local-git-branch-to-a-remote-with-a-different-name-easily
475+
log_info "Add all the files:"
476+
git_commit_push
439477

440-
## https://stackoverflow.com/questions/5738797/how-can-i-push-a-local-git-branch-to-a-remote-with-a-different-name-easily
441-
log_info "Add all the files:"
442-
git_commit_push
443-
log_info "Checkout ${GIT_DEFAULT_BRANCH} branch:" && \
444-
git checkout ${GIT_DEFAULT_BRANCH}
478+
# log_info "Checkout ${GIT_DEFAULT_BRANCH} branch:" && \
479+
# git checkout ${GIT_DEFAULT_BRANCH}
480+
481+
log_info "Returning to the original branch and applying stashed changes."
482+
if ! git -C "${REPO_DIR}" checkout -; then
483+
log_error "Failed to checkout original branch."
484+
fi
485+
486+
log_info "Returning to the original branch and applying stashed changes."
487+
if git -C "${REPO_DIR}" stash list | grep -q 'stash'; then
488+
if ! git -C "${REPO_DIR}" stash pop; then
489+
log_warn "Failed to apply stashed changes. You may have uncommitted changes. Please handle manually."
490+
fi
491+
else
492+
log_info "No stashed changes to apply."
493+
fi
445494
}
446495

447496

@@ -477,7 +526,7 @@ function main() {
477526
done
478527
shift $((OPTIND-1))
479528

480-
log_debug "PROJECT_DIR=${PROJECT_DIR}"
529+
log_debug "REPO_DIR=${REPO_DIR}"
481530
log_debug "TEMP_DIR=${TEMP_DIR}"
482531

483532
search_repo_keywords
@@ -487,7 +536,15 @@ function main() {
487536
exit ${RETURN_STATUS}
488537
fi
489538

490-
sync_public_branch
539+
trap on_error ERR
540+
541+
copy_project_to_temp_dir "${REPO_DIR}"
542+
sync_public_branch "${REPO_DIR}" "${GIT_PUBLIC_BRANCH}"
543+
544+
log_info "Sync completed successfully."
545+
cleanup
546+
547+
trap - ERR
491548

492549
}
493550

0 commit comments

Comments
 (0)