Google Drive へ gdrive でファイルアップロード
CLI でアップロードしたくなった。
サービスアカウントを使えるもので探した。
定番らしい gdrive は対応していいたので利用してみる。
インストール
$ curl -sL https://github.com/prasmussen/gdrive/releases/download/2.1.1/gdrive_2.1.1_linux_386.tar.gz | tar -zxvf -
amd64 は動かなかった。
GCP と Google Drive 側の設定
Drive API の有効化
$ gcloud services enable drive.googleapis.com --project="${PROJECT_ID}"
サービスアカウントを用意(手順は省略)。
Google Drive にテスト用のフォルダーを作成し共有設定で編集者にサービスアカウントを追加。
アップロード
サービスアカウントの鍵を作成し以下のようにする。
$ ./gdrive --service-account gha-creds-test-temp.json -c . upload --parent "<フォルダーの ID>" test.txt
-
--service-account
- サービスアカウントの鍵ファイル -
-c
-gdrive
の設定ファイルのパス(サービスアカウントの鍵ファイルのパス名を特定するために必要) -
--parent
- 共有した フォルダーの ID
同じものをアップロードした場合、別の ID が割り振られる(ドライブ上でも別のファイルになる)。
引っかかったところ
ためしに共有設定なしで実行してみた場合。
--parent
なしだとアップロードが成功する。id
も割り振られるがファイルが見当たらない。ファイルはどこに消えた?
--parent
をつけると Failed to get file: googleapi: Error 404: File not found: "フォルダーの ID"
となる。「ID の指定の仕方違うのかな?」と少し悩んだが共有したらアップロードできたので、そういうものらしい。
サービスアカウントとドライブ
前のコメントでどこかにアップロードされたファイルは $ gdrive info
で ViewURL がわかった。
ブラウザーで開いてみると「アクセス権が必要です」と言われる。
オーナー(?)がサービスアカウントのファイルが通常ユーザーの管理していないフォルダー(?)にアップロードされたのでこうなる(のかな?)
ならばと、ファイルが保存されているフォルダー(parent)を info
で確認すると name と path がMy Drive
になっている。
サービスアカウントにもドライブが付与されている?
$ gdrive mkdir
して一般ユーザーと共有したあとに $ gdrive upload
したら参照できた。
$ gdrive about
で確認すると別ドライブのようなのだが、少し調べた感じでははっきりした情報が出てこない。
一般ユーザーのフォルダーを共有する予定なので、この辺はとりあえず保留。
アップロード(既存ファイルは上書き)
アップロード先フォルダーに同名ファイルがあれば上書きしたかった(同一 ID のファイルにしたかった)のでとりあえずのスクリプト作成。
同名ファイルが複数あった場合は最初にヒットしたもが上書きされる。
2022-03-23 スクリプト修正
#!/bin/bash
set -e
SA="$(basename "gha-creds-test-temp.json")"
GDRIVE="./gdrive"
FILE_ID="$("${GDRIVE}" --service-account "${SA}" -c . \
list -q "'""${PARENT_ID}""' in parents" | \
sed "1d" | grep "${DEST_FILE_NAME}" | head -n 1 | cut -d" " -f1)"
if test -z "${FILE_ID}" ; then
"${GDRIVE}" --service-account "${SA}" -c . \
upload --no-progress --parent "${PARENT_ID}" --name "${DEST_FILE_NAME}" "${SRC_FILE_NAME}"
else
"${GDRIVE}" --service-account "${SA}" -c . \
update --no-progress "${FILE_ID}" --name "${DEST_FILE_NAME}" "${SRC_FILE_NAME}"
fi
$ PARENT_ID="<フォルダーの ID>" DEST_FILE_NAME="Drive 上でのファイル名>" SRC_FILE_NAME="<ローカルのファイル名>" bash ./send.sh
Workload Identitty 連携
GitHub Actions から使いたかったので試してみたが、以下のエラーになる。
Failed getting oauth client: google: read JWT from JSON credentials: 'type' field is "external_account" (expected "service_account")
おそらく https://github.com/golang/oauth2 からのエラーだと思われる。
なんとなくライブラリ側は対応しているように見えるけど Go のビルド環境を整えるのも大変なので Node.js で簡単なツールを作った。
こちらでは GitHub Actions からもファイルを送信できた。
GitHub Actions で Slidev の PDF をエクスポートしてGoogle Drive へ送信した
少し気になったので auth action が保存したファイルを Google Drive へ送信して確認してみた。
(どれを表に出したらマズいのかわからなかったのでほぼマスクしてある)
サービスアカウントの鍵をダウンロードしたものと様子がかなり違う。
{
"type": "external_account",
"audience": "***",
"subject_token_type": "urn:ietf:params:oauth:token-type:jwt",
"token_url": "https://sts.googleapis.com/v1/token",
"service_account_impersonation_url": "***",
"credential_source": {
"url": "***",
"headers": {
"Authorization": "Bearer ***"
},
"format": {
"type": "json",
"subject_token_field_name": "value"
}
}
}