Help us understand the problem. What is going on with this article?

【GitHub Actions】Go言語の自動テストからリリースまでを作ってみた

はじめに

今回作成したのはGo言語リポジトリ用ワークフロー。
GitHubに指定されたタグがPushされた時点でテストを実行し問題が無ければ、クロスコンパイルを実施しリリースまで行う。
以下にアップロードしているので利用したい方はこちらからどうぞ。

ワークフローの流れは以下となっている。

  1. タグがPushされたことをトリガーにワークフローが起動
  2. 依存したモジュールをダウンロード
  3. テストを実施
  4. テストに合格した場合、リリースプロセスが始動
  5. ソースコードをzipにまとめリリース
  6. クロスコンパイルを実施し、それぞれ生成物をzipにまとめてリリース

以下が実際のworkflowファイル

name: Release Go Project
on:
  push:
    tags:
      - v*

jobs:
  test:
    name: Test
    runs-on: ubuntu-latest
    steps:
    - name: Set up Go 1.13
      uses: actions/setup-go@v1
      with:
        go-version: 1.13

    - name: Check out code into the Go module directory
      uses: actions/checkout@v1

    - name: Get dependencies
      run: go get -v -t -d ./...

    - name: Test code
      run: go test -v .

  setup-release:
    name: Setup release
    needs: test # This workflow is executed after completed 'test' job
    runs-on: ubuntu-latest
    steps:
    - name: Create release
      id: create_release
      uses: actions/create-release@v1.0.0
      env:
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
      with:
        tag_name: ${{ github.ref }}
        release_name: Release ${{ github.ref }}
        draft: false
        prerelease: false

    - name: Get url to upload to release from output
      env:
        url: ${{ steps.create_release.outputs.upload_url }}
      run: |
        mkdir artifact
        echo $url > artifact/url.txt
    - name: Upload artifact to share url with other jobs
      uses: actions/upload-artifact@v1
      with:
        name: artifact
        path: artifact/url.txt

  release-code:
    name: Release Source Code
    needs: setup-release
    runs-on: ubuntu-latest
    steps:
    - name: Download artifact to get url to upload to release
      uses: actions/download-artifact@v1
      with:
        name: artifact

    - name: Get url to upload to release from artifact
      id: get_url
      run: |
        url=$(cat artifact/url.txt)
        echo "##[set-output name=upload_url;]$url"
    - name: Check out code into the Go module directory
      uses: actions/checkout@v1

    - name: Pack source code in zip file
      run: |
        mkdir dist
        ls -la
        cp *.go dist/
        zip -j -r release dist
    - name: Upload release asset
      uses: actions/upload-release-asset@v1.0.1
      env:
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
      with:
        upload_url: ${{ steps.get_url.outputs.upload_url }}
        asset_path: release.zip
        asset_name: source.zip
        asset_content_type: application/zip

  release-pkg:
    name: Release package
    needs: setup-release
    runs-on: ubuntu-latest
    strategy:
      matrix:
        os: [mac64, lin64, win64]
        include:
        - os: mac64
          goos: darwin
          arch: amd64
        - os: lin64
          goos: linux
          arch: amd64
        - os: win64
          goos: windows
          arch: amd64

    steps:
    - name: Set up Go 1.13
      uses: actions/setup-go@v1
      with:
        go-version: 1.13

    - name: Check out code into the Go module directory
      uses: actions/checkout@v1

    - name: Get dependencies
      run: go get -v -t -d ./...

    - name: Build
      env:
        goos: ${{ matrix.goos }}
        goarch: ${{ matrix.arch }}
      run: |
        mkdir dist
        cp test.txt dist/
        GOOS=$goos GOARCH=$goarch go build -v -o dist/app .
        zip -j -r release dist
    - name: Download artifact to get url to upload to release
      uses: actions/download-artifact@v1
      with:
        name: artifact

    - name: Get url to upload to release from artifact
      id: get_url
      run: |
        url=$(cat artifact/url.txt)
        echo "##[set-output name=upload_url;]$url"
    - name: Upload release asset
      uses: actions/upload-release-asset@v1.0.1
      env:
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
      with:
        upload_url: ${{ steps.get_url.outputs.upload_url }}
        asset_path: release.zip
        asset_name: release-${{ matrix.os }}.zip
        asset_content_type: application/zip

各種処理の概要

各種処理は、ジョブとステップの二通りで定義することができる。
今回は、テストや各種リリースの処理はそれぞれジョブとして分離し定義する形で設計している。
これは、クロスコンパイルを実施する際にGitHub Actionsの機能(matrix)を有効活用して実施したいと考えたためである。
以降、各処理の概要を説明していく。

ワークフローのトリガー

以下は、このワークフローが実行される条件を指定している。
この場合は、「v」から始まるタグがpushされた場合にトリガーされ実行されることが定義されている。

on:
  push:
    tags:
      - v*

実行条件は、ほかにも特定のブランチにpushされたや、特定のファイルやディレクトリ下に変更があった場合などを指定することができる。
もちろんpushのみではなく、forkされた場合やissueが立った際なども可能。詳しくは以下のリンクに載っている。

自動テストの実行

以下は、自動テストを実行するジョブが定義されている。

jobs:
  test:
    name: Test
    runs-on: ubuntu-latest
    steps:
    - name: Set up Go 1.13
      uses: actions/setup-go@v1
      with:
        go-version: 1.13

    - name: Check out code into the Go module directory
      uses: actions/checkout@v1

    - name: Get dependencies
      run: go get -v -t -d ./...

    - name: Test code
      run: go test -v .

