GitHub でリリースしたときにツイートする
目的
GitHub でリリースしたときにツイートしたい。
手動でツイートするのは面倒なので GitHub Actions を使って実装します。
Twitter アプリを登録する
2020年度版 Twitter API利用申請の例文からAPIキーの取得まで詳しく解説 | 新宿のホームページ制作会社 ITTI(イッティ) などを参考に投稿用の Twitter アプリを作成します。
正直ちょっと手間なのですが、認証に必要なので仕方ありません。
作成が完了したら Access token を生成してリポジトリの設定に以下の 4 つの Secrets を登録しておきます。
TWITTER_CONSUMER_API_KEY
TWITTER_CONSUMER_API_SECRET_KEY
TWITTER_ACCESS_TOKEN
TWITTER_ACCESS_TOKEN_SECRET
GitHub Actions
ワークフローファイル .github/workflows/released.yml
を作成します。
name: 'Tweet when released'
on:
release:
types: [released]
jobs:
tweet:
runs-on: ubuntu-latest
steps:
- name: Tweet
uses: snow-actions/tweet@v1.1.0
env:
CONSUMER_API_KEY: ${{ secrets.TWITTER_CONSUMER_API_KEY }}
CONSUMER_API_SECRET_KEY: ${{ secrets.TWITTER_CONSUMER_API_SECRET_KEY }}
ACCESS_TOKEN: ${{ secrets.TWITTER_ACCESS_TOKEN }}
ACCESS_TOKEN_SECRET: ${{ secrets.TWITTER_ACCESS_TOKEN_SECRET }}
with:
status: |
Released ${{ github.event.release.name }}
${{ github.event.release.html_url }}
on.release
トリガー
リリースが作られたときに実行されます。
デフォルトのままだと同時に 3 つくらい実行されてしまうので types
で絞ります。
他に published
や prereleased
などがあります。
詳細は ワークフローをトリガーするイベント - GitHub Docs 参照。
注意点として他のワークフローで secrets.GITHUB_TOKEN
を使ってリリースが作成された場合はこのワークフローはトリガーされません。
個人アクセストークンを使うかそちらのワークフローに組み込んでしまいましょう。
snow-actions/tweet
ツイートするアクションを探したときに画像を投稿できるものがなかったので作りました。
Tweet action · Actions · GitHub Marketplace
せっかくなので作ったときに工夫した点を紹介します。
認証と投稿パラメーターの分離
with
で全部渡してしまっても良かったのですが、認証用のパラメーターと投稿用のパラメーターが一緒になってると分かりづらいかなと思ったので認証用を env
投稿用を with
に分けました。
画像の投稿
今回の例には載せていませんが画像を投稿することができます。
ツイートを投稿する API は POST statuses/update です。
画像を投稿する際には media_ids
を渡さないといけませんが、それをそのまま with
で受け取るパラメーターにしてしまうと利用者が 画像のアップロード をしなくてはなりません。
それでは手間が大きいので画像のパスだけを渡してアップロードは内部で処理するようにしました。
現在は POST media/upload を使って実装しているので画像しかアップロードできませんが、将来的には Chunked media upload に置き換えて GIF や動画もアップロードできるようにしたいところです。
with
で配列を渡す
YAML で配列を定義する際は本来は以下のように書きます。
with:
media_paths:
- 1st.png
- 2nd.png
しかし with
では上のようには書けず文字列しか渡せません。
なので代わりに改行で渡して内部で分割するようにしました。
with:
media_paths: |
1st.png
2nd.png
実装はこのようになっています。
const mediaPaths = core.getInput('media_paths')
const mediaIds = await uploadMedia(
mediaPaths
.split('\n')
.filter(x => x !== '')
.map(mediaPath => path.join(process.cwd(), mediaPath))
)
最後に
結構前に公開していたのですが紹介記事を書いていなかったので書いてみました。
まだ基本機能しかないですが使ってフィードバックをいただけると嬉しいです。 PR も歓迎。
Discussion