0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【GitHub API Tips】GitHub APIを使ってissueを削除する方法

Last updated at Posted at 2025-02-06

はじめに

 GitHub APIを使って、リポジトリのissueを削除する方法を紹介する。

動作確認環境

  • Ubuntu 22.04 x86_64
  • Python 3.10.12

環境構築

Bash

BashでJSONを扱うためにjqをインストールする。

sudo apt update
sudo apt install -y jq

Python

Pythonの環境を準備しておく。requestsインストールする。

pip install requests

方法

 GitHub APIは、REST APIGraphQL APIが用意されている。REST APIは簡便で扱いやすいが、今回行いたいissueの削除は直接サポートされていない(以下のAPIリストの画像を参照)。

image.png

 そこで本記事では、REST APIとGraphQL APIを組み合わせて、issueを削除する方法を紹介する。

基本的な流れは以下である。

  1. APIのアクセストークンを取得し設定する
  2. REST APIで、削除したいissueのissueIdを取得する
  3. GraphQL APIで、上記issueIdを指定して、issueを削除する

APIのアクセストークンを取得し設定する

 Personal access tokens (classic)、または、Fine-grained personal access tokensを使い、削除したいリポジトリへの書き込み権限を与える。詳細は、こちらの記事を参照。本記事では、Personal access tokens (classic)で動作確認を行った。

REST APIで、削除したいissueのissueIdを取得する

REST APIのList repository issuesを使い、リポジトリ内の全てのissueIdを取得する。

以下のコマンドでAPIの動作確認が可能。

curl -L \
  -H "Accept: application/vnd.github+json" \
  -H "Authorization: Bearer <YOUR_TOKEN>" \
  -H "X-GitHub-Api-Version: 2022-11-28" \
  https://api.github.com/repos/OWNER/REPO/issues
  • : 上記設定したアクセストークン
  • OWNER : リポジトリ所有者名 (Organizationsまたは個人)
  • REPO : リポジトリ名

結果例

