Skip to content

Commit a855b27

Browse files
committed
Sync already deployed package.json %47
1 parent 516213a commit a855b27

File tree

1 file changed

+54
-22
lines changed

1 file changed

+54
-22
lines changed

sync.sh

Lines changed: 54 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -10,81 +10,113 @@
1010
# (ignoring .git/), performs a “full sync” and replaces occurrences
1111
# of the literal word "template" in files with the target’s basename.
1212
# If a target already has content, performs a selective sync
13-
# excluding package.json, README.md, and src/.
13+
# excluding package.json, README.md, and src/, then merges package.json.
1414
#
1515
# Usage:
1616
# sync.sh my-new-project
1717
# sync.sh ../foo ../bar
1818

19-
# Exit on error, undefined var, or pipe failure.
19+
# Exit immediately on:
20+
# - any command returning non-zero (set -e),
21+
# - use of unset variables (set -u),
22+
# - failure within a pipeline (set -o pipefail).
2023
set -euo pipefail
2124

22-
# Ensure at least one target directory is specified.
25+
# Ensure at least one target directory is given.
2326
if (( $# < 1 )); then
2427
echo "Usage: $0 <target> [<target>…]" >&2
2528
exit 1
2629
fi
2730

28-
# Determine this script’s own directory and name, so we can rsync from here
31+
# Determine this script’s directory and name for rsync source.
2932
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
3033
script_name="$(basename "${BASH_SOURCE[0]}")"
3134

32-
# Collect and validate absolute target paths
35+
# Collect and validate absolute paths for each target.
3336
targets=()
3437
for t; do
35-
mkdir -p "$t" # create if missing
36-
abs_t="$(cd "$t" && pwd -P)" # resolve to absolute path
37-
[[ ! -w $abs_t ]] && { # require write permission
38+
mkdir -p "$t" # create target if missing
39+
abs_t="$(cd "$t" && pwd -P)" # resolve to canonical absolute path
40+
41+
# Require write permission on the target directory.
42+
if [[ ! -w $abs_t ]]; then
3843
echo "Error: no write access to $abs_t" >&2
3944
exit 1
40-
}
45+
fi
46+
4147
targets+=("$abs_t")
4248
done
4349

4450
# is_empty_dir <dir>
45-
# Return 0 if <dir> contains no entries except maybe .git/,
46-
# Return 1 otherwise.
51+
# Returns 0 (success) if <dir> contains no entries
52+
# except optionally a .git/ directory; returns 1 otherwise.
4753
is_empty_dir() {
4854
find "$1" -mindepth 1 \
4955
! -path "$1/.git" \
5056
! -path "$1/.git/*" \
51-
-print -quit | grep -q . \
52-
&& return 1 \
53-
|| return 0
57+
-print -quit \
58+
| grep -q . && return 1 || return 0
5459
}
5560

56-
# Base rsync options: archive mode, exclude this script and .git/,
57-
# respect .gitignore filters.
61+
# Base rsync options:
62+
# --archive preserve permissions, timestamps, etc.
63+
# --exclude=script do not copy this sync script into targets
64+
# --exclude='.git/' skip Git meta directory
65+
# --filter=':- .gitignore' respect .gitignore rules
5866
RSYNC_OPTS=(
5967
--archive
6068
--exclude="$script_name"
6169
--exclude='.git/'
6270
--filter=':- .gitignore'
6371
)
6472

65-
# Perform sync for each validated target
73+
# Loop over each validated target and perform sync.
6674
for target in "${targets[@]}"; do
6775
echo ">>> Syncing to $target"
6876

6977
if is_empty_dir "$target"; then
70-
# Full initial sync: copy everything and replace "template"
78+
# Full initial sync: copy all files, then transform "template" placeholders.
7179
echo " Empty dir -> full sync"
72-
rsync "${RSYNC_OPTS[@]}" "$script_dir/." "$target"
80+
rsync "${RSYNC_OPTS[@]}" "$script_dir/." "$target/"
7381

82+
# Replace every occurrence of the literal word "template" in each text file
7483
base="$(basename "$target")"
75-
# Replace occurrences of 'template' in all text files
7684
find "$target" -type f -exec grep -Il . {} + |
7785
while IFS= read -r file; do
7886
sed -i "s/template/${base}/g" "$file"
7987
done
8088

8189
else
82-
# Selective sync: do not overwrite key project files
90+
# Selective sync: exclude key project files and directories.
8391
echo " Non-empty dir -> selective sync"
8492
rsync "${RSYNC_OPTS[@]}" \
8593
--exclude='package.json' \
8694
--exclude='README.md' \
8795
--exclude='src/**' \
88-
"$script_dir/." "$target"
96+
"$script_dir/." "$target/"
97+
98+
# Merge package.json: keep existing fields but apply updated template defaults.
99+
old="$target/package.json"
100+
tmpl="$script_dir/package.json"
101+
merged="$target/package.json.tmp"
102+
base="$(basename "$target")"
103+
104+
jq -n \
105+
--slurpfile new <(sed "s/template/${base}/g" "$tmpl") \
106+
--slurpfile old "$old" \
107+
'
108+
$new[0] as $new |
109+
$old[0] as $old |
110+
111+
# Start with full template structure, then override preserved fields.
112+
$new
113+
| .name = $old.name
114+
| .version = $old.version
115+
| .description = $old.description
116+
| .dependencies = ($new.dependencies + $old.dependencies)
117+
| .devDependencies = ($new.devDependencies + $old.devDependencies)
118+
| .scripts = ($new.scripts + $old.scripts)
119+
' > "$merged" \
120+
&& mv "$merged" "$old"
89121
fi
90122
done

0 commit comments

Comments
 (0)