各stepにそれぞれ処理が定義されており、上から順に以下の流れとなっている。

  1. Go1.13の環境をコンテナ内に作成
  2. リポジトリの中に入り、状態をトリガーされたコミットにする
  3. go getを用いてgo.modに記載されている依存関係のあるモジュールをインストール
  4. go testを用いて、テストを実行

この中で最も重要なのがactions/checkout@v1

これを実行することにより、リポジトリの中に入ることができる。
そのため、実行しないでgo get しても必要なモジュールをインストールしてくれない。

もし途中のステップで実行に失敗した場合、デフォルトだとそこで処理は止まり、ジョブは失敗扱いとなる。
例として、go getに失敗した場合、このテスト用ジョブはそこで中断されgo testが実行されることはない。

リリースの作成

以下は、リリースの作成を定義している。
実際の作成は、actions/create-release@v1.0.0に行ってもらっている。

  setup-release:
    name: Setup release
    needs: test # 'test' ジョブが成功した後に実行する 
    runs-on: ubuntu-latest
    steps:
    - name: Create release
      id: create_release
      uses: actions/create-release@v1.0.0
      env:
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
      with:
        tag_name: ${{ github.ref }}
        release_name: Release ${{ github.ref }}
        draft: false
        prerelease: false

    - name: Get url to upload to release from output
      env:
        url: ${{ steps.create_release.outputs.upload_url }}
      run: |
        mkdir artifact
        echo $url > artifact/url.txt
    - name: Upload artifact to share url with other jobs
      uses: actions/upload-artifact@v1
      with:
        name: artifact
        path: artifact/url.txt

ジョブは基本的にすべて並列に実行される。
しかし、バグなどの致命的欠陥を持ったままリリースはしたくないため、リリース処理などはテストの実施後に行いたいものである。
そのために、このジョブではneeds: testtestジョブが実行完了しないと実行されないように制限を入れている。

これにより、ジョブの逐次実行が可能となる。ジョブに依存関係を持たせたい場合はneedsを用いるべき。もちろん依存関係は一つのみでなく複数のジョブを指定することができる。詳しくは、以下のリンク参照。

このジョブの後半のステップは、作成されたリリースにパッケージをアップロードする際に必要となるURLを次のジョブに渡すために必要となる。
やっていることは${{ steps.create_release.outputs.upload_url }}で直前のリリース作成時に出力されたアップロード先URLを取得し、それをファイルに書き込み、actions/upload-artifact@v1でワークフローのアーティファクトにアップロードしている。
これだけではジョブ間の情報共有はできないのだが、共有先のジョブでactions/download-artifact@v1を実行することでアーティファクト経由で情報を共有することができる。ジョブ間の情報共有については以下のリンクを参照。

パッケージのアップロード

以下は、先ほど作成されたリリースにパッケージをアップロードする処理が定義されている。

  release-pkg:
    name: Release package
    needs: setup-release
    runs-on: ubuntu-latest
    strategy:
      matrix:
        os: [mac64, lin64, win64]
        include:
        - os: mac64
          goos: darwin
          arch: amd64
        - os: lin64
          goos: linux
          arch: amd64
        - os: win64
          goos: windows
          arch: amd64

    steps:
    #
    # 'test' ジョブと処理がほぼ一緒なので一部割愛
    #
    - - name: Build
      env:
        goos: ${{ matrix.goos }}
        goarch: ${{ matrix.arch }}
      run: |
        mkdir dist
        cp test.txt dist/
        GOOS=$goos GOARCH=$goarch go build -v -o dist/app .
        zip -j -r release dist
    - name: Download artifact to get url to upload to release
      uses: actions/download-artifact@v1
      with:
        name: artifact

    - name: Get url to upload to release from artifact
      id: get_url
      run: |
        url=$(cat artifact/url.txt)
        echo "##[set-output name=upload_url;]$url"
    - name: Upload release asset
      uses: actions/upload-release-asset@v1.0.1
      env:
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
      with:
        upload_url: ${{ steps.get_url.outputs.upload_url }}
        asset_path: release.zip
        asset_name: release-${{ matrix.os }}.zip
        asset_content_type: application/zip

このジョブは実行の定義自体は一種類しかないが、matrixを用いることによりパラメータを変化させて同時に3つのジョブを並列に実行するように定義されている。
matrixには対象OSごとの実行ファイルを作るために必要なパラメータを設定している。これによりジョブは、ビルド時に必要な情報を取得し、指定したOS用の実行ファイルを並列に生成かつリリースにアップロードすることができる。この仕組みを用いて、クロスコンパイルを実施している。

リリースへのアップロードにはactions/upload-release-asset@v1.0.1を用いている。これはアップロード先のURLが必要となるので、新規にリリースをアップロードしたい場合は先ほど利用したactions/create-release@v1.0.0と併用する必要がある。詳しくは以下のリンクを参照。

おわりに

GitHub Actionsが正式リリースされたとのことでいろいろ触ってみたが、結構いろいろ自動化できそうで楽しい。
今回は、触っていた際にたまった知見を活かして、Go言語のリリース用ワークフローを作成してみたが、まだやりたいことをシンプルに実現するにはあと一歩って感じがした。
実現できなくはないが、ちょっとめんどくさいところがまだある(特にジョブ間とワークフロー間の情報共有)ので、今後さらにアップデートを重ねてよりよくなっていくことに期待。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした