はじめに

インストールしたい OSS ツールのバージョンを GitHub API などを使って動的に取得したいことがありました。コードスニペットにしておくと便利そうだったので、bash と PowerShell でスクリプトを書いてみました。

シェルスクリプト

bash だとこんな感じになります。

repover() {
  check_rate_limit() {
    if [[ $1 == *"rate limit"* ]]; then
      echo "ERROR: Rate limit has been exceeded."
      return 1
    fi
    return 0
  }

  if [[ $# != 2 ]]; then
    echo "ERROR: Need two parameters. repo-author and repo-name."
    return 1
  fi

  if ! command -v jq &>/dev/null; then
    echo "ERROR: 'jq' is not installed but is required."
    return 1
  fi

  if ! response=$(curl -s "https://api.github.com/repos/$1/$2/releases/latest" 2>/dev/null); then
    echo "ERROR: Failed to get the latest release of $2 via the GitHub API."
    return 1
  fi

  check_rate_limit "$response" || return 1

  if ! ver=$(echo "$response" | jq -r .tag_name 2>/dev/null); then
    echo "ERROR: 'tag_name' does not exist in the response."
    return 1
  fi

  if [[ ! $ver == 'null' ]]; then
    echo "$ver"
    return 0
  fi

  if ! response=$(curl -s "https://api.github.com/repos/$1/$2/tags" 2>/dev/null); then
    echo "ERROR: Failed to get the latest tag of $2 via the GitHub API."
    return 1
  fi

  check_rate_limit "$response" || return 1

  if ! tags=$(echo "$response" | jq ".[].name" 2>/dev/null); then
    echo "ERROR: 'name' does not exist in the response."
    return 1
  fi

  if ! echo "$tags" | jq -r 'sort_by(.) | reverse | .[0]'; then
    echo "ERROR: Failed to parse the latest tag of $2 via the GitHub API."
    return 1
  fi
}

内容ですが、まず Rate Limit に引っかかったかを確認する関数を用意します。

check_rate_limit() {
  if [[ $1 == *"rate limit"* ]]; then
    echo "ERROR: Rate limit has been exceeded."
    return 1
  fi
  return 0
}

GitHub API を使って、引数で author/repo を渡して最新リリースを取得します。戻り値で Rate Limit を検出した場合はそのまま終了します。

if ! response=$(curl -s "https://api.github.com/repos/$1/$2/releases/latest" 2>/dev/null); then
  echo "ERROR: Failed to get the latest release of $2 via the GitHub API."
  return 1
fi

check_rate_limit "$response" || return 1

戻り値に tag_name キーが含まれているか確認します。なかったら終了します。

if ! ver=$(echo "$response" | jq -r .tag_name 2>/dev/null); then
  echo "ERROR: 'tag_name' does not exist in the response."
  return 1
fi

上記でバージョンが取れたらそれを返して正常終了します。

if [[ ! $ver == 'null' ]]; then
  echo "$ver"
  return 0
fi

バージョンが取得できなかった場合、タグを見にいきます。この場合も Rate Limit を確認します。

if ! response=$(curl -s "https://api.github.com/repos/$1/$2/tags" 2>/dev/null); then
  echo "ERROR: Failed to get the latest tag of $2 via the GitHub API."
  return 1
fi

check_rate_limit "$response" || return 1

戻り値に name キーが含まれているか確認します。なかったら終了します。

if ! tags=$(echo "$response" | jq ".[].name" 2>/dev/null); then
  echo "ERROR: 'name' does not exist in the response."
  return 1
fi

取得したタグを降順でソートし、最初の要素を返します。

if ! echo "$tags" | jq -r 'sort_by(.) | reverse | .[0]'; then
  echo "ERROR: Failed to parse the latest tag of $2 via the GitHub API."
  return 1
fi

使用例 (リリースがあるリポジトリ)

$ repover aws aws-cdk
v2.94.0

使用例 (リリースがなくタグで管理されているリポジトリ)

$ repover awslabs git-secrets
1.3.0

PowerShell スクリプト

かなり簡略化していますが、PowerShell で書く場合の例です。

#Requires -Version 6.0

function repover {
  param (
    [Alias('u')]
    [string]$author,
    [Alias('r')]
    [string]$repo
  )

  $content = (Invoke-WebRequest -Uri $('https://api.github.com/repos/{0}/{1}/releases/latest' -f $author, $repo) -UseBasicParsing -SkipHttpErrorCheck).Content | ConvertFrom-Json

  if ($content.PSObject.Properties.Name -contains 'tag_name') {
    return $content.tag_name
  }

  $content = (Invoke-WebRequest -Uri $('https://api.github.com/repos/{0}/{1}/tags' -f $author, $repo) -UseBasicParsing -SkipHttpErrorCheck).Content | ConvertFrom-Json

  if ($content.PSObject.Properties.Value -contains 'name') {
    return $content.name | Sort-Object -Descending -Unique | Select-Object -First 1
  }
}

やっていることは以下の通りです。

  1. Invoke-WebRequest で API を叩いて最新リリースを取得
  2. 戻り値に含まれる Content を JSON から PowerShell のオブジェクトに変換
  3. プロパティに tag_name があるか確認し、あれば返す
  4. tag_name からバージョンが取れなかった場合、Invoke-WebRequest でタグ取得の API を叩く
  5. 戻り値に含まれる Content を JSON から PowerShell のオブジェクトに変換
  6. プロパティに name があるか確認し、あれば降順ソートした先頭の要素を返す

使用例

PS> repover -author aws -repo aws-cdk
v2.94.0

PS> repover -author awslabs -repo git-secrets
1.3.0

おわりに

そこそこ便利に使っています。ターミナルは毎日のように開くものですし、よく使う API はひととおり関数化しておくと捗ります。