Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix template sync #222

Merged
merged 44 commits into from
Nov 28, 2023
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
2b0dcee
Revert "fix typo (#165)"
grst May 1, 2023
c343b6c
Cruft PR workflow (#168)
flying-sheep Jun 1, 2023
40cb337
Fix `git clone` in cruft PR workflow (#196)
flying-sheep Jun 1, 2023
aee3566
depend on cruft in cruft PRs workflow (#197)
flying-sheep Jun 1, 2023
f2e3dca
Fix cruft invocation typo (#198)
flying-sheep Jun 1, 2023
66efb06
Use namespaced head in cruft PRs (#199)
flying-sheep Jun 1, 2023
f9a029b
Use user.login instead of user.name (#200)
flying-sheep Jun 1, 2023
1929d97
Finish cruft PRs (#201)
flying-sheep Jun 1, 2023
e03a54d
Cruft PRs: wait for fork creation (#202)
flying-sheep Jun 2, 2023
03ef73a
Another backoff for cruft (#207)
grst Jun 6, 2023
a04720c
Merge branch 'main' into v0.2.x
grst Aug 4, 2023
3e8f6e7
Merge branch 'main' into v0.2.x
grst Aug 4, 2023
891571c
Update template sync
grst Aug 4, 2023
9527ee9
fix tests
grst Aug 4, 2023
8875ce7
fix tests
grst Aug 4, 2023
b89738e
Make name of forked repos include the original user
grst Aug 4, 2023
a41458d
debug log
grst Aug 4, 2023
cb46a7c
fix artifact upload
grst Aug 4, 2023
809e9aa
Fetch main branch from upstream
grst Aug 4, 2023
a400e6f
Fix get fork
grst Aug 4, 2023
e55346f
fix clone url
grst Aug 4, 2023
c0c2c27
fmt
flying-sheep Aug 7, 2023
3aeb28d
comment on why pre-commit is a runtime dep
flying-sheep Aug 7, 2023
5f2e04d
less positional
flying-sheep Aug 7, 2023
e742888
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Aug 7, 2023
98d2f88
Wrap entire command in try/catch
grst Aug 9, 2023
b663f51
Merge remote-tracking branch 'origin/main' into v0.2.x
grst Aug 9, 2023
ac28931
Merge branch 'v0.2.x' of github.com:scverse/cookiecutter-scverse into…
grst Aug 9, 2023
fccd4c3
Skip PR if already exists for current version.
grst Aug 9, 2023
60b1c0c
Fix log messages
grst Aug 9, 2023
419fb31
Fix tests
grst Aug 9, 2023
cd609e2
Try fix test
grst Aug 9, 2023
121bcbf
Arming bot: Apply to all repos
grst Aug 9, 2023
4fcf466
Fix global exception handler
grst Aug 9, 2023
23a9eaa
Fix global exception handler
grst Aug 9, 2023
8be748d
Simplify code
flying-sheep Aug 10, 2023
e27ca0a
Merge branch 'main' into v0.2.x
grst Aug 16, 2023
ff88292
Allow maintainer to modify the PR
grst Aug 16, 2023
abf05fc
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Aug 16, 2023
605f957
Fix logging for existing PRs
grst Aug 16, 2023
1c20533
Merge branch 'main' into v0.2.x
grst Aug 23, 2023
582c769
Merge branch 'main' into v0.2.x
grst Nov 27, 2023
9b534ce
Merge branch 'main' into v0.2.x
grst Nov 28, 2023
7539d2a
Set git default branch in tests
grst Nov 28, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/workflows/cruft-prs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,7 @@ jobs:
GITHUB_TOKEN: ${{ secrets.BOT_GH_TOKEN }}
FORCE_COLOR: "1"
COLUMNS: "150"
- uses: actions/upload-artifact@v3
with:
name: cruft-logs
path: log/
2 changes: 0 additions & 2 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ on:
branches: [main]
pull_request:
branches: [main]
schedule:
- cron: "0 5 1,15 * *"

defaults:
run:
Expand Down
1 change: 1 addition & 0 deletions scripts/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ dependencies = [
"GitPython",
"PyGitHub",
"PyYAML",
"pre-commit"
flying-sheep marked this conversation as resolved.
Show resolved Hide resolved
]

[project.optional-dependencies]
Expand Down
50 changes: 36 additions & 14 deletions scripts/src/scverse_template_scripts/cruft_prs.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@
from .backoff import retry_with_backoff

log = getLogger(__name__)
log_dir = Path("./log")
log_dir.mkdir(exist_ok=True)

PR_BODY_TEMPLATE = """\
`cookiecutter-scverse` released [{release.tag_name}]({release.html_url}).
Expand Down Expand Up @@ -86,6 +88,7 @@ def auth(self, url_str: str) -> str:
class PR:
con: GitHubConnection
release: GHRelease
repo_id: str # something like grst-infercnvpy

title_prefix: ClassVar[LiteralString] = "Update template to "
branch_prefix: ClassVar[LiteralString] = "template-update-"
Expand All @@ -96,7 +99,7 @@ def title(self) -> str:

@property
def branch(self) -> str:
return f"{self.branch_prefix}{self.release.tag_name}"
return f"{self.branch_prefix}{self.repo_id}-{self.release.tag_name}"

@property
def namespaced_head(self) -> str:
Expand Down Expand Up @@ -138,9 +141,18 @@ def get_repo_urls(gh: Github) -> Generator[str]:
yield repo["url"]


def run_cruft(cwd: Path) -> CompletedProcess:
args = [sys.executable, "-m", "cruft", "update", "--checkout=main", "--skip-apply-ask", "--project-dir=."]
return run(args, check=True, cwd=cwd)
def run_cruft(cwd: Path, git_tag: str, log_name: str) -> CompletedProcess:
args = [
sys.executable,
"-m",
"cruft",
"update",
f"--checkout={git_tag}",
"--skip-apply-ask",
"--project-dir=.",
]
with open(log_dir / f"{log_name}.txt", "w") as log_file:
return run(args, check=True, cwd=cwd, stdout=log_file, stderr=log_file)


# GitHub says that up to 5 minutes of wait are OK,
Expand All @@ -149,14 +161,20 @@ def run_cruft(cwd: Path) -> CompletedProcess:
# Due to exponential backoff, we’ll maximally wait 2⁹ sec, or 8.5 min


def cruft_update(con: GitHubConnection, repo: GHRepo, path: Path, pr: PR) -> bool:
def cruft_update( # noqa: PLR0913
con: GitHubConnection, tag_name: str, repo: GHRepo, origin: GHRepo, path: Path, pr: PR
) -> bool:
clone = retry_with_backoff(
lambda: Repo.clone_from(con.auth(repo.clone_url), path), retries=n_retries, exc_cls=GitCommandError
lambda: Repo.clone_from(con.auth(repo.clone_url), path),
retries=n_retries,
exc_cls=GitCommandError,
)
branch = clone.create_head(pr.branch, clone.active_branch)
upstream = clone.create_remote(name=pr.repo_id, url=origin.clone_url)
upstream.fetch()
branch = clone.create_head(pr.branch, f"{pr.repo_id}/{origin.default_branch}")
branch.checkout()

run_cruft(path)
run_cruft(path, tag_name, pr.branch)

if not clone.is_dirty():
return False
Expand All @@ -176,21 +194,23 @@ def cruft_update(con: GitHubConnection, repo: GHRepo, path: Path, pr: PR) -> boo


def get_fork(con: GitHubConnection, repo: GHRepo) -> GHRepo:
if fork := next((f for f in repo.get_forks() if f.owner.id == con.user.id), None):
return fork
"""Fork target repo into the scverse-bot namespace and wait until the fork has been created.
If the fork already exists it is reused.
"""
fork = repo.create_fork()
return retry_with_backoff(lambda: con.gh.get_repo(fork.id), retries=n_retries, exc_cls=UnknownObjectException)


def make_pr(con: GitHubConnection, release: GHRelease, repo_url: str) -> None:
pr = PR(con, release)
log.info(f"Sending PR to {repo_url}: {pr.title}")
repo_id = repo_url.replace("https://github.com/", "").replace("/", "-")
pr = PR(con, release, repo_id)
log.info(f"Sending PR to {repo_url} : {pr.title}")

# create fork, populate branch, do PR from it
origin = con.gh.get_repo(repo_url.removeprefix("https://github.com/"))
repo = get_fork(con, origin)
with TemporaryDirectory() as td:
updated = cruft_update(con, repo, Path(td), pr)
updated = cruft_update(con, release.tag_name, repo, origin, Path(td), pr)
if updated:
if old_pr := next((p for p in origin.get_pulls("open") if pr.matches(p)), None):
old_pr.edit(state="closed")
Expand All @@ -211,7 +231,9 @@ def main(tag_name: str) -> None:
release = get_template_release(con.gh, tag_name)
repo_urls = get_repo_urls(con.gh)
for repo_url in repo_urls:
make_pr(con, release, repo_url)
# TODO just use single-repo we control for testing
if repo_url.endswith("icbi-lab/infercnvpy"):
make_pr(con, release, repo_url)
grst marked this conversation as resolved.
Show resolved Hide resolved


def cli() -> None:
Expand Down
12 changes: 8 additions & 4 deletions scripts/tests/test_cruft.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
class MockGHRepo:
git_url: str # git://github.com/foo/bar.git
clone_url: str # https://github.com/foo/bar.git
default_branch: str # main


@dataclass
Expand All @@ -39,18 +40,21 @@ def repo(git_repo: GitRepo) -> GHRepo:
(git_repo.workspace / "b").write_text("b content")
git_repo.api.index.add(["a", "b"])
git_repo.api.index.commit("initial commit")
return cast(GHRepo, MockGHRepo(git_repo.uri, git_repo.uri))
return cast(GHRepo, MockGHRepo(git_repo.uri, git_repo.uri, "main"))


@pytest.fixture
def pr(con) -> PR:
return PR(con, cast(GHRelease, MockRelease()))
return PR(con, cast(GHRelease, MockRelease()), "scverse-test")


def test_cruft_update(con, repo, tmp_path, pr, git_repo: GitRepo, monkeypatch: pytest.MonkeyPatch):
old_active_branch_name = git_repo.api.active_branch.name
monkeypatch.setattr("scverse_template_scripts.cruft_prs.run_cruft", lambda p: (p / "b").write_text("b modified"))
changed = cruft_update(con, repo, tmp_path, pr)
monkeypatch.setattr(
"scverse_template_scripts.cruft_prs.run_cruft",
lambda p, _, __: (p / "b").write_text("b modified"),
)
changed = cruft_update(con, "main", repo, repo, tmp_path, pr)
assert changed # TODO: add test for short circuit
main_branch = git_repo.api.active_branch
assert main_branch.name == old_active_branch_name, "Shouldn’t change active branch"
Expand Down
2 changes: 2 additions & 0 deletions {{cookiecutter.project_name}}/.github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ on:
branches: [main]
pull_request:
branches: [main]
schedule:
- cron: "0 5 1,15 * *"

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
Expand Down
3 changes: 3 additions & 0 deletions {{cookiecutter.project_name}}/.pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ repos:
args: [--fix=lf]
- id: trailing-whitespace
- id: check-case-conflict
# Check that there are no merge conflicts (could be generated by template sync)
- id: check-merge-conflict
args: [--assume-in-merge]
- repo: local
hooks:
- id: forbid-to-commit
Expand Down