Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
26 changes: 2 additions & 24 deletions .ci/generation/py2cs/__main__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"""Command line: python -m py2cs check | manifest | diff | align | repos | plan | pr | close | status | verify | drafts | resolve | notes"""
"""Command line: python -m py2cs check | manifest | diff | align | repos | plan | pr | close | verify | drafts | resolve | notes"""

from __future__ import annotations

Expand All @@ -20,7 +20,6 @@
from py2cs.patch import FilePatch, PatchResult, run_patcher
from py2cs.plan import Patcher, make_plan
from py2cs.pr import PythonPr, close_linked, raise_pr
from py2cs.status import update_status
from py2cs.reader import ReadErrors, read_package
from py2cs.report import render_report
from py2cs.resolve import ResolveError, load_listing, resolve_repo
Expand Down Expand Up @@ -90,15 +89,6 @@ def main(argv: list[str] | None = None) -> int:
p.add_argument("--token-env", default="PY2CS_CSHARP_TOKEN")
p.add_argument("--dry-run", action="store_true")

p = sub.add_parser("status", help="set the commit status of a Python PR to whether its linked C# pull request is merged")
p.add_argument("--python-repo", required=True)
p.add_argument("--python-pr", required=True, type=int)
p.add_argument("--sha", required=True, help="head commit of the Python pull request")
p.add_argument("--no-csharp-change", action="store_true", help="the Python pull request needs no C# change: the status is success")
p.add_argument("--repos-file", required=True, type=Path, help="text file with the repositories C# pull requests are raised in")
p.add_argument("--token-env", default="PY2CS_PYTHON_TOKEN", help="environment variable with a token that can read the C# repositories and write statuses here")
p.add_argument("--dry-run", action="store_true")

p = sub.add_parser("verify", help="every module of a generated package imports, and it reads back as the C# manifest says")
p.add_argument("--package", required=True, type=Path)
p.add_argument("--manifest", type=Path)
Expand Down Expand Up @@ -132,7 +122,7 @@ def main(argv: list[str] | None = None) -> int:
return 2

def run(args: argparse.Namespace) -> int:
"""Dispatcher of the commands: runs the command named in the arguments (check, manifest, diff, align, repos, plan, pr, close, status, verify, drafts or resolve), prints its result and returns the exit code.
"""Dispatcher of the commands: runs the command named in the arguments (check, manifest, diff, align, repos, plan, pr, close, verify, drafts or resolve), prints its result and returns the exit code.

Each branch wires the py2cs modules together for one workflow step; unknown commands return 2.
"""
Expand Down Expand Up @@ -253,18 +243,6 @@ def patch_with(patches: list[FilePatch]) -> list[PatchResult]:
print(("would " if args.dry_run else "") + str(action))
return 0

if args.command == "status":
repos = list(load_allowed(args.repos_file))
client, offline = make_client(args.token_env, repos if args.dry_run else None)
if offline:
print("no token: offline dry run, nothing is read from or written to GitHub")
status = update_status(client, client, repos, args.python_repo, args.python_pr, args.sha, args.dry_run, args.no_csharp_change)
if status is None:
print(f"{args.python_repo}#{args.python_pr} has no linked C# pull request: no status")
else:
print(("would set " if args.dry_run else "set ") + f"the status {status.state}: {status.description}")
return 0

if args.command == "verify":
count, problems = verify_package(args.package, args.manifest)
for problem in problems:
Expand Down
2 changes: 1 addition & 1 deletion .ci/generation/py2cs/ci.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ def verify_package(package: Path, manifest: Path | None = None) -> tuple[int, li
return count, problems

def make_client(token_env: str | None, offline_repos: list[str] | None = None) -> tuple[GitHubClient, bool]:
"""Creates the GitHub client for the `pr`, `close` and `status` commands from a token held in an environment variable, and says whether it is an offline stand-in.
"""Creates the GitHub client for the `pr` and `close` commands from a token held in an environment variable, and says whether it is an offline stand-in.

The token is never on a command line. Without a token and with offline_repos, a MockGitHub with empty repositories stands in, so a dry run on a laptop prints what would be done without calling GitHub.
"""
Expand Down
47 changes: 2 additions & 45 deletions .ci/generation/py2cs/github.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ class PullRequest:
body: str = ""
draft: bool = True
state: str = "open"
merged: bool = False

@dataclass
class RemoteFile:
Expand Down Expand Up @@ -78,14 +77,6 @@ def upsert_comment(self, repo: str, number: int, marker: str, body: str) -> None
def close_pull_request(self, repo: str, number: int) -> None:
"""Contract: closes a pull request without merging it. `close` calls it on the linked C# draft when its Python PR is closed unmerged."""

