1
/
5

GitHub Packagesへのアップロードが409 CONFLICTエラーになる対応策

Photo by Charl Folscher on Unsplash

Wantedlyのモバイルエンジニアの久保出です。

今回は、GitHub Packagesへのアップロードが409 CONFLICTエラーになる問題の対応策について書きます。2022年7月の内容であることにご注意ください。

経緯

我々は、モバイルアプリのビジネスロジック共通化の目的で、Kotlin Multiplatform Mobile (KMM) を利用しています。KMMの経緯については、ぜひこちらの記事を参照ください。

KMMとiOS/Androidのリポジトリはそれぞれ分かれています。KMMをiOS/Androidで利用可能にするために、KMMのパッケージを生成し、各OSのパッケージ管理システムを利用して配布しています。iOSにはCocoaPods、AndroidにはMavenを利用しています。

CIで、このようなKMMの配信ワークフローを構築してあります。

しかし、その構築後、たまに(最近は頻繁に)Android側の配信タスクがエラーになるようになりました。エラー内容は以下です。

* What went wrong:
Execution failed for task ':api:entity:annotation:publishAndroidDebugPublicationToMavenRepository'.
> Failed to publish publication 'androidDebug' to repository 'maven'
> Could not PUT 'https://maven.pkg.github.com/wantedly/visit-app-shared/com/wantedly/visit/app/shared/annotation-android-debug/0.0.2/annotation-android-debug-0.0.2.aar'. Received status code 409 from server: Conflict

Mavenのリポジトリは、GitHub Packagesを利用しています。GitHub Packagesへのアップロード中に、409 CONFLICTエラーが発生、配信タスクも失敗し、CIも失敗となっていました。

原因

原因を調べると、次のForumの投稿に行き着きました。

409 from server: Conflict occurrs when publishing to GitHub Packages
I am facing following error intermittently when publishing to GitHub packages inside out GitHub actions workflows. * What went wrong: Execution failed for task ':module-ballerina-openapi:publishMavenJavaPublicationToGitHubPackagesRepository'. > Failed to
https://github.community/t/409-from-server-conflict-occurrs-when-publishing-to-github-packages/172806

どうやらGitHub側の問題であるようで、我々には根本的な対処が不可能でした。GitHubは問題を認知しているようですが、対応には時間がかかるとのことです。

問題点

Mavenは1つのパッケージの1つのバージョンにつき、複数のファイルをアップロードします。この問題が発生すると、本来アップロードされるべき複数ファイルのうち、一部のみがアップロードされた状態になります。そのため、単純にCIのリトライを行っても、アップロード済みのファイルが存在しているため、409エラーが必ず発生します。

この問題が発生すると、Mavenの状態が不完全であるため、Androidでは問題の起きたバージョンが利用できません。このため、問題が発生するたびに、Gitのタグを切り直すといった面倒なことを行って対処していました。

感覚ですが、2022年4月くらいからこの問題の頻度が増え、生産性への影響が増えたため、次のような暫定的な対処を行いました。

解決策

とった解決策は、CI側でリトライを行い、リトライのたびに不完全な状態のバージョンを削除するという方法です。

GitHubは、GitHub Packagesの特定のバージョンを削除するAPIを提供しています。そのAPIを使い、不完全な状態のバージョンを削除するスクリプトを組みました。以下はその内容です。

#!/usr/bin/env bash
# GitHub Packages frequently throws 409 errors, resulting in incomplete uploaded versions.
# This is a script to remove it.
# https://github.community/t/409-from-server-conflict-occurrs-when-publishing-to-github-packages/172806/17

set -ex

echo 'If you encounter some permission error, run `$ gh auth login -s read:packages,delete:packages`'

target_version=$1

if [ -z "$target_version" ]; then
echo "Please specify a version argument you want to delete."
exit 1
fi

echo "Trying to delete the packages that version is $target_version."

packages=$(gh api graphql -f query=' query { repository(owner: "wantedly", name: "visit-app-shared") { packages(first: 100) { nodes { name versions(first: 10) { nodes { id version } } } } } } '
)

# Extract the version IDs matching with the target version.
ids=$(echo $packages | jq -r ".data.repository.packages.nodes[].versions[] | flatten[] | select(.version == \"$target_version\") | .id")

i=0
for id in $ids; do
i=$((i+1))
# Using Accept header because it's a preview feature.
# https://docs.github.com/en/graphql/overview/schema-previews#access-to-package-version-deletion-preview
gh api graphql -H 'Accept: application/vnd.github.package-deletes-preview+json' -f query="
mutation {
deletePackageVersion(input: {clientMutationId: \"$i\", packageVersionId: \"$id\"}) { success }
}
"
done

このスクリプトは、GitHubのGraphQL APIを利用しています。(GraphQL APIは、Explorerから手軽に試せるので、試してみるといいでしょう)
まず、GraphQL APIを使い、対象リポジトリ(wantedly/visit-app-sharedがKMMリポジトリです)の全てのパッケージと、パッケージの最新バージョン10件を取得します。
そのレスポンスから、削除対象になるIDを抽出、パッケージを削除するAPI(deletePackageVersion)につなげています。パッケージを削除するAPIは、まだpreview段階のAPIなため、特殊なヘッダーが必要です。

パッケージの取得と削除には、read:packagesdelete:packagesのスコープ(権限)が必要です。CIで使っているGitHub認証トークンにスコープを追加する必要があります。

また、このスクリプトは、ghコマンドとjqコマンドを利用しています。CI上でこのスクリプトの実行前にインストールしておく必要があります。

あとは、以下のようなスクリプトをCIに組み込むだけです。

chmod u+x ./scripts/delete-android-packages.sh
COUNT=0
until ./gradlew publish -Pversion=$RELEASE_VERSION || [ $COUNT -eq 4 ]; do
./scripts/delete-android-packages.sh $RELEASE_VERSION
COUNT=$((COUNT+1))
done

まとめ

GitHub Packagesの409 CONFLICTエラーへの対処法を紹介しました。

最初は対処不可能な問題かと思いましたが、APIを駆使することで割と簡単に対処できました。一番苦労したのは、普段書かないシェルスクリプトを書いたことです。

GitHubにより根本的に解決されることを望みます。

このストーリーが気になったら、遊びに来てみませんか?
今見ているAndroidアプリの体験をともに高めていきませんか?
Wantedly, Inc.では一緒に働く仲間を募集しています
3 いいね!
3 いいね!

今週のランキング

久保出 雅俊さんにいいねを伝えよう
久保出 雅俊さんや会社があなたに興味を持つかも