yanyongyu / githubkit

The modern, all-batteries-included GitHub SDK for Python, including rest api, graphql, webhooks, like octokit!
MIT License
158 stars 21 forks source link

Question: installation and github app usage #102

Closed yalattas closed 1 month ago

yalattas commented 1 month ago

configs

from conf.settings import settings
from githubkit import GitHub, AppInstallationAuthStrategy
from githubkit.retry import RetryChainDecision, RetryRateLimit, RetryServerError
with open('./static/private-key.pem', 'rb') as pem_file:
    private_key = pem_file.read()
github = GitHub(
    AppInstallationAuthStrategy(
        app_id=settings.GITHUB_APP_ID,
        private_key=private_key,
        installation_id=settings.GITHUB_APP_INSTALLATION_ID,
        client_id=settings.GITHUB_APP_CLIENT_ID,
        client_secret=settings.GITHUB_APP_CLIENT_SECRET
    ),
    base_url="https://api.github.com/",
    accept_format="full+json",
    previews=["starfox"],
    user_agent="GitHubKit/Python",
    follow_redirects=True,
    timeout=None,
    http_cache=True,
    auto_retry=RetryChainDecision(RetryRateLimit(max_retry=1), RetryServerError(max_retry=1)),
)

API call

response = await github.rest("2022-11-28").repos.async_get(owner="My_organization", repo="MyRepository")

error

