def check_manifest(): """Check the project manifest""" if util.PYPROJECT.exists() or util.SETUP_PY.exists(): util.run("check-manifest -v") else: util.log( "Skipping check-manifest since there are no python package files")
def build_python(dist_dir): """Build Python dist files""" if not util.PYPROJECT.exists() and not util.SETUP_PY.exists(): util.log( "Skipping build-python since there are no python package files") return python.build_dist(dist_dir)
def check_python(dist_dir, test_cmd): """Check Python dist files""" for dist_file in glob(f"{dist_dir}/*"): if Path(dist_file).suffix not in [".gz", ".whl"]: util.log(f"Skipping non-python dist file {dist_file}") continue python.check_dist(dist_file, test_cmd=test_cmd)
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 extract_dist(dist_dir, target): """Extract dist files from a dist_dir into a target dir""" names = [] paths = sorted(glob(f"{dist_dir}/*.tgz")) util.log(f"Extracting {len(paths)} packages...") for package in paths: path = Path(package) data = extract_package(path) name = data["name"] # Skip if it is a private package if data.get("private", False): # pragma: no cover util.log(f"Skipping private package {name}") continue names.append(name) pkg_dir = target / name if not pkg_dir.parent.exists(): os.makedirs(pkg_dir.parent) tar = tarfile.open(path) tar.extractall(target) tar.close() shutil.move(str(target / "package"), str(pkg_dir)) return names
def prep_git(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() 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() and install: 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 start_local_pypi(): """Start a local PyPI server""" temp_dir = TemporaryDirectory() cmd = f"pypi-server -p 8081 -P . -a . -o -v {temp_dir.name}" proc = Popen(shlex.split(cmd), stderr=PIPE) # Wait for the server to start while True: line = proc.stderr.readline().decode("utf-8").strip() util.log(line) if "Listening on" in line: break atexit.register(proc.kill) atexit.register(temp_dir.cleanup)
def build_dist(package, dist_dir): """Build npm dist file(s) from a package""" # Clean the dist folder of existing npm tarballs os.makedirs(dist_dir, exist_ok=True) dest = Path(dist_dir) for pkg in glob(f"{dist_dir}/*.tgz"): os.remove(pkg) if osp.isdir(package): basedir = package tarball = osp.join(package, util.run("npm pack", cwd=package).split("\n")[-1]) else: basedir = osp.dirname(package) tarball = package data = extract_package(tarball) # Move the tarball into the dist folder if public if not data.get("private", False) == True: shutil.move(str(tarball), str(dest)) elif osp.isdir(package): os.remove(tarball) if not osp.isdir(package): return if "workspaces" in data: all_data = dict() for pattern in _get_workspace_packages(data): for path in glob(osp.join(basedir, pattern), recursive=True): path = Path(path) package_json = path / "package.json" if not osp.exists(package_json): continue data = json.loads(package_json.read_text(encoding="utf-8")) if data.get("private", False) == True: continue data["__path__"] = path all_data[data["name"]] = data i = 0 for (name, data) in sorted(all_data.items()): i += 1 path = data["__path__"] util.log(f'({i}/{len(all_data)}) Packing {data["name"]}...') tarball = path / util.run("npm pack", cwd=path, quiet=True) shutil.move(str(tarball), str(dest))
def publish_assets(dist_dir, npm_token, npm_cmd, twine_cmd, dry_run, use_checkout_dir): """Publish release asset(s)""" if use_checkout_dir: if not osp.exists(util.CHECKOUT_NAME): raise ValueError("Please run prep-git first") os.chdir(util.CHECKOUT_NAME) if dry_run: # Start local pypi server with no auth, allowing overwrites, # in a temporary directory temp_dir = TemporaryDirectory() cmd = f"pypi-server -p 8081 -P . -a . -o -v {temp_dir.name}" proc = Popen(shlex.split(cmd), stderr=PIPE) # Wait for the server to start while True: line = proc.stderr.readline().decode("utf-8").strip() util.log(line) if "Listening on" in line: break atexit.register(proc.kill) atexit.register(temp_dir.cleanup) twine_cmd = "twine upload --repository-url=http://localhost:8081" os.environ["TWINE_USERNAME"] = "******" os.environ["TWINE_PASSWORD"] = "******" npm_cmd = "npm publish --dry-run" if npm_token: npm.handle_auth_token(npm_token) found = False for path in glob(f"{dist_dir}/*.*"): name = Path(path).name suffix = Path(path).suffix if suffix in [".gz", ".whl"]: util.run(f"{twine_cmd} {name}", cwd=dist_dir) found = True elif suffix == ".tgz": util.run(f"{npm_cmd} {name}", cwd=dist_dir) found = True else: util.log(f"Nothing to upload for {name}") if not found: # pragma: no cover raise ValueError("No assets published, refusing to finalize release")
def tag_workspace_packages(): """Generate tags for npm workspace packages""" if not PACKAGE_JSON.exists(): return data = json.loads(PACKAGE_JSON.read_text(encoding="utf-8")) tags = util.run("git tag").splitlines() if not "workspaces" in data: return for pattern in _get_workspace_packages(data): for path in glob(pattern, recursive=True): sub_package_json = Path(path) / "package.json" sub_data = json.loads(sub_package_json.read_text(encoding="utf-8")) tag_name = f"{sub_data['name']}@{sub_data['version']}" if tag_name in tags: util.log(f"Skipping existing tag {tag_name}") else: util.run(f"git tag {tag_name}")
def publish_release(auth, dist_dir, npm_token, npm_cmd, twine_cmd, dry_run, release_url): """Publish release asset(s) and finalize GitHub release""" util.log(f"Publishing {release_url} in with dry run: {dry_run}") match = parse_release_url(release_url) if npm_token: npm.handle_auth_token(npm_token) found = False for path in glob(f"{dist_dir}/*.*"): name = Path(path).name suffix = Path(path).suffix if suffix in [".gz", ".whl"]: util.run(f"{twine_cmd} {name}", cwd=dist_dir) found = True elif suffix == ".tgz": util.run(f"{npm_cmd} {name}", cwd=dist_dir) found = True else: util.log(f"Nothing to upload for {name}") if not found: # pragma: no cover raise ValueError("No assets published, refusing to finalize release") # Take the release out of draft gh = GhApi(owner=match["owner"], repo=match["repo"], token=auth) release = util.release_for_url(gh, release_url) release = gh.repos.update_release( release.id, release.tag_name, release.target_commitish, release.name, release.body, dry_run, release.prerelease, ) # Set the GitHub action output util.actions_output("release_url", release.html_url)
def publish_release(auth, release_url): """Publish GitHub release""" util.log(f"Publishing {release_url}") match = parse_release_url(release_url) # Take the release out of draft gh = GhApi(owner=match["owner"], repo=match["repo"], token=auth) release = util.release_for_url(gh, release_url) release = gh.repos.update_release( release.id, release.tag_name, release.target_commitish, release.name, release.body, False, release.prerelease, ) # Set the GitHub action output util.actions_output("release_url", release.html_url)
def publish_assets(dist_dir, npm_token, npm_cmd, twine_cmd, dry_run, use_checkout_dir): """Publish release asset(s)""" if use_checkout_dir: if not osp.exists(util.CHECKOUT_NAME): raise ValueError("Please run prep-git first") os.chdir(util.CHECKOUT_NAME) if dry_run: # Start local pypi server with no auth, allowing overwrites, # in a temporary directory if len(glob(f"{dist_dir}/*.whl")): python.start_local_pypi() twine_cmd = "twine upload --repository-url=http://localhost:8081" os.environ["TWINE_USERNAME"] = "******" os.environ["TWINE_PASSWORD"] = "******" npm_cmd = "npm publish --dry-run" else: os.environ.setdefault("TWINE_USERNAME", "__token__") if len(glob(f"{dist_dir}/*.tgz")): npm.handle_npm_config(npm_token) found = False for path in glob(f"{dist_dir}/*.*"): name = Path(path).name suffix = Path(path).suffix if suffix in [".gz", ".whl"]: util.run(f"{twine_cmd} {name}", cwd=dist_dir) found = True elif suffix == ".tgz": util.run(f"{npm_cmd} {name}", cwd=dist_dir) found = True else: util.log(f"Nothing to upload for {name}") if not found: # pragma: no cover raise ValueError("No assets published, refusing to finalize release")
def check_npm(dist_dir, test_cmd): """Check npm package""" if not osp.exists("./package.json"): util.log("Skipping check-npm since there is no package.json file") return npm.check_dist(dist_dir, test_cmd=test_cmd)
def extract_release(auth, dist_dir, dry_run, release_url): """Download and verify assets from a draft GitHub release""" match = parse_release_url(release_url) owner, repo = match["owner"], match["repo"] gh = GhApi(owner=owner, repo=repo, token=auth) release = util.release_for_url(gh, release_url) assets = release.assets # Clean the dist folder dist = Path(dist_dir) if dist.exists(): shutil.rmtree(dist, ignore_errors=True) os.makedirs(dist) # Fetch, validate, and publish assets for asset in assets: util.log(f"Fetching {asset.name}...") url = asset.url headers = dict(Authorization=f"token {auth}", Accept="application/octet-stream") path = dist / asset.name with requests.get(url, headers=headers, stream=True) as r: r.raise_for_status() with open(path, "wb") as f: for chunk in r.iter_content(chunk_size=8192): f.write(chunk) suffix = Path(asset.name).suffix if suffix in [".gz", ".whl"]: python.check_dist(path) elif suffix == ".tgz": npm.check_dist(path) else: util.log(f"Nothing to check for {asset.name}") # Skip sha validation for dry runs since the remote tag will not exist if dry_run: return branch = release.target_commitish tag_name = release.tag_name sha = None for tag in gh.list_tags(): if tag.ref == f"refs/tags/{tag_name}": sha = tag.object.sha if sha is None: raise ValueError("Could not find tag") # Run a git checkout # Fetch the branch # Get the commmit message for the branch commit_message = "" with TemporaryDirectory() as td: url = gh.repos.get().html_url util.run(f"git clone {url} local", cwd=td) checkout = osp.join(td, "local") if not osp.exists(url): util.run(f"git fetch origin {branch}", cwd=checkout) commit_message = util.run(f"git log --format=%B -n 1 {sha}", cwd=checkout) for asset in assets: # Check the sha against the published sha valid = False path = dist / asset.name sha = util.compute_sha256(path) for line in commit_message.splitlines(): if asset.name in line: if sha in line: valid = True else: util.log("Mismatched sha!") if not valid: # pragma: no cover raise ValueError(f"Invalid file {asset.name}")
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 get_version_entry(branch, repo, version, *, auth=None, resolve_backports=False): """Get a changelog for the changes since the last tag on the given branch. Parameters ---------- branch : str The target branch respo : str The GitHub owner/repo version : str The new version auth : str, optional The GitHub authorization token resolve_backports: bool, optional Whether to resolve backports to the original PR Returns ------- str A formatted changelog entry with markers """ tags = util.run( f"git --no-pager tag --sort=-creatordate --merged {branch}") if not tags: # pragma: no cover raise ValueError(f"No tags found on branch {branch}") since = tags.splitlines()[0] branch = branch.split("/")[-1] util.log(f"Getting changes to {repo} since {since} on branch {branch}...") md = generate_activity_md(repo, since=since, kind="pr", heading_level=2, auth=auth, branch=branch) if not md: util.log("No PRs found") return f"## {version}\n\nNo merged PRs" entry = md.replace("[full changelog]", "[Full Changelog]") entry = entry.splitlines()[2:] for (ind, line) in enumerate(entry): if re.search(r"\[@meeseeksmachine\]", line) is not None: match = re.search(r"Backport PR #(\d+)", line) if match: entry[ind] = format_pr_entry(repo, match.groups()[0]) # Remove github actions PRs gh_actions = "[@github-actions](https://github.com/github-actions)" entry = [e for e in entry if gh_actions not in e] # Remove automated changelog PRs entry = [e for e in entry if PR_PREFIX not in e] entry = "\n".join(entry).strip() # Replace "*" unordered list marker with "-" since this is what # Prettier uses # TODO: remove after github_activity 0.1.4+ is available entry = re.sub(r"^\* ", "- ", entry) entry = re.sub(r"\n\* ", "\n- ", entry) output = f""" ## {version} {entry} """.strip() return output
def invoke(self, ctx): """Handle jupyter-releaser config while invoking a command""" # Get the command name and make sure it is valid cmd_name = ctx.protected_args[0] if not cmd_name in self.commands: super().invoke(ctx) if cmd_name == "list-envvars": envvars = dict() for cmd_name in self.commands: for param in self.commands[cmd_name].params: if isinstance(param, click.Option): if param.envvar: envvars[param.name] = param.envvar for key in sorted(envvars): util.log(f"{key.replace('_', '-')}: {envvars[key]}") return orig_dir = os.getcwd() if cmd_name.replace("-", "_") in self._needs_checkout_dir: if not osp.exists(util.CHECKOUT_NAME): raise ValueError("Please run prep-git first") os.chdir(util.CHECKOUT_NAME) # Read in the config config = util.read_config() hooks = config.get("hooks", {}) options = config.get("options", {}) # Print a separation header util.log(f'\n\n{"-" * 50}') util.log(cmd_name) util.log(f'{"-" * 50}\n\n') # Handle all of the parameters for param in self.commands[cmd_name].get_params(ctx): # Defer to env var overrides if param.envvar and os.environ.get(param.envvar): continue name = param.name if name in options or name.replace("_", "-") in options: arg = f"--{name.replace('_', '-')}" # Defer to cli overrides if arg not in ctx.args: val = options.get(name, options.get(name.replace("_", "-"))) if isinstance(val, list): for v in val: ctx.args.append(arg) ctx.args.append(v) else: ctx.args.append(arg) ctx.args.append(val) # Handle before hooks before = f"before-{cmd_name}" if before in hooks: before_hooks = hooks[before] if isinstance(before_hooks, str): before_hooks = [before_hooks] for hook in before_hooks: util.run(hook) # Run the actual command super().invoke(ctx) # Handle after hooks after = f"after-{cmd_name}" if after in hooks: after_hooks = hooks[after] if isinstance(after_hooks, str): after_hooks = [after_hooks] for hook in after_hooks: util.run(hook) os.chdir(orig_dir)
def forwardport_changelog( auth, ref, branch, repo, username, changelog_path, dry_run, git_url, release_url ): """Forwardport Changelog Entries to the Default Branch""" # Set up the git repo with the branch match = parse_release_url(release_url) gh = GhApi(owner=match["owner"], repo=match["repo"], token=auth) release = util.release_for_url(gh, release_url) tag = release.tag_name repo = f'{match["owner"]}/{match["repo"]}' # We want to target the main branch orig_dir = os.getcwd() branch = prep_git(None, None, repo, auth, username, git_url, install=False) os.chdir(util.CHECKOUT_NAME) # Bail if the tag has been merged to the branch tags = util.run(f"git --no-pager tag --merged {branch}") if tag in tags.splitlines(): util.log(f"Skipping since tag is already merged into {branch}") return # Get the entry for the tag util.run(f"git checkout {tag}") entry = changelog.extract_current(changelog_path) # Get the previous header for the branch full_log = Path(changelog_path).read_text(encoding="utf-8") start = full_log.index(changelog.END_MARKER) prev_header = "" for line in full_log[start:].splitlines(): if line.strip().startswith("#"): prev_header = line break if not prev_header: raise ValueError("No anchor for previous entry") # Check out the branch again util.run(f"git checkout -B {branch} origin/{branch}") default_entry = changelog.extract_current(changelog_path) # Look for the previous header default_log = Path(changelog_path).read_text(encoding="utf-8") if not prev_header in default_log: util.log( f'Could not find previous header "{prev_header}" in {changelog_path} on branch {branch}' ) return # If the previous header is the current entry in the default branch, we need to move the change markers if prev_header in default_entry: default_log = changelog.insert_entry(default_log, entry) # Otherwise insert the new entry ahead of the previous header else: insertion_point = default_log.index(prev_header) default_log = changelog.format( default_log[:insertion_point] + entry + default_log[insertion_point:] ) Path(changelog_path).write_text(default_log, encoding="utf-8") # Create a forward port PR title = f"{changelog.PR_PREFIX} Forward Ported from {tag}" commit_message = f'git commit -a -m "{title}"' body = title pr = make_changelog_pr( auth, ref, branch, repo, title, commit_message, body, dry_run=dry_run ) # Clean up after ourselves os.chdir(orig_dir) shutil.rmtree(util.CHECKOUT_NAME)
def publish_release(auth, dist_dir, npm_token, npm_cmd, twine_cmd, dry_run, release_url): """Publish release asset(s) and finalize GitHub release""" util.log(f"Publishing {release_url} in with dry run: {dry_run}") if dry_run: # Start local pypi server with no auth, allowing overwrites, # in a temporary directory temp_dir = TemporaryDirectory() cmd = f"pypi-server -p 8081 -P . -a . -o -v {temp_dir.name}" proc = Popen(shlex.split(cmd), stderr=PIPE) # Wait for the server to start while True: line = proc.stderr.readline().decode("utf-8").strip() util.log(line) if "Listening on" in line: break atexit.register(proc.kill) atexit.register(temp_dir.cleanup) twine_cmd = "twine upload --repository-url=http://localhost:8081" os.environ["TWINE_USERNAME"] = "******" os.environ["TWINE_PASSWORD"] = "******" npm_cmd = "npm publish --dry-run" match = parse_release_url(release_url) if npm_token: npm.handle_auth_token(npm_token) found = False for path in glob(f"{dist_dir}/*.*"): name = Path(path).name suffix = Path(path).suffix if suffix in [".gz", ".whl"]: util.run(f"{twine_cmd} {name}", cwd=dist_dir) found = True elif suffix == ".tgz": util.run(f"{npm_cmd} {name}", cwd=dist_dir) found = True else: util.log(f"Nothing to upload for {name}") if not found: # pragma: no cover raise ValueError("No assets published, refusing to finalize release") # Take the release out of draft gh = GhApi(owner=match["owner"], repo=match["repo"], token=auth) release = util.release_for_url(gh, release_url) release = gh.repos.update_release( release.id, release.tag_name, release.target_commitish, release.name, release.body, dry_run, release.prerelease, ) # Set the GitHub action output util.actions_output("release_url", release.html_url)
def build_npm(package, dist_dir): """Build npm package""" if not osp.exists("./package.json"): util.log("Skipping check-npm since there is no package.json file") return npm.build_dist(package, dist_dir)
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 --force") # 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 if install: # install python package with test deps if util.SETUP_PY.exists(): util.run('pip install ".[test]"') # prefer yarn if yarn lock exists elif util.YARN_LOCK.exists(): util.run("npm install -g yarn") util.run("yarn") # npm install otherwise elif util.PACKAGE_JSON.exists(): util.run("npm install") os.chdir(orig_dir) return branch