Shogo's Blog

Jun 22, 2023 - 2 minute read - github

GitHub Actionsの利用量をまとめる

GitHub Actionsの利用量はsettings/billingから確認できます。

今月分のGitHub Actions利用量、今月はあと3000分使える

それはいいんですが、問題は今月分しか確認できないこと!

「先月は〇〇分使ったから上限いくらに設定しておくか〜〜」ということができません。 APIは見つけたので、取得スクリプトを組んでみました。

集計スクリプト

GitHub REST APIをたたいて、結果をGoogle Spreadsheetにまとめます。 GitHub REST APIを叩く部分はGitHub CLIを使えばすぐにできるんですが、 Google Spreadsheetへの書き込みをシェルスクリプトで組むのはちょっと大変です。

今回はGoで書いてみました。

package main

import (
	"context"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
	"time"

	"google.golang.org/api/sheets/v4"
)

func main() {
	ctx := context.Background()
	token := os.Getenv("GITHUB_TOKEN")
	if token == "" {
		log.Fatal("GITHUB_TOKEN is required")
	}
	sheetID := os.Getenv("SHEET_ID")
	if sheetID == "" {
		log.Fatal("SHEET_ID is required")
	}
	org := os.Getenv("GITHUB_REPOSITORY_OWNER")
	if org == "" {
		log.Fatal("GITHUB_REPOSITORY_OWNER is required")
	}

	now := time.Now()
	usage, err := GetGitHubActionsBilling(ctx, token, org)
	if err != nil {
		log.Fatal(err)
	}

	svc, err := sheets.NewService(ctx)
	if err != nil {
		log.Fatal(err)
	}

	tableRange := "report!A1"
	var reportData sheets.ValueRange
	reportData.Values = append(reportData.Values, []any{
		now,
		usage.TotalMinutesUsed,
		usage.TotalPaidMinutesUsed,
		usage.IncludedMinutes,
		usage.MinutesUsedBreakdown["UBUNTU"],
		usage.MinutesUsedBreakdown["MACOS"],
		usage.MinutesUsedBreakdown["WINDOWS"],
	})
	_, err = svc.Spreadsheets.Values.Append(sheetID, tableRange, &reportData).
		ValueInputOption("USER_ENTERED").
		InsertDataOption("INSERT_ROWS").
		Context(ctx).
		Do()
	if err != nil {
		log.Fatal(err)
	}
}

type GitHubActionsBilling struct {
	TotalMinutesUsed     float64            `json:"total_minutes_used"`
	TotalPaidMinutesUsed float64            `json:"total_paid_minutes_used"`
	IncludedMinutes      float64            `json:"included_minutes"`
	MinutesUsedBreakdown map[string]float64 `json:"minutes_used_breakdown"`
}

func GetGitHubActionsBilling(ctx context.Context, token, org string) (*GitHubActionsBilling, error) {
	// build the request
	u := "https://api.github.com/orgs/" + org + "/settings/billing/actions"
	req, err := http.NewRequestWithContext(ctx, http.MethodGet, u, nil)
	if err != nil {
		return nil, err
	}
	req.Header.Set("Accept", "application/vnd.github+json")
	req.Header.Set("Authorization", "Bearer "+token)
	req.Header.Set("X-GitHub-Api-Version", "2022-11-28")

	// send the request
	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("unexpected status code: %d", resp.StatusCode)
	}

	// parse the response
	data, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}
	var usage *GitHubActionsBilling
	if err := json.Unmarshal(data, &usage); err != nil {
		return nil, fmt.Errorf("failed to parse response: %w", err)
	}
	return usage, nil
}

認証設定

実行にはGitHubとGoogleへの認証情報が必要です。

GitHubはsettings/tokensからPAT(Personal access token)を取得し、 環境変数に設定します。

export GITHUB_TOKEN=YOUR_GITHUB_TOKEN

Googleはgcloudコマンドを使います。 デフォルトの権限ではスプレッドシートへの書き込みができないので、スプレッドシートを書き込み権限をスコープに追加してログインします。

gcloud auth application-default login \
    --scopes=https://www.googleapis.com/auth/cloud-platform,https://www.googleapis.com/auth/spreadsheets

実行

他の必要なパラメーターは環境変数で設定します。

export SHEET_ID=hogehoge
export GITHUB_REPOSITORY_OWNER=org
go run main.go

あとでGitHub Actionsで自動化することを想定して、オーガニゼーション名はGITHUB_REPOSITORY_OWNERから取得するようにしました。 実際にGitHub Actionsを使って動かすのはまた今度。

まとめ

GitHub Actionsの利用量を記録するスクリプトを書きました。


ここまで書いたところでGitHub public roadmapに載っているのでは?と思ったら、 なんかそれっぽいのがありました。

Billing Overview

予定では「Q2 2023 – Apr-Jun」とありますが、最初公開されたときの予定はQ4 2021 – Oct-Dec。 そしてこの文章を書いているのは2023-06-22。 さて、僕のスクリプトが動き出すのとどっちが早いでしょう・・・?

参考