Conversation
Switch release automation to merged PR title semantics with concurrency cancellation, extract PHP-specific version bump logic into a script, and remove obsolete release branch/prerelease workflows. Made-with: Cursor
mogita
left a comment
There was a problem hiding this comment.
Review: 3 critical issues found in release automation
scripts/release/bump_version.php
Outdated
|
|
||
| function findLatestSemverTag(): string | ||
| { | ||
| $tagsRaw = runCommand('git tag --list --sort=-version:refname'); |
There was a problem hiding this comment.
Critical: Mixed tag formats will return the wrong base version.
This repo has both bare (5.0.0) and v-prefixed (v1.0.0) tags. --sort=-version:refname can sort v1.0.0 before 5.0.0 in many Git versions, so the foreach below takes the first match and returns 1.0.0 instead of 5.0.0.
Fix: normalize all tags to bare numeric form and sort with version_compare in PHP rather than relying on git's sort order:
$tags = preg_split('/\R/', $tagsRaw) ?: [];
$versions = [];
foreach ($tags as $tag) {
$normalized = ltrim(trim($tag), 'v');
if (preg_match('/^\d+\.\d+\.\d+$/', $normalized)) {
$versions[] = $normalized;
}
}
usort($versions, 'version_compare');
return end($versions) ?: '0.0.0';
.github/workflows/release.yml
Outdated
| run: | | ||
| php scripts/release/bump_version.php \ | ||
| --title "$PR_TITLE" \ | ||
| --body "$PR_BODY" \ |
There was a problem hiding this comment.
Critical: Multi-line PR body can smuggle extra CLI flags into argv.
$PR_BODY can be multi-line. When double-quoted in the shell, a PR body containing a newline followed by --output /etc/passwd would be word-split into separate positional arguments, overwriting the output path.
Fix: write the body to a temp file and pass it via --body-file:
run: |
PR_BODY_FILE=$(mktemp)
printf '%s' "$PR_BODY" > "$PR_BODY_FILE"
php scripts/release/bump_version.php \
--title "$PR_TITLE" \
--body-file "$PR_BODY_FILE" \
--output "$GITHUB_OUTPUT"Then update bump_version.php to support --body-file.
scripts/release/bump_version.php
Outdated
| } | ||
|
|
||
| $composer['version'] = 'v' . $version; | ||
| $updated = json_encode($composer, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES); |
There was a problem hiding this comment.
Critical: json_decode + json_encode round-trip reorders composer.json keys.
The existing composer.json has a specific key order (name, description, version, type, ...). After json_decode/json_encode, keys may be reordered, producing a large spurious diff on the first release and making git history noisy for every subsequent one.
Fix: use a targeted regex replacement instead of round-tripping through JSON:
$updated = preg_replace(
'/"version":\s*"[^"]*"/',
'"version": "v' . $version . '"',
$raw,
1
);
if ($updated === null || $updated === $raw) {
throw new ReleaseScriptException('Could not update version in composer.json');
}Harden release version resolution for mixed tag formats, avoid composer.json reformatting by updating only the version field, and pass PR body via file to prevent argument injection edge cases. Made-with: Cursor
Summary
main/masterfeat=> minor,fix/bug=> patch,!/BREAKING CHANGE=> major) viascripts/release/bump_version.phpREADME.mdand.github/RELEASE_SETUP.mdTest plan
php -l scripts/release/bump_version.phpphp scripts/release/bump_version.php --title "chore: update docs" --body ""returnsshould_release=falsefix:PR in a test repo/branch and verify version bump + tag + GitHub release + Packagist updateMade with Cursor