GitRelease.py
1 ############################ Copyrights and license ############################ 2 # # 3 # Copyright 2015 Ed Holland <eholland@alertlogic.com> # 4 # Copyright 2016 Benjamin Whitney <benjamin.whitney@ironnetcybersecurity.com> # 5 # Copyright 2016 Jannis Gebauer <ja.geb@me.com> # 6 # Copyright 2016 Peter Buckley <dx-pbuckley@users.noreply.github.com> # 7 # Copyright 2017 Chris McBride <thehighlander@users.noreply.github.com> # 8 # Copyright 2017 Simon <spam@esemi.ru> # 9 # Copyright 2018 Daniel Kesler <kesler.daniel@gmail.com> # 10 # Copyright 2018 Kuba <jakub.glapa@adspired.com> # 11 # Copyright 2018 Maarten Fonville <mfonville@users.noreply.github.com> # 12 # Copyright 2018 Shinichi TAMURA <shnch.tmr@gmail.com> # 13 # Copyright 2018 Wan Liuyang <tsfdye@gmail.com> # 14 # Copyright 2018 edquist <edquist@users.noreply.github.com> # 15 # Copyright 2018 nurupo <nurupo.contributions@gmail.com> # 16 # Copyright 2018 sfdye <tsfdye@gmail.com> # 17 # # 18 # This file is part of PyGithub. # 19 # http://pygithub.readthedocs.io/ # 20 # # 21 # PyGithub is free software: you can redistribute it and/or modify it under # 22 # the terms of the GNU Lesser General Public License as published by the Free # 23 # Software Foundation, either version 3 of the License, or (at your option) # 24 # any later version. # 25 # # 26 # PyGithub is distributed in the hope that it will be useful, but WITHOUT ANY # 27 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS # 28 # FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more # 29 # details. # 30 # # 31 # You should have received a copy of the GNU Lesser General Public License # 32 # along with PyGithub. If not, see <http://www.gnu.org/licenses/>. # 33 # # 34 ################################################################################ 35 from __future__ import annotations 36 37 from datetime import datetime 38 from os.path import basename 39 from typing import Any, BinaryIO 40 41 import github.GitReleaseAsset 42 import github.NamedUser 43 from github.GithubObject import Attribute, CompletableGithubObject, NotSet, Opt 44 from github.PaginatedList import PaginatedList 45 46 from . import Consts 47 48 49 class GitRelease(CompletableGithubObject): 50 """ 51 This class represents GitReleases. The reference can be found here https://docs.github.com/en/rest/reference/repos#releases 52 """ 53 54 def _initAttributes(self) -> None: 55 self._id: Attribute[int] = NotSet 56 self._body: Attribute[str] = NotSet 57 self._title: Attribute[str] = NotSet 58 self._tag_name: Attribute[str] = NotSet 59 self._target_commitish: Attribute[str] = NotSet 60 self._draft: Attribute[bool] = NotSet 61 self._prerelease: Attribute[bool] = NotSet 62 self._generate_release_notes: Attribute[bool] = NotSet 63 self._author: Attribute[github.NamedUser.NamedUser] = NotSet 64 self._url: Attribute[str] = NotSet 65 self._upload_url: Attribute[str] = NotSet 66 self._html_url: Attribute[str] = NotSet 67 self._created_at: Attribute[datetime] = NotSet 68 self._published_at: Attribute[datetime] = NotSet 69 self._tarball_url: Attribute[str] = NotSet 70 self._zipball_url: Attribute[str] = NotSet 71 self._assets: Attribute[list[github.GitReleaseAsset.GitReleaseAsset]] = NotSet 72 73 def __repr__(self) -> str: 74 return self.get__repr__({"title": self._title.value}) 75 76 @property 77 def id(self) -> int: 78 self._completeIfNotSet(self._id) 79 return self._id.value 80 81 @property 82 def body(self) -> str: 83 self._completeIfNotSet(self._body) 84 return self._body.value 85 86 @property 87 def title(self) -> str: 88 self._completeIfNotSet(self._title) 89 return self._title.value 90 91 @property 92 def tag_name(self) -> str: 93 self._completeIfNotSet(self._tag_name) 94 return self._tag_name.value 95 96 @property 97 def target_commitish(self) -> str: 98 self._completeIfNotSet(self._target_commitish) 99 return self._target_commitish.value 100 101 @property 102 def draft(self) -> bool: 103 self._completeIfNotSet(self._draft) 104 return self._draft.value 105 106 @property 107 def prerelease(self) -> bool: 108 self._completeIfNotSet(self._prerelease) 109 return self._prerelease.value 110 111 @property 112 def author(self) -> github.NamedUser.NamedUser: 113 self._completeIfNotSet(self._author) 114 return self._author.value 115 116 @property 117 def created_at(self) -> datetime: 118 self._completeIfNotSet(self._created_at) 119 return self._created_at.value 120 121 @property 122 def published_at(self) -> datetime: 123 self._completeIfNotSet(self._published_at) 124 return self._published_at.value 125 126 @property 127 def url(self) -> str: 128 self._completeIfNotSet(self._url) 129 return self._url.value 130 131 @property 132 def upload_url(self) -> str: 133 self._completeIfNotSet(self._upload_url) 134 return self._upload_url.value 135 136 @property 137 def html_url(self) -> str: 138 self._completeIfNotSet(self._html_url) 139 return self._html_url.value 140 141 @property 142 def tarball_url(self) -> str: 143 self._completeIfNotSet(self._tarball_url) 144 return self._tarball_url.value 145 146 @property 147 def zipball_url(self) -> str: 148 self._completeIfNotSet(self._zipball_url) 149 return self._zipball_url.value 150 151 @property 152 def assets(self) -> list[github.GitReleaseAsset.GitReleaseAsset]: 153 self._completeIfNotSet(self._assets) 154 return self._assets.value 155 156 def delete_release(self) -> None: 157 """ 158 :calls: `DELETE /repos/{owner}/{repo}/releases/{release_id} <https://docs.github.com/en/rest/reference/repos#delete-a-release>`_ 159 """ 160 headers, data = self._requester.requestJsonAndCheck("DELETE", self.url) 161 162 def update_release( 163 self, 164 name: str, 165 message: str, 166 draft: bool = False, 167 prerelease: bool = False, 168 tag_name: Opt[str] = NotSet, 169 target_commitish: Opt[str] = NotSet, 170 ) -> GitRelease: 171 """ 172 :calls: `PATCH /repos/{owner}/{repo}/releases/{release_id} <https://docs.github.com/en/rest/reference/repos#update-a-release>`_ 173 """ 174 assert tag_name is NotSet or isinstance(tag_name, str), "tag_name must be a str/unicode object" 175 assert target_commitish is NotSet or isinstance( 176 target_commitish, str 177 ), "target_commitish must be a str/unicode object" 178 assert isinstance(name, str), name 179 assert isinstance(message, str), message 180 assert isinstance(draft, bool), draft 181 assert isinstance(prerelease, bool), prerelease 182 if tag_name is NotSet: 183 tag_name = self.tag_name 184 post_parameters = { 185 "tag_name": tag_name, 186 "name": name, 187 "body": message, 188 "draft": draft, 189 "prerelease": prerelease, 190 } 191 # Do not set target_commitish to self.target_commitish when omitted, just don't send it 192 # altogether in that case, in order to match the Github API behaviour. Only send it when set. 193 if target_commitish is not NotSet: 194 post_parameters["target_commitish"] = target_commitish 195 headers, data = self._requester.requestJsonAndCheck("PATCH", self.url, input=post_parameters) 196 return github.GitRelease.GitRelease(self._requester, headers, data, completed=True) 197 198 def upload_asset( 199 self, path: str, label: str = "", content_type: Opt[str] = NotSet, name: Opt[str] = NotSet 200 ) -> github.GitReleaseAsset.GitReleaseAsset: 201 """ 202 :calls: `POST https://<upload_url>/repos/{owner}/{repo}/releases/{release_id}/assets <https://docs.github.com/en/rest/reference/repos#upload-a-release-asset>`_ 203 """ 204 assert isinstance(path, str), path 205 assert isinstance(label, str), label 206 assert name is NotSet or isinstance(name, str), name 207 208 post_parameters: dict[str, Any] = {"label": label} 209 if name is NotSet: 210 post_parameters["name"] = basename(path) 211 else: 212 post_parameters["name"] = name 213 headers: dict[str, Any] = {} 214 if content_type is not NotSet: 215 headers["Content-Type"] = content_type 216 resp_headers, data = self._requester.requestBlobAndCheck( 217 "POST", 218 self.upload_url.split("{?")[0], 219 parameters=post_parameters, 220 headers=headers, 221 input=path, 222 ) 223 return github.GitReleaseAsset.GitReleaseAsset(self._requester, resp_headers, data, completed=True) 224 225 def upload_asset_from_memory( 226 self, 227 file_like: BinaryIO, 228 file_size: int, 229 name: str, 230 content_type: Opt[str] = NotSet, 231 label: str = "", 232 ) -> github.GitReleaseAsset.GitReleaseAsset: 233 """Uploads an asset. Unlike ``upload_asset()`` this method allows you to pass in a file-like object to upload. 234 Note that this method is more strict and requires you to specify the ``name``, since there's no file name to infer these from. 235 :calls: `POST https://<upload_url>/repos/{owner}/{repo}/releases/{release_id}/assets <https://docs.github.com/en/rest/reference/repos#upload-a-release-asset>`_ 236 :param file_like: binary file-like object, such as those returned by ``open("file_name", "rb")``. At the very minimum, this object must implement ``read()``. 237 :param file_size: int, size in bytes of ``file_like`` 238 """ 239 assert isinstance(name, str), name 240 assert isinstance(file_size, int), file_size 241 assert isinstance(label, str), label 242 243 post_parameters = {"label": label, "name": name} 244 content_type = content_type if content_type is not NotSet else Consts.defaultMediaType 245 headers = {"Content-Type": content_type, "Content-Length": str(file_size)} 246 247 resp_headers, data = self._requester.requestMemoryBlobAndCheck( 248 "POST", 249 self.upload_url.split("{?")[0], 250 parameters=post_parameters, 251 headers=headers, 252 file_like=file_like, 253 ) 254 return github.GitReleaseAsset.GitReleaseAsset(self._requester, resp_headers, data, completed=True) 255 256 def get_assets(self) -> PaginatedList[github.GitReleaseAsset.GitReleaseAsset]: 257 """ 258 :calls: `GET /repos/{owner}/{repo}/releases/{release_id}/assets <https://docs.github.com/en/rest/reference/repos#list-release-assets>`_ 259 """ 260 return github.PaginatedList.PaginatedList( 261 github.GitReleaseAsset.GitReleaseAsset, 262 self._requester, 263 f"{self.url}/assets", 264 None, 265 ) 266 267 def _useAttributes(self, attributes: dict[str, Any]) -> None: 268 if "id" in attributes: 269 self._id = self._makeIntAttribute(attributes["id"]) 270 if "body" in attributes: 271 self._body = self._makeStringAttribute(attributes["body"]) 272 if "name" in attributes: 273 self._title = self._makeStringAttribute(attributes["name"]) 274 if "tag_name" in attributes: 275 self._tag_name = self._makeStringAttribute(attributes["tag_name"]) 276 if "target_commitish" in attributes: 277 self._target_commitish = self._makeStringAttribute(attributes["target_commitish"]) 278 if "draft" in attributes: 279 self._draft = self._makeBoolAttribute(attributes["draft"]) 280 if "prerelease" in attributes: 281 self._prerelease = self._makeBoolAttribute(attributes["prerelease"]) 282 if "generate_release_notes" in attributes: 283 self._generate_release_notes = self._makeBoolAttribute(attributes["generate_release_notes"]) 284 if "author" in attributes: 285 self._author = self._makeClassAttribute(github.NamedUser.NamedUser, attributes["author"]) 286 if "url" in attributes: 287 self._url = self._makeStringAttribute(attributes["url"]) 288 if "upload_url" in attributes: 289 self._upload_url = self._makeStringAttribute(attributes["upload_url"]) 290 if "html_url" in attributes: 291 self._html_url = self._makeStringAttribute(attributes["html_url"]) 292 if "created_at" in attributes: 293 self._created_at = self._makeDatetimeAttribute(attributes["created_at"]) 294 if "published_at" in attributes: 295 self._published_at = self._makeDatetimeAttribute(attributes["published_at"]) 296 if "tarball_url" in attributes: 297 self._tarball_url = self._makeStringAttribute(attributes["tarball_url"]) 298 if "zipball_url" in attributes: 299 self._zipball_url = self._makeStringAttribute(attributes["zipball_url"]) 300 if "assets" in attributes: 301 self._assets = self._makeListOfClassesAttribute( 302 github.GitReleaseAsset.GitReleaseAsset, attributes["assets"] 303 )