こんにちは!今日は、GitHub の新しい Project V2 API を使って、プロジェクトにドラフトIssueを追加する Python スクリプトを一緒に作っていきましょう。
デモ動画
必要な環境変数
このスクリプトを実行するには、以下の環境変数が必要です。
- GITHUB_PERSONAL_ACCESS_TOKEN: GitHub の Personal Access Token
- GITHUB_USER_LOGIN: GitHub のユーザーログイン名
- GITHUB_PROJECT_NUMBER: 対象のプロジェクト番号
これらの環境変数は、スクリプトを実行する前に設定しておいてください。
インポートと環境変数の取得
import os
import requests
from termcolor import colored
from art import *
script_name = os.path.basename(__file__)
tprint(script_name)
token = os.environ.get("GITHUB_PERSONAL_ACCESS_TOKEN")
user_login = os.environ.get("GITHUB_USER_LOGIN")
project_number = os.environ.get("GITHUB_PROJECT_NUMBER")ここでは、必要なライブラリをインポートし、環境変数から必要な情報を取得しています。
- os: ファイル名を取得するために使用
- requests: GitHub API にリクエストを送信するために使用
- termcolor: 出力をカラフルにするために使用
- art: スクリプト名をアスキーアートで表示するために使用
環境変数の検証
def validate_environment_variables():
    if token is None or user_login is None or project_number is None:
        print("Error: Required environment variables are not set.")
        exit(1)validate_environment_variables() 関数は、必要な環境変数が設定されているかどうかを確認します。設定されていない場合は、エラーメッセージを表示してスクリプトを終了します。
プロジェクト情報の取得
def get_project_info():
    query_project = """
    query {
      user(login: "USER_LOGIN") {
        projectV2(number: NUMBER) {
          title
          id
        }
      }
    }
    """
    query_project = query_project.replace("USER_LOGIN", user_login)
    query_project = query_project.replace("NUMBER", project_number)
    data_project = {
        "query": query_project
    }
    response_project = requests.post("https://api.github.com/graphql", headers=headers, json=data_project)
    if "errors" in response_project.json():
        print(f"Error: {response_project.json()['errors'][0]['message']}")
        exit(1)
    project_title = response_project.json()["data"]["user"]["projectV2"]["title"]
    project_id = response_project.json()["data"]["user"]["projectV2"]["id"]
    print(colored(f"Project Title: {project_title}", "green"))
    print(colored(f"Project ID: {project_id}", "cyan"))
    return project_idget_project_info() 関数は、指定されたプロジェクトの情報を取得します。
- GraphQL クエリを定義し、USER_LOGINとNUMBERをそれぞれuser_loginとproject_numberに置き換えます。
- クエリを実行し、レスポンスをチェックします。エラーがある場合は、エラーメッセージを表示してスクリプトを終了します。
- レスポンスからプロジェクトのタイトルとIDを取得し、表示します。
- プロジェクトIDを返します。
プロジェクト内のアイテムの取得
def get_project_items(project_id):
    query_items = """
    query {
      node(id: "PROJECT_ID") {
        ... on ProjectV2 {
          items(first: 20) {
            nodes {
              id
              content {
                ... on DraftIssue {
                  title
                  body
                }
                ... on Issue {
                  title
                  number
                  assignees(first: 10) {
                    nodes {
                      login
                    }
                  }
                }
                ... on PullRequest {
                  title
                  number
                  assignees(first: 10) {
                    nodes {
                      login
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
    """
    query_items = query_items.replace("PROJECT_ID", project_id)
    data_items = {
        "query": query_items
    }
    response_items = requests.post("https://api.github.com/graphql", headers=headers, json=data_items)
    if "errors" in response_items.json():
        print(f"Error: {response_items.json()['errors'][0]['message']}")
        exit(1)
    items = response_items.json()["data"]["node"]["items"]["nodes"]
    print("---------------------------")
    print(colored("Items:", "magenta"))
    for item in items:
        print(colored(f"Item ID: {item['id'][:6]}_XXXXXXXXXXXX", "cyan"))
        content = item["content"]
        if "title" in content:
            print(colored(f"Title: {content['title']}", "green"))
        if "body" in content:
            print(colored(f"Body: {content['body']}", "green"))
        if "number" in content:
            print(colored(f"Number: {content['number']}", "green"))
        if "assignees" in content:
            assignees = [assignee["login"] for assignee in content["assignees"]["nodes"]]
            print(colored(f"Assignees: {', '.join(assignees)}", "green"))
        print("---")get_project_items() 関数は、指定されたプロジェクト内のアイテムを取得します。
- GraphQL クエリを定義し、PROJECT_IDをプロジェクトIDに置き換えます。
- クエリを実行し、レスポンスをチェックします。エラーがある場合は、エラーメッセージを表示してスクリプトを終了します。
- レスポンスからアイテムのリストを取得します。
- 各アイテムの情報(ID、タイトル、本文、番号、担当者)を表示します。
プロジェクトにドラフトIssueを追加
def add_draft_issue_to_project(project_id):
    draft_title = input("Enter the title for the new draft issue: ")
    draft_body = input("Enter the body for the new draft issue: ")
    mutation_add_draft_issue = """
    mutation {
      addProjectV2DraftIssue(input: {projectId: "PROJECT_ID", title: "TITLE", body: "BODY"}) {
        projectItem {
          id
        }
      }
    }
    """
    mutation_add_draft_issue = mutation_add_draft_issue.replace("PROJECT_ID", project_id)
    mutation_add_draft_issue = mutation_add_draft_issue.replace("TITLE", draft_title)
    mutation_add_draft_issue = mutation_add_draft_issue.replace("BODY", draft_body)
    data_add_draft_issue = {
        "query": mutation_add_draft_issue
    }
    response_add_draft_issue = requests.post("https://api.github.com/graphql", headers=headers, json=data_add_draft_issue)
    if "errors" in response_add_draft_issue.json():
        print(f"Error: {response_add_draft_issue.json()['errors'][0]['message']}")
    else:
        added_draft_issue_id = response_add_draft_issue.json()["data"]["addProjectV2DraftIssue"]["projectItem"]["id"]
        print(colored(f"Added draft issue with ID: {added_draft_issue_id}", "green"))add_draft_issue_to_project() 関数は、指定されたプロジェクトにドラフトIssueを追加します。
- ユーザーにドラフトIssueのタイトルと本文の入力を求めます。
- GraphQL ミューテーションを定義し、PROJECT_ID、TITLE、BODYをそれぞれプロジェクトID、ドラフトIssueのタイトル、ドラフトIssueの本文に置き換えます。
- ミューテーションを実行し、レスポンスをチェックします。エラーがある場合は、エラーメッセージを表示します。
- レスポンスから追加されたドラフトIssueのIDを取得し、表示します。
メイン処理
if __name__ == "__main__":
    validate_environment_variables()
    headers = {
        "Authorization": f"Bearer {token}",
        "Content-Type": "application/json"
    }
    project_id = get_project_info()
    get_project_items(project_id)
    add_draft_issue_to_project(project_id)メイン処理では、以下のステップを実行します。
- 環境変数を検証します。
- リクエストヘッダーを設定します。
- プロジェクト情報を取得します。
- プロジェクト内のアイテムを取得します。
- プロジェクトにドラフトIssueを追加します。
これで、GitHub プロジェクトにドラフトIssueを追加する Python スクリプトの説明は以上となります。このスクリプトを実行することで、GitHub の Project V2 API を使ってプロジェクトにドラフトIssueを簡単に追加することができます。
実行方法
- 
必要な環境変数を設定します。 - GITHUB_PERSONAL_ACCESS_TOKEN: GitHub の Personal Access Token
- GITHUB_USER_LOGIN: GitHub のユーザーログイン名
- GITHUB_PROJECT_NUMBER: 対象のプロジェクト番号
 
- 
スクリプトを実行します。 python script_name.py
- 
スクリプトが実行されると、以下のような出力が表示されます。 Project Title: My Project Project ID: PVT_kwDOABCD1234567890 --------------------------- Items: Item ID: ABC123_XXXXXXXXXXXX Title: Issue 1 Number: 1 Assignees: user1, user2 --- Item ID: DEF456_XXXXXXXXXXXX Title: Pull Request 1 Number: 2 Assignees: user3 --- Enter the title for the new draft issue: Draft Issue 1 Enter the body for the new draft issue: This is a draft issue. Added draft issue with ID: GHI789_XXXXXXXXXXXX
- 
プロジェクトにドラフトIssueが追加されたことを確認します。 
注意点
- このスクリプトを実行するには、適切な権限を持つ Personal Access Token が必要です。トークンには、read:orgとprojectのスコープが必要です。
- プロジェクト番号は、プロジェクトの URL から取得できます。例えば、https://github.com/orgs/my-org/projects/1の場合、プロジェクト番号は1になります。
- このスクリプトは、プロジェクト内の最初の 20 個のアイテムのみを取得します。より多くのアイテムを取得するには、items(first: 20)の数値を変更してください。
全体コード
import os
import requests
from termcolor import colored
from art import *
script_name = os.path.basename(__file__)
tprint(script_name)
token = os.environ.get("GITHUB_PERSONAL_ACCESS_TOKEN")
user_login = os.environ.get("GITHUB_USER_LOGIN")
project_number = os.environ.get("GITHUB_PROJECT_NUMBER")
def validate_environment_variables():
    if token is None or user_login is None or project_number is None:
        print("Error: Required environment variables are not set.")
        exit(1)
def get_project_info():
    query_project = """
    query {
      user(login: "USER_LOGIN") {
        projectV2(number: NUMBER) {
          title
          id
        }
      }
    }
    """
    query_project = query_project.replace("USER_LOGIN", user_login)
    query_project = query_project.replace("NUMBER", project_number)
    data_project = {
        "query": query_project
    }
    response_project = requests.post("https://api.github.com/graphql", headers=headers, json=data_project)
    if "errors" in response_project.json():
        print(f"Error: {response_project.json()['errors'][0]['message']}")
        exit(1)
    project_title = response_project.json()["data"]["user"]["projectV2"]["title"]
    project_id = response_project.json()["data"]["user"]["projectV2"]["id"]
    print(colored(f"Project Title: {project_title}", "green"))
    print(colored(f"Project ID: {project_id}", "cyan"))
    return project_id
def get_project_items(project_id):
    query_items = """
    query {
      node(id: "PROJECT_ID") {
        ... on ProjectV2 {
          items(first: 20) {
            nodes {
              id
              content {
                ... on DraftIssue {
                  title
                  body
                }
                ... on Issue {
                  title
                  number
                  assignees(first: 10) {
                    nodes {
                      login
                    }
                  }
                }
                ... on PullRequest {
                  title
                  number
                  assignees(first: 10) {
                    nodes {
                      login
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
    """
    query_items = query_items.replace("PROJECT_ID", project_id)
    data_items = {
        "query": query_items
    }
    response_items = requests.post("https://api.github.com/graphql", headers=headers, json=data_items)
    if "errors" in response_items.json():
        print(f"Error: {response_items.json()['errors'][0]['message']}")
        exit(1)
    items = response_items.json()["data"]["node"]["items"]["nodes"]
    print("---------------------------")
    print(colored("Items:", "magenta"))
    for item in items:
        print(colored(f"Item ID: {item['id'][:6]}_XXXXXXXXXXXX", "cyan"))
        content = item["content"]
        if "title" in content:
            print(colored(f"Title: {content['title']}", "green"))
        if "body" in content:
            print(colored(f"Body: {content['body']}", "green"))
        if "number" in content:
            print(colored(f"Number: {content['number']}", "green"))
        if "assignees" in content:
            assignees = [assignee["login"] for assignee in content["assignees"]["nodes"]]
            print(colored(f"Assignees: {', '.join(assignees)}", "green"))
        print("---")
def add_draft_issue_to_project(project_id):
    draft_title = input("Enter the title for the new draft issue: ")
    draft_body = input("Enter the body for the new draft issue: ")
    mutation_add_draft_issue = """
    mutation {
      addProjectV2DraftIssue(input: {projectId: "PROJECT_ID", title: "TITLE", body: "BODY"}) {
        projectItem {
          id
        }
      }
    }
    """
    mutation_add_draft_issue = mutation_add_draft_issue.replace("PROJECT_ID", project_id)
    mutation_add_draft_issue = mutation_add_draft_issue.replace("TITLE", draft_title)
    mutation_add_draft_issue = mutation_add_draft_issue.replace("BODY", draft_body)
    data_add_draft_issue = {
        "query": mutation_add_draft_issue
    }
    response_add_draft_issue = requests.post("https://api.github.com/graphql", headers=headers, json=data_add_draft_issue)
    if "errors" in response_add_draft_issue.json():
        print(f"Error: {response_add_draft_issue.json()['errors'][0]['message']}")
    else:
        added_draft_issue_id = response_add_draft_issue.json()["data"]["addProjectV2DraftIssue"]["projectItem"]["id"]
        print(colored(f"Added draft issue with ID: {added_draft_issue_id}", "green"))
if __name__ == "__main__":
    validate_environment_variables()
    headers = {
        "Authorization": f"Bearer {token}",
        "Content-Type": "application/json"
    }
    project_id = get_project_info()
    get_project_items(project_id)
    add_draft_issue_to_project(project_id)まとめ
この記事では、GitHub の Project V2 API を使ってプロジェクトにドラフトIssueを追加する Python スクリプトを作成しました。このスクリプトを使えば、プロジェクトの管理がより簡単になります。
GitHub API には他にも多くの機能があるので、このスクリプトをベースにして、自分のニーズに合わせてカスタマイズしていくことをお勧めします。
Happy coding!
リポジトリ
参考サイト



 
  
  
  
  

コメント