aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFranck Cuny <franck@fcuny.net>2024-01-25 17:47:06 -0800
committerFranck Cuny <franck@fcuny.net>2024-01-25 17:47:06 -0800
commita29ce6603158b4d924278cdbd7a81e63633cf0f3 (patch)
tree5a18617a9f882de2ae5e4600d2704b30f0e921eb
parentbuild binaries and run commands on CI (diff)
downloadinfra-a29ce6603158b4d924278cdbd7a81e63633cf0f3.tar.gz
gha-billing: how many minutes are left in the cycle
Diffstat (limited to '')
-rw-r--r--cmd/ghabilling/README.md15
-rw-r--r--cmd/ghabilling/main.go91
2 files changed, 106 insertions, 0 deletions
diff --git a/cmd/ghabilling/README.md b/cmd/ghabilling/README.md
new file mode 100644
index 0000000..2aa08ce
--- /dev/null
+++ b/cmd/ghabilling/README.md
@@ -0,0 +1,15 @@
+# `gha-billing`
+
+Print information about how many free minutes of GitHub actions are left for this cycle.
+
+The API for this is documented [here](https://docs.github.com/en/rest/billing/billing?apiVersion=2022-11-28#get-github-actions-billing-for-an-organization).
+
+For this you need a [token](https://github.com/settings/personal-access-tokens) with the following permissions:
+- [plan](https://docs.github.com/en/rest/authentication/permissions-required-for-fine-grained-personal-access-tokens?apiVersion=2022-11-28#user-permissions-for-plan)
+
+## usage
+
+```sh
+➜ world git:(main) ✗ go run ./cmd/ghabilling -t github_pat_<TOKEN>
+this cycle, 14 minutes have been used, and 1986 minutes are remaining
+```
diff --git a/cmd/ghabilling/main.go b/cmd/ghabilling/main.go
new file mode 100644
index 0000000..bb5f41a
--- /dev/null
+++ b/cmd/ghabilling/main.go
@@ -0,0 +1,91 @@
+package main
+
+import (
+ "context"
+ "encoding/json"
+ "flag"
+ "fmt"
+ "net/http"
+ "os"
+ "time"
+
+ "github.com/fcuny/world/internal/version"
+)
+
+const API_URL = "https://api.github.com"
+
+const usage = `Usage:
+ gha-billing -t [TOKEN]
+
+Options:
+ -t, --token GitHub API's token
+ -v, --version Print version information
+ -h, --help Print this message
+`
+
+// https://docs.github.com/en/rest/billing/billing?apiVersion=2022-11-28#get-github-actions-billing-for-an-organization
+type githubActionBilling struct {
+ TotalMinutesUsed float64 `json:"total_minutes_used"`
+ TotalPaidMinutesUsed float64 `json:"total_paid_minutes_used"`
+ IncludedMinutes float64 `json:"included_minutes"`
+ MinutesUsedBreakdown map[string]int `json:"minutes_used_breakdown"`
+}
+
+func main() {
+ flag.Usage = func() { fmt.Fprintf(os.Stderr, "%s\n", usage) }
+
+ var (
+ tokenFlag string
+ userFlag string
+ versionFlag bool
+ )
+
+ flag.StringVar(&tokenFlag, "token", "", "GitHub API token")
+ flag.StringVar(&tokenFlag, "t", "", "GitHub API token")
+ flag.StringVar(&userFlag, "user", "fcuny", "GitHub API token")
+ flag.StringVar(&userFlag, "u", "fcuny", "GitHub API token")
+ flag.BoolVar(&versionFlag, "version", false, "Print version information")
+ flag.BoolVar(&versionFlag, "v", false, "Print version information")
+
+ flag.Parse()
+
+ if versionFlag {
+ information := version.VersionAndBuildInfo()
+ fmt.Println(information)
+ return
+ }
+
+ if tokenFlag == "" {
+ fmt.Fprintf(os.Stderr, "The API token is not set\n")
+ }
+
+ ctx := context.TODO()
+
+ url := fmt.Sprintf("%s/users/%s/settings/billing/actions", API_URL, userFlag)
+ req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "could not create a request: %v\n", err)
+ os.Exit(1)
+ }
+ req.Header.Set("Authorization", fmt.Sprintf("token %s", tokenFlag))
+ req.Header.Set("Accept", "application/vnd.github.v3+json")
+
+ client := http.Client{
+ Timeout: 30 * time.Second,
+ }
+
+ res, err := client.Do(req)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "error making http request: %s\n", err)
+ os.Exit(1)
+ }
+
+ var b githubActionBilling
+ if err := json.NewDecoder(res.Body).Decode(&b); err != nil {
+ fmt.Fprintf(os.Stderr, "error parsing the JSON response: %v\n", err)
+ os.Exit(1)
+ }
+
+ timeRemaining := b.IncludedMinutes - b.TotalMinutesUsed
+ fmt.Printf("this cycle, %d minutes have been used, and %d minutes are remaining\n", int(b.TotalMinutesUsed), int(timeRemaining))
+}