虎の穴開発室ブログ

虎の穴ラボ株式会社所属のエンジニアが書く技術ブログです

MENU

Go言語で作ったツールについてGitHub Actionsを使って自動でReleaseを作成してみる

こんにちは。10月に入社しました虎の穴ラボの Y.K. です。

この記事は「虎の穴ラボアドベントカレンダー」9日目の記事です。

昨日は大場さんによる「html-ESLint導入に向けて」が投稿されました。
明日はnsdさんによる「誰か教えてくれ、アジャイル開発!!」が投稿されます。
こちらもぜひご覧ください!

はじめに

虎の穴ラボでは一部のプロダクトや勉強会の題材としてGo言語を使用しており、今後もGo言語を利用したプロダクトや社内向けツール等々を開発することが増えてくるのではないかと思っています。

Go言語で作成したプログラムはGo言語のインストールがされていれば go install コマンドで簡単に導入することができますが、社内にはエンジニア以外のメンバーも存在するため、環境を問わず簡単に配布できるような環境が必要であると考えます。

今回はそんな将来に備えて、GitHub Actionsを用いてGo言語で作ったプログラムのバイナリ生成とReleaseの作成・ファイルアップロードの自動化をしてみた内容について紹介します。

事前準備・前提

GitHubにてレポジトリが作成されておりプログラムのプッシュができる状態になっていること

構成

golang-cat (master)$ tree -a -I .git .
.
├── .github
│   └── workflows
│       └── release.yml
├── .gitignore
├── Makefile
├── README.md
├── bin
│   ├── golang-cat
│   └── goxz
│       ├── golang-cat_darwin_amd64.zip
│       ├── golang-cat_darwin_arm64.zip
│       ├── golang-cat_linux_amd64.tar.gz
│       ├── golang-cat_linux_arm64.tar.gz
│       ├── golang-cat_windows_amd64.zip
│       └── golang-cat_windows_arm64.zip
├── go.mod
├── go.sum
└── main.go
  • プログラム本体: main.go
  • ビルド周りの設定: Makefile
  • バイナリ格納場所: bin/

今回作成したプログラムの構成は上記のようになっています。
ビルド周りの設定についてはMakefileに記述し、GitHub Actionsにおいてもこちらを用いてバイナリを生成する構成にしました。

作成したプログラム

一部バグがありますが、シンタックスハイライトができるcatコマンドのようなものを作ってみました。

引数で受け取ったファイルを読み込んで、中身をシンタックスハイライト用ライブラリに渡してあとはおまかせという実装になっています。 ほとんどライブラリ任せというのも少し味気ないので、io.Writerインタフェースを実装した自作の型を最終的な出力を行うメソッドに渡すことで行番号の表示をできるような実装をしてみました。

シンタックスハイライト用のライブラリとしては、静的サイトジェネレーターであるHugoにて使用されているChromaというライブラリを使ってみました。

github.com

今回はこちらのプログラムを対象にGitHub Actionsを用いたRelease作成の自動化に取り組んでいきたいと思います。

Makefileによるビルド・デプロイ設定

ビルドやデプロイ時の設定等についてまとめたMakefileは以下のようにしました。

# ./Makefile

BIN_NAME := golang-cat
BIN_DIR := ./bin
X_BIN_DIR := $(BIN_DIR)/goxz
VERSION := $$(make -s app-version)

GOBIN ?= $(shell go env GOPATH)/bin

.PHONY: all
all: build

# 実行環境に適したバイナリを生成する
.PHONY: build
build:
  mkdir -p $(BIN_DIR)
  go build -o $(BIN_DIR)/$(BIN_NAME) main.go

# 複数のプラットフォームを対象にそれぞれの環境に適したバイナリを生成しzip化する
.PHONY: x-build
x-build: $(GOBIN)/goxz
  goxz -d $(X_BIN_DIR) -n $(BIN_NAME) .

# 生成したバイナリをGitHubのReleaseにアップロードする (x-buildで生成したものを対象にしています)
.PHONY: upload-binary
upload-binary: $(GOBIN)/ghr
  ghr "v$(VERSION)" $(X_BIN_DIR)

# アプリのバージョンを出力する
.PHONY: app-version 
app-version: $(GOBIN)/gobump
   @gobump show -r .