def find_linked_pull_request(self, repo: str, head_branch: str) -> PullRequest | None:
"""Contract: returns the newest pull request from the branch whatever its state (open, closed or merged), or None. `status` uses it to see whether the linked C# pull request is merged.
"""

def set_commit_status(self, repo: str, sha: str, state: str, context: str, description: str, url: str) -> None:
"""Contract: sets a commit status (state, context, description, link) on a commit of the repository. `status` uses it to publish the state of the linked C# pull request on the Python PR.
"""

# ------------------------------------------------------------------------------------------------ real client

Transport = Callable[[str, str, dict[str, str], bytes | None], tuple[int, bytes]]
Expand Down Expand Up @@ -210,31 +201,11 @@ def close_pull_request(self, repo: str, number: int) -> None:
"""
self._request("PATCH", f"/repos/{repo}/pulls/{number}", {"state": "closed"})

def find_linked_pull_request(self, repo: str, head_branch: str) -> PullRequest | None:
"""Looks up the newest pull request from the branch, open, closed or merged, or returns None.

Used by `status` to find out whether the linked C# pull request is merged.
"""
owner = repo.split("/")[0]
found = self._request("GET", f"/repos/{repo}/pulls?state=all&sort=created&direction=desc&head={urllib.parse.quote(owner + ':' + head_branch, safe='')}")
return self._pull_request(found[0]) if found else None

def set_commit_status(self, repo: str, sha: str, state: str, context: str, description: str, url: str) -> None:
"""Posts a commit status to the GitHub statuses API, cutting the description to the 140 characters GitHub allows. Used by `status` to show the state of the linked C# pull request on the Python PR.
"""
body = {"state": state, "context": context, "description": description[:140], "target_url": url}
self._request("POST", f"/repos/{repo}/statuses/{sha}", body)

@staticmethod
def _pull_request(data: dict[str, Any]) -> PullRequest:
# The list endpoint has merged_at but not merged
"""Converts a pull request object from the GitHub API into a PullRequest.

It treats a pull request with merged_at as merged, because the list endpoints do not return the merged flag.
"""
merged = bool(data.get("merged") or data.get("merged_at"))
"""Converts a pull request object from the GitHub API into a PullRequest. Used by every client method that returns one."""
return PullRequest(int(data["number"]), str(data["html_url"]), str(data["head"]["ref"]), str(data["base"]["ref"]), str(data.get("title", "")),
str(data.get("body") or ""), bool(data.get("draft", False)), str(data.get("state", "open")), merged)
str(data.get("body") or ""), bool(data.get("draft", False)), str(data.get("state", "open")))

# ------------------------------------------------------------------------------------------------ mock client

Expand All @@ -246,8 +217,6 @@ class MockRepository:
pull_requests: list[PullRequest] = field(default_factory=lambda: [])
labels: dict[int, list[str]] = field(default_factory=lambda: {})
comments: dict[int, list[str]] = field(default_factory=lambda: {})
# (sha, context) -> (state, description, url)
statuses: dict[tuple[str, str], tuple[str, str, str]] = field(default_factory=lambda: {})

class MockGitHub:
"""An in-memory GitHub. `calls` records every call that changes something, so tests can check what was (not) written."""
Expand Down Expand Up @@ -342,15 +311,3 @@ def close_pull_request(self, repo: str, number: int) -> None:
for pull in self._repo(repo).pull_requests:
if pull.number == number:
pull.state = "closed"

def find_linked_pull_request(self, repo: str, head_branch: str) -> PullRequest | None:
"""Returns the newest pull request (highest number) from the branch whatever its state, or None. Stands in for GitHub in tests and offline dry runs.
"""
matches = [p for p in self._repo(repo).pull_requests if p.head == head_branch]
return max(matches, key=lambda p: p.number) if matches else None

def set_commit_status(self, repo: str, sha: str, state: str, context: str, description: str, url: str) -> None:
"""Stores the status for the sha and context, cutting the description to 140 characters, and logs the call. Stands in for GitHub in tests and offline dry runs.
"""
self.calls.append(("set_commit_status", repo, sha, state))
self._repo(repo).statuses[(sha, context)] = (state, description[:140], url)
2 changes: 1 addition & 1 deletion .ci/generation/py2cs/pr.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ class PrResult:
pull_request: PullRequest | None = None

def branch_name(number: int) -> str:
"""Returns the branch python-schema/pr-<N> that holds the C# draft for Python pull request N. `pr`, `close` and `status` all derive the branch from it.
"""Returns the branch python-schema/pr-<N> that holds the C# draft for Python pull request N. `pr` and `close` both derive the branch from it.
"""
return f"{BRANCH_PREFIX}{number}"

Expand Down
61 changes: 0 additions & 61 deletions .ci/generation/py2cs/status.py

This file was deleted.

Loading
Loading