Skip to content

fix: [FEEDS-1358]rewrite CI release flow with PR-title semver and concurrency cancellation#31

Merged
itsmeadi merged 3 commits intomasterfrom
fix/ci-release-flow
Apr 1, 2026
Merged

fix: [FEEDS-1358]rewrite CI release flow with PR-title semver and concurrency cancellation#31
itsmeadi merged 3 commits intomasterfrom
fix/ci-release-flow

Conversation

@itsmeadi
Copy link
Copy Markdown
Collaborator

@itsmeadi itsmeadi commented Apr 1, 2026

Summary

  • replace release-branch based release flow with merged PR auto-release on main/master
  • derive bump type from PR title/body (feat => minor, fix/bug => patch, !/BREAKING CHANGE => major) via scripts/release/bump_version.php
  • add release concurrency cancellation, remove legacy release workflows, and document the new process in README.md and .github/RELEASE_SETUP.md

Test plan

  • php -l scripts/release/bump_version.php
  • smoke run: php scripts/release/bump_version.php --title "chore: update docs" --body "" returns should_release=false
  • merge a fix: PR in a test repo/branch and verify version bump + tag + GitHub release + Packagist update

Made with Cursor

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
@itsmeadi itsmeadi changed the title fix: rewrite CI release flow with PR-title semver and concurrency cancellation fix: [FEEDS-1358]rewrite CI release flow with PR-title semver and concurrency cancellation Apr 1, 2026
Copy link
Copy Markdown
Collaborator

@mogita mogita left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review: 3 critical issues found in release automation


function findLatestSemverTag(): string
{
$tagsRaw = runCommand('git tag --list --sort=-version:refname');
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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';

run: |
php scripts/release/bump_version.php \
--title "$PR_TITLE" \
--body "$PR_BODY" \
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

}

$composer['version'] = 'v' . $version;
$updated = json_encode($composer, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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
@itsmeadi itsmeadi merged commit df114e8 into master Apr 1, 2026
5 checks passed
github-actions bot added a commit that referenced this pull request Apr 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants