Search code examples
pythongithubpygithub

Getting error "'Repository' object has no attribute 'create_deployment'" when using the PyGithub API


I am trying to use PyGithub (https://pygithub.readthedocs.io/en/latest/apis.html) to get the deployment and report the status somewhere else. I have the following script:

from github import Github

# Github Enterprise with custom hostname
g = Github(base_url="https://{hostname}/api/v3", login_or_token="sometoken")
# get the repository
repo = g.get_repo("PyGithub/PyGithub")
# HTTP POST to /repos/:owner/:repo/deployments
deployment = repo.create_deployment(ref = 'master', environment = 'DEV', description= 'Deploying to the environment')
# getting the status
status = deployment.get_status(deployment.id)
# HTTP POST to /repos/:owner/:repo/deployments/:deployment_id/statuses
deployment.create_status(state = status.state, environment = 'DEV', description = 'deploying completed')

But it complains with:

'Repository' object has no attribute 'create_deployment'

while I see the function in (https://pygithub.readthedocs.io/en/latest/github_objects/Repository.html#github.Repository.Repository).

What am I missing?

I basically want to use those two endpoints. I was not able to find any other github API in python but even that would be fine as well.


Solution

  • First, make sure you are using an updated version of PyGithub.

    The Deployments API was only added on release version 1.47.

    • Release Notes:

      "Add Deployments API (#1424) (3d93ee1)"

    • PR #1424:

      Add a new class, Deployment to describe a deployment performed utilising GitHub. Add three methods onto Repository to list them and create them.

    If you are using <1.47, then that method doesn't exist yet.

    Second, I think you misunderstood parameters for deployment.get_status.

    The docs are a bit unclear, but it is expecting a Deployment Status ID, not a Deployment ID. The method makes a request to the /repos/{owner}/{repo}/deployments/{deployment_id}/statuses/{status_id} endpoint to "view a deployment status for a deployment", and the code appends the passed-in id to the /statuses/ URL:

    https://github.com/PyGithub/PyGithub/blob/master/github/Deployment.py#L180

    def get_status(self, id_):
        """
        :calls: `GET /repos/:owner/deployments/:deployment_id/statuses/:status_id  <https://developer.github.com/v3/repos/deployments/#get-a-deployment-status>`_
        :param id_: int
        :rtype: :class:`github.DeploymentStatus.DeploymentStatus`
        """
        assert isinstance(id_, int), id_
        headers, data = self._requester.requestJsonAndCheck(
            "GET",
            self.url + "/statuses/" + str(id_),
            headers={"Accept": self._get_accept_header()},
        )
    

    The correct API would probably be get_statuses for listing all the deployment statuses for a particular deployment. Then grab one of those IDs and pass that to get_status to get a specific DeploymentStatus object. (Though, you could also just loop through the get_statuses list, each one is already a DeploymentStatus object). If you don't have any Deployment Status yet, then you create one with create_status:

    # create a Deployment
    # POST /repos/{owner}/{repo}/deployments
    deployment = repo.create_deployment(ref='master', description='Deploying to the environment')
    
    # list Deployment Statuses
    # https://docs.github.com/en/rest/reference/repos#list-deployment-statuses
    # GET /repos/{owner}/{repo}/deployments/{deployment_id}/statuses
    for status in deployment.get_statuses():
        print(status.id)  # For new deployments, this would be empty
    
    # create a Deployment Status
    # https://docs.github.com/en/rest/reference/repos#create-a-deployment-status
    # POST /repos/{owner}/{repo}/deployments/{deployment_id}/statuses
    new_deployment_status = deployment.create_status(state='inactive', description='inactive deployment')
    print(new_deployment_status.state)
    
    # list Deployment Statuses
    # https://docs.github.com/en/rest/reference/repos#list-deployment-statuses
    # GET /repos/{owner}/{repo}/deployments/{deployment_id}/statuses
    for status in deployment.get_statuses():
        print(status.state, status.id)  # This should now the newly created status
    #         'inactive',   461997990
    
    # get a specific Deployment Status
    deployment_status = deployment.get_statuses()[0]
    deployment_status_0 = deployment.get_status(deployment_status.id))