こんにちは、先日どういう話の流れだったか、娘が「世の中には色んな人がいるのは良いと言うことを学んだ」と言ってるの聞いて、多様性って大事だけどそんな言い回しをどこで覚えてきたのかと感心した木村です。
さて、今回はDockerコンテナを使うGitHubカスタムアクションの作り方についてお話ししたいと思います。
GitHubカスタムアクションならびにJavaScriptカスタムアクションについては前回の記事で紹介していますので、合わせてご覧ください。
Dockerコンテナカスタムアクションの作成
GitHubカスタムアクションならびにDockerコンテナカスタムアクションの概要については前回のブログで説明していますので、早速作成していきます。
なお、Dockerコンテナを作成するための、docker環境のセットアップについてもここでは割愛します。
リポジトリの作成
以下のコマンドを実行します。
$ mkdir sample-action-docker $ cd sample-action-docker $ git init $ git add . $ git commit -m 'initial commit'
action.yml
の作成
JavaScriptカスタムアクションと同様に、action.yml
を作成します。アクションの動作は前回と同じく、2つの入力値を受け取り、2つの出力値を返すものとします。
name: 'アクションの名前' description: 'アクションの説明' branding: icon: upload-cloud color: blue inputs: id_of_input: # 入力値のID description: '入力値の説明' required: true # 必須かどうか default: 'default value' # デフォルト値 id_of_input2: # 入力値のID description: '入力値の説明' required: true # 必須かどうか default: 'default value 2' # デフォルト値 outputs: id_of_output: # 出力値のID description: '出力値の説明' id_of_output2: # 出力値のID description: '出力値の説明' runs: using: 'docker' image: 'Dockerfile' # 実行するコンテナのDockerfile env: ID_OF_INPUT: ${{ inputs.id_of_input }} # 入力値を環境変数に渡す ID_OF_INPUT2: ${{ inputs.id_of_input2 }} # 入力値を環境変数に渡す
runs
以外の要素はJavaScriptアクションと一緒です。
runs.using
をdocker
とし、runs.image
に実行するコンテナのDockerfileを指定します。そして、入力はaction.yml
の中ではinputs.入力値のID
という形で参照できますので、これをruns.env
で環境変数にマップしてコンテナに渡してあげます。
Dockerfile
の作成
次に、処理の本体となるコンテナのDockerfileを作成します。まずは前回のJavaScriptカスタムアクションと同じく、受け取った2つの値をそのまま2つの出力に引き渡すだけのコンテナを作成してみます。
FROM ubuntu:22.04 COPY entrypoint.sh /entrypoint.sh ENTRYPOINT ["/entrypoint.sh"]
コンテナで実行されるentrypoint.sh
を作成します。JavaScriptカスタムアクションではcore.setFailed()
を使ってエラーを発生させていましたが、Dockerコンテナでは最後にENTRYPOINT
などで実行されるコマンドの終了コードに応じてエラー終了または正常終了が判定されます。今回の場合、必須の入力値が設定されていない場合はexit 1
と0でない戻り値でシェルスクリプトを終了させることでエラーとしています。
そして、出力については${GITHUB_OUTPUT}
環境変数で示されるファイルに
ID_OF_OUTPUT=VALUE_OF_OUTPUT ID_OF_OUTPUT2=VALUE_OF_OUTPUT2
という形で、出力1つにつき1行になるように記載してあげればOKです。
最後に、デバッグのために出力内容を標準出力に出しています。これはワークフローの実行結果の所に表示されます。
#!/bin/bash if [ -z "ID_OF_INPUT" ] then echo "No id_of_input environment variable supplied" exit 1 fi if [ -z "ID_OF_INPUT2" ] then echo "No id_of_input2 environment variable supplied" exit 1 fi echo "id_of_output=${ID_OF_INPUT}" >> "${GITHUB_OUTPUT}" echo "id_of_output2=${ID_OF_INPUT2}" >> "${GITHUB_OUTPUT}" cat "${GITHUB_OUTPUT}"
出力が複数行になる場合は適切にエスケープして1行にすることでも対応可能ですが、以下のようにdelimiterを使ってあげると良いです。
delimiterを使った$GITHUB_OUTPUT
ファイルは以下のような形になります。
ID_OF_OUTPUT<<__DELIMITER1__ VALUE_OF_OUTPUT_LINE1 VALUE_OF_OUTPUT_LINE2 __DELIMITER1__ ID_OF_OUTPUT2<<__DELIMITER2__ VALUE_OF_OUTPUT2_LINE1 VALUE_OF_OUTPUT2_LINE2 __DELIMITER2__
delimiterの値は固定にならないようにランダムにするのがセキュリティ上望ましいとされていますので、シェルスクリプト内では以下のようにすると良いでしょう。
#!/bin/bash if [ -z "ID_OF_INPUT" ] then echo "No id_of_input environment variable supplied" exit 1 fi if [ -z "ID_OF_INPUT2" ] then echo "No id_of_input2 environment variable supplied" exit 1 fi delimiter="$(openssl rand -hex 8)" echo "id_of_output<<${delimiter}" >> "${GITHUB_OUTPUT}" echo "${ID_OF_INPUT}" >> "${GITHUB_OUTPUT}" echo "${delimiter}" >> "${GITHUB_OUTPUT}" delimiter="$(openssl rand -hex 8)" echo "id_of_output2<<${delimiter}" >> "${GITHUB_OUTPUT}" echo "${ID_OF_INPUT}" >> "${GITHUB_OUTPUT}" echo "${delimiter} >> "${GITHUB_OUTPUT}" cat "${GITHUB_OUTPUT}"
entrypoint.sh
を実行可能にします。
$ git add entrypoint.sh $ git update-index --chmod=+x entrypoint.sh
ローカルでのテスト
では、ローカルでテストしてみましょう。ローカルで実行する場合は${GITHUB_OUTPUT}
は設定されていないので適当な値を設定して実行します。
$ docker build -t sample-action-docker:latest ./ $ docker run --rm --name sample-aciton-docker -e ID_OF_INPUT=value_of_input -e ID_OF_INPUT2=value_of_input2 -e GITHUB_OUTPUT=/output.txt sample-action-docker:latest
以下のように出力されれば成功です。
id_of_output=value_of_input id_of_output2=value_of_input2
GitHub Actionsでのテスト
今回はリポジトリのワークフローからの呼び出しテストを省略して、いきなり公開されたアクションとして呼んでみましょう。
リポジトリにコミットしてからハッシュタグを取得します。
$ git add . $ git commit -m '[add] add a container action' $ git show --format='%H' --no-patch
.github/workflows/action.yml
を作成します。xxxxx
の所は、上記で取得したハッシュに置き換えてください。
on: push: jobs: test: runs-on: ubuntu-latest steps: - name: test action id: test uses: your-github-user-name/sample-container-action@xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx with: id_of_input: 'input value 1' id_of_input2: 'input value 2' - name: view result run: | echo ${{ env.id_of_output }} echo ${{ env.id_of_output2 }} env: id_of_output: ${{ steps.test.outputs.id_of_output }} id_of_output2: ${{ steps.test.outputs.id_of_output2 }}
このワークフローファイルをコミットしてGitHubにプッシュして、GitHubでActionsの結果を確認します。
無事に動いて、出力も取得できていますね!
まとめ
今回は、Dockerコンテナカスタムアクション作り方を説明しました。 この程度の内容であればワークフローファイルに直接書いても大差ありませんが、例えば特定のバイナリファイルを実行する必要があったり、使い慣れた言語でアクションを書きたいといった場合にはDockerコンテナカスタムアクションを作ると良いでしょう。
GitHubマーケットプレイスへの公開についてはまた次回のブログで紹介しようと思います。
皆さんのお役に立てば幸いです。