diff --git a/.github/actions/changelog/create-dependabot-entry/action.yml b/.github/actions/changelog/create-dependabot-entry/action.yml index a8363c104..ccb4a8ef7 100644 --- a/.github/actions/changelog/create-dependabot-entry/action.yml +++ b/.github/actions/changelog/create-dependabot-entry/action.yml @@ -22,6 +22,9 @@ outputs: created: description: Whether a changelog entry was created and pushed. value: ${{ steps.create.outputs.created }} + status: + description: Whether the branch already had an entry, auto-created one, or still remains missing. + value: ${{ steps.create.outputs.status }} message: description: Generated changelog entry message, or empty when no entry was needed. value: ${{ steps.create.outputs.message }} diff --git a/.github/actions/changelog/create-dependabot-entry/run.sh b/.github/actions/changelog/create-dependabot-entry/run.sh index 81695103d..393c03884 100755 --- a/.github/actions/changelog/create-dependabot-entry/run.sh +++ b/.github/actions/changelog/create-dependabot-entry/run.sh @@ -1,24 +1,62 @@ #!/usr/bin/env bash set -euo pipefail -if composer dev-tools changelog:check -- --file="${INPUT_CHANGELOG_FILE}" --against="origin/${INPUT_BASE_REF}" >/dev/null 2>&1; then - echo "created=false" >> "$GITHUB_OUTPUT" - exit 0 -fi +entry_message="$(php -r 'require "vendor/autoload.php"; $resolver = new \FastForward\DevTools\Changelog\DependabotChangelogEntryMessageResolver(); echo $resolver->resolve(getenv("INPUT_PULL_REQUEST_TITLE") ?: "", (int) (getenv("INPUT_PULL_REQUEST_NUMBER") ?: 0));')" +git fetch --no-tags --depth=1 origin "+refs/heads/${INPUT_BASE_REF}:refs/remotes/origin/${INPUT_BASE_REF}" git fetch --no-tags --depth=1 origin "+refs/heads/${INPUT_HEAD_REF}:refs/remotes/origin/${INPUT_HEAD_REF}" git switch -C "${INPUT_HEAD_REF}" "refs/remotes/origin/${INPUT_HEAD_REF}" git config user.name "github-actions[bot]" git config user.email "41898282+github-actions[bot]@users.noreply.github.com" -entry_message="$(php -r 'require "vendor/autoload.php"; $resolver = new \FastForward\DevTools\Changelog\DependabotChangelogEntryMessageResolver(); echo $resolver->resolve(getenv("INPUT_PULL_REQUEST_TITLE") ?: "", (int) (getenv("INPUT_PULL_REQUEST_NUMBER") ?: 0));')" +if composer dev-tools changelog:check -- --file="${INPUT_CHANGELOG_FILE}" --against="origin/${INPUT_BASE_REF}" >/dev/null 2>&1; then + { + echo "created=false" + echo "status=already-present" + printf 'message=%s\n' "${entry_message}" + } >> "$GITHUB_OUTPUT" + + exit 0 +fi composer dev-tools changelog:entry -- --type=changed --file="${INPUT_CHANGELOG_FILE}" "${entry_message}" git add "${INPUT_CHANGELOG_FILE}" + +if git diff --cached --quiet -- "${INPUT_CHANGELOG_FILE}"; then + { + echo "created=false" + echo "status=missing" + printf 'message=%s\n' "${entry_message}" + } >> "$GITHUB_OUTPUT" + + exit 1 +fi + git commit -m "Add changelog entry for Dependabot PR #${INPUT_PULL_REQUEST_NUMBER}" git push origin "HEAD:${INPUT_HEAD_REF}" +if ! composer dev-tools changelog:check -- --file="${INPUT_CHANGELOG_FILE}" --against="origin/${INPUT_BASE_REF}" >/dev/null 2>&1; then + { + echo "created=false" + echo "status=missing" + printf 'message=%s\n' "${entry_message}" + } >> "$GITHUB_OUTPUT" + + exit 1 +fi + +if ! grep -F --quiet -- "- ${entry_message}" "${INPUT_CHANGELOG_FILE}"; then + { + echo "created=false" + echo "status=missing" + printf 'message=%s\n' "${entry_message}" + } >> "$GITHUB_OUTPUT" + + exit 1 +fi + { echo "created=true" + echo "status=auto-created" printf 'message=%s\n' "${entry_message}" } >> "$GITHUB_OUTPUT" diff --git a/.github/wiki b/.github/wiki index 0fedf5428..79fb0c2fb 160000 --- a/.github/wiki +++ b/.github/wiki @@ -1 +1 @@ -Subproject commit 0fedf54289a6a7a5e5242a803c7bdb0a1edb4a3c +Subproject commit 79fb0c2fb28fca4e9d769e5e13d42df7bd876952 diff --git a/.github/workflows/changelog.yml b/.github/workflows/changelog.yml index f4e1cf9e5..b4782d965 100644 --- a/.github/workflows/changelog.yml +++ b/.github/workflows/changelog.yml @@ -141,8 +141,9 @@ jobs: - Changelog file: `${{ env.CHANGELOG_FILE }}` - Compared base ref: `origin/${{ env.BASE_REF }}` + - Dependabot fallback status: `${{ steps.dependabot_entry.outputs.status || 'not needed' }}` - Dependabot fallback entry created: `${{ steps.dependabot_entry.outputs.created || 'false' }}` - - Dependabot fallback entry message: `${{ steps.dependabot_entry.outputs.message || 'not needed' }}` + - Dependabot fallback generated message: `${{ steps.dependabot_entry.outputs.message || 'not needed' }}` - Validation result: success prepare_release_pull_request: diff --git a/CHANGELOG.md b/CHANGELOG.md index 4d38cf3cc..968d04dcf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - GitHub Actions(deps): Bump marocchino/sticky-pull-request-comment from 2 to 3 (#182) - GitHub Actions(deps): Bump actions/github-script from 8 to 9 (#183) - Auto-create and push minimal changelog entries for same-repository Dependabot pull requests before changelog validation reruns (#186) +- Resolve Dependabot changelog fallback state from the actual PR head branch and report `already-present`, `auto-created`, or `missing` in the workflow summary so rebased PRs cannot pass on inherited `Unreleased` entries alone (#191) ## [1.20.0] - 2026-04-23 diff --git a/tests/Changelog/Checker/UnreleasedEntryCheckerTest.php b/tests/Changelog/Checker/UnreleasedEntryCheckerTest.php index 496159875..ed8b066c4 100644 --- a/tests/Changelog/Checker/UnreleasedEntryCheckerTest.php +++ b/tests/Changelog/Checker/UnreleasedEntryCheckerTest.php @@ -175,6 +175,59 @@ public function hasPendingChangesWillReturnTrueWhenBaselineDoesNotContainNewEntr self::assertTrue($this->checker->hasPendingChanges(self::FILE, 'origin/main')); } + /** + * @return void + */ + #[Test] + public function hasPendingChangesWillIgnoreEntriesOnlyInheritedFromTheBaseBranch(): void + { + $this->filesystem->readFile(self::FILE) + ->willReturn('current changelog') + ->shouldBeCalledOnce(); + $this->parser->parse('current changelog') + ->willReturn($this->createDocument([ + 'Auto-create and push minimal changelog entries for same-repository Dependabot pull requests before changelog validation reruns (#186)', + ])) + ->shouldBeCalledOnce(); + $this->gitClient->show('origin/main', self::FILE, self::WORKING_DIRECTORY) + ->willReturn('baseline changelog') + ->shouldBeCalledOnce(); + $this->parser->parse('baseline changelog') + ->willReturn($this->createDocument([ + 'Auto-create and push minimal changelog entries for same-repository Dependabot pull requests before changelog validation reruns (#186)', + ])) + ->shouldBeCalledOnce(); + + self::assertFalse($this->checker->hasPendingChanges(self::FILE, 'origin/main')); + } + + /** + * @return void + */ + #[Test] + public function hasPendingChangesWillStillDetectBranchSpecificEntriesAlongsideInheritedOnes(): void + { + $this->filesystem->readFile(self::FILE) + ->willReturn('current changelog') + ->shouldBeCalledOnce(); + $this->parser->parse('current changelog') + ->willReturn($this->createDocument([ + 'Auto-create and push minimal changelog entries for same-repository Dependabot pull requests before changelog validation reruns (#186)', + 'GitHub Actions(deps): Bump actions/github-script from 8 to 9 (#183)', + ])) + ->shouldBeCalledOnce(); + $this->gitClient->show('origin/main', self::FILE, self::WORKING_DIRECTORY) + ->willReturn('baseline changelog') + ->shouldBeCalledOnce(); + $this->parser->parse('baseline changelog') + ->willReturn($this->createDocument([ + 'Auto-create and push minimal changelog entries for same-repository Dependabot pull requests before changelog validation reruns (#186)', + ])) + ->shouldBeCalledOnce(); + + self::assertTrue($this->checker->hasPendingChanges(self::FILE, 'origin/main')); + } + /** * @return void */