backend  | 2024-05-24 18:23:43,000 - INFO - httptools_impl: 172.19.0.1:55210 - "GET /v1/github/graphql/ HTTP/1.1" 500
backend  | ERROR:    Exception in ASGI application
backend  | Traceback (most recent call last):
backend  |   File "/usr/local/lib/python3.12/site-packages/githubkit/core.py", line 306, in _arequest
backend  |     return await client.request(
backend  |            ^^^^^^^^^^^^^^^^^^^^^
backend  |   File "/usr/local/lib/python3.12/site-packages/httpx/_client.py", line 1574, in request
backend  |     return await self.send(request, auth=auth, follow_redirects=follow_redirects)
backend  |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
backend  |   File "/usr/local/lib/python3.12/site-packages/httpx/_client.py", line 1661, in send
backend  |     response = await self._send_handling_auth(
backend  |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
backend  |   File "/usr/local/lib/python3.12/site-packages/httpx/_client.py", line 1686, in _send_handling_auth
backend  |     request = await auth_flow.__anext__()
backend  |               ^^^^^^^^^^^^^^^^^^^^^^^^^^^
backend  |   File "/usr/local/lib/python3.12/site-packages/githubkit/auth/app.py", line 233, in async_auth_flow
backend  |     token_request.headers["Authorization"] = f"Bearer {await self.aget_jwt()}"
backend  |                                                        ^^^^^^^^^^^^^^^^^^^^^
backend  |   File "/usr/local/lib/python3.12/site-packages/githubkit/auth/app.py", line 89, in aget_jwt
backend  |     token = self._create_jwt()
backend  |             ^^^^^^^^^^^^^^^^^^
backend  |   File "/usr/local/lib/python3.12/site-packages/githubkit/auth/app.py", line 70, in _create_jwt
backend  |     return jwt.encode(
backend  |            ^^^^^^^^^^
backend  | AttributeError: module 'jwt' has no attribute 'encode'
backend  | 
backend  | The above exception was the direct cause of the following exception:
backend  | 
backend  | Traceback (most recent call last):
backend  |   File "/usr/local/lib/python3.12/site-packages/uvicorn/protocols/http/httptools_impl.py", line 411, in run_asgi
backend  |     result = await app(  # type: ignore[func-returns-value]
backend  |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
backend  |   File "/usr/local/lib/python3.12/site-packages/uvicorn/middleware/proxy_headers.py", line 69, in __call__
backend  |     return await self.app(scope, receive, send)
backend  |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
backend  |   File "/usr/local/lib/python3.12/site-packages/fastapi/applications.py", line 1054, in __call__
backend  |     await super().__call__(scope, receive, send)
backend  |   File "/usr/local/lib/python3.12/site-packages/starlette/applications.py", line 123, in __call__
backend  |     await self.middleware_stack(scope, receive, send)
backend  |   File "/usr/local/lib/python3.12/site-packages/starlette/middleware/errors.py", line 186, in __call__
backend  |     raise exc
backend  |   File "/usr/local/lib/python3.12/site-packages/starlette/middleware/errors.py", line 164, in __call__
backend  |     await self.app(scope, receive, _send)
backend  |   File "/usr/local/lib/python3.12/site-packages/prometheus_fastapi_instrumentator/middleware.py", line 174, in __call__
backend  |     raise exc
backend  |   File "/usr/local/lib/python3.12/site-packages/prometheus_fastapi_instrumentator/middleware.py", line 172, in __call__
backend  |     await self.app(scope, receive, send_wrapper)
backend  |   File "/usr/local/lib/python3.12/site-packages/starlette/middleware/exceptions.py", line 65, in __call__
backend  |     await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send)
backend  |   File "/usr/local/lib/python3.12/site-packages/starlette/_exception_handler.py", line 64, in wrapped_app
backend  |     raise exc
backend  |   File "/usr/local/lib/python3.12/site-packages/starlette/_exception_handler.py", line 53, in wrapped_app
backend  |     await app(scope, receive, sender)
backend  |   File "/usr/local/lib/python3.12/site-packages/starlette/routing.py", line 756, in __call__
backend  |     await self.middleware_stack(scope, receive, send)
backend  |   File "/usr/local/lib/python3.12/site-packages/starlette/routing.py", line 776, in app
backend  |     await route.handle(scope, receive, send)
backend  |   File "/usr/local/lib/python3.12/site-packages/starlette/routing.py", line 297, in handle
backend  |     await self.app(scope, receive, send)
backend  |   File "/usr/local/lib/python3.12/site-packages/starlette/routing.py", line 77, in app
backend  |     await wrap_app_handling_exceptions(app, request)(scope, receive, send)
backend  |   File "/usr/local/lib/python3.12/site-packages/starlette/_exception_handler.py", line 64, in wrapped_app
backend  |     raise exc
backend  |   File "/usr/local/lib/python3.12/site-packages/starlette/_exception_handler.py", line 53, in wrapped_app
backend  |     await app(scope, receive, sender)
backend  |   File "/usr/local/lib/python3.12/site-packages/starlette/routing.py", line 72, in app
backend  |     response = await func(request)
backend  |                ^^^^^^^^^^^^^^^^^^^
backend  |   File "/usr/local/lib/python3.12/site-packages/fastapi/routing.py", line 278, in app
backend  |     raw_response = await run_endpoint_function(
backend  |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
backend  |   File "/usr/local/lib/python3.12/site-packages/fastapi/routing.py", line 191, in run_endpoint_function
backend  |     return await dependant.call(**values)
backend  |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
backend  |   File "/app/apps/github/urls.py", line 65, in success
backend  |     save = await EventHandler.process_event()
backend  |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
backend  |   File "/app/apps/github/controller.py", line 10, in process_event
backend  |     response = await github.rest("2022-11-28").repos.async_get(owner="BarakahApp", repo="gitops")
backend  |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
backend  |   File "/usr/local/lib/python3.12/site-packages/githubkit/versions/v2022_11_28/rest/repos.py", line 1232, in async_get
backend  |     return await self._github.arequest(
backend  |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
backend  |   File "/usr/local/lib/python3.12/site-packages/githubkit/core.py", line 548, in arequest
backend  |     raw_resp = await self._arequest(
backend  |                ^^^^^^^^^^^^^^^^^^^^^
backend  |   File "/usr/local/lib/python3.12/site-packages/githubkit/core.py", line 320, in _arequest
backend  |     raise RequestError(repr(e)) from e
backend  | githubkit.exception.RequestError: AttributeError("module 'jwt' has no attribute 'encode'")

it seems to be that I have jwt package but this kit requires PyJWT

[tool.poetry.dependencies]
PyJWT = { version = "^2.4.0", extras = ["crypto"], optional = true }

Yet, I don't think this is a problematic since I tried it with tokenStrategy and it worked. But when I install PyJWT, error is different and I am working to fix it on my end. Just thought to bring this to your attention

githubkit.exception.RequestError: RequestFailed(method=POST, url=https://api.github.com/app/installations/123456789/access_tokens, status_code=404 Not Found)

Appreciate your guidance.

In addition, I am pretty confused about the documentation. The call I am making to get repositories was a sample I found it online. From where can I find reference of the functionality ? for example if I want to get a pull request comment or issue or action or anything ? and creating pull request or merge them and so on. Finally, I am planning to use GraphQL. Hence, I choose this package and your effort are really amazing and appreciated. Appreciate a guidance or that as well. How can I make a GraphQL call, validate the response and create commit via mutation using GraphQL

yalattas commented 1 month ago

I just figured out the 404 error and was due having wrong installation ID for different app

Yet, for JWT package got resolved only after I change dependency from having jwt to PyJWT

yanyongyu commented 1 month ago

githubkit requires the PyJWT package to work with GitHub APP. Please install the requirements with pip install githubkit[auth-app], as described in the docs. You do not need to install the pyjwt package by your self. If you follow the installation guide, the correct package will be installed automatically.

From where can I find reference of the functionality? for example if I want to get a pull request comment or issue or action or anything ? and creating pull request or merge them and so on.

You can find all the rest apis at the docs.github.com. For example, if you want to get a issue, you can find the docs here. and you can work with githubkit like this: (if you use github app, sync example)

from githubkit import GitHub, App

# auth with your github app
g = GitHub(
    AppAuthStrategy(
        "<app_id>", "<private_key>"
    )
)

# get the repo installation first
resp = g.rest.apps.get_repo_installation("owner", "repo")
installation_id = resp.parsed_data.id

# then, switch to installation auth
g_repo = g.with_auth(g.auth.as_installation(installation_id))

# you can now get all repo info by the installation authed rest api
resp = g_repo.rest.issues.get("owner", "repo", issue_number)
issue = resp.parsed_data

all the auth flow mentioned above, you can find the docs at GitHub APP Guide. GitHub APP cannot get the repo data directly since the owner of the repo should install the app first. then, the installation_id will be created after the owner has installed the app. you can access the repo data now by auth with the installation_id.

Other apis to create a pull request or merge it also can be found at the github rest docs, like the pulls section.

How can I make a GraphQL call, validate the response and create commit via mutation using GraphQL

The GraphQL usage is very simple, the docs is here. You can first edit the graphql request body at the GitHub GraphQL Editor online then copy the graphql request to your code, send it, and the return value will be the response data. Note that, you still need to auth correctly like the rest example above.

yalattas commented 1 month ago

Indeed I don't need to explicitly install PyJWT. Yet, I was using jwt package in a different module and since I installed jwt package explicitly and caused the mentioned error in GithubKit

When I removed jwt package and refactored my code to use PyJWT in the other module. GithubKit issue was resolved directly. That's why I am mentioning it here.

In addition, everything is working now in GithubKit

Finally, as you're referring to Github API as a documentation, which meant GithubKit is relying on Github schema only and not supporting functionalities on top of Github API. Correct me if I wrong

Will continue to build on GithubKit. Will get back to you if I faced any issue

Thanks for developing this amazing app

yanyongyu commented 1 month ago

What you mentioned is that module name of the two package jwt and pyjwt are same. If you think this is an issue, please report to them. It's not the error with githubkit and githubkit can not prevent you to install the jwt. Both packages use the same module name will cause conflicts, you should not install them at the same time.

githubkit is a GitHub SDK, implements the GitHub API best practice and provide additional functionalities like data validation, auto retry, version switching. you should read the github docs first.

yanyongyu commented 1 month ago

If you have any question about githubkit, feel free to open an issue.