GitHub Actionsのアーティファクトは上書きできる
同一のアーティファクトに複数回上書きできる
タイトルのとおりです。これはactions/upload-artifactのREADMEにも書かれているのですが、自分は全然知りませんでした。
今回はこの上書きの挙動をいろいろ確認してみようと思います。
同一ジョブ内で上書きする
まずは同一ジョブ内で同一アーティファクトを複数回上書きしてみます。以下はactions/upload-artifactのREADMEにある例にアーティファクトの中身を確認するジョブを付け足したものです。
ちなみに、actions/upload-artifactではname
パラメータを省略するとartifact
という名前にフォールバックされますが、actions/download-artifactでname
パラメータを省略すると全てのアーティファクトをダウンロードするという意味になります。注意しましょう。
name: Example workflow
on: push
jobs:
overwrite-artifact:
runs-on: ubuntu-latest
steps:
- run: echo hi > world.txt
- uses: actions/upload-artifact@v3
with:
path: world.txt
- run: echo howdy > extra-file.txt
- uses: actions/upload-artifact@v3
with:
path: extra-file.txt
- run: echo hello > world.txt
- uses: actions/upload-artifact@v3
with:
path: world.txt
check-artifact:
needs: overwrite-artifact
runs-on: ubuntu-latest
steps:
- uses: actions/download-artifact@v3
with:
name: artifact
- run: |
echo "$ ls"
ls
echo "$ cat world.txt"
cat world.txt
echo "$ cat extra-file.txt"
cat extra-file.txt
check-artifact
ジョブの最後のステップの実行結果は以下のようになります。
$ ls
extra-file.txt
world.txt
$ cat world.txt
hello
$ cat extra-file.txt
howdy
ご覧の通り、別々のステップでアップロードしたextra-file.txt
, world.txt
が両方とも入っていて、world.txt
は後からアップロードされた内容で上書きされているのがわかります。
複数ジョブから上書きする
では、複数ジョブから上書きするとどうなるでしょうか。READMEには以下のように書かれています。
Warning: Be careful when uploading to the same artifact via multiple jobs as artifacts may become corrupted. When uploading a file with an identical name and path in multiple jobs, uploads may fail with 503 errors due to conflicting uploads happening at the same time. Ensure uploads to identical locations to not interfere with each other.
要約すると、複数のジョブから同名ファイルを上書きすると競合が発生し、503エラーでアップロードが失敗する可能性があるので注意せよということのようです。
逆に言うと、同名のファイルを書き込まないよう注意すれば複数ジョブからアーティファクトを上書きできるということに見えます。やってみましょう。
以下のようなワークフローを用意しました。
name: Example workflow
on: push
jobs:
job-a:
runs-on: ubuntu-latest
steps:
- run: echo hello > world.txt
- uses: actions/upload-artifact@v3
with:
path: world.txt
job-b:
runs-on: ubuntu-latest
steps:
- run: echo howdy > extra-file.txt
- uses: actions/upload-artifact@v3
with:
path: extra-file.txt
check-artifact:
needs:
- job-a
- job-b
runs-on: ubuntu-latest
steps:
- uses: actions/download-artifact@v3
with:
name: artifact
- run: |
echo "$ ls"
ls
echo "$ cat world.txt"
cat world.txt
echo "$ cat extra-file.txt"
cat extra-file.txt
実行結果は以下のようになります。
$ ls
extra-file.txt
world.txt
$ cat world.txt
hello
$ cat extra-file.txt
howdy
マトリクスジョブから上書きする
なお、マトリクスジョブでも同様に上書きできます。
name: Example workflow
on: push
jobs:
overwrite-artifact:
runs-on: ubuntu-latest
strategy:
matrix:
name: [a, b]
steps:
- run: echo ${{ matrix.name }} > ${{ matrix.name }}.txt
- uses: actions/upload-artifact@v3
with:
path: ${{ matrix.name }}.txt
check-artifact:
needs: overwrite-artifact
runs-on: ubuntu-latest
steps:
- uses: actions/download-artifact@v3
with:
name: artifact
- run: |
echo "$ ls"
ls
結果は以下のとおりです。
$ ls
a.txt
b.txt
おまけ: ディレクトリ構成に注意
さて、アーティファクトを上書きできるとなると、例えば以下のようなことをやりたくなるのではないでしょうか。
name: Example workflow
on: push
jobs:
output-node-version:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [14, 16, 18]
steps:
- uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
- run: |
mkdir node-${{ matrix.node-version }}
node -v > node-${{ matrix.node-version }}/version.txt
- uses: actions/upload-artifact@v3
with:
path: node-${{ matrix.node-version }}/version.txt
check-artifact:
needs: output-node-version
runs-on: ubuntu-latest
steps:
- uses: actions/download-artifact@v3
with:
name: artifact
- run: |
echo "$ ls"
ls
このワークフローはnode-14/version.txt
, node-16/version.txt
, node-18/version.txt
という3つのファイルがアーティファクトに保存されることを期待しています。
しかし、実際には以下のとおりversion.txt
という一つのファイルしか保存されていません。(あるいはタイミング次第で503エラーが発生する可能性もあります。)
$ ls
version.txt
これはactions/upload-artifactが指定された全ファイルの最も深い共通の親ディレクトリをルートとしてファイルをアップロードするからです。
これを回避するには、以下のようにルートとしたいディレクトリにダミーファイルを置く方法があります。
name: Example workflow
on: push
jobs:
output-node-version:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [14, 16, 18]
steps:
- uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
- run: |
mkdir node-${{ matrix.node-version }}
node -v > node-${{ matrix.node-version }}/version.txt
touch .dummy-node-${{ matrix.node-version }}
- uses: actions/upload-artifact@v3
with:
path: |
node-${{ matrix.node-version }}/version.txt
.dummy-node-${{ matrix.node-version }}
check-artifact:
needs: output-node-version
runs-on: ubuntu-latest
steps:
- uses: actions/download-artifact@v3
with:
name: artifact
- run: |
echo "$ ls -a"
ls -a
結果は以下のとおりです。
$ ls -a
.
..
.dummy-node-14
.dummy-node-16
.dummy-node-18
node-14
node-16
node-18
正直、あまり綺麗なやり方ではないですね。
以下のissueでroot-directory
パラメータを使って任意のディレクトリをルートとして指定できる機能の要望が出ているので、素直にそちらが使えるようになってくれると嬉しいです。
まとめ
actions/upload-artifactでアーティファクトを上書きする際の挙動をいろいろと確認してみました。
あまり多用すると思わぬ不具合を引き起こしそうな機能ではありますが、大量のアーティファクトが作られるようなワークフローではアーティファクト一覧を整理するためにこの機能を使っていくつかのアーティファクトをまとめてみると便利かもしれません。
また、現在はactions/download-artifactで単一 or 全ての2パターンしかダウンロード対象のアーティファクトを指定する方法が無いため、後続のジョブで使うアーティファクトを一つにまとめておくとactions/download-artifactを複数ステップ呼ばなくて済むので便利です。
ただし、こちらも複数アーティファクトを指定できるようにする機能要望が出ているので、早くそちらが実装されてほしいですね。
Discussion