From e160cd58cce87766ff062cc0de79be80708aec94 Mon Sep 17 00:00:00 2001 From: KimSia Sim Date: Sun, 24 Oct 2021 21:08:41 +0800 Subject: [PATCH] =?UTF-8?q?=E2=99=BB=20Make=20changes=20so=20it's=20closer?= =?UTF-8?q?=20to=20main=20branch?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ♻Rewrite to make things conform closer to main branch ♻ Fix f string ♻ Change to f string And also switch to the accept header recommended in https://docs.github.com/en/rest/reference/repos#create-a-repository-using-a-template ♻ Move away from `application/vnd.github.baptiste-preview+json` ✅Make tests pass --- github/AuthenticatedUser.py | 4 +- github/Organization.py | 6 +- github/Repository.py | 718 ++++++++++++++---- ...ticatedUser.testCreateRepoFromTemplate.txt | 2 +- ...CreateRepoFromTemplateWithAllArguments.txt | 2 +- ...rganization.testCreateRepoFromTemplate.txt | 2 +- ...CreateRepoFromTemplateWithAllArguments.txt | 2 +- 7 files changed, 593 insertions(+), 143 deletions(-) diff --git a/github/AuthenticatedUser.py b/github/AuthenticatedUser.py index 0655834e08..38ba1db4e5 100644 --- a/github/AuthenticatedUser.py +++ b/github/AuthenticatedUser.py @@ -517,7 +517,7 @@ def create_repo_from_template( private=github.GithubObject.NotSet, ): """ - :calls: `POST /repos/{template_owner}/{template_repo}/generate `_ + :calls: `POST /repos/{template_owner}/{template_repo}/generate `_ :param name: string :param repo :class:`github.Repository.Repository` :param description: string @@ -542,7 +542,7 @@ def create_repo_from_template( "POST", f"/repos/{repo.owner.login}/{repo.name}/generate", input=post_parameters, - headers={"Accept": Consts.mediaTypeTemplatesPreview}, + headers={"Accept": "application/vnd.github.v3+json"}, ) return github.Repository.Repository(self._requester, headers, data, completed=True) diff --git a/github/Organization.py b/github/Organization.py index dcc97c1ad2..30901d68c9 100644 --- a/github/Organization.py +++ b/github/Organization.py @@ -411,7 +411,7 @@ def create_repo_from_template( private=github.GithubObject.NotSet, ): """self.name - :calls: `POST /repos/{template_owner}/{template_repo}/generate `_ + :calls: `POST /repos/{template_owner}/{template_repo}/generate `_ :param name: string :param repo :class:`github.Repository.Repository` :param description: string @@ -436,9 +436,9 @@ def create_repo_from_template( post_parameters["private"] = private headers, data = self._requester.requestJsonAndCheck( "POST", - "/repos/" + repo.owner.login + "/" + repo.name + "/generate", + f"/repos/{repo.owner.login}/{repo.name}/generate", input=post_parameters, - headers={"Accept": Consts.mediaTypeTemplatesPreview}, + headers={"Accept": "application/vnd.github.v3+json"}, ) return github.Repository.Repository( self._requester, headers, data, completed=True diff --git a/github/Repository.py b/github/Repository.py index 1f7effa17f..2ad4f6d2ec 100644 --- a/github/Repository.py +++ b/github/Repository.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- - ############################ Copyrights and license ############################ # # # Copyright 2012 Christopher Gilbert # @@ -66,6 +64,7 @@ # Copyright 2018 Zilei Gu # # Copyright 2018 Yves Zumbach # # Copyright 2018 Leying Chen # +# Copyright 2020 Pascal Hofmann # # # # This file is part of PyGithub. # # http://pygithub.readthedocs.io/ # @@ -93,11 +92,14 @@ from deprecated import deprecated import github.Branch +import github.CheckRun +import github.CheckSuite import github.Clones import github.Commit import github.CommitComment import github.Comparison import github.ContentFile +import github.Deployment import github.Download import github.Event import github.GitBlob @@ -120,10 +122,13 @@ import github.Path import github.Permissions import github.Project +import github.PublicKey import github.PullRequest import github.Referrer import github.Repository import github.RepositoryKey +import github.RepositoryPreferences +import github.SelfHostedActionsRunner import github.SourceImport import github.Stargazer import github.StatsCodeFrequency @@ -134,6 +139,8 @@ import github.Tag import github.Team import github.View +import github.Workflow +import github.WorkflowRun from . import Consts @@ -290,6 +297,14 @@ def delete_branch_on_merge(self): self._completeIfNotSet(self._delete_branch_on_merge) return self._delete_branch_on_merge.value + @property + def deployments_url(self): + """ + :type: string + """ + self._completeIfNotSet(self._deployments_url) + return self._deployments_url.value + @property def description(self): """ @@ -402,6 +417,14 @@ def has_issues(self): self._completeIfNotSet(self._has_issues) return self._has_issues.value + @property + def has_pages(self): + """ + :type: bool + """ + self._completeIfNotSet(self._has_pages) + return self._has_pages.value + @property def has_projects(self): """ @@ -642,6 +665,14 @@ def pushed_at(self): self._completeIfNotSet(self._pushed_at) return self._pushed_at.value + @property + def releases_url(self): + """ + :type: string + """ + self._completeIfNotSet(self._releases_url) + return self._releases_url.value + @property def size(self): """ @@ -811,7 +842,7 @@ def add_to_collaborators(self, collaborator, permission=github.GithubObject.NotS put_parameters = None headers, data = self._requester.requestJsonAndCheck( - "PUT", self.url + "/collaborators/" + collaborator, input=put_parameters + "PUT", f"{self.url}/collaborators/{collaborator}", input=put_parameters ) # return an invitation object if there's data returned by the API. If data is empty # there's a pending invitation for the given user. @@ -833,31 +864,32 @@ def get_collaborator_permission(self, collaborator): if isinstance(collaborator, github.NamedUser.NamedUser): collaborator = collaborator._identity headers, data = self._requester.requestJsonAndCheck( - "GET", self.url + "/collaborators/" + collaborator + "/permission", + "GET", + f"{self.url}/collaborators/{collaborator}/permission", ) return data["permission"] def get_pending_invitations(self): """ - :calls: `GET /repos/:owner/:repo/invitations `_ + :calls: `GET /repos/{owner}/{repo}/invitations `_ :rtype: :class:`github.PaginatedList.PaginatedList` of :class:`github.Invitation.Invitation` """ return github.PaginatedList.PaginatedList( github.Invitation.Invitation, self._requester, - self.url + "/invitations", + f"{self.url}/invitations", None, ) def remove_invitation(self, invite_id): """ - :calls: `DELETE /repos/:owner/:repo/invitations/:invitation_id `_ + :calls: `DELETE /repos/{owner}/{repo}/invitations/{invitation_id} `_ :rtype: None """ assert isinstance(invite_id, int), invite_id headers, data = self._requester.requestJsonAndCheck( - "DELETE", self.url + "/invitations/" + str(invite_id) + "DELETE", f"{self.url}/invitations/{invite_id}" ) def compare(self, base, head): @@ -870,7 +902,7 @@ def compare(self, base, head): assert isinstance(base, str), base assert isinstance(head, str), head headers, data = self._requester.requestJsonAndCheck( - "GET", self.url + "/compare/" + base + "..." + head + "GET", f"{self.url}/compare/{base}...{head}" ) return github.Comparison.Comparison( self._requester, headers, data, completed=True @@ -890,7 +922,7 @@ def create_git_blob(self, content, encoding): "encoding": encoding, } headers, data = self._requester.requestJsonAndCheck( - "POST", self.url + "/git/blobs", input=post_parameters + "POST", f"{self.url}/git/blobs", input=post_parameters ) return github.GitBlob.GitBlob(self._requester, headers, data, completed=True) @@ -932,7 +964,7 @@ def create_git_commit( if committer is not github.GithubObject.NotSet: post_parameters["committer"] = committer._identity headers, data = self._requester.requestJsonAndCheck( - "POST", self.url + "/git/commits", input=post_parameters + "POST", f"{self.url}/git/commits", input=post_parameters ) return github.GitCommit.GitCommit( self._requester, headers, data, completed=True @@ -952,7 +984,7 @@ def create_git_ref(self, ref, sha): "sha": sha, } headers, data = self._requester.requestJsonAndCheck( - "POST", self.url + "/git/refs", input=post_parameters + "POST", f"{self.url}/git/refs", input=post_parameters ) return github.GitRef.GitRef(self._requester, headers, data, completed=True) @@ -968,6 +1000,20 @@ def create_git_tag_and_release( draft=False, prerelease=False, ): + """ + Convenience function that calls :meth:`Repository.create_git_tag` and + :meth:`Repository.create_git_release`. + :param tag: string + :param tag_message: string + :param release_name: string + :param release_message: string + :param object: string + :param type: string + :param tagger: :class:github.InputGitAuthor.InputGitAuthor + :param draft: bool + :param prerelease: bool + :rtype: :class:`github.GitRelease.GitRelease` + """ self.create_git_tag(tag, tag_message, object, type, tagger) return self.create_git_release( tag, @@ -1027,7 +1073,7 @@ def create_git_release( ): post_parameters["target_commitish"] = target_commitish.sha headers, data = self._requester.requestJsonAndCheck( - "POST", self.url + "/releases", input=post_parameters + "POST", f"{self.url}/releases", input=post_parameters ) return github.GitRelease.GitRelease( self._requester, headers, data, completed=True @@ -1061,7 +1107,7 @@ def create_git_tag( if tagger is not github.GithubObject.NotSet: post_parameters["tagger"] = tagger._identity headers, data = self._requester.requestJsonAndCheck( - "POST", self.url + "/git/tags", input=post_parameters + "POST", f"{self.url}/git/tags", input=post_parameters ) return github.GitTag.GitTag(self._requester, headers, data, completed=True) @@ -1084,7 +1130,7 @@ def create_git_tree(self, tree, base_tree=github.GithubObject.NotSet): if base_tree is not github.GithubObject.NotSet: post_parameters["base_tree"] = base_tree._identity headers, data = self._requester.requestJsonAndCheck( - "POST", self.url + "/git/trees", input=post_parameters + "POST", f"{self.url}/git/trees", input=post_parameters ) return github.GitTree.GitTree(self._requester, headers, data, completed=True) @@ -1118,7 +1164,7 @@ def create_hook( if active is not github.GithubObject.NotSet: post_parameters["active"] = active headers, data = self._requester.requestJsonAndCheck( - "POST", self.url + "/hooks", input=post_parameters + "POST", f"{self.url}/hooks", input=post_parameters ) return github.Hook.Hook(self._requester, headers, data, completed=True) @@ -1185,7 +1231,7 @@ def create_issue( for element in labels ] headers, data = self._requester.requestJsonAndCheck( - "POST", self.url + "/issues", input=post_parameters + "POST", f"{self.url}/issues", input=post_parameters ) return github.Issue.Issue(self._requester, headers, data, completed=True) @@ -1206,7 +1252,7 @@ def create_key(self, title, key, read_only=False): "read_only": read_only, } headers, data = self._requester.requestJsonAndCheck( - "POST", self.url + "/keys", input=post_parameters + "POST", f"{self.url}/keys", input=post_parameters ) return github.RepositoryKey.RepositoryKey( self._requester, headers, data, completed=True @@ -1233,7 +1279,7 @@ def create_label(self, name, color, description=github.GithubObject.NotSet): post_parameters["description"] = description headers, data = self._requester.requestJsonAndCheck( "POST", - self.url + "/labels", + f"{self.url}/labels", input=post_parameters, headers={"Accept": Consts.mediaTypeLabelDescriptionSearchPreview}, ) @@ -1275,7 +1321,7 @@ def create_milestone( else: post_parameters["due_on"] = due_on.isoformat() headers, data = self._requester.requestJsonAndCheck( - "POST", self.url + "/milestones", input=post_parameters + "POST", f"{self.url}/milestones", input=post_parameters ) return github.Milestone.Milestone( self._requester, headers, data, completed=True @@ -1283,7 +1329,7 @@ def create_milestone( def create_project(self, name, body=github.GithubObject.NotSet): """ - :calls: `POST /repos/:owner/:repo/projects `_ + :calls: `POST /repos/{owner}/{repo}/projects `_ :param name: string :param body: string :rtype: :class:`github.Project.Project` @@ -1297,7 +1343,7 @@ def create_project(self, name, body=github.GithubObject.NotSet): if body is not github.GithubObject.NotSet: post_parameters["body"] = body headers, data = self._requester.requestJsonAndCheck( - "POST", self.url + "/projects", headers=import_header, input=post_parameters + "POST", f"{self.url}/projects", headers=import_header, input=post_parameters ) return github.Project.Project(self._requester, headers, data, completed=True) @@ -1309,6 +1355,7 @@ def create_pull(self, *args, **kwds): :param issue: :class:`github.Issue.Issue` :param base: string :param head: string + :param draft: bool :param maintainer_can_modify: bool :rtype: :class:`github.PullRequest.PullRequest` """ @@ -1318,7 +1365,13 @@ def create_pull(self, *args, **kwds): return self.__create_pull_2(*args, **kwds) def __create_pull_1( - self, title, body, base, head, maintainer_can_modify=github.GithubObject.NotSet + self, + title, + body, + base, + head, + maintainer_can_modify=github.GithubObject.NotSet, + draft=False, ): assert isinstance(title, str), title assert isinstance(body, str), body @@ -1327,6 +1380,7 @@ def __create_pull_1( assert maintainer_can_modify is github.GithubObject.NotSet or isinstance( maintainer_can_modify, bool ), maintainer_can_modify + assert isinstance(draft, bool), draft if maintainer_can_modify is not github.GithubObject.NotSet: return self.__create_pull( title=title, @@ -1334,9 +1388,12 @@ def __create_pull_1( base=base, head=head, maintainer_can_modify=maintainer_can_modify, + draft=draft, ) else: - return self.__create_pull(title=title, body=body, base=base, head=head) + return self.__create_pull( + title=title, body=body, base=base, head=head, draft=draft + ) def __create_pull_2(self, issue, base, head): assert isinstance(issue, github.Issue.Issue), issue @@ -1346,13 +1403,67 @@ def __create_pull_2(self, issue, base, head): def __create_pull(self, **kwds): post_parameters = kwds + headers, data = self._requester.requestJsonAndCheck( - "POST", self.url + "/pulls", input=post_parameters + "POST", f"{self.url}/pulls", input=post_parameters ) return github.PullRequest.PullRequest( self._requester, headers, data, completed=True ) + def create_repository_dispatch( + self, event_type, client_payload=github.GithubObject.NotSet + ): + """ + :calls: POST /repos/{owner}/{repo}/dispatches + :param event_type: string + :param client_payload: dict + :rtype: bool + """ + assert isinstance(event_type, str), event_type + assert client_payload is github.GithubObject.NotSet or isinstance( + client_payload, dict + ), client_payload + post_parameters = {"event_type": event_type} + if client_payload is not github.GithubObject.NotSet: + post_parameters["client_payload"] = client_payload + status, headers, data = self._requester.requestJson( + "POST", f"{self.url}/dispatches", input=post_parameters + ) + return status == 204 + + def create_secret(self, secret_name, unencrypted_value): + """ + :calls: `PUT /repos/{owner}/{repo}/actions/secrets/{secret_name} `_ + :param secret_name: string + :param unencrypted_value: string + :rtype: bool + """ + assert isinstance(secret_name, str), secret_name + assert isinstance(unencrypted_value, str), unencrypted_value + public_key = self.get_public_key() + payload = public_key.encrypt(unencrypted_value) + put_parameters = { + "key_id": public_key.key_id, + "encrypted_value": payload, + } + status, headers, data = self._requester.requestJson( + "PUT", f"{self.url}/actions/secrets/{secret_name}", input=put_parameters + ) + return status == 201 + + def delete_secret(self, secret_name): + """ + :calls: `DELETE /repos/{owner}/{repo}/actions/secrets/{secret_name} `_ + :param secret_name: string + :rtype: bool + """ + assert isinstance(secret_name, str), secret_name + status, headers, data = self._requester.requestJson( + "DELETE", f"{self.url}/actions/secrets/{secret_name}" + ) + return status == 204 + def create_source_import( self, vcs, @@ -1387,7 +1498,7 @@ def create_source_import( import_header = {"Accept": Consts.mediaTypeImportPreview} headers, data = self._requester.requestJsonAndCheck( - "PUT", self.url + "/import", headers=import_header, input=put_parameters + "PUT", f"{self.url}/import", headers=import_header, input=put_parameters ) return github.SourceImport.SourceImport( @@ -1521,9 +1632,9 @@ def get_archive_link(self, archive_format, ref=github.GithubObject.NotSet): """ assert isinstance(archive_format, str), archive_format assert ref is github.GithubObject.NotSet or isinstance(ref, str), ref - url = self.url + "/" + archive_format + url = f"{self.url}/{archive_format}" if ref is not github.GithubObject.NotSet: - url += "/" + ref + url += f"/{ref}" headers, data = self._requester.requestJsonAndCheck("GET", url) return headers["location"] @@ -1533,7 +1644,7 @@ def get_assignees(self): :rtype: :class:`github.PaginatedList.PaginatedList` of :class:`github.NamedUser.NamedUser` """ return github.PaginatedList.PaginatedList( - github.NamedUser.NamedUser, self._requester, self.url + "/assignees", None + github.NamedUser.NamedUser, self._requester, f"{self.url}/assignees", None ) def get_branch(self, branch): @@ -1544,7 +1655,7 @@ def get_branch(self, branch): """ assert isinstance(branch, str), branch headers, data = self._requester.requestJsonAndCheck( - "GET", self.url + "/branches/" + branch + "GET", f"{self.url}/branches/{branch}" ) return github.Branch.Branch(self._requester, headers, data, completed=True) @@ -1575,7 +1686,7 @@ def get_branches(self): :rtype: :class:`github.PaginatedList.PaginatedList` of :class:`github.Branch.Branch` """ return github.PaginatedList.PaginatedList( - github.Branch.Branch, self._requester, self.url + "/branches", None + github.Branch.Branch, self._requester, f"{self.url}/branches", None ) def get_collaborators(self, affiliation=github.GithubObject.NotSet): @@ -1591,13 +1702,13 @@ def get_collaborators(self, affiliation=github.GithubObject.NotSet): assert isinstance(affiliation, str), affiliation assert ( affiliation in allowed_affiliations - ), "Affiliation can be one of " + ", ".join(allowed_affiliations) + ), f"Affiliation can be one of {', '.join(allowed_affiliations)}" url_parameters["affiliation"] = affiliation return github.PaginatedList.PaginatedList( github.NamedUser.NamedUser, self._requester, - self.url + "/collaborators", + f"{self.url}/collaborators", url_parameters, ) @@ -1609,7 +1720,7 @@ def get_comment(self, id): """ assert isinstance(id, int), id headers, data = self._requester.requestJsonAndCheck( - "GET", self.url + "/comments/" + str(id) + "GET", f"{self.url}/comments/{id}" ) return github.CommitComment.CommitComment( self._requester, headers, data, completed=True @@ -1623,7 +1734,7 @@ def get_comments(self): return github.PaginatedList.PaginatedList( github.CommitComment.CommitComment, self._requester, - self.url + "/comments", + f"{self.url}/comments", None, ) @@ -1635,7 +1746,7 @@ def get_commit(self, sha): """ assert isinstance(sha, str), sha headers, data = self._requester.requestJsonAndCheck( - "GET", self.url + "/commits/" + sha + "GET", f"{self.url}/commits/{sha}" ) return github.Commit.Commit(self._requester, headers, data, completed=True) @@ -1693,7 +1804,7 @@ def get_commits( else: url_parameters["author"] = author return github.PaginatedList.PaginatedList( - github.Commit.Commit, self._requester, self.url + "/commits", url_parameters + github.Commit.Commit, self._requester, f"{self.url}/commits", url_parameters ) def get_contents(self, path, ref=github.GithubObject.NotSet): @@ -1713,7 +1824,7 @@ def get_contents(self, path, ref=github.GithubObject.NotSet): url_parameters["ref"] = ref headers, data = self._requester.requestJsonAndCheck( "GET", - self.url + "/contents/" + urllib.parse.quote(path), + f"{self.url}/contents/{urllib.parse.quote(path)}", parameters=url_parameters, ) @@ -1736,13 +1847,145 @@ def get_contents(self, path, ref=github.GithubObject.NotSet): self._requester, headers, data, completed=True ) + def get_deployments( + self, + sha=github.GithubObject.NotSet, + ref=github.GithubObject.NotSet, + task=github.GithubObject.NotSet, + environment=github.GithubObject.NotSet, + ): + """ + :calls: `GET /repos/{owner}/{repo}/deployments `_ + :param: sha: string + :param: ref: string + :param: task: string + :param: environment: string + :rtype: :class:`github.PaginatedList.PaginatedList` of :class:`github.Deployment.Deployment` + """ + assert sha is github.GithubObject.NotSet or isinstance(sha, str), sha + assert ref is github.GithubObject.NotSet or isinstance(ref, str), ref + assert task is github.GithubObject.NotSet or isinstance(task, str), task + assert environment is github.GithubObject.NotSet or isinstance( + environment, str + ), environment + parameters = {} + if sha is not github.GithubObject.NotSet: + parameters["sha"] = sha + if ref is not github.GithubObject.NotSet: + parameters["ref"] = ref + if task is not github.GithubObject.NotSet: + parameters["task"] = task + if environment is not github.GithubObject.NotSet: + parameters["environment"] = environment + return github.PaginatedList.PaginatedList( + github.Deployment.Deployment, + self._requester, + f"{self.url}/deployments", + parameters, + headers={"Accept": Consts.deploymentEnhancementsPreview}, + ) + + def get_deployment(self, id_): + """ + :calls: `GET /repos/{owner}/{repo}/deployments/{deployment_id} `_ + :param: id_: int + :rtype: :class:`github.Deployment.Deployment` + """ + assert isinstance(id_, int), id_ + headers, data = self._requester.requestJsonAndCheck( + "GET", + f"{self.url}/deployments/{id_}", + headers={"Accept": Consts.deploymentEnhancementsPreview}, + ) + return github.Deployment.Deployment( + self._requester, headers, data, completed=True + ) + + def create_deployment( + self, + ref, + task=github.GithubObject.NotSet, + auto_merge=github.GithubObject.NotSet, + required_contexts=github.GithubObject.NotSet, + payload=github.GithubObject.NotSet, + environment=github.GithubObject.NotSet, + description=github.GithubObject.NotSet, + transient_environment=github.GithubObject.NotSet, + production_environment=github.GithubObject.NotSet, + ): + """ + :calls: `POST /repos/{owner}/{repo}/deployments `_ + :param: ref: string + :param: task: string + :param: auto_merge: bool + :param: required_contexts: list of status contexts + :param: payload: dict + :param: environment: string + :param: description: string + :param: transient_environment: bool + :param: production_environment: bool + :rtype: :class:`github.Deployment.Deployment` + """ + assert isinstance(ref, str), ref + assert task is github.GithubObject.NotSet or isinstance(task, str), task + assert auto_merge is github.GithubObject.NotSet or isinstance( + auto_merge, bool + ), auto_merge + assert required_contexts is github.GithubObject.NotSet or isinstance( + required_contexts, list + ), required_contexts # need to do better checking here + assert payload is github.GithubObject.NotSet or isinstance( + payload, dict + ), payload + assert environment is github.GithubObject.NotSet or isinstance( + environment, str + ), environment + assert description is github.GithubObject.NotSet or isinstance( + description, str + ), description + assert transient_environment is github.GithubObject.NotSet or isinstance( + transient_environment, bool + ), transient_environment + assert production_environment is github.GithubObject.NotSet or isinstance( + production_environment, bool + ), production_environment + + post_parameters = {"ref": ref} + if task is not github.GithubObject.NotSet: + post_parameters["task"] = task + if auto_merge is not github.GithubObject.NotSet: + post_parameters["auto_merge"] = auto_merge + if required_contexts is not github.GithubObject.NotSet: + post_parameters["required_contexts"] = required_contexts + if payload is not github.GithubObject.NotSet: + post_parameters["payload"] = payload + if environment is not github.GithubObject.NotSet: + post_parameters["environment"] = environment + if description is not github.GithubObject.NotSet: + post_parameters["description"] = description + if transient_environment is not github.GithubObject.NotSet: + post_parameters["transient_environment"] = transient_environment + if production_environment is not github.GithubObject.NotSet: + post_parameters["production_environment"] = production_environment + + headers, data = self._requester.requestJsonAndCheck( + "POST", + f"{self.url}/deployments", + input=post_parameters, + headers={"Accept": Consts.deploymentEnhancementsPreview}, + ) + + return github.Deployment.Deployment( + self._requester, headers, data, completed=True + ) + def get_top_referrers(self): """ - :calls: `GET /repos/:owner/:repo/traffic/popular/referrers `_ + :calls: `GET /repos/{owner}/{repo}/traffic/popular/referrers `_ :rtype: :class:`list` of :class:`github.Referrer.Referrer` """ headers, data = self._requester.requestJsonAndCheck( - "GET", self.url + "/traffic/popular/referrers" + "GET", f"{self.url}/traffic/popular/referrers" ) if isinstance(data, list): return [ @@ -1752,11 +1995,11 @@ def get_top_referrers(self): def get_top_paths(self): """ - :calls: `GET /repos/:owner/:repo/traffic/popular/paths `_ + :calls: `GET /repos/{owner}/{repo}/traffic/popular/paths `_ :rtype: :class:`list` of :class:`github.Path.Path` """ headers, data = self._requester.requestJsonAndCheck( - "GET", self.url + "/traffic/popular/paths" + "GET", f"{self.url}/traffic/popular/paths" ) if isinstance(data, list): return [ @@ -1766,7 +2009,7 @@ def get_top_paths(self): def get_views_traffic(self, per=github.GithubObject.NotSet): """ - :calls: `GET /repos/:owner/:repo/traffic/views `_ + :calls: `GET /repos/{owner}/{repo}/traffic/views `_ :param per: string, must be one of day or week, day by default :rtype: None or list of :class:`github.View.View` """ @@ -1777,7 +2020,7 @@ def get_views_traffic(self, per=github.GithubObject.NotSet): if per is not github.GithubObject.NotSet: url_parameters["per"] = per headers, data = self._requester.requestJsonAndCheck( - "GET", self.url + "/traffic/views", parameters=url_parameters + "GET", f"{self.url}/traffic/views", parameters=url_parameters ) if ( (isinstance(data, dict)) @@ -1792,9 +2035,9 @@ def get_views_traffic(self, per=github.GithubObject.NotSet): def get_clones_traffic(self, per=github.GithubObject.NotSet): """ - :calls: `GET /repos/:owner/:repo/traffic/clones `_ + :calls: `GET /repos/{owner}/{repo}/traffic/clones `_ :param per: string, must be one of day or week, day by default - :rtype: None or list of :class:`github.Clone.Clone` + :rtype: None or list of :class:`github.Clones.Clones` """ assert per is github.GithubObject.NotSet or ( isinstance(per, str) and (per == "day" or per == "week") @@ -1803,7 +2046,7 @@ def get_clones_traffic(self, per=github.GithubObject.NotSet): if per is not github.GithubObject.NotSet: url_parameters["per"] = per headers, data = self._requester.requestJsonAndCheck( - "GET", self.url + "/traffic/clones", parameters=url_parameters + "GET", f"{self.url}/traffic/clones", parameters=url_parameters ) if ( (isinstance(data, dict)) @@ -1818,7 +2061,7 @@ def get_clones_traffic(self, per=github.GithubObject.NotSet): def get_projects(self, state=github.GithubObject.NotSet): """ - :calls: `GET /repos/:owner/:repo/projects `_ + :calls: `GET /repos/{owner}/{repo}/projects `_ :rtype: :class:`github.PaginatedList.PaginatedList` of :class:`github.Project.Project` :param state: string """ @@ -1830,7 +2073,7 @@ def get_projects(self, state=github.GithubObject.NotSet): return github.PaginatedList.PaginatedList( github.Project.Project, self._requester, - self.url + "/projects", + f"{self.url}/projects", url_parameters, {"Accept": Consts.mediaTypeProjectsPreview}, ) @@ -1846,7 +2089,7 @@ def create_file( ): """Create a file in this repository. - :calls: `PUT /repos/:owner/:repo/contents/:path `_ + :calls: `PUT /repos/{owner}/{repo}/contents/{path} `_ :param path: string, (required), path of the file in the repository :param message: string, (required), commit message :param content: string, (required), the actual data in the file @@ -1882,7 +2125,7 @@ def create_file( headers, data = self._requester.requestJsonAndCheck( "PUT", - self.url + "/contents/" + urllib.parse.quote(path), + f"{self.url}/contents/{urllib.parse.quote(path)}", input=put_parameters, ) @@ -1907,7 +2150,7 @@ def update_file( ): """This method updates a file in a repository - :calls: `PUT /repos/:owner/:repo/contents/:path `_ + :calls: `PUT /repos/{owner}/{repo}/contents/{path} `_ :param path: string, Required. The content path. :param message: string, Required. The commit message. :param content: string, Required. The updated file content, either base64 encoded, or ready to be encoded. @@ -1946,7 +2189,7 @@ def update_file( headers, data = self._requester.requestJsonAndCheck( "PUT", - self.url + "/contents/" + urllib.parse.quote(path), + f"{self.url}/contents/{urllib.parse.quote(path)}", input=put_parameters, ) @@ -1970,7 +2213,6 @@ def delete_file( ): """This method deletes a file in a repository - :calls: `DELETE /repos/{owner}/{repo}/contents/{path} `_ :param path: string, Required. The content path. :param message: string, Required. The commit message. @@ -2005,7 +2247,7 @@ def delete_file( headers, data = self._requester.requestJsonAndCheck( "DELETE", - self.url + "/contents/" + urllib.parse.quote(path), + f"{self.url}/contents/{urllib.parse.quote(path)}", input=url_parameters, ) @@ -2044,7 +2286,7 @@ def get_contributors(self, anon=github.GithubObject.NotSet): return github.PaginatedList.PaginatedList( github.NamedUser.NamedUser, self._requester, - self.url + "/contributors", + f"{self.url}/contributors", url_parameters, ) @@ -2056,7 +2298,7 @@ def get_download(self, id): """ assert isinstance(id, int), id headers, data = self._requester.requestJsonAndCheck( - "GET", self.url + "/downloads/" + str(id) + "GET", f"{self.url}/downloads/{id}" ) return github.Download.Download(self._requester, headers, data, completed=True) @@ -2066,7 +2308,7 @@ def get_downloads(self): :rtype: :class:`github.PaginatedList.PaginatedList` of :class:`github.Download.Download` """ return github.PaginatedList.PaginatedList( - github.Download.Download, self._requester, self.url + "/downloads", None + github.Download.Download, self._requester, f"{self.url}/downloads", None ) def get_events(self): @@ -2075,7 +2317,7 @@ def get_events(self): :rtype: :class:`github.PaginatedList.PaginatedList` of :class:`github.Event.Event` """ return github.PaginatedList.PaginatedList( - github.Event.Event, self._requester, self.url + "/events", None + github.Event.Event, self._requester, f"{self.url}/events", None ) def get_forks(self): @@ -2084,7 +2326,7 @@ def get_forks(self): :rtype: :class:`github.PaginatedList.PaginatedList` of :class:`github.Repository.Repository` """ return github.PaginatedList.PaginatedList( - Repository, self._requester, self.url + "/forks", None + Repository, self._requester, f"{self.url}/forks", None ) def create_fork(self, organization=github.GithubObject.NotSet): @@ -2100,7 +2342,9 @@ def create_fork(self, organization=github.GithubObject.NotSet): if organization is not github.GithubObject.NotSet: post_parameters["organization"] = organization headers, data = self._requester.requestJsonAndCheck( - "POST", self.url + "/forks", input=post_parameters, + "POST", + f"{self.url}/forks", + input=post_parameters, ) return Repository(self._requester, headers, data, completed=True) @@ -2112,7 +2356,7 @@ def get_git_blob(self, sha): """ assert isinstance(sha, str), sha headers, data = self._requester.requestJsonAndCheck( - "GET", self.url + "/git/blobs/" + sha + "GET", f"{self.url}/git/blobs/{sha}" ) return github.GitBlob.GitBlob(self._requester, headers, data, completed=True) @@ -2124,7 +2368,7 @@ def get_git_commit(self, sha): """ assert isinstance(sha, str), sha headers, data = self._requester.requestJsonAndCheck( - "GET", self.url + "/git/commits/" + sha + "GET", f"{self.url}/git/commits/{sha}" ) return github.GitCommit.GitCommit( self._requester, headers, data, completed=True @@ -2141,7 +2385,7 @@ def get_git_ref(self, ref): prefix = "/git/" assert isinstance(ref, str), ref headers, data = self._requester.requestJsonAndCheck( - "GET", self.url + prefix + ref + "GET", f"{self.url}{prefix}{ref}" ) return github.GitRef.GitRef(self._requester, headers, data, completed=True) @@ -2175,7 +2419,7 @@ def get_git_tag(self, sha): """ assert isinstance(sha, str), sha headers, data = self._requester.requestJsonAndCheck( - "GET", self.url + "/git/tags/" + sha + "GET", f"{self.url}/git/tags/{sha}" ) return github.GitTag.GitTag(self._requester, headers, data, completed=True) @@ -2195,7 +2439,7 @@ def get_git_tree(self, sha, recursive=github.GithubObject.NotSet): # GitHub API requires the recursive parameter be set to 1. url_parameters["recursive"] = 1 headers, data = self._requester.requestJsonAndCheck( - "GET", self.url + "/git/trees/" + sha, parameters=url_parameters + "GET", f"{self.url}/git/trees/{sha}", parameters=url_parameters ) return github.GitTree.GitTree(self._requester, headers, data, completed=True) @@ -2207,7 +2451,7 @@ def get_hook(self, id): """ assert isinstance(id, int), id headers, data = self._requester.requestJsonAndCheck( - "GET", self.url + "/hooks/" + str(id) + "GET", f"{self.url}/hooks/{id}" ) return github.Hook.Hook(self._requester, headers, data, completed=True) @@ -2217,7 +2461,7 @@ def get_hooks(self): :rtype: :class:`github.PaginatedList.PaginatedList` of :class:`github.Hook.Hook` """ return github.PaginatedList.PaginatedList( - github.Hook.Hook, self._requester, self.url + "/hooks", None + github.Hook.Hook, self._requester, f"{self.url}/hooks", None ) def get_issue(self, number): @@ -2228,7 +2472,7 @@ def get_issue(self, number): """ assert isinstance(number, int), number headers, data = self._requester.requestJsonAndCheck( - "GET", self.url + "/issues/" + str(number) + "GET", f"{self.url}/issues/{number}" ) return github.Issue.Issue(self._requester, headers, data, completed=True) @@ -2322,7 +2566,7 @@ def get_issues( else: url_parameters["creator"] = creator._identity return github.PaginatedList.PaginatedList( - github.Issue.Issue, self._requester, self.url + "/issues", url_parameters + github.Issue.Issue, self._requester, f"{self.url}/issues", url_parameters ) def get_issues_comments( @@ -2355,7 +2599,7 @@ def get_issues_comments( return github.PaginatedList.PaginatedList( github.IssueComment.IssueComment, self._requester, - self.url + "/issues/comments", + f"{self.url}/issues/comments", url_parameters, ) @@ -2368,7 +2612,7 @@ def get_issues_event(self, id): assert isinstance(id, int), id headers, data = self._requester.requestJsonAndCheck( "GET", - self.url + "/issues/events/" + str(id), + f"{self.url}/issues/events/{id}", headers={"Accept": Consts.mediaTypeLockReasonPreview}, ) return github.IssueEvent.IssueEvent( @@ -2383,7 +2627,7 @@ def get_issues_events(self): return github.PaginatedList.PaginatedList( github.IssueEvent.IssueEvent, self._requester, - self.url + "/issues/events", + f"{self.url}/issues/events", None, headers={"Accept": Consts.mediaTypeLockReasonPreview}, ) @@ -2396,7 +2640,7 @@ def get_key(self, id): """ assert isinstance(id, int), id headers, data = self._requester.requestJsonAndCheck( - "GET", self.url + "/keys/" + str(id) + "GET", f"{self.url}/keys/{id}" ) return github.RepositoryKey.RepositoryKey( self._requester, headers, data, completed=True @@ -2410,7 +2654,7 @@ def get_keys(self): return github.PaginatedList.PaginatedList( github.RepositoryKey.RepositoryKey, self._requester, - self.url + "/keys", + f"{self.url}/keys", None, ) @@ -2422,7 +2666,7 @@ def get_label(self, name): """ assert isinstance(name, str), name headers, data = self._requester.requestJsonAndCheck( - "GET", self.url + "/labels/" + urllib.parse.quote(name) + "GET", f"{self.url}/labels/{urllib.parse.quote(name)}" ) return github.Label.Label(self._requester, headers, data, completed=True) @@ -2432,7 +2676,7 @@ def get_labels(self): :rtype: :class:`github.PaginatedList.PaginatedList` of :class:`github.Label.Label` """ return github.PaginatedList.PaginatedList( - github.Label.Label, self._requester, self.url + "/labels", None + github.Label.Label, self._requester, f"{self.url}/labels", None ) def get_languages(self): @@ -2441,18 +2685,18 @@ def get_languages(self): :rtype: dict of string to integer """ headers, data = self._requester.requestJsonAndCheck( - "GET", self.url + "/languages" + "GET", f"{self.url}/languages" ) return data def get_license(self): """ - :calls: `GET /repos/:owner/:repo/license `_ + :calls: `GET /repos/{owner}/{repo}/license `_ :rtype: :class:`github.ContentFile.ContentFile` """ headers, data = self._requester.requestJsonAndCheck( - "GET", self.url + "/license" + "GET", f"{self.url}/license" ) return github.ContentFile.ContentFile( self._requester, headers, data, completed=True @@ -2466,7 +2710,7 @@ def get_milestone(self, number): """ assert isinstance(number, int), number headers, data = self._requester.requestJsonAndCheck( - "GET", self.url + "/milestones/" + str(number) + "GET", f"{self.url}/milestones/{number}" ) return github.Milestone.Milestone( self._requester, headers, data, completed=True @@ -2500,7 +2744,7 @@ def get_milestones( return github.PaginatedList.PaginatedList( github.Milestone.Milestone, self._requester, - self.url + "/milestones", + f"{self.url}/milestones", url_parameters, ) @@ -2512,10 +2756,22 @@ def get_network_events(self): return github.PaginatedList.PaginatedList( github.Event.Event, self._requester, - "/networks/" + self.owner.login + "/" + self.name + "/events", + f"/networks/{self.owner.login}/{self.name}/events", None, ) + def get_public_key(self): + """ + :calls: `GET /repos/{owner}/{repo}/actions/secrets/public-key `_ + :rtype: :class:`github.PublicKey.PublicKey` + """ + headers, data = self._requester.requestJsonAndCheck( + "GET", f"{self.url}/actions/secrets/public-key" + ) + return github.PublicKey.PublicKey( + self._requester, headers, data, completed=True + ) + def get_pull(self, number): """ :calls: `GET /repos/{owner}/{repo}/pulls/{number} `_ @@ -2524,7 +2780,7 @@ def get_pull(self, number): """ assert isinstance(number, int), number headers, data = self._requester.requestJsonAndCheck( - "GET", self.url + "/pulls/" + str(number) + "GET", f"{self.url}/pulls/{number}" ) return github.PullRequest.PullRequest( self._requester, headers, data, completed=True @@ -2568,7 +2824,7 @@ def get_pulls( return github.PaginatedList.PaginatedList( github.PullRequest.PullRequest, self._requester, - self.url + "/pulls", + f"{self.url}/pulls", url_parameters, ) @@ -2617,7 +2873,7 @@ def get_pulls_review_comments( return github.PaginatedList.PaginatedList( github.PullRequestComment.PullRequestComment, self._requester, - self.url + "/pulls/comments", + f"{self.url}/pulls/comments", url_parameters, ) @@ -2632,12 +2888,39 @@ def get_readme(self, ref=github.GithubObject.NotSet): if ref is not github.GithubObject.NotSet: url_parameters["ref"] = ref headers, data = self._requester.requestJsonAndCheck( - "GET", self.url + "/readme", parameters=url_parameters + "GET", f"{self.url}/readme", parameters=url_parameters ) return github.ContentFile.ContentFile( self._requester, headers, data, completed=True ) + def get_self_hosted_runner(self, runner_id): + """ + :calls: `GET /repos/{owner}/{repo}/actions/runners/{id} `_ + :param runner_id: int + :rtype: :class:`github.SelfHostedActionsRunner.SelfHostedActionsRunner` + """ + assert isinstance(runner_id, int), runner_id + headers, data = self._requester.requestJsonAndCheck( + "GET", f"{self.url}/actions/runners/{runner_id}" + ) + return github.SelfHostedActionsRunner.SelfHostedActionsRunner( + self._requester, headers, data, completed=True + ) + + def get_self_hosted_runners(self): + """ + :calls: `GET /repos/{owner}/{repo}/actions/runners `_ + :rtype: :class:`github.PaginatedList.PaginatedList` of :class:`github.SelfHostedActionsRunner.SelfHostedActionsRunner` + """ + return github.PaginatedList.PaginatedList( + github.SelfHostedActionsRunner.SelfHostedActionsRunner, + self._requester, + f"{self.url}/actions/runners", + None, + list_item="runners", + ) + def get_source_import(self): """ :calls: `GET /repos/{owner}/{repo}/import `_ @@ -2645,7 +2928,9 @@ def get_source_import(self): """ import_header = {"Accept": Consts.mediaTypeImportPreview} headers, data = self._requester.requestJsonAndCheck( - "GET", self.url + "/import", headers=import_header, + "GET", + f"{self.url}/import", + headers=import_header, ) if not data: return None @@ -2660,7 +2945,7 @@ def get_stargazers(self): :rtype: :class:`github.PaginatedList.PaginatedList` of :class:`github.NamedUser.NamedUser` """ return github.PaginatedList.PaginatedList( - github.NamedUser.NamedUser, self._requester, self.url + "/stargazers", None + github.NamedUser.NamedUser, self._requester, f"{self.url}/stargazers", None ) def get_stargazers_with_dates(self): @@ -2671,7 +2956,7 @@ def get_stargazers_with_dates(self): return github.PaginatedList.PaginatedList( github.Stargazer.Stargazer, self._requester, - self.url + "/stargazers", + f"{self.url}/stargazers", None, headers={"Accept": Consts.mediaTypeStarringPreview}, ) @@ -2682,7 +2967,7 @@ def get_stats_contributors(self): :rtype: None or list of :class:`github.StatsContributor.StatsContributor` """ headers, data = self._requester.requestJsonAndCheck( - "GET", self.url + "/stats/contributors" + "GET", f"{self.url}/stats/contributors" ) if not data: return None @@ -2700,7 +2985,7 @@ def get_stats_commit_activity(self): :rtype: None or list of :class:`github.StatsCommitActivity.StatsCommitActivity` """ headers, data = self._requester.requestJsonAndCheck( - "GET", self.url + "/stats/commit_activity" + "GET", f"{self.url}/stats/commit_activity" ) if not data: return None @@ -2718,7 +3003,7 @@ def get_stats_code_frequency(self): :rtype: None or list of :class:`github.StatsCodeFrequency.StatsCodeFrequency` """ headers, data = self._requester.requestJsonAndCheck( - "GET", self.url + "/stats/code_frequency" + "GET", f"{self.url}/stats/code_frequency" ) if not data: return None @@ -2736,7 +3021,7 @@ def get_stats_participation(self): :rtype: None or :class:`github.StatsParticipation.StatsParticipation` """ headers, data = self._requester.requestJsonAndCheck( - "GET", self.url + "/stats/participation" + "GET", f"{self.url}/stats/participation" ) if not data: return None @@ -2751,7 +3036,7 @@ def get_stats_punch_card(self): :rtype: None or :class:`github.StatsPunchCard.StatsPunchCard` """ headers, data = self._requester.requestJsonAndCheck( - "GET", self.url + "/stats/punch_card" + "GET", f"{self.url}/stats/punch_card" ) if not data: return None @@ -2766,7 +3051,7 @@ def get_subscribers(self): :rtype: :class:`github.PaginatedList.PaginatedList` of :class:`github.NamedUser.NamedUser` """ return github.PaginatedList.PaginatedList( - github.NamedUser.NamedUser, self._requester, self.url + "/subscribers", None + github.NamedUser.NamedUser, self._requester, f"{self.url}/subscribers", None ) def get_tags(self): @@ -2775,7 +3060,7 @@ def get_tags(self): :rtype: :class:`github.PaginatedList.PaginatedList` of :class:`github.Tag.Tag` """ return github.PaginatedList.PaginatedList( - github.Tag.Tag, self._requester, self.url + "/tags", None + github.Tag.Tag, self._requester, f"{self.url}/tags", None ) def get_releases(self): @@ -2784,7 +3069,7 @@ def get_releases(self): :rtype: :class:`github.PaginatedList.PaginatedList` of :class:`github.GitRelease.GitRelease` """ return github.PaginatedList.PaginatedList( - github.GitRelease.GitRelease, self._requester, self.url + "/releases", None + github.GitRelease.GitRelease, self._requester, f"{self.url}/releases", None ) def get_release(self, id): @@ -2795,14 +3080,14 @@ def get_release(self, id): """ if isinstance(id, int): headers, data = self._requester.requestJsonAndCheck( - "GET", self.url + "/releases/" + str(id) + "GET", f"{self.url}/releases/{id}" ) return github.GitRelease.GitRelease( self._requester, headers, data, completed=True ) elif isinstance(id, str): headers, data = self._requester.requestJsonAndCheck( - "GET", self.url + "/releases/tags/" + id + "GET", f"{self.url}/releases/tags/{id}" ) return github.GitRelease.GitRelease( self._requester, headers, data, completed=True @@ -2814,7 +3099,7 @@ def get_latest_release(self): :rtype: :class:`github.GitRelease.GitRelease` """ headers, data = self._requester.requestJsonAndCheck( - "GET", self.url + "/releases/latest" + "GET", f"{self.url}/releases/latest" ) return github.GitRelease.GitRelease( self._requester, headers, data, completed=True @@ -2826,7 +3111,7 @@ def get_teams(self): :rtype: :class:`github.PaginatedList.PaginatedList` of :class:`github.Team.Team` """ return github.PaginatedList.PaginatedList( - github.Team.Team, self._requester, self.url + "/teams", None + github.Team.Team, self._requester, f"{self.url}/teams", None ) def get_topics(self): @@ -2836,7 +3121,7 @@ def get_topics(self): """ headers, data = self._requester.requestJsonAndCheck( "GET", - self.url + "/topics", + f"{self.url}/topics", headers={"Accept": Consts.mediaTypeTopicsPreview}, ) return data["names"] @@ -2958,7 +3243,7 @@ def has_in_assignees(self, assignee): assignee = assignee._identity status, headers, data = self._requester.requestJson( - "GET", self.url + "/assignees/" + assignee + "GET", f"{self.url}/assignees/{assignee}" ) return status == 204 @@ -2976,17 +3261,17 @@ def has_in_collaborators(self, collaborator): collaborator = collaborator._identity status, headers, data = self._requester.requestJson( - "GET", self.url + "/collaborators/" + collaborator + "GET", f"{self.url}/collaborators/{collaborator}" ) return status == 204 def _legacy_convert_issue(self, attributes): convertedAttributes = { "number": attributes["number"], - "url": "/repos" + urllib.parse.urlparse(attributes["html_url"]).path, + "url": f"/repos{urllib.parse.urlparse(attributes['html_url']).path}", "user": { "login": attributes["user"], - "url": "/users/" + attributes["user"], + "url": f"/users/{attributes['user']}", }, } if "labels" in attributes: # pragma no branch @@ -3003,20 +3288,13 @@ def legacy_search_issues(self, state, keyword): :calls: `GET /legacy/issues/search/{owner}/{repository}/{state}/{keyword} `_ :param state: "open" or "closed" :param keyword: string - :rtype: :class:`github.PaginatedList.PaginatedList` of :class:`github.Issue.Issue` + :rtype: List of :class:`github.Issue.Issue` """ assert state in ["open", "closed"], state assert isinstance(keyword, str), keyword headers, data = self._requester.requestJsonAndCheck( "GET", - "/legacy/issues/search/" - + self.owner.login - + "/" - + self.name - + "/" - + state - + "/" - + urllib.parse.quote(keyword), + f"/legacy/issues/search/{self.owner.login}/{self.name}/{state}/{urllib.parse.quote(keyword)}", ) return [ github.Issue.Issue( @@ -3068,20 +3346,20 @@ def get_notifications( return github.PaginatedList.PaginatedList( github.Notification.Notification, self._requester, - self.url + "/notifications", + f"{self.url}/notifications", params, ) def mark_notifications_as_read(self, last_read_at=datetime.datetime.utcnow()): """ - :calls: `PUT /repos/:owner/:repo/notifications `_ + :calls: `PUT /repos/{owner}/{repo}/notifications `_ :param last_read_at: datetime """ assert isinstance(last_read_at, datetime.datetime) put_parameters = {"last_read_at": last_read_at.strftime("%Y-%m-%dT%H:%M:%SZ")} headers, data = self._requester.requestJsonAndCheck( - "PUT", self.url + "/notifications", input=put_parameters + "PUT", f"{self.url}/notifications", input=put_parameters ) def merge(self, base, head, commit_message=github.GithubObject.NotSet): @@ -3104,7 +3382,7 @@ def merge(self, base, head, commit_message=github.GithubObject.NotSet): if commit_message is not github.GithubObject.NotSet: post_parameters["commit_message"] = commit_message headers, data = self._requester.requestJsonAndCheck( - "POST", self.url + "/merges", input=post_parameters + "POST", f"{self.url}/merges", input=post_parameters ) if data is None: return None @@ -3120,7 +3398,7 @@ def replace_topics(self, topics): post_parameters = {"names": topics} headers, data = self._requester.requestJsonAndCheck( "PUT", - self.url + "/topics", + f"{self.url}/topics", headers={"Accept": Consts.mediaTypeTopicsPreview}, input=post_parameters, ) @@ -3132,7 +3410,7 @@ def get_vulnerability_alert(self): """ status, _, _ = self._requester.requestJson( "GET", - self.url + "/vulnerability-alerts", + f"{self.url}/vulnerability-alerts", headers={"Accept": Consts.vulnerabilityAlertsPreview}, ) return status == 204 @@ -3144,7 +3422,7 @@ def enable_vulnerability_alert(self): """ status, _, _ = self._requester.requestJson( "PUT", - self.url + "/vulnerability-alerts", + f"{self.url}/vulnerability-alerts", headers={"Accept": Consts.vulnerabilityAlertsPreview}, ) return status == 204 @@ -3156,7 +3434,7 @@ def disable_vulnerability_alert(self): """ status, _, _ = self._requester.requestJson( "DELETE", - self.url + "/vulnerability-alerts", + f"{self.url}/vulnerability-alerts", headers={"Accept": Consts.vulnerabilityAlertsPreview}, ) return status == 204 @@ -3168,7 +3446,7 @@ def enable_automated_security_fixes(self): """ status, _, _ = self._requester.requestJson( "PUT", - self.url + "/automated-security-fixes", + f"{self.url}/automated-security-fixes", headers={"Accept": Consts.automatedSecurityFixes}, ) return status == 204 @@ -3180,7 +3458,7 @@ def disable_automated_security_fixes(self): """ status, _, _ = self._requester.requestJson( "DELETE", - self.url + "/automated-security-fixes", + f"{self.url}/automated-security-fixes", headers={"Accept": Consts.automatedSecurityFixes}, ) return status == 204 @@ -3199,12 +3477,30 @@ def remove_from_collaborators(self, collaborator): collaborator = collaborator._identity headers, data = self._requester.requestJsonAndCheck( - "DELETE", self.url + "/collaborators/" + collaborator + "DELETE", f"{self.url}/collaborators/{collaborator}" ) + def remove_self_hosted_runner(self, runner): + """ + :calls: `DELETE /repos/{owner}/{repo}/actions/runners/{runner_id} `_ + :param runner: int or :class:`github.SelfHostedActionsRunner.SelfHostedActionsRunner` + :rtype: bool + """ + assert isinstance( + runner, github.SelfHostedActionsRunner.SelfHostedActionsRunner + ) or isinstance(runner, int), runner + + if isinstance(runner, github.SelfHostedActionsRunner.SelfHostedActionsRunner): + runner = runner.id + + status, _, _ = self._requester.requestJson( + "DELETE", f"{self.url}/actions/runners/{runner}" + ) + return status == 204 + def subscribe_to_hub(self, event, callback, secret=github.GithubObject.NotSet): """ - :calls: `POST /hub `_ + :calls: `POST /hub `_ :param event: string :param callback: string :param secret: string @@ -3214,7 +3510,7 @@ def subscribe_to_hub(self, event, callback, secret=github.GithubObject.NotSet): def unsubscribe_from_hub(self, event, callback): """ - :calls: `POST /hub `_ + :calls: `POST /hub `_ :param event: string :param callback: string :param secret: string @@ -3222,6 +3518,57 @@ def unsubscribe_from_hub(self, event, callback): """ return self._hub("unsubscribe", event, callback, github.GithubObject.NotSet) + def create_check_suite(self, head_sha): + """ + :calls: `POST /repos/{owner}/{repo}/check-suites `_ + :param head_sha: string + :rtype: :class:`github.CheckSuite.CheckSuite` + """ + assert isinstance(head_sha, str), head_sha + headers, data = self._requester.requestJsonAndCheck( + "POST", + f"{self.url}/check-suites", + input={"head_sha": head_sha}, + ) + return github.CheckSuite.CheckSuite( + self._requester, headers, data, completed=True + ) + + def get_check_suite(self, check_suite_id): + """ + :calls: `GET /repos/{owner}/{repo}/check-suites/{check_suite_id} `_ + :param check_suite_id: int + :rtype: :class:`github.CheckSuite.CheckSuite` + """ + assert isinstance(check_suite_id, int), check_suite_id + requestHeaders = {"Accept": "application/vnd.github.v3+json"} + headers, data = self._requester.requestJsonAndCheck( + "GET", + f"{self.url}/check-suites/{check_suite_id}", + headers=requestHeaders, + ) + return github.CheckSuite.CheckSuite( + self._requester, headers, data, completed=True + ) + + def update_check_suites_preferences(self, auto_trigger_checks): + """ + :calls: `PATCH /repos/{owner}/{repo}/check-suites/preferences `_ + :param auto_trigger_checks: list of dict + :rtype: :class:`github.RepositoryPreferences.RepositoryPreferences` + """ + assert all( + isinstance(element, dict) for element in auto_trigger_checks + ), auto_trigger_checks + headers, data = self._requester.requestJsonAndCheck( + "PATCH", + f"{self.url}/check-suites/preferences", + input={"auto_trigger_checks": auto_trigger_checks}, + ) + return github.RepositoryPreferences.RepositoryPreferences( + self._requester, headers, data, completed=True + ) + def _hub(self, mode, event, callback, secret): assert isinstance(mode, str), mode assert isinstance(event, str), event @@ -3230,9 +3577,9 @@ def _hub(self, mode, event, callback, secret): post_parameters = collections.OrderedDict() post_parameters["hub.callback"] = callback - post_parameters["hub.topic"] = ( - "https://github.com/" + self.full_name + "/events/" + event - ) + post_parameters[ + "hub.topic" + ] = f"https://github.com/{self.full_name}/events/{event}" post_parameters["hub.mode"] = mode if secret is not github.GithubObject.NotSet: post_parameters["hub.secret"] = secret @@ -3243,18 +3590,110 @@ def _hub(self, mode, event, callback, secret): @property def _identity(self): - return self.owner.login + "/" + self.name + return f"{self.owner.login}/{self.name}" def get_release_asset(self, id): assert isinstance(id, (int)), id resp_headers, data = self._requester.requestJsonAndCheck( - "GET", self.url + "/releases/assets/" + str(id) + "GET", f"{self.url}/releases/assets/{id}" ) return github.GitReleaseAsset.GitReleaseAsset( self._requester, resp_headers, data, completed=True ) + def create_check_run( + self, + name, + head_sha, + details_url=github.GithubObject.NotSet, + external_id=github.GithubObject.NotSet, + status=github.GithubObject.NotSet, + started_at=github.GithubObject.NotSet, + conclusion=github.GithubObject.NotSet, + completed_at=github.GithubObject.NotSet, + output=github.GithubObject.NotSet, + actions=github.GithubObject.NotSet, + ): + """ + :calls: `POST /repos/{owner}/{repo}/check-runs `_ + :param name: string + :param head_sha: string + :param details_url: string + :param external_id: string + :param status: string + :param started_at: datetime.datetime + :param conclusion: string + :param completed_at: datetime.datetime + :param output: dict + :param actions: list of dict + :rtype: :class:`github.CheckRun.CheckRun` + """ + assert isinstance(name, str), name + assert isinstance(head_sha, str), head_sha + assert details_url is github.GithubObject.NotSet or isinstance( + details_url, str + ), details_url + assert external_id is github.GithubObject.NotSet or isinstance( + external_id, str + ), external_id + assert status is github.GithubObject.NotSet or isinstance(status, str), status + assert started_at is github.GithubObject.NotSet or isinstance( + started_at, datetime.datetime + ), started_at + assert conclusion is github.GithubObject.NotSet or isinstance( + conclusion, str + ), conclusion + assert completed_at is github.GithubObject.NotSet or isinstance( + completed_at, datetime.datetime + ), completed_at + assert output is github.GithubObject.NotSet or isinstance(output, dict), output + assert actions is github.GithubObject.NotSet or all( + isinstance(element, dict) for element in actions + ), actions + + post_parameters = { + "name": name, + "head_sha": head_sha, + } + if details_url is not github.GithubObject.NotSet: + post_parameters["details_url"] = details_url + if external_id is not github.GithubObject.NotSet: + post_parameters["external_id"] = external_id + if status is not github.GithubObject.NotSet: + post_parameters["status"] = status + if started_at is not github.GithubObject.NotSet: + post_parameters["started_at"] = started_at.strftime("%Y-%m-%dT%H:%M:%SZ") + if completed_at is not github.GithubObject.NotSet: + post_parameters["completed_at"] = completed_at.strftime( + "%Y-%m-%dT%H:%M:%SZ" + ) + if conclusion is not github.GithubObject.NotSet: + post_parameters["conclusion"] = conclusion + if output is not github.GithubObject.NotSet: + post_parameters["output"] = output + if actions is not github.GithubObject.NotSet: + post_parameters["actions"] = actions + + headers, data = self._requester.requestJsonAndCheck( + "POST", + f"{self.url}/check-runs", + input=post_parameters, + ) + return github.CheckRun.CheckRun(self._requester, headers, data, completed=True) + + def get_check_run(self, check_run_id): + """ + :calls: `GET /repos/{owner}/{repo}/check-runs/{check_run_id} `_ + :param check_run_id: int + :rtype: :class:`github.CheckRun.CheckRun` + """ + assert isinstance(check_run_id, int), check_run_id + headers, data = self._requester.requestJsonAndCheck( + "GET", f"{self.url}/check-runs/{check_run_id}" + ) + return github.CheckRun.CheckRun(self._requester, headers, data, completed=True) + def _initAttributes(self): self._allow_merge_commit = github.GithubObject.NotSet self._allow_rebase_merge = github.GithubObject.NotSet @@ -3274,6 +3713,7 @@ def _initAttributes(self): self._created_at = github.GithubObject.NotSet self._default_branch = github.GithubObject.NotSet self._delete_branch_on_merge = github.GithubObject.NotSet + self._deployments_url = github.GithubObject.NotSet self._description = github.GithubObject.NotSet self._downloads_url = github.GithubObject.NotSet self._events_url = github.GithubObject.NotSet @@ -3288,6 +3728,7 @@ def _initAttributes(self): self._git_url = github.GithubObject.NotSet self._has_downloads = github.GithubObject.NotSet self._has_issues = github.GithubObject.NotSet + self._has_pages = github.GithubObject.NotSet self._has_projects = github.GithubObject.NotSet self._has_wiki = github.GithubObject.NotSet self._homepage = github.GithubObject.NotSet @@ -3318,6 +3759,7 @@ def _initAttributes(self): self._private = github.GithubObject.NotSet self._pulls_url = github.GithubObject.NotSet self._pushed_at = github.GithubObject.NotSet + self._releases_url = github.GithubObject.NotSet self._size = github.GithubObject.NotSet self._source = github.GithubObject.NotSet self._ssh_url = github.GithubObject.NotSet @@ -3388,6 +3830,10 @@ def _useAttributes(self, attributes): self._delete_branch_on_merge = self._makeBoolAttribute( attributes["delete_branch_on_merge"] ) + if "deployments_url" in attributes: # pragma no branch + self._deployments_url = self._makeStringAttribute( + attributes["deployments_url"] + ) if "description" in attributes: # pragma no branch self._description = self._makeStringAttribute(attributes["description"]) if "downloads_url" in attributes: # pragma no branch @@ -3418,6 +3864,8 @@ def _useAttributes(self, attributes): self._has_downloads = self._makeBoolAttribute(attributes["has_downloads"]) if "has_issues" in attributes: # pragma no branch self._has_issues = self._makeBoolAttribute(attributes["has_issues"]) + if "has_pages" in attributes: # pragma no branch + self._has_pages = self._makeBoolAttribute(attributes["has_pages"]) if "has_projects" in attributes: # pragma no branch self._has_projects = self._makeBoolAttribute(attributes["has_projects"]) if "has_wiki" in attributes: # pragma no branch @@ -3494,6 +3942,8 @@ def _useAttributes(self, attributes): self._pulls_url = self._makeStringAttribute(attributes["pulls_url"]) if "pushed_at" in attributes: # pragma no branch self._pushed_at = self._makeDatetimeAttribute(attributes["pushed_at"]) + if "releases_url" in attributes: # pragma no branch + self._releases_url = self._makeStringAttribute(attributes["releases_url"]) if "size" in attributes: # pragma no branch self._size = self._makeIntAttribute(attributes["size"]) if "source" in attributes: # pragma no branch diff --git a/tests/ReplayData/AuthenticatedUser.testCreateRepoFromTemplate.txt b/tests/ReplayData/AuthenticatedUser.testCreateRepoFromTemplate.txt index 3ab48b9c2e..17d22aa222 100644 --- a/tests/ReplayData/AuthenticatedUser.testCreateRepoFromTemplate.txt +++ b/tests/ReplayData/AuthenticatedUser.testCreateRepoFromTemplate.txt @@ -25,7 +25,7 @@ POST api.github.com None /repos/actions/hello-world-docker-action/generate -{'Accept': 'application/vnd.github.baptiste-preview+json', 'Authorization': 'Basic login_and_password_removed', 'User-Agent': 'PyGithub/Python', 'Content-Type': 'application/json'} +{'Accept': 'application/vnd.github.v3+json', 'Authorization': 'Basic login_and_password_removed', 'User-Agent': 'PyGithub/Python', 'Content-Type': 'application/json'} {"name": "hello-world-docker-action-new", "owner": "jacquev6"} 201 [('Server', 'GitHub.com'), ('Date', 'Tue, 11 Feb 2020 16:56:49 GMT'), ('Content-Type', 'application/json; charset=utf-8'), ('Content-Length', '11775'), ('Status', '201 Created'), ('X-RateLimit-Limit', '5000'), ('X-RateLimit-Remaining', '4995'), ('X-RateLimit-Reset', '1581443568'), ('Cache-Control', 'private, max-age=60, s-maxage=60'), ('Vary', 'Accept, Authorization, Cookie, X-GitHub-OTP, Accept-Encoding, Accept'), ('ETag', '"eabd6fce61227c57848e030e45c468c3"'), ('X-OAuth-Scopes', 'public_repo, read:org, read:user, user:email'), ('X-Accepted-OAuth-Scopes', ''), ('Location', 'https://api.github.com/repos/jacquev6/hello-world-docker-action-new'), ('X-GitHub-Media-Type', 'github.baptiste-preview; format=json'), ('Access-Control-Expose-Headers', 'ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type'), ('Access-Control-Allow-Origin', '*'), ('Strict-Transport-Security', 'max-age=31536000; includeSubdomains; preload'), ('X-Frame-Options', 'deny'), ('X-Content-Type-Options', 'nosniff'), ('X-XSS-Protection', '1; mode=block'), ('Referrer-Policy', 'origin-when-cross-origin, strict-origin-when-cross-origin'), ('Content-Security-Policy', "default-src 'none'"), ('X-GitHub-Request-Id', '8C7A:1AEB:84521:F940F:5E42DCCE')] diff --git a/tests/ReplayData/AuthenticatedUser.testCreateRepoFromTemplateWithAllArguments.txt b/tests/ReplayData/AuthenticatedUser.testCreateRepoFromTemplateWithAllArguments.txt index 13bbac4fb8..c150a5773a 100644 --- a/tests/ReplayData/AuthenticatedUser.testCreateRepoFromTemplateWithAllArguments.txt +++ b/tests/ReplayData/AuthenticatedUser.testCreateRepoFromTemplateWithAllArguments.txt @@ -25,7 +25,7 @@ POST api.github.com None /repos/actions/hello-world-docker-action/generate -{'Accept': 'application/vnd.github.baptiste-preview+json', 'Authorization': 'Basic login_and_password_removed', 'User-Agent': 'PyGithub/Python', 'Content-Type': 'application/json'} +{'Accept': 'application/vnd.github.v3+json', 'Authorization': 'Basic login_and_password_removed', 'User-Agent': 'PyGithub/Python', 'Content-Type': 'application/json'} {"name": "hello-world-docker-action-new", "owner": "jacquev6", "description": "My repo from template", "private": true} 201 [('Server', 'GitHub.com'), ('Date', 'Tue, 11 Feb 2020 19:18:59 GMT'), ('Content-Type', 'application/json; charset=utf-8'), ('Content-Length', '11794'), ('Status', '201 Created'), ('X-RateLimit-Limit', '5000'), ('X-RateLimit-Remaining', '4994'), ('X-RateLimit-Reset', '1581452316'), ('Cache-Control', 'private, max-age=60, s-maxage=60'), ('Vary', 'Accept, Authorization, Cookie, X-GitHub-OTP, Accept-Encoding, Accept'), ('ETag', '"7fe9f51a711de4ffab9b930e33e3d875"'), ('X-OAuth-Scopes', 'public_repo, read:org, read:user, user:email'), ('X-Accepted-OAuth-Scopes', ''), ('Location', 'https://api.github.com/repos/jacquev6/hello-world-docker-action-new'), ('X-GitHub-Media-Type', 'github.baptiste-preview; format=json'), ('Access-Control-Expose-Headers', 'ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type'), ('Access-Control-Allow-Origin', '*'), ('Strict-Transport-Security', 'max-age=31536000; includeSubdomains; preload'), ('X-Frame-Options', 'deny'), ('X-Content-Type-Options', 'nosniff'), ('X-XSS-Protection', '1; mode=block'), ('Referrer-Policy', 'origin-when-cross-origin, strict-origin-when-cross-origin'), ('Content-Security-Policy', "default-src 'none'"), ('X-GitHub-Request-Id', '3FB4:2492:632B7:FC388:5E42FE21')] diff --git a/tests/ReplayData/Organization.testCreateRepoFromTemplate.txt b/tests/ReplayData/Organization.testCreateRepoFromTemplate.txt index 9d97746918..95846f8a67 100644 --- a/tests/ReplayData/Organization.testCreateRepoFromTemplate.txt +++ b/tests/ReplayData/Organization.testCreateRepoFromTemplate.txt @@ -14,7 +14,7 @@ POST api.github.com None /repos/actions/hello-world-docker-action/generate -{'Accept': 'application/vnd.github.baptiste-preview+json', 'Authorization': 'Basic login_and_password_removed', 'User-Agent': 'PyGithub/Python', 'Content-Type': 'application/json'} +{'Accept': 'application/vnd.github.v3+json', 'Authorization': 'Basic login_and_password_removed', 'User-Agent': 'PyGithub/Python', 'Content-Type': 'application/json'} {"name": "hello-world-docker-action-new", "owner": "BeaverSoftware"} 201 [('Server', 'GitHub.com'), ('Date', 'Wed, 12 Feb 2020 13:54:29 GMT'), ('Content-Type', 'application/json; charset=utf-8'), ('Content-Length', '12600'), ('Status', '201 Created'), ('X-RateLimit-Limit', '5000'), ('X-RateLimit-Remaining', '4997'), ('X-RateLimit-Reset', '1581519266'), ('Cache-Control', 'private, max-age=60, s-maxage=60'), ('Vary', 'Accept, Authorization, Cookie, X-GitHub-OTP, Accept-Encoding, Accept'), ('ETag', '"36abdc4630fb044196d6efbfc0f644e0"'), ('X-OAuth-Scopes', 'public_repo, read:org, read:user, user:email'), ('X-Accepted-OAuth-Scopes', ''), ('Location', 'https://api.github.com/repos/BeaverSoftware/hello-world-docker-action-new'), ('X-GitHub-Media-Type', 'github.baptiste-preview; format=json'), ('Access-Control-Expose-Headers', 'ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type'), ('Access-Control-Allow-Origin', '*'), ('Strict-Transport-Security', 'max-age=31536000; includeSubdomains; preload'), ('X-Frame-Options', 'deny'), ('X-Content-Type-Options', 'nosniff'), ('X-XSS-Protection', '1; mode=block'), ('Referrer-Policy', 'origin-when-cross-origin, strict-origin-when-cross-origin'), ('Content-Security-Policy', "default-src 'none'"), ('X-GitHub-Request-Id', '8C3C:2BF7:ACD4:17C45:5E440393')] diff --git a/tests/ReplayData/Organization.testCreateRepoFromTemplateWithAllArguments.txt b/tests/ReplayData/Organization.testCreateRepoFromTemplateWithAllArguments.txt index 8e45505ef5..210ab41645 100644 --- a/tests/ReplayData/Organization.testCreateRepoFromTemplateWithAllArguments.txt +++ b/tests/ReplayData/Organization.testCreateRepoFromTemplateWithAllArguments.txt @@ -14,7 +14,7 @@ POST api.github.com None /repos/actions/hello-world-docker-action/generate -{'Accept': 'application/vnd.github.baptiste-preview+json', 'Authorization': 'Basic login_and_password_removed', 'User-Agent': 'PyGithub/Python', 'Content-Type': 'application/json'} +{'Accept': 'application/vnd.github.v3+json', 'Authorization': 'Basic login_and_password_removed', 'User-Agent': 'PyGithub/Python', 'Content-Type': 'application/json'} {"name": "hello-world-docker-action-new", "owner": "BeaverSoftware", "description": "My repo from template", "private": true} 201 [('Server', 'GitHub.com'), ('Date', 'Wed, 12 Feb 2020 18:18:12 GMT'), ('Content-Type', 'application/json; charset=utf-8'), ('Content-Length', '12619'), ('Status', '201 Created'), ('X-RateLimit-Limit', '5000'), ('X-RateLimit-Remaining', '4991'), ('X-RateLimit-Reset', '1581534987'), ('Cache-Control', 'private, max-age=60, s-maxage=60'), ('Vary', 'Accept, Authorization, Cookie, X-GitHub-OTP, Accept-Encoding, Accept'), ('ETag', '"7ab10890a25b4661a4310dc8dbf4491f"'), ('X-OAuth-Scopes', 'public_repo, read:org, read:user, user:email'), ('X-Accepted-OAuth-Scopes', ''), ('Location', 'https://api.github.com/repos/BeaverSoftware/hello-world-docker-action-new'), ('X-GitHub-Media-Type', 'github.baptiste-preview; format=json'), ('Access-Control-Expose-Headers', 'ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type'), ('Access-Control-Allow-Origin', '*'), ('Strict-Transport-Security', 'max-age=31536000; includeSubdomains; preload'), ('X-Frame-Options', 'deny'), ('X-Content-Type-Options', 'nosniff'), ('X-XSS-Protection', '1; mode=block'), ('Referrer-Policy', 'origin-when-cross-origin, strict-origin-when-cross-origin'), ('Content-Security-Policy', "default-src 'none'"), ('X-GitHub-Request-Id', 'C35C:1792:11E12C:2D04D2:5E444162')]