mirror of
https://github.com/davidism/modify-repos.git
synced 2025-08-09 11:28:04 +00:00
Compare commits
18 commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
4901f86f5e | ||
![]() |
b675431f94 | ||
![]() |
37fc6104ce | ||
![]() |
3a7c0d8874 | ||
![]() |
4ecdea1a73 | ||
![]() |
0c6e5ed62b | ||
![]() |
3e778fb77e | ||
![]() |
4dba24283e | ||
![]() |
d7dbd26e42 | ||
![]() |
7ab21ffd32 | ||
![]() |
d80175d9a2 | ||
![]() |
1c7b8fff96 | ||
![]() |
409fe0ef5c | ||
![]() |
353ba6a098 | ||
![]() |
b0b6e11dcc | ||
![]() |
d968a7e815 | ||
![]() |
78bbd954d9 | ||
![]() |
dff0232764 |
10 changed files with 102 additions and 18 deletions
2
.github/workflows/pre-commit.yaml
vendored
2
.github/workflows/pre-commit.yaml
vendored
|
@ -20,6 +20,6 @@ jobs:
|
||||||
with:
|
with:
|
||||||
path: ~/.cache/pre-commit
|
path: ~/.cache/pre-commit
|
||||||
key: pre-commit|${{ hashFiles('pyproject.toml', '.pre-commit-config.yaml') }}
|
key: pre-commit|${{ hashFiles('pyproject.toml', '.pre-commit-config.yaml') }}
|
||||||
- run: uv run --group pre-commit pre-commit run --show-diff-on-failure --color=always --all-files
|
- run: uv run --locked --group pre-commit pre-commit run --show-diff-on-failure --color=always --all-files
|
||||||
- uses: pre-commit-ci/lite-action@5d6cc0eb514c891a40562a58a8e71576c5c7fb43 # v1.1.0
|
- uses: pre-commit-ci/lite-action@5d6cc0eb514c891a40562a58a8e71576c5c7fb43 # v1.1.0
|
||||||
if: ${{ !cancelled() }}
|
if: ${{ !cancelled() }}
|
||||||
|
|
3
.github/workflows/tests.yaml
vendored
3
.github/workflows/tests.yaml
vendored
|
@ -5,7 +5,6 @@ on:
|
||||||
push:
|
push:
|
||||||
branches: [main, stable]
|
branches: [main, stable]
|
||||||
paths: ['src/**', 'pyproject.toml']
|
paths: ['src/**', 'pyproject.toml']
|
||||||
tags: ['*']
|
|
||||||
jobs:
|
jobs:
|
||||||
typing:
|
typing:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
@ -23,4 +22,4 @@ jobs:
|
||||||
with:
|
with:
|
||||||
path: ./.mypy_cache
|
path: ./.mypy_cache
|
||||||
key: mypy|${{ hashFiles('pyproject.toml') }}
|
key: mypy|${{ hashFiles('pyproject.toml') }}
|
||||||
- run: uv run tox run -e typing
|
- run: uv run --locked tox run -e typing
|
||||||
|
|
15
CHANGES.md
15
CHANGES.md
|
@ -1,3 +1,18 @@
|
||||||
|
## Version 0.2.0
|
||||||
|
|
||||||
|
Released 2025-01-14
|
||||||
|
|
||||||
|
- If a PR already exists from a previous failed run, force push to update the
|
||||||
|
existing branch and PR.
|
||||||
|
- Add shortcut `repo.commit(message)` method.
|
||||||
|
|
||||||
|
## Version 0.1.1
|
||||||
|
|
||||||
|
Released 2025-01-14
|
||||||
|
|
||||||
|
- Submit is disabled by default.
|
||||||
|
- Error if target and work branch are the same.
|
||||||
|
|
||||||
## Version 0.1.0
|
## Version 0.1.0
|
||||||
|
|
||||||
Released 2025-01-14
|
Released 2025-01-14
|
||||||
|
|
|
@ -44,3 +44,47 @@ develop and preview your changes first.
|
||||||
|
|
||||||
[uv]: https://docs.astral.sh/uv/
|
[uv]: https://docs.astral.sh/uv/
|
||||||
[gh]: https://cli.github.com/
|
[gh]: https://cli.github.com/
|
||||||
|
|
||||||
|
## Automatic Commit
|
||||||
|
|
||||||
|
After calling `modify`, the script will automatically add any tracked files
|
||||||
|
and create a commit if it detects there are uncommitted changes.
|
||||||
|
|
||||||
|
If you add a completely new file, it will not be tracked by Git yet, and this
|
||||||
|
won't be detected or committed. Therefore, you should call
|
||||||
|
{meth}`.Repo.add_files` to track any new files. Other modifications, such as
|
||||||
|
changing an existing file or using {meth}`.Repo.rm_files`, will already be
|
||||||
|
tracked by Git.
|
||||||
|
|
||||||
|
You can set {attr}`.GitRepo.add_untracked` to also detect and add completely
|
||||||
|
new untracked files. This is disabled by default as it might end up adding files
|
||||||
|
that were generated as a side effect of other changes.
|
||||||
|
|
||||||
|
```python
|
||||||
|
class MyScript(GitHubScript):
|
||||||
|
def modify(self, repo: GitHubRepo) -> None:
|
||||||
|
repo.add_untracked = True
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
## Merge vs PR
|
||||||
|
|
||||||
|
By default, the GitHub provider creates PRs. You can instruct a repo to merge
|
||||||
|
and push directly to the target instead. This is disabled by default because it
|
||||||
|
provides one less opportunity to ensure your script worked correctly.
|
||||||
|
|
||||||
|
Set {attr}`GitHubRepo.direct_submit` to `True` to enable this merge and push
|
||||||
|
behavior.
|
||||||
|
|
||||||
|
```python
|
||||||
|
class MyScript(GitHubScript):
|
||||||
|
def modify(self, repo: GitHubRepo) -> None:
|
||||||
|
repo.direct_submit = True
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
## Updating
|
||||||
|
|
||||||
|
You may have run your script with submit enabled, then noticed that more is
|
||||||
|
needed. If a branch and open PR already exist from a previous run of the script
|
||||||
|
that, a force push will be used to update.
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[project]
|
[project]
|
||||||
name = "modify-repos"
|
name = "modify-repos"
|
||||||
version = "0.1.0"
|
version = "0.2.0"
|
||||||
description = "Apply changes across multiple repos at once."
|
description = "Apply changes across multiple repos at once."
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
authors = [{ name = "David Lord" }]
|
authors = [{ name = "David Lord" }]
|
||||||
|
|
|
@ -5,6 +5,7 @@ from shutil import which
|
||||||
from subprocess import CompletedProcess
|
from subprocess import CompletedProcess
|
||||||
|
|
||||||
from ..utils import run_cmd
|
from ..utils import run_cmd
|
||||||
|
from ..utils import wrap_text
|
||||||
from .base import Repo
|
from .base import Repo
|
||||||
|
|
||||||
|
|
||||||
|
@ -64,6 +65,20 @@ class GitRepo(Repo):
|
||||||
self.git_cmd("merge", "--ff-only", self.script.branch)
|
self.git_cmd("merge", "--ff-only", self.script.branch)
|
||||||
self.git_cmd("push")
|
self.git_cmd("push")
|
||||||
|
|
||||||
|
def commit(self, message: str, add: bool = False) -> None:
|
||||||
|
"""Create a commit with the given message.
|
||||||
|
|
||||||
|
:param message: The commit message.
|
||||||
|
:param add: Update tracked files while committing. Disabled by default.
|
||||||
|
Alternatively, call {meth}`add_files` first.
|
||||||
|
"""
|
||||||
|
args = ["commit", "--message", wrap_text(message, width=72)]
|
||||||
|
|
||||||
|
if add:
|
||||||
|
args.insert(1, "-a")
|
||||||
|
|
||||||
|
self.git_cmd(*args)
|
||||||
|
|
||||||
def add_files(
|
def add_files(
|
||||||
self, *items: str | Path, update: bool = False, all: bool = False
|
self, *items: str | Path, update: bool = False, all: bool = False
|
||||||
) -> None:
|
) -> None:
|
||||||
|
|
|
@ -66,14 +66,21 @@ class GitHubRepo(GitRepo):
|
||||||
super().submit()
|
super().submit()
|
||||||
return
|
return
|
||||||
|
|
||||||
self.git_cmd("push", "--set-upstream", "origin", self.script.branch)
|
result = self.gh_cmd("pr", "view", "--json", "closed", "--jq", ".closed")
|
||||||
self.gh_cmd(
|
has_pr = not result.returncode and result.stdout.strip() == "false"
|
||||||
"pr",
|
|
||||||
"create",
|
if not has_pr:
|
||||||
"--base",
|
self.git_cmd("push", "--set-upstream", "origin", self.script.branch)
|
||||||
self.script.target,
|
self.gh_cmd(
|
||||||
"--title",
|
"pr",
|
||||||
self.script.title,
|
"create",
|
||||||
"--body",
|
"--base",
|
||||||
self.script.body,
|
self.script.target,
|
||||||
)
|
"--title",
|
||||||
|
self.script.title,
|
||||||
|
"--body",
|
||||||
|
self.script.body,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
# If open PR already exists from previous run, force push.
|
||||||
|
self.git_cmd("push", "--force", "origin", self.script.branch)
|
||||||
|
|
|
@ -41,7 +41,11 @@ class Script[RepoType: Repo]:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, *, submit: bool = False) -> None:
|
def __init__(self, *, submit: bool = False) -> None:
|
||||||
""" """
|
if self.target == self.branch:
|
||||||
|
raise ValueError(
|
||||||
|
"Work branch name must be different than target branch name."
|
||||||
|
)
|
||||||
|
|
||||||
source_file = inspect.getsourcefile(self.__class__)
|
source_file = inspect.getsourcefile(self.__class__)
|
||||||
|
|
||||||
if source_file is None:
|
if source_file is None:
|
||||||
|
|
|
@ -17,7 +17,7 @@ class GitHubScript(Script[GitHubRepo]):
|
||||||
orgs: list[str]
|
orgs: list[str]
|
||||||
"""The list of GitHub users/orgs to clone repositories from."""
|
"""The list of GitHub users/orgs to clone repositories from."""
|
||||||
|
|
||||||
def __init__(self, *, submit: bool = True, orgs: list[str] | None = None) -> None:
|
def __init__(self, *, submit: bool = False, orgs: list[str] | None = None) -> None:
|
||||||
super().__init__(submit=submit)
|
super().__init__(submit=submit)
|
||||||
|
|
||||||
if orgs is not None:
|
if orgs is not None:
|
||||||
|
|
2
uv.lock
generated
2
uv.lock
generated
|
@ -335,7 +335,7 @@ wheels = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "modify-repos"
|
name = "modify-repos"
|
||||||
version = "0.1.0"
|
version = "0.2.0"
|
||||||
source = { editable = "." }
|
source = { editable = "." }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "click" },
|
{ name = "click" },
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue