新しい IBM Developer JP サイトへようこそ!サイトのデザインが一新され、旧 developerWorks のコンテンツも統合されました。 詳細はこちら

サーバーレスのアクションを使用した GitHub のタスク自動化

GitHub のアクセス権限は複雑になっています。オーナー に許可される操作を理解するのと、メンバー に許可される操作を理解するのとでは、微妙な差異があります。組織が成長するにつれ、メンバーに許可される操作に対して適用される制限も増えてきます。私たちの組織では、日常業務で以下の問題が発生しました。

  • xyz 組織の GitHub が github.com/xyz で公開されています。
  • デフォルトでは、xyz 組織のメンバーがリポジトリーを作成することは許可されません。
  • そのため、メンバーから組織のオーナーに新しいリポジトリーのリクエストが e-メールで送信されて、オーナーが手作業でリポジトリーを作成することになります (これでは面倒です)。

このチュートリアルでは、GitHub 組織の新しいリポジトリーの作成をリクエストするのに簡単な方法を紹介します。この方法では、新しいリポジトリーをリクエストする際は特定のリポジトリー内での問題を提出するよう、メンバーに依頼します (このサンプル・ソリューションでは GitHub Enterprise インスタンスを使用しますが、この方法はどの GitHub リポジトリーでも有効です)。この方法では、複数のオーナーが同じリクエスト・キューを確認できるとともに、送信されたリクエストの数を記録できます (しかも、私はサーバーレスを試してみたかったので、このソリューションはよい口実になりました)。

ソリューションの概要は以下のとおりです。

サーバーレス・アクションを使用した GitHub タスク自動化チュートリアルのアーキテクチャー図

  1. ユーザーが作成してもらいたいリポジトリーに関する詳細を説明する GitHub 問題を提出します。
  2. 問題が承認されると、ペイロードがサーバーレス・アクションに送信されます。
  3. サーバーレス・アクションが Python コードを呼び出します。
  4. Python requests モジュールを使用して GitHub API を呼び出します。

このチュートリアルを完了すると、以下の方法がわかるようになります。

  • IBM Cloud Functions を利用してアクションをセットアップする
  • IBM Cloud Functions から Webhook を使用してアクションをトリガーする
  • GitHub API を操作する

前提条件

  • 無料枠の IBM Cloud アカウント。
  • GitHub アカウント。
  • 自分がオーナーとなっている GitHub 組織。オーナーとして組織にアクセスできない場合は、独自の組織を作成できます。

所要時間

このチュートリアルの所要時間は約 45 分です。

手順

このチュートリアルは以下のステップで構成されています。

  1. GitHub の個人アクセス・トークンを生成する
  2. サーバーレス・トリガーおよびアクションを作成する
  3. Webhook 対応の GitHub リポジトリーをセットアップする
  4. 実行する!
  5. コードの内容を詳しく調べる

1. GitHub の個人アクセス・トークンを生成する

GitHub API を (Python コードから) プログラムで呼び出せるよう、個人アクセス・トークンを生成します。そのための手順は GitHub のドキュメントで詳しく説明されていますが、要約すると、以下の方法で個人アクセス・トークンを生成します。

  • GitHub のプロファイル設定で、「Developer Settings (開発者用設定)」をクリックします。
  • Personal access tokens (個人アクセス・トークン)」を選択します。
  • Generate new token (新規トークンを生成)」をクリックします。
  • repo (リポジトリー)」項目が選択されていることを確認します。
  • ページの下部にある「Generate token (トークンを生成)」をクリックします。

ランダムな文字列が生成されます。この文字列をコピーして安全な場所に貼り付けます。次のいくつかのステップで必要になります。ご使用の環境内で GitHub Enterprise アカウントを使用している場合は、このステップを繰り返します。

2. サーバーレス・トリガーおよびアクションを作成する

トリガーまたはアクションの作成に取り掛かるには、IBM Cloud にログインし、ナビゲーション・メニューから「Functions」項目を選択するか、IBM Cloud Functions に直接アクセスします。

カスタム・トリガーを作成する

最初に必要な作業として、IBM Cloud 上でサーバーレス・トリガーを作成します。トリガーを作成すると、GitHub Webhook に割り当てる URL が作成されます。GitHub Webhook はイベント (新しい問題や新しいプル・リクエストなど) が発生するたびに起動し、サーバーレス・トリガーに関連付けられた URL にペイロード (JSON 形式のイベントの解釈など) を送信します。

  • Functions」の概要ページで、「Create Trigger (トリガーの作成)」を選択します。

    サーバーレス・トリガーの作成を開始する画面のスクリーン・キャプチャー

  • Custom Trigger (カスタム・トリガー)」項目を選択します。

    トリガーのタイプを選択する画面のスクリーン・キャプチャー

  • 新しく作成するトリガーに名前を付けて、説明を入力します。

    新しいトリガーを作成する画面のスクリーン・キャプチャー

  • トリガーが作成されたら、そのエンドポイントを確認します。目のアイコンをクリックすると、完全な URL と API 鍵およびシークレットが表示されます。

    トリガーのエンドポイントが表示された画面のスクリーン・キャプチャー

この URL もどこか安全な場所に保存します。この後すぐに必要になります!

アクションを作成する

次は、トリガーが起動された時点で実行される Python コード (アクション) を作成します。

  • Functions」の概要ページで、「Create Action (アクションの作成)」を選択します。

    サーバーレス・アクションの作成を開始する画面のスクリーン・キャプチャー

  • 新しく作成するアクションに名前を付けて、デフォルト・パッケージを選択します。ランタイムには「Python 3」を選択してください。

    アクションに名前を付ける画面のスクリーン・キャプチャー

  • オンライン・エディターがポップアップ表示されたら、以下のコードをコピーし、エディター内に貼り付けます。チュートリアルの手順の後、コードの内容を詳しく見ていきます。

  • アクションが作成されたら、そのアクションに、前のステップで作成したトリガーを関連付ける必要があります。それには、左側にあるメニューから「Connected Triggers (接続済みトリガー)」項目を選択します。

    「Connected Triggers (接続済みトリガー)」項目を示す画面のスクリーン・キャプチャー

  • Add Trigger (トリガーの追加)」をクリックし、「Custom Trigger (カスタム・トリガー)」を選択して、前に作成したトリガーを見つけます。

  • 作成したアクションを再表示し、「Parameters (パラメーター)」メニューを選択します。パラメーターとして環境変数 GHE_TOKENGH_PUB_TOKEN の 2 つを追加する必要があります。環境変数の値には、ステップ 1 で生成された値を使用します。
  • Add (追加)」をクリックします。

    アクションのパラメーターを示す画面のスクリーン・キャプチャー

作業の完了まで、あと一息です!

3. Webhook 対応の GitHub リポジトリーをセットアップする

  • ユーザーが新しいリポジトリーをリクエストするために問題を提出できる GitHub リポジトリーを作成します。
  • ユーザーが入力できるよう、以下の問題テンプレートを用意します。これは私が使用したテンプレートです。このテンプレートで、リポジトリーの名前、説明、ライセンス、管理者として追加するユーザー名を入力するよう求めます。

    ## Essentials
    
    * name: repo_name
    * users: user_1, user_2
    * description: this is for fun, ain't it grand!
    * license: apache-2.0
    
  • リポジトリーの設定で「Hooks (フック)」メニューを選択し、サーバーレス・トリガーを呼び出すように Webhook をセットアップします。

    Webhook の概要を示す画面のスクリーン・キャプチャー

  • 新しい Webhook を作成します。「Payload URL (ペイロード URL)」に、ステップ 2 で保存したトリガーのエンドポイントを設定します。

  • Content type (コンテンツ・タイプ)」の値として、application/json を設定します。
  • Enable SSL verification (SSL 検証を有効にする)」をオンに設定します。ペイロード URL の例を以下に示します。

    https://KEY:SECRET@us-south.functions.cloud.ibm.com/api/v1/namespaces/your_namespace/triggers/your_trigger
    

    GitHub 内の Webhook の設定を示す画面のスクリーン・キャプチャー

  • Which events would you like to trigger this webhook? (この Webhook をトリガーさせるイベントを選択してください)」項目で、「Let me select individual events (個々のイベントを選択する)」を選択します。

  • リストが表示されたら、「Issue comments (コメントの発行)」を選択します。

    GitHub 内の Webhook イベントを示す画面のスクリーン・キャプチャー

これで、リポジトリーがアクションに応答するようにセットアップされ、アクションがトリガーに関連付けられました。トリガーで使用する GitHub トークンも設定されたので、テストを開始できます!

