GitHub Provider

This provider supports cloning repos from GitHub and creating PRs. It uses the [gh GitHub CLI], which must be installed and logged in already.

class modify_repos.GitHubScript(*, submit=False, orgs=None)

Bases: Script[GitHubRepo]

Subclass this to define how to select and modify GitHub repositories. Uses the GitHub CLI, which must already be installed and logged in.

Parameters:
  • submit (bool) – Whether to submit the changes. This is disabled by default, to give you a chance to develop the changes first.

  • orgs (list[str]) – The list of users/orgs to clone repositories from.

property commit_message: str

The message to use for the automatic commit in commit(). Defaults to title and body separated by a blank line.

property full_target: str

The upstream target branch, which is target prefixed by origin/.

list_repos()

Get the filtered list of repos that will be cloned. Override list_all_repos() and select_for_clone() to control what is returned here. Called by run().

Return type:

list[RepoType]

modify(repo)

Perform modifications to the repo. Called by run() while the current directory is the cloned repo dir.

If this leaves uncommitted changes, Repo.commit_if_needed() will detect that and commit automatically. You can also add and commit manually to skip that behavior.

Parameters:

repo (Repo) – The repo to modify.

Return type:

None

read_text(path, strip=True)

Read a text file, where path is relative to the script’s directory root_dir. The file will be read as UTF-8.

Parameters:
  • path (str | PathLike[str]) – Path to file to read. Relative paths are relative to root_dir.

  • strip (bool) – Strip leading and trailing empty spaces and lines. Enabled by default. The text is often formatted into an existing file, so stripping spaces makes working with it more predictable.

Return type:

str

render_template(name, /, **kwargs)

Render the named template file with context. Uses jinja_env, which finds templates next to the script file.

Parameters:
  • name (str) – Template name to load.

  • kwargs (Any) – Context to pass to the render call.

Return type:

str

run()

Call Repo.run() for each selected repo.

Return type:

None

select_for_clone(repo)

Select what repos are returned by list_repos(). Each repo from list_all_repos() is passed, and will be used if this method returns true for it.

For example, override this to return true if the repo name matches a set of names.

Parameters:

repo (Repo) – The repo to filter.

Return type:

bool

select_for_modify(repo)

Select whether modify() will be called on the repo. Called by run() while the current directory is the cloned repo dir.

For example, override this to return false if the repo does not contain a file to be removed, or already contains a file to be added.

Parameters:

repo (Repo) – The repo to filter.

Return type:

bool

target: str = 'main'

The name of the target branch to branch off of and merge into.

orgs: list[str]

The list of GitHub users/orgs to clone repositories from.

branch: str

The name of the work branch to create.

title: str

A short title describing the change. Used as the first line of the automatic commit, as well as the title of the PR. By convention, this should be at most 50 characters.

body: str

Additional description about the change. Used in the commit message after the title, separated by an empty line. Also used as the body of the PR. This will be re-wrapped to 72 characters to match convention.

root_dir: Path

The directory containing the running script. Used to reference resource files and templates.

jinja_env: jinja2.Environment

A Jinja environment configured to use root_dir as a template folder. See render_template().

clones_dir: Path

Directory where repos are cloned to. Uses the appropriate user cache dir for the platform.

enable_submit

Whether to submit the changes. This is disabled by default, to give you a chance to develop the changes first. It is set from the submit param.

list_all_repos()

Get the list of all repos that may be cloned. Override this to define how to generate this list. Called by list_repos().

Return type:

list[GitHubRepo]

class modify_repos.GitHubRepo(script, org, name)

Bases: GitRepo

Subclass this to define how to manipulate Git repositories. This extends the plain GitRepo to clone and create PRs using the GitHub CLI.

Parameters:
  • script (Script[t.Any]) – The script being run to modify this repo.

  • org (str) – The GitHub user/org that owns the repo.

  • name (str) – The GitHub repo name.

direct_submit: bool = False

Whether to merge and push directly to the target branch, rather than creating a PR. This is disabled by default as a PR will give more opportunity to review any mistakes with the automated changes.

gh_cmd(*args)

Call and pass args to the gh command.

Parameters:

args (str | Path) – Command line arguments to the gh command.

Return type:

CompletedProcess[str]

property full_name: str

The org/name identifier for the repo.

clone()

Clone the repository unconditionally. This is called by clone_if_needed().

Return type:

None

add_files(*items, update=False, all=False)

Call git add.

Parameters:
  • items (str | Path) – Files to add or update. Can be empty.

  • update (bool) – Update all tracked files.

  • all (bool) – Add all files, including untracked, excluding ignored.

Return type:

None

add_untracked: bool = False

Whether to consider untracked files when checking if there are changes and adding files in the auto commit(). By default this is false to avoid accidentally adding generated files, but this means you need to remember to call git_add() for any completely new files.

auto_commit()

Create a commit unconditionally. This is called by auto_commit_if_needed() to create the automatic commit when there are uncommitted changes, and should add those changes to the commit. It should not be called to create other intermediate commits. It should use Script.message as the commit message.

Return type:

None

auto_commit_if_needed()

Create a commit if there are uncommitted changes. Calls needs_commit() and auto_commit_if_needed(). Called by run().

Return type:

None

clone_if_needed()

Clone the repository if the local directory doesn’t exist. Calls clone(). Called by run().

Return type:

None

commit(message, add=False)

Create a commit with the given message.

Parameters:
  • message (str) – The commit message.

  • add (bool) – Update tracked files while committing. Disabled by default. Alternatively, call {meth}`add_files` first.

Return type:

None

git_cmd(*args)

Call and pass args to the git command.

Parameters:

args (str | Path) – Command line arguments to the git command.

Return type:

CompletedProcess[str]

property local_dir: Path

The path where this repo is cloned.

needs_commit()

Check if there are uncommitted changes. Called by auto_commit_if_needed().

Return type:

bool

needs_submit()

Check if there are commits that have not been pushed upstream. Called by submit_if_needed().

Return type:

bool

reset_branch()

Create or reset the work branch. This should ensure the branch is freshly created from the target branch. Called by run().

Return type:

None

reset_target()

Reset the base branch that will be branched off of and merged into. This should ensure the repository is clean and up to date, discarding any changes from previous unsuccessful runs. Called by run().

Return type:

None

rm_files(*items)

Call git rm for any given files that exist. Missing files are skipped so that the command doesn’t return an error.

Parameters:

items (str | Path) – Files to delete if they exist.

Return type:

None

run()

Run the full workflow for this repo: clone, reset, modify, commit, submit. Calls many of the other methods defined in this class. Called by Script.run().

Return type:

None

submit()

Submit the changes upstream. What this means depends on the implementation; whether it merges, creates a PR, or something else.

Return type:

None

submit_if_needed()

Submit the changes if there are any changes. Is disabled by default by Script.enable_submit, to prevent accidental submission of a script in development. Calls needs_submit() and submit(). Called by run().

Return type:

None

remote_id: str

A value that identifies where this repo was cloned from. The format depends on how the script finds repos.

script

The script being run to modify this repo.