🐦

GitHub でリリースしたときにツイートする

2020/10/25に公開

目的

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 を作成します。

.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 で絞ります。
他に publishedprereleased などがあります。
詳細は ワークフローをトリガーするイベント - 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