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 make_changelog_pr(auth, branch, repo, title, commit_message, body, dry_run=False): repo = repo or util.get_repo() # Make a new branch with a uuid suffix pr_branch = f"changelog-{uuid.uuid1().hex}" if not dry_run: util.run("git --no-pager diff") util.run("git stash") util.run(f"git fetch origin {branch}") util.run(f"git checkout -b {pr_branch} origin/{branch}") util.run("git stash apply") # Add a commit with the message util.run(commit_message) # Create the pull owner, repo_name = repo.split("/") gh = GhApi(owner=owner, repo=repo_name, token=auth) base = branch head = pr_branch maintainer_can_modify = True if dry_run: util.log("Skipping pull request due to dry run") return util.run(f"git push origin {pr_branch}") # title, head, base, body, maintainer_can_modify, draft, issue pull = gh.pulls.create(title, head, base, body, maintainer_can_modify, False, None) util.actions_output("pr_url", pull.html_url)
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 prep_git(branch, repo, auth, username, url): """Set up git""" repo = repo or util.get_repo() user_name = "" try: user_name = util.run("git config --global user.email") except Exception as e: pass if not user_name: # Use email address for the GitHub Actions bot # https://github.community/t/github-actions-bot-email-address/17204/6 util.run( 'git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com"' ) util.run('git config --global user.name "GitHub Action"') # Set up the repository checkout_dir = os.environ.get("RH_CHECKOUT_DIR", util.CHECKOUT_NAME) checkout_exists = False if osp.exists(osp.join(checkout_dir, ".git")): print("Git checkout already exists", file=sys.stderr) checkout_exists = True if not checkout_exists: util.run(f"git init {checkout_dir}") orig_dir = os.getcwd() os.chdir(checkout_dir) if not url: if auth: url = f"https://{username}:{auth}@github.com/{repo}.git" else: url = f"https://github.com/{repo}.git" if osp.exists(url): url = util.normalize_path(url) if not checkout_exists: util.run(f"git remote add origin {url}") branch = branch or util.get_default_branch() util.run(f"git fetch origin {branch}") # Make sure we have *all* tags util.run("git fetch origin --tags") util.run(f"git checkout {branch}") # Install the package with test deps if util.SETUP_PY.exists(): util.run('pip install ".[test]"') os.chdir(orig_dir) return branch
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 prep_git(ref, branch, repo, auth, username, url, install=True): """Set up git""" repo = repo or util.get_repo() user_name = "" try: user_name = util.run("git config --global user.email") except Exception as e: pass if not user_name: # Use email address for the GitHub Actions bot # https://github.community/t/github-actions-bot-email-address/17204/6 util.run( 'git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com"' ) util.run('git config --global user.name "GitHub Action"') # Set up the repository checkout_dir = os.environ.get("RH_CHECKOUT_DIR", util.CHECKOUT_NAME) checkout_exists = False if osp.exists(osp.join(checkout_dir, ".git")): util.log("Git checkout already exists") checkout_exists = True if not checkout_exists: util.run(f"git init {checkout_dir}") orig_dir = os.getcwd() os.chdir(checkout_dir) if not url: if auth: url = f"https://{username}:{auth}@github.com/{repo}.git" else: url = f"https://github.com/{repo}.git" if osp.exists(url): url = util.normalize_path(url) if not checkout_exists: util.run(f"git remote add origin {url}") branch = branch or util.get_default_branch() ref = ref or "" # Make sure we have *all* tags util.run("git fetch origin --tags") # Handle the ref if ref.startswith("refs/pull/"): pull = ref[len("refs/pull/") :] ref_alias = f"refs/pull/{pull}" else: ref = None # Reuse existing branch if possible if ref: util.run(f"git fetch origin +{ref}:{ref_alias}") util.run(f"git fetch origin {ref}") checkout_cmd = f"git checkout -B {branch} {ref_alias}" else: util.run(f"git fetch origin {branch}") checkout_cmd = f"git checkout {branch}" if checkout_exists: try: util.run(f"git checkout {branch}") except Exception: util.run(checkout_cmd) else: util.run(checkout_cmd) # Install the package with test deps if util.SETUP_PY.exists() and install: util.run('pip install ".[test]"') os.chdir(orig_dir) return branch
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_repo(git_repo, mocker): repo = f"{git_repo.parent.name}/{git_repo.name}" assert util.get_repo() == repo