Opinionated java-library project template
1#!/usr/bin/env bash
2set -euo pipefail
3
4usage() {
5 cat <<'USAGE'
6Usage: ./.scripts/rename-template.sh \
7 -g <groupId> \
8 -p <projectName> \
9 -n <repoName> \
10 -s <snapshotsRepoUrl> \
11 -r <releasesRepoUrl> \
12 -a <author> \
13 -y <year>
14
15Example:
16 ./.scripts/rename-template.sh \
17 -g com.acme \
18 -p my-project \
19 -n acmeRepo \
20 -s https://repo.acme.com/snapshots \
21 -r https://repo.acme.com/releases \
22 -a "Acme, Inc." \
23 -y 2026
24USAGE
25}
26
27GROUP_ID=""
28PROJECT_NAME=""
29REPO_NAME=""
30SNAPSHOTS_REPO_URL=""
31RELEASES_REPO_URL=""
32AUTHOR=""
33COPYRIGHT_YEAR=""
34
35while getopts ":g:p:n:s:r:a:y:h" opt; do
36 case "$opt" in
37 g) GROUP_ID="$OPTARG" ;;
38 p) PROJECT_NAME="$OPTARG" ;;
39 n) REPO_NAME="$OPTARG" ;;
40 s) SNAPSHOTS_REPO_URL="$OPTARG" ;;
41 r) RELEASES_REPO_URL="$OPTARG" ;;
42 a) AUTHOR="$OPTARG" ;;
43 y) COPYRIGHT_YEAR="$OPTARG" ;;
44 h)
45 usage
46 exit 0
47 ;;
48 \?)
49 echo "Unknown option: -$OPTARG" >&2
50 usage
51 exit 1
52 ;;
53 :)
54 echo "Missing argument for -$OPTARG" >&2
55 usage
56 exit 1
57 ;;
58 esac
59done
60
61if [[ -z "$GROUP_ID" || -z "$PROJECT_NAME" || -z "$REPO_NAME" || -z "$SNAPSHOTS_REPO_URL" || -z "$RELEASES_REPO_URL" || -z "$AUTHOR" || -z "$COPYRIGHT_YEAR" ]]; then
62 usage
63 exit 1
64fi
65
66PROJECT_NAME="${PROJECT_NAME##.}"
67if [[ -z "$PROJECT_NAME" ]]; then
68 echo "Project name cannot be only dots." >&2
69 exit 1
70fi
71
72normalize_segment() {
73 local segment="$1"
74 local normalized
75 normalized="$(echo "$segment" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9_]/_/g')"
76 normalized="${normalized##_}"
77 normalized="${normalized%%_}"
78 if [[ -z "$normalized" ]]; then
79 normalized="app"
80 fi
81 echo "$normalized"
82}
83
84build_package_name() {
85 local group_id="$1"
86 local project_name="$2"
87 local -a package_segments
88 local -a group_parts
89
90 IFS='.' read -r -a group_parts <<<"$group_id"
91 for part in "${group_parts[@]}"; do
92 package_segments+=("$(normalize_segment "$part")")
93 done
94 package_segments+=("$(normalize_segment "$project_name")")
95 IFS='.'
96 echo "${package_segments[*]}"
97}
98
99PACKAGE_NAME="$(build_package_name "$GROUP_ID" "$PROJECT_NAME")"
100REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
101TEMPLATE_PACKAGE="com.example.template"
102
103SOURCE_MODULE_DIR="$REPO_ROOT/template-api"
104
105FILES=(
106 "$REPO_ROOT/build.gradle.kts"
107 "$REPO_ROOT/settings.gradle.kts"
108 "$REPO_ROOT/build-logic/src/main/kotlin/extensions.kt"
109 "$REPO_ROOT/build-logic/src/main/kotlin/publishing-conventions.gradle.kts"
110 "$REPO_ROOT/license.txt"
111 "$REPO_ROOT/license_header.txt"
112 "$REPO_ROOT/.checkstyle/suppressions.xml"
113)
114
115if [[ -d "$SOURCE_MODULE_DIR/src/main/java" ]]; then
116 while IFS= read -r -d '' file; do
117 FILES+=("$file")
118 done < <(find "$SOURCE_MODULE_DIR/src/main/java" -type f -print0)
119fi
120
121if [[ -d "$SOURCE_MODULE_DIR/src/test/java" ]]; then
122 while IFS= read -r -d '' file; do
123 FILES+=("$file")
124 done < <(find "$SOURCE_MODULE_DIR/src/test/java" -type f -print0)
125fi
126
127SED_INPLACE=(-i)
128if [[ "$(uname)" == "Darwin" ]]; then
129 SED_INPLACE=(-i '')
130fi
131
132escape_sed() {
133 local value="$1"
134 value="${value//\\/\\\\}"
135 value="${value//&/\\&}"
136 value="${value//\//\\/}"
137 echo "$value"
138}
139
140replace_placeholders() {
141 local file="$1"
142 local safe_group safe_project safe_package
143 local safe_repo_name
144 local safe_snapshots_repo_url safe_releases_repo_url
145 local safe_project_api
146 local safe_author safe_copyright_year
147
148 safe_group="$(escape_sed "$GROUP_ID")"
149 safe_project="$(escape_sed "$PROJECT_NAME")"
150 safe_package="$(escape_sed "$PACKAGE_NAME")"
151 safe_repo_name="$(escape_sed "$REPO_NAME")"
152 safe_snapshots_repo_url="$(escape_sed "$SNAPSHOTS_REPO_URL")"
153 safe_releases_repo_url="$(escape_sed "$RELEASES_REPO_URL")"
154 safe_project_api="$(escape_sed "${PROJECT_NAME}-api")"
155 safe_author="$(escape_sed "$AUTHOR")"
156 safe_copyright_year="$(escape_sed "$COPYRIGHT_YEAR")"
157
158 sed "${SED_INPLACE[@]}" \
159 -e "s/{{GROUP_ID}}/$safe_group/g" \
160 -e "s/{{PROJECT_NAME}}/$safe_project/g" \
161 -e "s/{{PACKAGE_NAME}}/$safe_package/g" \
162 -e "s/{{REPO_NAME}}/$safe_repo_name/g" \
163 -e "s/{{SNAPSHOTS_REPO_URL}}/$safe_snapshots_repo_url/g" \
164 -e "s/{{RELEASES_REPO_URL}}/$safe_releases_repo_url/g" \
165 -e "s/{{AUTHOR}}/$safe_author/g" \
166 -e "s/{{COPYRIGHT_YEAR}}/$safe_copyright_year/g" \
167 -e "s/template-api/$safe_project_api/g" \
168 "$file"
169}
170
171for file in "${FILES[@]}"; do
172 [[ -f "$file" ]] || continue
173 replace_placeholders "$file"
174 echo "Updated: $file"
175done
176
177move_package_dir() {
178 local base_dir="$1"
179 local old_dir
180 local new_dir
181 old_dir="$base_dir/$(echo "$TEMPLATE_PACKAGE" | tr '.' '/')"
182 new_dir="$base_dir/$(echo "$PACKAGE_NAME" | tr '.' '/')"
183
184 if [[ -d "$old_dir" ]]; then
185 mkdir -p "$new_dir"
186 shopt -s dotglob nullglob
187 mv "$old_dir"/* "$new_dir"/
188 shopt -u dotglob nullglob
189 rmdir "$old_dir" 2>/dev/null || true
190 fi
191
192 if [[ -d "$base_dir" ]]; then
193 find "$base_dir" -type d -empty -delete
194 fi
195}
196
197move_package_dir "$SOURCE_MODULE_DIR/src/main/java"
198move_package_dir "$SOURCE_MODULE_DIR/src/test/java"
199
200if [[ -d "$REPO_ROOT/template-api" ]]; then
201 mv "$REPO_ROOT/template-api" "$REPO_ROOT/${PROJECT_NAME}-api"
202fi
203
204if [[ -d "$REPO_ROOT/.git" ]]; then
205 rm -rf "$REPO_ROOT/.git"
206fi
207
208if command -v git >/dev/null 2>&1; then
209 git -C "$REPO_ROOT" init
210else
211 echo "Warning: git not found; skipping git init." >&2
212fi
213
214echo ""
215echo "Done!"
216echo ""