🎠

Check! Terraform で GitHub の最新リリースの assets のダウンロードURLを取得する

2021/01/19に公開

Prologue

Infrastructure as Code で自動化させてると、GitHub の最新のリリースに添付されている assets のURLを知りたいときってありません?

自分の場合は、Azure Functions の zip デプロイ(※)で利用したい!

具体的には、GitHub で管理している FaaS のコードの更新を GitHub Actions で検知して自動ビルド・パッケージ化し、リリースに添付しておく。デプロイ時は最新リリースに添付されているパッケージファイルを参照するよう記述する。

だがしかし、ここで困るのが、リリースのページは /<repoisitory>/releases/latest という固定URLでアクセスできるのだが、ここに添付されているファイルのURLは <repository>/releases/download/v1.0.0/package.zip というようにタグ名が挟まっていて定まらない!

コードでこの URL を特定するには、こういう処理が必要になってしまいます。

curl -s https://api.github.com/repos/<repository>/releases/latest | jq -r ".assets[0].browser_download_url"

https://github.com/<repository>/releases/download/v1.0.0/package.zip`

面倒だなーと思っていたところ、どうやら Terraform には GitHub のプロバイダがあって、最新リリースの情報を引っ張れそう!

https://registry.terraform.io/providers/integrations/github

わーーい!

ということで、ちょうど今対応中のプロジェクトが、Terraform 導入なので使ってみました。便利!!

※ Azure Functions は zip ファイルを用いてコード展開ができます。これと パッケージファイルからの実行 を組み合わせると、リソースのデプロイ時にコードもデプロイできるので非常に便利です。具体的には、コードをアーカイブ化した zip ファイルをパブリックにアクセスできる場所に置いて置き、アプリケーション設定で WEBSITE_RUN_FROM_PACKAGE でそのURLを指定すると、デプロイ時に取得して展開してくれます。

Terraform で GitHub プロバイダを利用する準備

GitHub の認証情報を設定する(必要に応じて)

GitHub プロバイダを利用するとき、リソースを作成したり、パブリックでない情報を取得するには GitHUb の認証情報の設定が必要です。(※ パブリックリポジトリのリリース情報を取得するには不要でした。)

  • アクセストークン
  • ユーザー名(※ 組織の場合やエンタープライズ版を利用する場合は、別途ドキュメントをご確認ください。)

まず、アクセストークンを作成しましょう。

https://docs.github.com/ja/github/authenticating-to-github/creating-a-personal-access-token

そして、認証情報を引き渡す方法は 2種類あります。

  • 環境変数として渡す
  • スクリプトの変数として渡す

認証情報を環境変数として渡す

terraform コマンドを実行する前に、下記の環境変数を指定しておきます。

環境変数 説明
GITHUB_TOKEN 取得したアクセストークンを指定する
GITHUB_OWNER ユーザー名を指定する

認証情報をスクリプトの変数として渡す

スクリプトの変数として渡す場合には、下記のようにプロバイダの設定に記述します。

# 中略

provider "github" {
  token = "${var.github_token}"
  owner = "${var.github_owner}"
}

# 中略

data "github_release" で最新リリースに添付されている特定のファイルのダウンロードURLを入手する

それではさっそく、最新リリースに添付されている目的の asset のURLを取得してみましょう!

バージョンなどは執筆時点のものです。適宜ドキュメントをご確認ください。

terraform {
  required_providers {
    github = {
      source  = "integrations/github"
      version = "4.3.0"
    }
  }
}

variable "repo" {
  type = string
}

variable "owner" {
  type = string
}

variable "asset_name" {
  type = string
}

data "github_release" "latest" {
  repository  = var.repo  # リリース情報を取得する対象のリポジトリ
  owner       = var.owner # リリース情報を取得する対象のオーナー名
  retrieve_by = "latest"
}

data "http" "get_assets" {
  url = data.github_release.latest.asserts_url

  request_headers = {
    Accept = "application/vnd.github.v3+json"
  }
}

output "download_url" {
  value = [for asset in jsondecode(data.http.get_assets.body) : asset if lookup(asset, "name") == var.asset_name][0].browser_download_url
}

ここでは、参考に、下記のパブリックリポジトリを引数を与えて実行します。

.auto.tfvars
repo       = "learn-azure-functions-with-network-options"
owner      = "dzeyelid"
asset_name = "functions.zip"
terraform init
terraform plan
terraform apply

apply の結果はこの通りです。目的のダウンロードURLを取得することができました!

Apply complete! Resources: 0 added, 0 changed, 0 destroyed.

Outputs:

download_url = "https://github.com/dzeyelid/learn-azure-functions-with-network-options/releases/download/v0.1.0/functions.zip"

解説

data "github_release" は、 REST API /repos/{owner}/{repo}/releases/latest の実行結果を返却してくれます。

このうち、 assets_url は、対象のリリースが持つ assets の情報を取得するための URL です。この assets の情報を得るために、 data "http" を利用してこの URL のレスポンスを取得します。

その結果、下記のようなデータが返ってきます。

[
  {
    "browser_download_url" = "https://github.com/dzeyelid/learn-azure-functions-with-network-options/releases/download/v0.1.0/functions.zip"
    "content_type" = "application/zip"
    "created_at" = "2021-01-18T10:12:00Z"
    "download_count" = 0
    "id" = 3.0850289e+07
    "label" = ""
    "name" = "functions.zip"
    "node_id" = "MDEyOlJlbGVhc2VBc3NldDMwODUwMjg5"
    "size" = 297996
    "state" = "uploaded"
    "updated_at" = "2021-01-18T10:12:00Z"
    "uploader" = {
      "avatar_url" = "https://avatars2.githubusercontent.com/in/15368?v=4"
      "events_url" = "https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}"
      "followers_url" = "https://api.github.com/users/github-actions%5Bbot%5D/followers"
      "following_url" = "https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}"
      "gists_url" = "https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}"
      "gravatar_id" = ""
      "html_url" = "https://github.com/apps/github-actions"
      "id" = 4.1898282e+07
      "login" = "github-actions[bot]"
      "node_id" = "MDM6Qm90NDE4OTgyODI="
      "organizations_url" = "https://api.github.com/users/github-actions%5Bbot%5D/orgs"
      "received_events_url" = "https://api.github.com/users/github-actions%5Bbot%5D/received_events"
      "repos_url" = "https://api.github.com/users/github-actions%5Bbot%5D/repos"
      "site_admin" = false
      "starred_url" = "https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}"
      "subscriptions_url" = "https://api.github.com/users/github-actions%5Bbot%5D/subscriptions"
      "type" = "Bot"
      "url" = "https://api.github.com/users/github-actions%5Bbot%5D"
    }
    "url" = "https://api.github.com/repos/dzeyelid/learn-azure-functions-with-network-options/releases/assets/30850289"
  },
]

配列になっているので、その一部を利用したい場合は適宜検索します。この例では、下記の処理で、 name が指定した asset の名前に一致するオブジェクトを抽出することで、 URL を特定しています。

抜粋
[for asset in jsondecode(data.http.get_assets.body) : asset if lookup(asset, "name") == var.asset_name][0].browser_download_url

Epilogue

Terraform はネットワーク周りで便利な関数があったり、上記のようにいろんなプロバイダがあるので複雑なことをしようとしたときは時に便利ですねぇ!

Discussion