# 以下、上記のターゲットにて使用するツールが導入されていなかった場合に`go install`で導入を行う
$(GOBIN)/goxz:
   @go install github.com/Songmu/goxz/cmd/goxz@latest

$(GOBIN)/ghr:
   @go install github.com/tcnksm/ghr@latest

$(GOBIN)/gobump:
   @go install github.com/x-motemen/gobump/cmd/gobump@master

今回の構成では gobump(バージョン管理)、goxz(クロスコンパイル + ZIPファイル化)、ghr(Releaseへのファイルアップロード) という3種類のGo言語製ツールを用いることとしました。

直接的な実装にはあまり関係がないためツールのインストールはgo installコマンドを用いてグローバルにインストールしています。 筆者の環境(go1.19.3 darwin/amd64)ではgobumpをインストールする際にバージョン指定をlatestでインストールできなかったのでmasterを指定するようにしています。

また、今回はやりませんでしたがテストやgobumpを用いたバージョンアップ処理等についてもターゲットを作成しておくと楽かもしれません。

READMEの抜粋のような形になってしまいますが、使用したツールそれぞれについて簡単な叩き方の例とレポジトリへのリンクをご紹介します。

gobump について

github.com

セマンテックバージョニング2.0.0 に従った形でバージョンの管理を行うことができるようになるツールです。

ソースコード内に定義された変数または定数を外から書き換えることでバージョンを管理するため、ビルドするファイルにて以下のような定義を行う必要があります

// ./main.go

package main

import (
    "bytes"
    "flag"
    "fmt"
    "io"
    "os"

    "github.com/alecthomas/chroma"
    "github.com/alecthomas/chroma/formatters"
    "github.com/alecthomas/chroma/lexers"
    "github.com/alecthomas/chroma/styles"
)

const version = "0.0.1"    // gobumpコマンドを叩くことでここが書き換わったり、値をみてバージョンを教えてくれるようになる

// 以下省略

下記のようにコマンドを叩くことでバージョンを上げることができます。

# wオプション: ソースコード内のバージョンを書き換えます。(つけなかった場合、バージョン変更後のソースコードが出力されます。)
# vオプション: バージョンアップ後の結果を表示します。
# rオプション: 結果のバージョンのみを出力します。(つけなかった場合JSON形式で出力されます。) ※ 公式のREADMEには乗っていないので削除されたりする可能性があると思います。

gobump major -w -v -r .  # セマンテックバージョニングのメジャーバージョンをあげます。
gobump minor -w -v -r .  # セマンテックバージョニングのマイナーバージョンをあげます。
gobump patch -w -v -r .  # セマンテックバージョニングのパッチバージョンをあげます。

また、下記のようなコマンドを叩くことで現在のバージョンを確認することができます。

gobump show .  # JSON形式で現在のバージョンを出力する。
gobump show -r .  # 現在のバージョンのみを出力する。

goxzについて

github.com

クロスビルド + 出力したバイナリとライセンスやREADMEを含んだZIPファイルの生成を行ってくれるツールです。

今回のプロジェクトでは下記のように叩いています。

# dオプション: 出力先のディレクトリ指定(指定をしない場合、`./goxz`に出力されます。)
# nオプション: 出力するバイナリ名を指定(指定をしない場合、ディレクトリ名が使用されます。)
goxz -d ./bin/goxz -n golang-cat .

出力されるZIPファイルには、下記の単語から始まるファイルが自動で含まれるようになっています。

  • LICENSE
  • README
  • INSTALL
  • CREDIT
  • CHANGELOG

他に含めたいファイルがある場合はincludeオプションで指定することができます。

また、上記のオプションの他にosオプションやarchオプションで出力するバイナリのプラットフォームを指定したり、pvオプションで出力されるZIPファイルの名前にバージョン情報を含めたりすることができます。

ghr について

github.com

Git管理されたレポジトリ上で実行をすることで、GitHub上にReleaseの作成と成果物のアップロードをすることができるツールです。
今回はリリースの作成についてはGitHub Actionsに任せて、ファイルのアップロードのみを行うようにしています。

今回は以下のような形で使っています。

# gobumpで取得した現在のバージョンと一致するtagを対象にしたReleaseに`./bin/goxz`以下のファイルをアップロードする。
ghr "v$(make app-version)" ./bin/goxz