[
  {
    "id": 1,
    "node_id": "MDU6SXNzdWUx",
    "url": "https://api.github.com/repos/octocat/Hello-World/issues/1347",
    "repository_url": "https://api.github.com/repos/octocat/Hello-World",
    "labels_url": "https://api.github.com/repos/octocat/Hello-World/issues/1347/labels{/name}",
    "comments_url": "https://api.github.com/repos/octocat/Hello-World/issues/1347/comments",
    "events_url": "https://api.github.com/repos/octocat/Hello-World/issues/1347/events",
    "html_url": "https://github.com/octocat/Hello-World/issues/1347",
    "number": 1347,
    "state": "open",
    "title": "Found a bug",
    "body": "I'm having a problem with this.",
    "user": {
      "login": "octocat",
      "id": 1,
      "node_id": "MDQ6VXNlcjE=",
      "avatar_url": "https://github.com/images/error/octocat_happy.gif",
      "gravatar_id": "",
      "url": "https://api.github.com/users/octocat",
      "html_url": "https://github.com/octocat",
      "followers_url": "https://api.github.com/users/octocat/followers",
      "following_url": "https://api.github.com/users/octocat/following{/other_user}",
      "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}",
      "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}",
      "subscriptions_url": "https://api.github.com/users/octocat/subscriptions",
      "organizations_url": "https://api.github.com/users/octocat/orgs",
      "repos_url": "https://api.github.com/users/octocat/repos",
      "events_url": "https://api.github.com/users/octocat/events{/privacy}",
      "received_events_url": "https://api.github.com/users/octocat/received_events",
      "type": "User",
      "site_admin": false
    },
    "labels": [
      {
        "id": 208045946,
        "node_id": "MDU6TGFiZWwyMDgwNDU5NDY=",
        "url": "https://api.github.com/repos/octocat/Hello-World/labels/bug",
        "name": "bug",
        "description": "Something isn't working",
        "color": "f29513",
        "default": true
      }
    ],
    "assignee": {
      "login": "octocat",
      "id": 1,
      "node_id": "MDQ6VXNlcjE=",
      "avatar_url": "https://github.com/images/error/octocat_happy.gif",
      "gravatar_id": "",
      "url": "https://api.github.com/users/octocat",
      "html_url": "https://github.com/octocat",
      "followers_url": "https://api.github.com/users/octocat/followers",
      "following_url": "https://api.github.com/users/octocat/following{/other_user}",
      "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}",
      "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}",
      "subscriptions_url": "https://api.github.com/users/octocat/subscriptions",
      "organizations_url": "https://api.github.com/users/octocat/orgs",
      "repos_url": "https://api.github.com/users/octocat/repos",
      "events_url": "https://api.github.com/users/octocat/events{/privacy}",
      "received_events_url": "https://api.github.com/users/octocat/received_events",
      "type": "User",
      "site_admin": false
    },
    "assignees": [
      {
        "login": "octocat",
        "id": 1,
        "node_id": "MDQ6VXNlcjE=",
        "avatar_url": "https://github.com/images/error/octocat_happy.gif",
        "gravatar_id": "",
        "url": "https://api.github.com/users/octocat",
        "html_url": "https://github.com/octocat",
        "followers_url": "https://api.github.com/users/octocat/followers",
        "following_url": "https://api.github.com/users/octocat/following{/other_user}",
        "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}",
        "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}",
        "subscriptions_url": "https://api.github.com/users/octocat/subscriptions",
        "organizations_url": "https://api.github.com/users/octocat/orgs",
        "repos_url": "https://api.github.com/users/octocat/repos",
        "events_url": "https://api.github.com/users/octocat/events{/privacy}",
        "received_events_url": "https://api.github.com/users/octocat/received_events",
        "type": "User",
        "site_admin": false
      }
    ],
    "milestone": {
      "url": "https://api.github.com/repos/octocat/Hello-World/milestones/1",
      "html_url": "https://github.com/octocat/Hello-World/milestones/v1.0",
      "labels_url": "https://api.github.com/repos/octocat/Hello-World/milestones/1/labels",
      "id": 1002604,
      "node_id": "MDk6TWlsZXN0b25lMTAwMjYwNA==",
      "number": 1,
      "state": "open",
      "title": "v1.0",
      "description": "Tracking milestone for version 1.0",
      "creator": {
        "login": "octocat",
        "id": 1,
        "node_id": "MDQ6VXNlcjE=",
        "avatar_url": "https://github.com/images/error/octocat_happy.gif",
        "gravatar_id": "",
        "url": "https://api.github.com/users/octocat",
        "html_url": "https://github.com/octocat",
        "followers_url": "https://api.github.com/users/octocat/followers",
        "following_url": "https://api.github.com/users/octocat/following{/other_user}",
        "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}",
        "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}",
        "subscriptions_url": "https://api.github.com/users/octocat/subscriptions",
        "organizations_url": "https://api.github.com/users/octocat/orgs",
        "repos_url": "https://api.github.com/users/octocat/repos",
        "events_url": "https://api.github.com/users/octocat/events{/privacy}",
        "received_events_url": "https://api.github.com/users/octocat/received_events",
        "type": "User",
        "site_admin": false
      },
      "open_issues": 4,
      "closed_issues": 8,
      "created_at": "2011-04-10T20:09:31Z",
      "updated_at": "2014-03-03T18:58:10Z",
      "closed_at": "2013-02-12T13:22:01Z",
      "due_on": "2012-10-09T23:39:01Z"
    },
    "locked": true,
    "active_lock_reason": "too heated",
    "comments": 0,
    "pull_request": {
      "url": "https://api.github.com/repos/octocat/Hello-World/pulls/1347",
      "html_url": "https://github.com/octocat/Hello-World/pull/1347",
      "diff_url": "https://github.com/octocat/Hello-World/pull/1347.diff",
      "patch_url": "https://github.com/octocat/Hello-World/pull/1347.patch"
    },
    "closed_at": null,
    "created_at": "2011-04-22T13:33:48Z",
    "updated_at": "2011-04-22T13:33:48Z",
    "closed_by": {
      "login": "octocat",
      "id": 1,
      "node_id": "MDQ6VXNlcjE=",
      "avatar_url": "https://github.com/images/error/octocat_happy.gif",
      "gravatar_id": "",
      "url": "https://api.github.com/users/octocat",
      "html_url": "https://github.com/octocat",
      "followers_url": "https://api.github.com/users/octocat/followers",
      "following_url": "https://api.github.com/users/octocat/following{/other_user}",
      "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}",
      "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}",
      "subscriptions_url": "https://api.github.com/users/octocat/subscriptions",
      "organizations_url": "https://api.github.com/users/octocat/orgs",
      "repos_url": "https://api.github.com/users/octocat/repos",
      "events_url": "https://api.github.com/users/octocat/events{/privacy}",
      "received_events_url": "https://api.github.com/users/octocat/received_events",
      "type": "User",
      "site_admin": false
    },
    "author_association": "COLLABORATOR",
    "state_reason": "completed"
  }
]

