correspond.channels.github#
GitHub: issues, pull request conversations and discussions, through the gh CLI.
correspond never holds a GitHub token: every call is gh api, authenticated as whatever
account gh is logged in as on this machine, so writes act as that user. Request bodies
go to gh on stdin, never on its command line, so a long comment cannot hit the argument
limit and never shows in the process table.
References
github:owner/repo: the repository.readlists recent open issues and pull requests,listenpolls its issue, comment, review comment and discussion activity,sendopens an issue (a title is required), or a discussion whenDraft.nativenames acategory.github:owner/repo#N: an issue, a pull request, or a discussion. GitHub numbers the three in one sequence; correspond asks which. Reading or listening to a pull request includes its review comments on the diff, each threaded under the comment that opened its thread (reply_to), and a reply to one of them joins that thread.labelandunlabelwork on an issue or pull request only: GitHub discussions have categories, not labels.closeandreopenwork on all three: an issue or pull request throughPATCH .../issues/N, a discussion through GraphQLcloseDiscussion/reopenDiscussion. Each kind takes its own close reasons (issue:completed,not_planned,duplicate; discussion:resolved,outdated,duplicate, withcompletedaccepted asresolved).
Opening an issue takes Draft.native keys labels, assignees and milestone,
set in the creating request so watchers are notified once. GitHub silently drops an
assignee who cannot be assigned, so the plan reports what the created issue actually
carries (landed) and what did not stick (not_applied). Draft.native
category (a discussion category’s name or slug) opens a discussion instead, through
GraphQL createDiscussion; it takes none of the issue fields.
Message ids match the anchors in GitHub’s own URLs: issue-N (the opening post of issue
or pull request N), issuecomment-ID, discussion_rID (a review comment on a pull
request’s diff), discussion-N, discussioncomment-ID.
Messages read through the API are platform (GitHub authenticated the account), with the
author’s author_association (OWNER, MEMBER, CONTRIBUTOR, NONE, …) as
authority. A webhook delivery is graded by GitHub.verify(): crypto when its
X-Hub-Signature-256 matches the configured secret, forged otherwise.
GitHub.audience() says who can read a conversation: the repository’s readership,
from its visibility, and its collaborators when the account may list them.
>>> GitHub().parse_ref("OctoCat/Hello-World#1").encoded
'github:octocat/hello-world#1'
>>> signature("a-secret", b"{}")
'sha256=...'
Module Attributes
Reader classes of a repository that cannot be listed, as |
Functions
|
The |
Classes
|
GitHub through |
- class correspond.channels.github.GitHub(*, run=<function run>, gh='gh', recent_discussions=3, deliveries=None, clock=<built-in function time_ns>)[source]#
Bases:
objectGitHub through
gh: read, listen, send, edit, react, verify webhook deliveries, and say who reads a conversation.- audience(ref, *, draft=None)[source]#
Who can read a repository’s issues, pull requests and discussions, asked of GitHub now.
Every conversation in a repository has the repository’s readership. A draft changes nothing: a mention decides who is notified, not who can read. One
gh api repos/{owner}/{repo}call gives the visibility and the owner’s type:public: scopepublic; readers are not listed.private: scopenamedwhen a user owns it,orgotherwise.internal: scopeorg, with every member of the enterprise as a class.a 404 or a 403:
public, defaulted. GitHub answers 404 both for a repository that does not exist and for one the account cannot see.
For a private or internal repository, collaborators (team members, members reading through the base permission, and owners included) are listed only when the account can write to it, since GitHub lists them to no one else. The list is a lower bound and the audience is never
complete: installed apps and webhooks read the repository without being listable throughgh, and an organisation’s security-manager teams read every repository. An organisation’s base permission is read when the account may see it (owners only), and otherwise assumed to be the documented default, read;read,writeandadminreach the same readers and give the same class. Theghaccount itself is never consulted, so the answer, and its hash, do not depend on who asks. Every repository emails bodies to watchers and participants, and nothing sent is retractable. Nothing is cached. A rate limit or a transport failure raises;correspond.ops.audience()resolves it to public.- Return type:
- property capabilities: Capabilities#
Issues, pull request conversations and discussions.
- close(ref, *, reason=None, dry_run=False)[source]#
Close an issue or pull request (
PATCH .../issues/N) or a discussion (GraphQL).reasonis the kind’s own: an issue’sstate_reason(completed,not_planned,duplicate) or a discussion’s (resolved,outdated,duplicate;completedis accepted asresolved); a pull request takes none. The plan reads the state back:was(before) andnow/now_reason(what the response says), so closing an already-closed conversation is visible as such.- Return type:
- property deliveries: MutableMapping[str, Any]#
What the webhook receiver stored (read when
deliveryiswebhook).
- edit(ref, message_id, draft, *, dry_run=False)[source]#
Replace the body of an issue, a comment, a review comment, a discussion or a discussion comment.
- Return type:
- label(ref, labels, *, dry_run=False)[source]#
Add labels to an issue or pull request (
POST .../labels).GitHub creates a label that does not already exist in the repository rather than rejecting it, so
landedin the plan is mostly a confirmation: the requested names the response’s own (now-current) label list actually carries, which is read back and reported the way an assignee is not (a login that is not a member of the repository is silently dropped fromassignees; a label name never is). The response lists every label now on the issue, including ones this call never mentioned, solandedis filtered to the requested names, not the full list.- Return type:
- parse_ref(id)[source]#
owner/repoorowner/repo#N, lower-cased (GitHub names are case-insensitive).- Return type:
- poll(ref, *, cursor=None, limit=None)[source]#
Issue, comment, review comment, review and discussion activity since
cursor; a first poll looks back a day.The cursor is opaque JSON: the update time the next poll starts from (
t; GitHub’ssinceincludes it), the creation time from which an issue still counts as new (l, which stays behind while the issues list is being read in parts), and the events already delivered att(seen). So neitherlimitnor several changes within one second can make a poll repeat itself, and a list cut short (more thanMAX_PAGESpages changed) is resumed from where it was cut. A bare ISO time is also accepted as a cursor.Discussions have no
sincefilter, so a repository’s are walked newest activity first until a page reaches one older than the cursor (_active_discussions()), and each active one is read whole; one discussion is read whole on every poll. A discussion counts as new when it was created at or after the cursor, and a discussion comment ismessage.updatedwhen it was created before it.
- prepare(ref, draft)[source]#
The draft as it will post:
notifyhandles checked, the missing mentions added. Pure.- Return type:
- react(ref, message_id, reaction, *, dry_run=False)[source]#
Add one of
REACTIONSto an issue, a comment, a review comment, a discussion or a discussion comment.- Return type:
- read(ref, *, since=None, limit=None)[source]#
An issue, pull request or discussion with its comments; for a repository, recent open issues and pull requests.
- reopen(ref, *, dry_run=False)[source]#
Reopen a closed issue, pull request or discussion; the plan reads the state back as for
close().- Return type:
- send(ref, draft, *, dry_run=False)[source]#
Comment on an issue, pull request or discussion; on a repository, open an issue (
titlerequired), or a discussion with nativecategory.draft.reply_toanswers a discussion comment, or a review comment on a pull request’s diff (discussion_rID), joining its thread.draft.notifyhandles (octocat,org/team) the text does not @mention yet are mentioned on a closingccline, which GitHub notifies; the plan lists them undermentions_added.- Return type:
- unlabel(ref, labels, *, dry_run=False)[source]#
Remove labels from an issue or pull request.
GitHub has no bulk removal: one
DELETE .../labels/{name}per label. A label already absent from the issue answers 404 the same way a missing issue does, so the issue’s existence is checked first (_require_issue()) and every 404 after that is read as “was not on the issue”, not as an error.- Return type:
- correspond.channels.github.WATCHERS = 'watchers and participants receive the body by email'#
Reader classes of a repository that cannot be listed, as
GitHub.audience()words them.