-
-
Notifications
You must be signed in to change notification settings - Fork 3
fix(release): target merge commits for aio tags #44
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -155,6 +155,38 @@ def find_release_commit(version: str) -> str: | |
| ) | ||
|
|
||
|
|
||
| def git_is_ancestor(ancestor: str, descendant: str) -> bool: | ||
| return ( | ||
| git_completed("merge-base", "--is-ancestor", ancestor, descendant).returncode | ||
| == 0 | ||
| ) | ||
|
|
||
|
|
||
| def find_release_target_commit(version: str) -> str: | ||
| release_commit = find_release_commit(version) | ||
| head = git_output("rev-parse", "HEAD").strip() | ||
|
|
||
| if release_commit == head: | ||
| return release_commit | ||
|
|
||
| if not git_is_ancestor(release_commit, head): | ||
| raise SystemExit( | ||
| f"Release commit {release_commit} for {version} is not reachable from HEAD." | ||
| ) | ||
|
|
||
| first_parent_commits = git_output("rev-list", "--first-parent", "HEAD").splitlines() | ||
| if release_commit in first_parent_commits: | ||
| return release_commit | ||
|
|
||
| first_parent_path = git_output( | ||
| "rev-list", "--first-parent", "--reverse", f"{release_commit}..HEAD" | ||
| ).splitlines() | ||
| if first_parent_path: | ||
| return first_parent_path[0] | ||
|
Comment on lines
+182
to
+185
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When Useful? React with 👍 / 👎. |
||
|
|
||
| return release_commit | ||
|
|
||
|
|
||
| def main() -> None: | ||
| parser = argparse.ArgumentParser(description="Release helpers for AIO repos.") | ||
| subparsers = parser.add_subparsers(dest="command", required=True) | ||
|
|
@@ -191,6 +223,8 @@ def main() -> None: | |
| ) | ||
| commit_parser = subparsers.add_parser("find-release-commit") | ||
| commit_parser.add_argument("version") | ||
| target_parser = subparsers.add_parser("find-release-target-commit") | ||
| target_parser.add_argument("version") | ||
| args = parser.parse_args() | ||
| if args.command == "upstream-version": | ||
| print(read_upstream_version(args.dockerfile, args.upstream_config)) | ||
|
|
@@ -210,6 +244,8 @@ def main() -> None: | |
| print(latest_changelog_version(args.changelog)) | ||
| elif args.command == "find-release-commit": | ||
| print(find_release_commit(args.version)) | ||
| elif args.command == "find-release-target-commit": | ||
| print(find_release_target_commit(args.version)) | ||
| else: | ||
| print(extract_release_notes(args.version, args.changelog)) | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,63 @@ | ||
| from __future__ import annotations | ||
|
|
||
| from subprocess import ( # nosec B404 - tests construct return objects only | ||
| CompletedProcess, | ||
| ) | ||
|
|
||
| import pytest | ||
|
|
||
| from scripts import release | ||
|
|
||
|
|
||
| def test_find_release_target_commit_returns_squash_release_commit( | ||
| monkeypatch: pytest.MonkeyPatch, | ||
| ) -> None: | ||
| def fake_git_output(*args: str) -> str: | ||
| if args == ("log", "--format=%H\t%s", "HEAD"): | ||
| return "release-sha\tchore(release): v2.0.0-aio.3\n" | ||
| if args == ("rev-parse", "HEAD"): | ||
| return "release-sha\n" | ||
| raise AssertionError(f"unexpected git_output args: {args}") | ||
|
|
||
| monkeypatch.setattr(release, "git_output", fake_git_output) | ||
|
|
||
| assert ( | ||
| release.find_release_target_commit("v2.0.0-aio.3") == "release-sha" | ||
| ) # nosec B101 | ||
|
|
||
|
|
||
| def test_find_release_target_commit_returns_merge_commit( | ||
| monkeypatch: pytest.MonkeyPatch, | ||
| ) -> None: | ||
| def fake_git_output(*args: str) -> str: | ||
| if args == ("log", "--format=%H\t%s", "HEAD"): | ||
| return "\n".join( | ||
| [ | ||
| "merge-sha\tMerge pull request #43 from JSONbored/release/v2.0.0-aio.3", | ||
| "release-sha\tchore(release): v2.0.0-aio.3", | ||
| ] | ||
| ) | ||
| if args == ("rev-parse", "HEAD"): | ||
| return "merge-sha\n" | ||
| if args == ("rev-list", "--first-parent", "HEAD"): | ||
| return "merge-sha\nprevious-main-sha\n" | ||
| if args == ( | ||
| "rev-list", | ||
| "--first-parent", | ||
| "--reverse", | ||
| "release-sha..HEAD", | ||
| ): | ||
| return "merge-sha\n" | ||
| raise AssertionError(f"unexpected git_output args: {args}") | ||
|
|
||
| def fake_git_completed(*args: str) -> CompletedProcess[str]: | ||
| if args == ("merge-base", "--is-ancestor", "release-sha", "merge-sha"): | ||
| return CompletedProcess(args=args, returncode=0) | ||
| raise AssertionError(f"unexpected git_completed args: {args}") | ||
|
|
||
| monkeypatch.setattr(release, "git_output", fake_git_output) | ||
| monkeypatch.setattr(release, "git_completed", fake_git_completed) | ||
|
|
||
| assert ( | ||
| release.find_release_target_commit("v2.0.0-aio.3") == "merge-sha" | ||
| ) # nosec B101 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This condition makes every
workflow_dispatchrun onmainassignrelease_package_tageven whenGITHUB_SHAis not the resolved release target commit. If someone manually reruns CI after newer commits land, the existing*-aio.Ntag can be republished from non-release code, breaking release tag immutability and reproducibility for users pulling that version tag.Useful? React with 👍 / 👎.