GitHub Actions上での自作Actionsリリース自動化をシンプルにしてみた

ついにGitHub ActionsもGAになりました :tada::confetti_ball:

という訳で、記念に記事でも書きたいと思います。
前に[【GitHub Actions】自作Actionsのリリースを自動化する](https://qiita.com/homines22/items/92ee4efe4317bb5a30ad)を書いたのですが、あれからもっと完結にリリースを自動化できるようになったのでご紹介します。
前記事では面倒なことをしていたので、これからリリース自動化したいなと考えている方は本記事を参考にしてください。

# 使用するActions
リリースの自動化に使用するActionsは[actions/create-release](https://github.com/actions/create-release)です。

## 使用方法
README.mdに使用例が書かれています。

```yaml
jobs:
  build:
    name: Create Release
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@master
      - name: Create Release
        id: create_release
        uses: actions/create-release@v1
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        with:
          tag_name: ${{ github.ref }}
          release_name: Release ${{ github.ref }}
          draft: false
          prerelease: false
```

with以下のパラメーターの名前から推測できると思いますので特に説明しませんが、[action.yml](https://github.com/actions/create-release/blob/master/action.yml)に使用できるパラメーターと説明が書かれているので一読することをお勧めします。
正常にリリースができればhtml_urlを出力できるのでTwitter APIとかでSNSにリリースを自動で投稿することができます。html_urlの取得方法は上記の例だと`steps.create_release.outputs.html_url`になります。

## ソースコード紹介
actions/create-releaseを少し深掘りしてみます。
このActionsは内部的に[@actions/github](https://github.com/actions/toolkit/tree/master/packages/github)を使用しています。さらに@actions/githubは[@octokit/rest](https://www.npmjs.com/package/@octokit/rest)と[@octokit/graphql](https://www.npmjs.com/package/@octokit/graphql)を使用しています。create-releaseではGraphQLのクライアントは使用せず、@octokit/restの[octokit.repos.createRelease](https://octokit.github.io/rest.js/#octokit-routes-repos-create-release)でリリースしています。

actions/create-relaseで実際にリリースをするコアなコードは[こちら](https://github.com/actions/create-release/blob/master/src/create-release.js#L7-L31)になります。一応↓にも書いておきます。

```javascript
 const github = new GitHub(process.env.GITHUB_TOKEN);

 // Get owner and repo from context of payload that triggered the action
 const { owner, repo } = context.repo;

 // Get the inputs from the workflow file: https://github.com/actions/toolkit/tree/master/packages/core#inputsoutputs
 const tagName = core.getInput('tag_name', { required: true });

 // This removes the 'refs/tags' portion of the string, i.e. from 'refs/tags/v1.10.15' to 'v1.10.15'
 const tag = tagName.replace('refs/tags/', '');
 const releaseName = core.getInput('release_name', { required: true }).replace('refs/tags/', '');
 const draft = core.getInput('draft', { required: false }) === 'true';
 const prerelease = core.getInput('prerelease', { required: false }) === 'true';

 // Create a release
 // API Documentation: https://developer.github.com/v3/repos/releases/#create-a-release
 // Octokit Documentation: https://octokit.github.io/rest.js/#octokit-routes-repos-create-release
 const createReleaseResponse = await github.repos.createRelease({
    owner,
    repo,
    tag_name: tag,
    name: releaseName,
    draft,
    prerelease
 });
```

@octokit/restを@actions/githubでラップしているので`github.repos.createRelease`となっていますが、`octokit.repos.createRelease`とほぼ同等です。ちなみに`octokit.repos.createRelease`では`body`というパラメーターがあってリリースの説明を記述できますが、`github.repos.createRelease`ではそのパラメーターが実装されていません。2019/11/14時点で[プルリク](https://github.com/actions/create-release/pull/11)が上がっていたので、そのうちマージされると思います。なるはやでして欲しいところです :pray:

リリース名がタグと同じになるようにreplaceされているので、release_nameには`${{ github.ref }}`を渡しておけば大丈夫です。

# リリース自動化
それでは本題の自動リリースのシンプル化です。最初にworkflowのYAMLを載せておきます。

```yaml
name: Prepare for release

on:
  push:
    tags:
      - 'v*'

jobs:
  release:
    name: Build production
    runs-on: ubuntu-18.04
    steps:
      - uses: actions/checkout@v1

      - uses: actions/setup-node@v1
        with:
          node-version: '10.x'

      - name: Create Release
        uses: actions/create-release@v1.0.0
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        with:
          tag_name: ${{ github.ref }}
          release_name: ${{ github.ref }}
          draft: 'false'
          prerelease: 'false'

      - name: Slack Notification
        uses: homoluctus/slatify@master
        if: always()
        with:
          type: ${{ job.status }}
          job_name: ':rocket: *Publish new release ${{ github.ref }}*'
          channel: '#develop'
          url: ${{ secrets.SLACK_WEBHOOK }}
```

## 各ブロックの説明

### トリガー

```yaml
on:
  push:
    tags:
      - 'v*'
```

プレフィックスが`v`のタグがプッシュされるとworkflowがスタートします。`*`は一般的な正規表現で使われる意味と同じです。なので、リリースしたいときは`v1.0`みたいな感じでタグ付けしてプッシュすればOKです。

## リリース

```yaml
      - name: Create Release
        uses: actions/create-release@v1.0.0
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        with:
          tag_name: ${{ github.ref }}
          release_name: ${{ github.ref }}
          draft: 'false'
          prerelease: 'false'
```

`tag_name`と`release_name`には`${{ github.ref }}`を指定しています。`${{ github.ref }}`の実体は`ref/tags/v1.0`みたいになっています。actions/create-releaseのソースコードの説明でreplaceしていると言った通り`ref/tags`は取り除かれるので、tag_nameとrelease_nameはv1.0になります。もちろ他の文言にすることもできます。そこは自由です。

# おわりに
冒頭でも言いましたが、ついにGitHub ActionsもGAになりました :tada::confetti_ball:
GitHub Actionsは他のCI/CDにも劣らずかなり優秀です。
これからコミュニティが発展していき、開発者や運用者たちがもっと楽になればいいなと思っています。