4. 実行する

  • まず、誰かに問題を提出してもらいます。以下の例に示されているように、リポジトリーの名前と説明、追加するユーザー名、使用するライセンスが指定されています。必要な情報はすべて揃っています。

    元の問題を示す画面のスクリーン・キャプチャー

  • どのようなコメントを残してもトリガーが呼び出されますが、サーバーレス・アクション内では、明示的に /approve コメントの有無を確認するようにしています。したがって、提出する問題に該当するメッセージを残し、GitHub からトリガーに送信されたペイロードを確認してください。

    承認を求める問題を示す画面のスクリーン・キャプチャー

  • 以下に、ペイロードの例を示します。読みやすくするために大幅に省略されていますが、この例で問題の本文、問題のコメント、コメントしたユーザー、問題番号を確認できます。この blob 全体をサーバーレス・アクションの params 変数から入手できるようになっています。

    {
      "action": "created",
      "issue": {
        "number": 18,
        "title": "Repo for Studio Learning Path assets",
        "user": {
          "login": "Rich-Hagarty",
        },
        "body": "## Essentials\r\n\r\n* name: watson-studio-learning-path-assets\r\n* users: rhagarty\r\n* description: repo to store all assets (such as notebooks, data, etc) for Watson Studio Learning Path tutorials\r\n\r\n## Tips\r\n\r\n* Repo names **CANNOT** have spaces.\r\n* User IDs must be from **public github**, if you're not sure, go to https://github.com/ and login.\r\n*"
      },
      "comment": {
        "body": "/approve"
      },
      "sender": {
        "login": "stevemar"
      }
    }
    
  • サーバーレス・アクションに含まれるスクリプトの最後の部分で、フォローアップ・コメント (リポジトリーが作成されたことを伝えるコメント) と URL を送信します。

    自動コメントを示す画面のスクリーン・キャプチャー

5. コードの内容を詳しく調べる

このチュートリアルで使用している完全なソース・コードは、Gist 上に用意されています。いくつかのコード・スニペットを見ていきましょう。

コメントの送信者を確認する

以下のシンプルなコードではセキュリティーを確保するために、2 人のユーザーをハードコーディングして、/approve コメントが残されたときに、それが信頼できるユーザーから送信されたことを確認します (とびきり簡潔なコードとは言えませんが、機能することは確かです)。

def main(params):

    if params['comment']['body'] == "/approve":

        sender = params['sender']['login']
        if sender == 'stevemar' or sender == 'chrisfer':
            print("proceeding with repo create")
        else:
            return { 'message': 'approve comment made by unauthorized user' }

マークダウンを解析する

送信された問題の本文は JSON ペイロード内に含まれていますが、未加工のマークダウン形式になっています。そのため、ここではちょっとした知恵を働かせてテキストを抽出し、すべての例外ケースに照らし合わせる必要がありました。その結果、例外ケースが捕捉されるように複数の単体テストを作成することになりました。

def _get_info_from_body(body):

    m = re.search(r'\* name:(.*)(\r\n|$)', body)
    repo_name = m.group(1).strip() if m else None
    repo_name = repo_name.strip() if repo_name else None

    m = re.search(r'\* users:(.*)(\r\n|$)', body)
    users = m.group(1).strip() if m else []
    users = [x.strip() for x in users.split(',')] if users else []

    m = re.search(r'\* description:(.*)(\r\n|$)', body)
    description = m.group(1).strip() if m else ''

    m = re.search(r'\* license:(.*)(\r\n|$)', body)
    license = m.group(1).strip() if m else 'apache-2.0'
    license = license.strip() if license else 'apache-2.0'

    return {'repo_name': repo_name, 'users': users,
            'description': description, 'license': license}

GitHub API を呼び出す

GitHub API は非常に詳しく文書化されています。私は GitHub API を呼び出すために、 python-requests ライブラリーを使用しました。

    gh_token = params['GH_PUB_TOKEN']
    # set up auth headers to call github APIs
    headers = {'Authorization': 'token %s' % gh_token}

    # create the repo -- https://developer.github.com/v3/repos/#create
    url = 'https://api.github.com/orgs/' + PUBLIC_ORG + '/repos'
    payload = {
        'name': info['repo_name'],
        'description': info['description'],
        'license_template': info['license'],
        'auto_init': 'true'
    }
    r = requests.post(url, headers=headers, data=json.dumps(payload))

まとめ

このチュートリアルを作成するのは楽しいことでした。読者の皆さんも同じだけ楽しんでいただけたことを願います。IBM Cloud Functions を利用してアクションをセットアップする方法、そのアクションを Webhook でトリガーする方法、Python コードを使用して GitHub API を呼び出す方法について学んだ知識を、組織のワークフローを改善するために生かしてください。