ansible で private な GitHub Releases からファイルをダウンロードする方法

GitHub の private リポジトリで開発を進めている Golang プロジェクトがあります。CiecleCI 上でビルドを行ってバイナリファイルを GitHub Releases にアップロードしているものがあります。

これまでは「手動で最新版ファイルをダウンロードして ansible でデプロイする」といった方法を取っていました。都度行うには手間なので ansible の playbook 1発で実行させたくなりました。

実行したい処理

  • 特定の GitHub Releases から最新版のファイルをダウンロード
  • ansible 実行対象サーバのファイルと差し替え
  • GitHub Releases の各バージョンには 1 ファイルのみ アップロードされている状態

準備

GitHub の API にアクセスする必要があるため Personal Access Token を取得します。管理画面 から新しいトークンの作成ができます。権限は 1 番上の repo にチェックを入れるだけです。

Personal Access Token の権限設定

取得したトークンを環境変数に設定します。(実際には direnv を使っています)

$ export GITHUB_TOKEN=YOUR-GITHUB-TOKEN

参考 playbook

- name: private  github releases からファイルをダウンロードする
  hosts: all

  vars:
    github:
      user: 'USER-NAME'        # 対象リポジトリのユーザ名
      repos: 'REPOSITORY-NAME' # 対象リポジトリ名
      token: "{{ lookup('env', 'GITHUB_TOKEN') }}" # 環境変数 GITHUB_TOKEN をロード
      dest: '/path/to/savepath' # DL したファイルの保存先パス

  tasks:
    - name: '{{ github.user }}/{{ github.repos }} 最新版情報取得'
      uri:
        url: 'https://api.github.com/repos/{{ github.user }}/{{ github.repos }}/releases/latest'
        HEADER_Authorization: 'token {{ github.token }}'
        return_content: yes
        body_format: json
      register: response # 最新版の情報を取得

    - name: GitHub API からダウンロード用 URL 取得
      uri:
        url: '{{ response.json.assets[0].url }}'
        HEADER_Authorization: 'token {{ github.token }}'
        HEADER_Accept: 'application/octet-stream'
        follow_redirects: no
        return_content: yes
        body_format: json
        status_code: 302
      register: response # DL 対象ファイルの URL を取得

    - name: '{{ github.user }}/{{ github.repos }} からファイルをダウンロード'
      get_url:
        url: '{{ response.location }}'
        dest: '{{ github.dest }}'
        force: yes 
        owner: root
        group: root
        mode: 0500
      become: True

処理手順

  1. uri モジュールを使って最新版リリース情報を取得
    • レスポンス: Get the latest release
    • 取得したいファイルは 1 つだけなので, assets の 0 番の URL が処理対象
  2. uri モジュールで 1 で取得した JSON を使って DL 対象の URL にアクセス
    • レスポンス: Get a single release asset
    • status: 302 が返り, レスポンスヘッダの Location に DL 用 URL が書かれている
  3. 2 のレスポンスヘッダ Location に記載されている URL にアクセスしファイル取得

本来なら 1 のデータ取得後に get_url モジュールで 2 へアクセスすればファイルが落とせそうな気がします。しかし get_url が HTTP Status: 302 Found のリダイレクトにエラーを吐くので, 直接レスポンスの Location を見て DL しています。

curl であれば 1 で取得した response.json.assets[0].url に対して次のコマンドでファイルを DL できます。

$ curl -L -H "Authorization: token $GITHUB_TOKEN" -H "Accept: application/octet-stream" https://api.github.com/repos/USER-NAME/REPOSITORY-NAME/releases/assets/:asset_id

command モジュールで curl を使ったほうが早い気もしますが, 折角 ansible を使ているので playbook で頑張りました。

get_url モジュールの取扱間違いの可能性

もしかすると get_url の扱いを間違えている結果, 余計な処理を間に挟まなければならないのかもしれません。一応ドキュメントは読んでいますが, お気づきの点がある方はひっそり Twitter あたりで教えて欲しいください。