def build_entry(branch, repo, auth, changelog_path, resolve_backports): """Build a python version entry""" repo = repo or util.get_repo() branch = branch or util.get_branch() # Get the new version version = util.get_version() # Get the existing changelog and run some validation changelog = Path(changelog_path).read_text(encoding="utf-8") if START_MARKER not in changelog or END_MARKER not in changelog: raise ValueError("Missing insert marker for changelog") if changelog.find(START_MARKER) != changelog.rfind(START_MARKER): raise ValueError("Insert marker appears more than once in changelog") # Get changelog entry entry = get_version_entry( f"origin/{branch}", repo, version, auth=auth, resolve_backports=resolve_backports, ) changelog = insert_entry(changelog, entry, version=version) Path(changelog_path).write_text(changelog, encoding="utf-8") # Stage changelog util.run(f"git add {util.normalize_path(changelog_path)}")
def draft_changelog(version_spec, branch, repo, auth, dry_run): """Create a changelog entry PR""" repo = repo or util.get_repo() branch = branch or util.get_branch() version = util.get_version() tags = util.run("git --no-pager tag") if f"v{version}" in tags.splitlines(): raise ValueError(f"Tag v{version} already exists") # Check out any unstaged files from version bump util.run("git checkout -- .") title = f"{changelog.PR_PREFIX} for {version} on {branch}" commit_message = f'git commit -a -m "{title}"' body = title # Check for multiple versions if util.PACKAGE_JSON.exists(): body += npm.get_package_versions(version) body += '\n\nAfter merging this PR run the "Draft Release" Workflow with the following inputs' body += f""" | Input | Value | | ------------- | ------------- | | Target | {repo} | | Branch | {branch} | | Version Spec | {version_spec} | """ util.log(body) make_changelog_pr(auth, branch, repo, title, commit_message, body, dry_run=dry_run)
def check_entry(branch, repo, auth, changelog_path, since, resolve_backports, output): """Check changelog entry""" branch = branch or util.get_branch() # Get the new version version = util.get_version() # Finalize changelog changelog = Path(changelog_path).read_text(encoding="utf-8") start = changelog.find(START_MARKER) end = changelog.find(END_MARKER) if start == -1 or end == -1: # pragma: no cover raise ValueError("Missing new changelog entry delimiter(s)") if start != changelog.rfind(START_MARKER): # pragma: no cover raise ValueError("Insert marker appears more than once in changelog") final_entry = changelog[start + len(START_MARKER):end] repo = repo or util.get_repo() raw_entry = get_version_entry( f"origin/{branch}", repo, version, since=since, auth=auth, resolve_backports=resolve_backports, ) if f"# {version}" not in final_entry: # pragma: no cover util.log(final_entry) raise ValueError(f"Did not find entry for {version}") final_prs = re.findall(r"\[#(\d+)\]", final_entry) raw_prs = re.findall(r"\[#(\d+)\]", raw_entry) for pr in raw_prs: # Allow for changelog PR to not be in changelog itself skip = False for line in raw_entry.splitlines(): if f"[#{pr}]" in line and "changelog" in line.lower(): skip = True break if skip: continue if not f"[#{pr}]" in final_entry: # pragma: no cover raise ValueError(f"Missing PR #{pr} in changelog") for pr in final_prs: if not f"[#{pr}]" in raw_entry: # pragma: no cover raise ValueError( f"PR #{pr} does not belong in changelog for {version}") if output: Path(output).write_text(final_entry, encoding="utf-8")
def tag_release(branch, repo, dist_dir, no_git_tag_workspace): """Create release commit and tag""" # Get the new version version = util.get_version() # Get the branch branch = branch or util.get_branch() # Create the release commit util.create_release_commit(version, dist_dir) # Create the annotated release tag tag_name = f"v{version}" util.run(f'git tag {tag_name} -a -m "Release {tag_name}"') # Create annotated release tags for workspace packages if given if not no_git_tag_workspace: npm.tag_workspace_packages()
def test_extract_dist_py(py_package, runner, mocker, open_mock, tmp_path, git_prep): changelog_entry = mock_changelog_entry(py_package, runner, mocker) # Create the dist files run("python -m build .", cwd=util.CHECKOUT_NAME) # Finalize the release runner(["tag-release"]) os.makedirs("staging") shutil.move(f"{util.CHECKOUT_NAME}/dist", "staging") def helper(path, **kwargs): return MockRequestResponse(f"staging/dist/{path}") get_mock = mocker.patch("requests.get", side_effect=helper) tag_name = f"v{VERSION_SPEC}" dist_names = [osp.basename(f) for f in glob("staging/dist/*.*")] releases = [ dict( tag_name=tag_name, target_commitish=util.get_branch(), assets=[ dict(name=dist_name, url=dist_name) for dist_name in dist_names ], ) ] sha = run("git rev-parse HEAD", cwd=util.CHECKOUT_NAME) tags = [dict(ref=f"refs/tags/{tag_name}", object=dict(sha=sha))] url = normalize_path(osp.join(os.getcwd(), util.CHECKOUT_NAME)) open_mock.side_effect = [ MockHTTPResponse(releases), MockHTTPResponse(tags), MockHTTPResponse(dict(html_url=url)), ] runner(["extract-release", HTML_URL]) assert len(open_mock.mock_calls) == 3 assert len(get_mock.mock_calls) == len(dist_names) == 2
def draft_release( ref, branch, repo, auth, changelog_path, version_cmd, dist_dir, dry_run, post_version_spec, assets, ): """Publish Draft GitHub release and handle post version bump""" branch = branch or util.get_branch() repo = repo or util.get_repo() assets = assets or glob(f"{dist_dir}/*") version = util.get_version() body = changelog.extract_current(changelog_path) prerelease = util.is_prerelease(version) # Bump to post version if given if post_version_spec: post_version = bump_version(post_version_spec, version_cmd) util.log(f"Bumped version to {post_version}") util.run(f'git commit -a -m "Bump to {post_version}"') if dry_run: return owner, repo_name = repo.split("/") gh = GhApi(owner=owner, repo=repo_name, token=auth) # Remove draft releases over a day old if bool(os.environ.get("GITHUB_ACTIONS")): for release in gh.repos.list_releases(): if str(release.draft).lower() == "false": continue created = release.created_at d_created = datetime.strptime(created, r"%Y-%m-%dT%H:%M:%SZ") delta = datetime.utcnow() - d_created if delta.days > 0: gh.repos.delete_release(release.id) remote_url = util.run("git config --get remote.origin.url") if not os.path.exists(remote_url): util.run(f"git push origin HEAD:{branch} --follow-tags --tags") util.log(f"Creating release for {version}") util.log(f"With assets: {assets}") release = gh.create_release( f"v{version}", branch, f"Release v{version}", body, True, prerelease, files=assets, ) # Set the GitHub action output util.actions_output("release_url", release.html_url)
def test_get_branch(git_repo): assert util.get_branch() == "bar" run("git checkout foo") assert util.get_branch() == "foo"
def test_prep_git_pr(py_package, runner): """With RH_BRANCH""" env = dict(RH_BRANCH="foo", GITHUB_ACTIONS="") result = runner(["prep-git", "--git-url", py_package], env=env) os.chdir(util.CHECKOUT_NAME) assert util.get_branch() == "foo", util.get_branch()
def test_prep_git_simple(py_package, runner): """Standard local run with no env variables.""" result = runner(["prep-git", "--git-url", py_package], env=dict(GITHUB_ACTIONS="")) os.chdir(util.CHECKOUT_NAME) assert util.get_branch() == "bar", util.get_branch()