Index
repo_scaffold.github_init ¶
GitHub bootstrap helpers for repo-scaffold gh-init.
Three layers, split across this package:
- :mod:
repo_scaffold.github_init.config—GhInitConfig/GhInitResultandbuild_configcollect the repo metadata, secrets, and variables to apply. - :mod:
repo_scaffold.github_init.client—GhInitClient, a thin wrapper around PyGithub so tests can swap the whole client out without monkey-patching the SDK. - this module —
init_repositoryorchestrates the calls and returns the URLs the CLI prints back to the user, plus thegit_push/deploy_docssubprocess helpers.
git_push performs the optional initial commit + push via subprocess, with no
extra dependency on GitPython.
GhInitClient ¶
Tiny PyGithub wrapper that orchestrator and tests both consume.
Construct a client backed by the given personal access token.
Source code in repo_scaffold/github_init/client.py
token
property
¶
The personal access token this client authenticates with.
Exposed so the orchestrator can feed it to the non-interactive
git push / mkdocs gh-deploy subprocesses (see git_push and
deploy_docs), which need it to authenticate an HTTPS remote that has
no credential helper and no TTY.
authenticated_login ¶
Return the login of the token's user. Raises on a bad token.
enable_pages ¶
Configure GitHub Pages to deploy from branch/path.
PyGithub 2.x has no Pages helper, so this calls the REST API directly:
POST .../pages to create the site, falling back to PUT to update
the source when a site already exists.
Source code in repo_scaffold/github_init/client.py
get_or_create_repo ¶
get_or_create_repo(
owner: str | None,
name: str,
*,
description: str,
private: bool,
allow_existing: bool,
) -> Repository
Create the repo on the right account, or look it up if already there.
Source code in repo_scaffold/github_init/client.py
protect_branch ¶
Enable branch protection on branch (requires admin on the repo).
Rules are intentionally compatible with the generated version-bump
workflow: enforce_admins is left off so the release token (owned by
a repo admin) can still push chore(version): commits and tags
directly. Branch protection is unavailable on private repos without a
paid GitHub plan; such a failure surfaces to the caller.
Source code in repo_scaffold/github_init/client.py
set_homepage ¶
set_secret ¶
Create or replace an Actions secret. create_secret is PUT-based.
set_variable ¶
Create an Actions variable, falling back to edit if it exists.
Source code in repo_scaffold/github_init/client.py
GhInitConfig
dataclass
¶
GhInitConfig(
project_path: Path,
name: str,
description: str,
private: bool,
default_branch: str,
secrets: dict[str, str] = dict(),
variables: dict[str, str] = dict(),
owner: str | None = None,
push: bool = True,
force_push: bool = False,
allow_existing: bool = False,
setup_pages: bool = True,
protect_branch: bool = False,
)
Resolved configuration for one gh-init invocation.
GhInitResult
dataclass
¶
GhInitResult(
html_url: str,
actions_url: str,
pages_url: str,
skipped_secrets: list[str],
pushed: bool,
pages_configured: bool = False,
pages_branch: str = PAGES_BRANCH,
pages_error: str | None = None,
homepage_set: bool = False,
branch_protected: bool = False,
protection_error: str | None = None,
)
Outputs surfaced to the CLI after a successful run.
_github_error_message ¶
Best-effort human-readable message from a GithubException.
Source code in repo_scaffold/github_init/__init__.py
build_config ¶
build_config(
project_path: Path,
*,
owner: str | None = None,
name: str | None = None,
description: str | None = None,
private: bool = False,
default_branch: str | None = None,
push: bool = True,
force_push: bool = False,
allow_existing: bool = False,
setup_pages: bool = True,
protect_branch: bool = False,
extra_env: dict[str, str] | None = None,
prompter: Callable[[str, str | None], str]
| None = None,
) -> GhInitConfig
Resolve a GhInitConfig from CLI args, environment, .env, and pyproject.
Precedence per key (highest first): explicit kwarg, extra_env (used for
secrets/variables and for pulling values from os.environ at the call
site), then the project's .env file, then pyproject.toml defaults,
then prompter if provided.
prompter receives (key, default) and returns the user's answer, or
an empty string to skip. When prompter is None (i.e. --no-input)
missing optional values are simply dropped.
Source code in repo_scaffold/github_init/config.py
156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 | |
deploy_docs ¶
Build the MkDocs site and push it to the gh-pages branch.
Runs the project's deploy-gh-pages just recipe (mkdocs gh-deploy
--force), which builds with the right docs dependency group/extra, writes
.nojekyll, and pushes to origin. Raises RuntimeError on failure
so the orchestrator can surface it without aborting the whole bootstrap.
mkdocs gh-deploy shells out to git push itself, so when token is
given the PAT is injected via GIT_CONFIG_PARAMETERS (an
http.extraheader Authorization: Basic header) and the terminal
prompt is disabled. This authenticates the push non-interactively without
ever writing the token to .git/config.
Source code in repo_scaffold/github_init/__init__.py
detect_default_branch ¶
Return the current local git branch name, or None if not a git repo.
Source code in repo_scaffold/github_init/config.py
detect_owner ¶
Best-effort GitHub owner for the project and the source it came from.
Resolution order: a github.com/<owner>/... URL in pyproject.toml's
[project.urls], then the Cocogitto [changelog].owner field in
cog.toml. Returns (None, None) when no owner can be determined, in
which case the caller falls back to the authenticated user.
Source code in repo_scaffold/github_init/config.py
git_push ¶
git_push(
project_path: Path,
remote_url: str,
branch: str,
force: bool,
*,
token: str | None = None,
) -> None
Initialize git in project_path if needed, commit, and push to origin.
Steps
git initif no.gitdirectory exists.- Stage everything and create an initial commit (only when there is no existing HEAD — re-runs against an already-initialized repo skip this).
- Rename the current branch to
branch. - (Re-)add
originpointing atremote_url. - Push
branchtooriginwith-u(and optionally--force).
remote_url is the repo's plain HTTPS clone URL (no embedded creds), so a
non-interactive push has no way to authenticate: there is no TTY under
capture_output and no credential helper is assumed. When token is
given, the PAT is sent as an Authorization: Basic header via a one-shot
-c http.extraheader=... so the push authenticates without the token ever
being written to .git/config.
Source code in repo_scaffold/github_init/__init__.py
init_repository ¶
Apply the config to GitHub and (optionally) push the initial commit.
Source code in repo_scaffold/github_init/__init__.py
191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 | |
load_pyproject ¶
Return the [project] table from the project's pyproject.toml.
Source code in repo_scaffold/github_init/config.py
parse_dotenv ¶
Parse a tiny subset of dotenv syntax: KEY=VALUE lines, # comments.
Quoted values are unquoted. Anything more exotic (export, multiline, command substitution) is intentionally not supported — projects that need that should set real env vars before running gh-init.