上記の、node_idが、issueに割り当てられているユニークIDであるissueIdである。削除したいissueIdをメモしておく。また、numberは、GitHubリポジトリから参照できるissue番号なので、こちらもメモしておく。

GraphQL APIで、上記issueIdを指定してissueを削除する

GraphQL APIの、deleteIssueで、上記のissueIdを指定してissueを削除する。deleteIssue APIの入力の詳細はDeleteIssueInputを参照。

 以下にissueIdからissueを削除するBashスクリプトの例を示す。リポジトリ名、issue番号は、削除には不要だがわかりやすさのために記載している。

#!/bin/bash

ISSUE_ID=<ISSUE_ID> # issueId
TOKEN=<YOUR_TOKEN> # アクセストークン
REPO=<REPO> # リポジトリ名 (API呼び出しには不要)
ISSUE_NUMBER=<ISSUE_NUMBER> # issue番号 (API呼び出しには不要)

# GraphQLエンドポイント
URL="https://api.github.com/graphql"

# GraphQLクエリ
QUERY=$(cat <<EOF
{
  "query": "mutation { deleteIssue(input: {issueId: \\"$ISSUE_ID\\"}) { repository { id } } }"
}
EOF
)

# curlコマンドでGraphQLクエリを実行
RESPONSE=$(curl -s -H "Authorization: bearer $TOKEN" -H "Content-Type: application/json" -X POST -d "$QUERY" $URL)

# ステータスコードの取得
STATUS_CODE=$(echo $RESPONSE | jq -r '.status_code')

# 結果の確認
if [ "$STATUS_CODE" -eq 200 ]; then
  echo "$REPO issue #$ISSUE_NUMBER deleted successfully."
  exit 0
else
  echo "Failed to delete $REPO issue #$ISSUE_NUMBER: $STATUS_CODE"
  echo $RESPONSE | jq
  exit 1
fi

例: リポジトリ内の全てのissueを削除する(Python)

 Pythonを使って、あるリポジトリ内の全てのissueを削除する例を示す。

警告
下記スクリプトを実行する場合は、テスト用リポジトリで試すことを推奨

リポジトリ内の全てのissueを削除する例 (Python)
#!/usr/bin/python3
# -*- coding: utf-8 -*-

OWNER="<OWNER>"
REPO="<REPO>"
TOKEN="<TOKEN>"

# リポジトリ内の全てのGitHub issueを削除する関数
def delete_all_github_issues(self):
    headers = {'Authorization': f'token {TOKEN}'}
    issues_url = f'https://api.github.com/repos/{OWNER}/{REPO}/issues'
    response = requests.get(issues_url, headers=headers)
    issues = response.json()
    print(issues)
    if isinstance(issues, list):
        print(f'{REPO} issues count {len(issues)}')
    else:
        print(f'{REPO} issues are not found: {issues}')
        return False

    success = True
    for issue in issues:
        issue_number = issue['number']
        issue_id = issue['node_id']
        # issueIdからissueを削除するスクリプトを呼び出す
        success = success & self._delete_github_issue(REPO, issue_number, issue_id)
    return success

# リポジトリ内の、あるGitHub issueを削除する関数
def _delete_github_issue(self, repo_name: str, issue_number: int, issue_id: int):
    url = 'https://api.github.com/graphql'
    headers = {
        'Authorization': f'bearer {TOKEN}',
        'Content-Type': 'application/json'
    }
    query = """
    mutation {
    deleteIssue(input: {issueId: "%s"}) {
        repository {
        id
        }
    }
    }
    """ % issue_id

    response = requests.post(url, headers=headers, json={'query': query})

    if response.status_code == 200:
        print(f'{repo_name} issue #{issue_number} deleted successfully.')
        return True
    else:
        print(f'Failed to delete {repo_name} issue #{issue_number}: {response.status_code}')
        print(response.json())
        return False

def main():
    delete_all_github_issues()

if __name__ == '__main__':
    main()

まとめ

 GitHub APIを使って、リポジトリのissueを削除する方法を紹介した。リポジトリの移行などでissueの削除を自動化したい場合に有用。

参考

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?