Undefined Title

actがtask runnerとしていい感じ

2019-06-15

GitHub Actions、Sign upしましたか? 私は開通してちょっと試して、ああまだTravisCIやCircleCIのようなCIサービスの代わりにはならないな、 とちょっと残念に思いしばらく触ってませんでした。

で、ふとblogのdeployくらいには使えるよなと思いたち改めて調べてたら actというローカルでGitHub Actionsを実行するコマンドを見つけ、これがなかなかいいのです。 GitHub Actionsをローカルで実行できるのも便利ですが、普通にタスクランナーとしてもいい感じです。

例えばblogをAWS S3 web hostingするとします。 タスクを分けるとしたら2つのタスク、build -> deployとなるでしょう。 実際にWorkflow定義して行きましょう。

Workflowの定義

Workflowを定義してみます。 GitHub ActionsのWorkflow editorで見るとこんな感じです。

workflow

タスクは2つだって言ったのに4タスクあるように見えますが、最初のグレーのノードは初期ノード、 2つ目のMastermasterブランチのpush以外は以降の実行をskipするためのタスクです。 なので実際はBuildDeployの2つのタスクと考えてください。

.github/main.workflowはこうなります。

workflow "Deploy blog" {
  on = "push"
  resolves = ["Deploy"]
}

action "Master" {
  uses = "actions/bin/filter@master"
  args = "branch master"
}

action "Build" {
  needs = ["Master"]
  uses = "./.github/build"
}

action "Deploy" {
  needs = ["Build"]
  uses = "./.github/deploy"
  env = {
    AWS_DEFAULT_REGION = "ap-northeast-1"
  }
  secrets = ["AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY"]
  args = "s3://foobar-my-blog-bucket"
}

ここでact -lを実行すると以下のように表示が得られ、依存関係を見ることができます。

act-workflow

Build Actionの定義

今までタスクタスク書いてきましたが、GitHub ActionsではActionと言うのでそれにならいます。

Build Actionを実装します。静的サイト生成にはhugoを使うことにします。

.github/build/Dockerfileはこんな感じ。

FROM python:3.6

# See https://feathericons.com/ for icon
LABEL "com.github.actions.name"="Build"
LABEL "com.github.actions.description"="Build"
LABEL "com.github.actions.icon"="square"
LABEL "com.github.actions.color"="purple"

WORKDIR /
RUN curl -OL https://github.com/gohugoio/hugo/releases/download/v0.55.6/hugo_0.55.6_Linux-64bit.tar.gz; \
    tar xfz hugo_0.55.6_Linux-64bit.tar.gz; \
    chmod +x hugo

COPY entrypoint.sh /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]

python:3.6を使ってるのは次のDeploy Actionで使ってるからです。 /hugoにバイナリを置いてます。

.github/build/entrypoint.shはシンプルです。/hugoを実行。

#!/usr/bin/env bash
/hugo

これくらいならCMD使ってDockerfileに書いてしまってもいいんですが、なんとなくDockerfileでは実行環境を整える、 entrypoint.shで実際の処理、とするのがいい気がしているのでそうしてます。

Build Actionの実行

ここまででこうなってます。

.github/
  |-- main.workflow
  |-- build/
  |     |-- Dockerfile
  |     `-- entrypoint.sh

.github/があるディレクトリでact -a Buildを実行してみます。

masterブランチのチェックをしてファイルを生成するログが表示されます。

$ act -a Build
INFO[0000] using github ref: refs/heads/master
INFO[0000] using github ref: refs/heads/master
[Master] git clone 'https://github.com/actions/bin' # ref=master
[Master] docker build -t bin:master /var/folders/qc/_9vx0zd114383ghrcdtt8ysr0000gn/T/act/actions/bin/filter@master/filter
[Master] docker run image=bin:master entrypoint=[] cmd=["branch" "master"]
refs/heads/master matches refs/heads/master
[Build] docker build -t build:579e412 /my/path/to/.github/build
[Build] docker run image=build:579e412 entrypoint=[] cmd=[]
...
                   | EN
+------------------+----+
  Pages            |  4
  Paginator pages  |  0
  Non-page files   |  0
  Static files     |  0
  Processed images |  0
  Aliases          |  0
  Sitemaps         |  1
  Cleaned          |  0

Total in 52 ms

Hugoは./publicを作成します、GitHub Actionsではrepositoryのdirectoryは${GITHUB_WORKSPACE}(default: /github/workspace)に展開されますが、 ローカルではカレントディレクトリです。カレントディレクトリにpublicが生成されているはずなので確認してください。

Deploy Actionの実装

次にDeploy Actionを実装します。(AWS CLIをDocker環境で実行するを参考にさせて頂きました。)

FROM python:3.6

# See https://feathericons.com/ for icon
LABEL "com.github.actions.name"="Deploy"
LABEL "com.github.actions.description"="Deploy to the s3 bucket"
LABEL "com.github.actions.icon"="upload"
LABEL "com.github.actions.color"="purple"

#
ARG pip_installer="https://bootstrap.pypa.io/get-pip.py"
ARG awscli_version="1.16.170"
RUN pip install awscli==${awscli_version}
ENV PATH $PATH:/root/.local/bin
COPY entrypoint.sh /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]
#!/usr/bin/env bash
aws s3 sync \
  --no-follow-symlinks --delete \
  public "$1"

Dockerfileで環境を作ってentrypoint.shで実際の処理です。

ここでもう一度Deploy Actionを振り返ります。

action "Deploy" {
  needs = ["Build"]
  uses = "./.github/deploy"
  env = {
    AWS_DEFAULT_REGION = "ap-northeast-1"
  }
  secrets = ["AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY"]
  args = "s3://foobar-my-blog-bucket"
}

だいたい想像はつくと思いますが、entrypoint.shで環境変数AWS_DEFAULT_REGION, AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEYが使えます。 envはworkflow内で定義、secretsはGitHubのvisual editorで設定します。 argsはentrypoint.shへの引数です。S3 bucket名を渡してます。

action-secrets

actでローカル実行してみます。

$ act -a Deploy
INFO[0000] using github ref: refs/heads/master
INFO[0000] using github ref: refs/heads/master
Provide value for 'AWS_ACCESS_KEY_ID':
...

ローカルで環境変数が設定されていない場合は入力を求められます。 とりあえずenterを押して空で進めます。依存関係が設定されているBuild Actionが先に走ります。

...
Total in 525 ms
[Deploy] docker build -t deploy:579e412 /my/path/to/.github/deploy
[Deploy] docker run image=deploy:579e412 entrypoint=[] cmd=["s3://foobar-my-blog-bucket"]
fatal error: Unable to locate credentials
Error: exit with `FAILURE`: 1

Access keyが設定されていないので期待通り失敗します。 ちゃんと権限があるkeyが設定されていればaws s3 syncが成功してuploadするはずです。

まとめ