@@ -33,10 +33,34 @@ MAIN_BRANCH=main
3333DEVELOP_BRANCH=develop
3434RELEASE_BRANCH_NAME=release/${BUMPED_VERSION}
3535
36+ # Ensure local tags are fully in sync with remote tags before release prep.
37+ # Uses process substitution to avoid orphaned temp files on early exit.
38+ verify_local_tags_match_remote () {
39+ # --refs strips peeled annotated-tag entries (^{}) for clean one-line refs.
40+ if ! diff -u \
41+ <( git ls-remote --tags --refs origin 2> /dev/null | awk ' {print $1" "$2}' | sort) \
42+ <( git show-ref --tags 2> /dev/null | awk ' {print $1" "$2}' | sort) \
43+ > /dev/null; then
44+ echo
45+ echo " Error: local tags do not match tags on origin."
46+ echo " Please sync tags before running release prep:"
47+ echo " git fetch origin --tags --prune-tags"
48+ echo
49+ diff -u \
50+ <( git ls-remote --tags --refs origin 2> /dev/null | awk ' {print $1" "$2}' | sort) \
51+ <( git show-ref --tags 2> /dev/null | awk ' {print $1" "$2}' | sort) || true
52+ return 1
53+ fi
54+
55+ echo " Tag sync check passed: local tags match origin."
56+ return 0
57+ }
58+
3659echo
3760echo " Creating release branch"
3861echo
39- git fetch
62+ git fetch origin --tags --prune-tags
63+ verify_local_tags_match_remote
4064git checkout ${DEVELOP_BRANCH}
4165git pull
4266git checkout -b ${RELEASE_BRANCH_NAME}
5680# Function to fetch WordPress versions and calculate required version
5781update_wp_requires_version () {
5882 echo " Fetching WordPress version list..."
59-
83+
6084 # Get current "Tested up to" version from readme.txt
6185 local tested_up_to=$( grep " Tested up to:" " ${README_PATH} " | sed -E ' s/Tested up to: ([0-9]+\.[0-9]+).*/\1/' )
62-
86+
6387 if [[ -z " ${tested_up_to} " ]]; then
6488 echo " Error: Could not extract 'Tested up to' version from readme.txt" >&2
6589 return 1
6690 fi
67-
91+
6892 echo " Current 'Tested up to': ${tested_up_to} "
69-
93+
7094 # Try to fetch WordPress versions from the API first
7195 local wp_versions_json=$( curl -s --max-time 10 " https://api.wordpress.org/core/version-check/1.7/" 2> /dev/null || echo " " )
72-
96+
7397 if [[ -n " ${wp_versions_json} " ]]; then
7498 echo " Using WordPress API for version data..."
7599 # Parse JSON response to get version list in reverse chronological order
@@ -83,7 +107,7 @@ update_wp_requires_version() {
83107 echo " API unavailable, trying to parse releases page..."
84108 # Fallback: try to parse the releases page
85109 local releases_html=$( curl -s --max-time 15 " https://wordpress.org/download/releases/" 2> /dev/null || echo " " )
86-
110+
87111 if [[ -n " ${releases_html} " ]]; then
88112 # Extract version numbers from the releases page
89113 # Look for patterns like "WordPress 6.8" or "wordpress-6.7.zip"
@@ -97,20 +121,20 @@ update_wp_requires_version() {
97121 return 1
98122 fi
99123 fi
100-
124+
101125 if [[ -z " ${versions_list} " ]]; then
102126 echo " Error: No WordPress versions found in response" >&2
103127 return 1
104128 fi
105-
129+
106130 echo " Found WordPress versions (latest first):"
107131 echo " ${versions_list} " | head -10
108-
132+
109133 # Find the current tested version in the list and go back 2 versions (to support last 3 versions)
110134 local found_current=false
111135 local version_count=0
112136 local target_version=" "
113-
137+
114138 while IFS= read -r version; do
115139 if [[ " ${found_current} " == true ]]; then
116140 version_count=$(( version_count + 1 ))
@@ -123,13 +147,13 @@ update_wp_requires_version() {
123147 echo " Found current tested version ${tested_up_to} in version list"
124148 fi
125149 done <<< " ${versions_list}"
126-
150+
127151 if [[ " ${found_current} " == false ]]; then
128152 echo " Warning: Current 'Tested up to' version ${tested_up_to} not found in WordPress version list" >&2
129153 echo " Available versions: $( echo " ${versions_list} " | tr ' \n' ' ' ) " >&2
130154 return 1
131155 fi
132-
156+
133157 if [[ -z " ${target_version} " ]]; then
134158 echo " Warning: Could not find version 2 releases back from ${tested_up_to} " >&2
135159 # Try to use the oldest version we found if we don't have enough history
@@ -140,19 +164,19 @@ update_wp_requires_version() {
140164 return 1
141165 fi
142166 fi
143-
167+
144168 # Ensure we don't go below WordPress 5.0 (reasonable minimum)
145169 local min_major=$( echo " ${target_version} " | cut -d. -f1)
146170 if [[ ${min_major} -lt 5 ]]; then
147171 target_version=" 5.0"
148172 echo " Adjusted to minimum supported version: ${target_version} "
149173 fi
150-
174+
151175 echo " Setting 'Requires at least' to: ${target_version} (2 versions back from ${tested_up_to} to support last 3 versions)"
152-
176+
153177 # Update the readme.txt file
154178 sed -i.bak -E " s/(Requires at least: )[0-9]+\.[0-9]+/\1${target_version} /" " ${README_PATH} "
155-
179+
156180 return 0
157181}
158182
@@ -170,6 +194,56 @@ echo "Committing version bump"
170194echo
171195git add ${MAIN_FILE_PATH} ${PACKAGE_JSON_PATH} ${README_PATH}
172196git commit -m " Bump version ${VERSION} -> ${BUMPED_VERSION} "
197+
198+ echo
199+ echo " Updating @since placeholder tags to ${BUMPED_VERSION} "
200+ echo
201+
202+ # Stash any existing uncommitted work (tracked modifications + untracked files)
203+ # so that the subsequent git diff picks up only what the tool changes.
204+ # Track stash ref before/after to guard against popping an unrelated stash when
205+ # git stash push exits 0 even if there was nothing to save.
206+ STASH_MESSAGE=" prep_release: pre-since-tag stash"
207+ STASH_BEFORE=$( git rev-parse -q --verify refs/stash || true)
208+ git stash push --include-untracked -m " ${STASH_MESSAGE} "
209+ STASH_AFTER=$( git rev-parse -q --verify refs/stash || true)
210+ STASH_CREATED=false
211+ if [[ -n " ${STASH_AFTER} " && " ${STASH_AFTER} " != " ${STASH_BEFORE} " ]]; then
212+ STASH_CREATED=true
213+ fi
214+
215+ # Ensure the stash is always restored, even if the script exits early.
216+ # Setting STASH_CREATED=false before popping prevents the EXIT trap double-pop.
217+ restore_stash () {
218+ if [[ " ${STASH_CREATED} " == true ]]; then
219+ echo
220+ echo " Restoring stashed changes"
221+ echo
222+ STASH_CREATED=false
223+ git stash pop
224+ fi
225+ }
226+ trap restore_stash EXIT
227+
228+ php tools/update-since-tags.php --version=" ${BUMPED_VERSION} " --changed-since-last-tag
229+
230+ # Stage only the tracked PHP files modified by the tool.
231+ # Use NUL-delimited output + mapfile + xargs -0 so paths with spaces are safe.
232+ mapfile -d ' ' SINCE_CHANGES < <( git diff --name-only -z -- ' *.php' )
233+ if (( ${# SINCE_CHANGES[@]} > 0 )) ; then
234+ echo
235+ echo " Committing @since tag updates"
236+ echo
237+ printf ' %s\0' " ${SINCE_CHANGES[@]} " | xargs -0 git add --
238+ git commit -m " Update @since placeholders to ${BUMPED_VERSION} "
239+ else
240+ echo " No @since placeholder tags found — skipping commit."
241+ fi
242+
243+ # Restore stashed work now (trap will also fire on EXIT, guard against double-pop).
244+ restore_stash
245+ trap - EXIT
246+
173247git push -u origin ${RELEASE_BRANCH_NAME}
174248
175249echo
0 commit comments