ghrを使用する際は、アップロードするために必要な権限を備えたTokenを以下のような形で与える必要があります。

  • GITHUB_TOKENと言う名前の環境変数にセットする。
  • コマンド実行時にtokenオプションとして与える。
  • gitconfigにgithub.tokenという名前で値をセットする。

アップロードする際のユーザ名やレポジトリ名については、基本的に実行したディレクトリの.git/configを見て指定してくれますがusernameオプションやrepositoryオプション等から変更することができます。
またリリースの作成を行う場合、nameオプションを用いてリリース名の設定や、bodyオプションを用いたリリーステキストの設定、draftオプションをつけることでドラフトリリースにしたりと様々な設定を行うことができます。


これまでに紹介した3つのツール全てで[command] -hと叩くことで公式の叩き方やオプションについての説明が出力されるため、使い方等で悩んだ場合は一度叩いてみると良いかもしれません。

GitHub Actionsの設定

GitHub Actionsは以下のようにしました。

# ./.github/workflows/release.yml

name: Make New Release

on:
  push:
    tags:
      - "v*.*.*"

jobs:
  release:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout Source
        uses: actions/checkout@v3

      - name: Setup Go
        uses: actions/setup-go@v3
        with:
          go-version: 1.x

      - name: Cross Build & Archiving
        run: make x-build

      - name: Release
        uses: softprops/action-gh-release@v1
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        with:
          name: Release ${{ github.ref }}
          tag_name: ${{ github.ref }}
          generate_release_notes: true

      - name: Upload Binary
        run: make upload-binary
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

GitHubにv*.*.*というタグがプッシュされたことを条件に

  1. 最新のソースを取得 (Checkout Source )
  2. Go言語の環境設定 (Setup Go )
  3. クロスビルドしてzipファイル化 ( Cross Build & Archiving )
  4. GitHub Releaseの作成 ( Release )
  5. 3で作ったzipファイルをReleaseにアップロード ( Upload Binary )

という手順で処理が走るようにしています。

Releaseを作成する際にはgenerate_release_notes: trueというオプションを付けることで自動で簡単なリリースノートが作成されるようにしています。

実際のリリース作成手順

ツールと設定ファイルをレポジトリにプッシュしたら準備完了です!
今回の構成でのリリース手順は以下のようになっています。

まず、すべての変更がコミットされた状態で以下のようにコマンドを叩き、バージョンをあげます。

make app-version # 現在のバージョンを確認
gobump patch -w -v -r . # 指定した階層のバージョンを書き換えて結果を出力する

コマンドを叩くと、それぞれのコマンドにおいてバージョンが出力されるので正しくバージョンが上がっていることを確認します。 想定と違う結果になってしまった場合は、git checkout .等で元に戻しましょう。

今回はpatchバージョンを上げるコマンドを例にしましたが、majorminorなどでも問題ありません。

次に、gobumpコマンドによって発生したソースコードの変更をコミットした後にタグを打ちます。
今回の設定であればgobumpコマンドの出力するバージョン情報の頭に v をつけるだけで大丈夫です。

git add .
git commit -m "Update: v$(make app-version)"
git tag v$(make app-version)

最後にタグをpushします。

git push origin v$(make app-version)

タグpushをすることでGitHub Actionsが動き始め、バイナリの生成とReleaseの作成・ファイルの公開を行ってくれます。

GitHub Actionsの処理が終了したらReleaseを確認してみましょう。下記のように簡単なリリースノートと環境ごとの実行ファイル・ソースコードが公開されていると思います!

まとめ

Go言語で作成されたツール群とGitHub Actionsを組み合わせることでGitHub上で簡単にReleaseを作成することができました。

今回は、既に知っていたツール群を使ってみたいというモチベーションもありこのような構成にしましたが、 GoReleaserなど他のツール使用した方法も存在するため比較しつつ最適な方法を検討していきたいです!

今回の内容が実装されたレポジトリは以下になります。

github.com

P.S.

採用

虎の穴では一緒に働く仲間を募集中です!
この記事を読んで、興味を持っていただけた方はぜひ弊社の採用情報をご覧下さい。
yumenosora.co.jp

LINEスタンプ

エンジニア専用のメイドちゃんスタンプが完成しました!
「あの場面」で思わず使いたくなるようなスタンプから、日常で役立つスタンプを合計40個用意しました。
エンジニアの皆さん、エンジニアでない方もぜひスタンプを確認してみてください。 store.line.me