1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

GitHub Actionsを使って指定した(だいたいの)日時にPull Requestをマージしたい

Posted at

私は「ツナギメエフエム」というPodcastを運営しています。

Podcastの音声ファイルは「Spotify for Creators」の機能によりエピソード毎に指定した日時に配信を行っているのですが、配信対象のエピソードに対応するwebサイトの公開は音声ファイルの配信後に手動で行っているため、以前から自動化したいと考えていました。

PodcastのwebサイトはGitHubでリポジトリ管理を行ない、Podcastの新しいエピソードを配信するタイミングでPull Requestを作成し、マージするとCloudflare Pagesに新しいエピソードのページがデプロイされるという仕組みになっています。


ということで、今回は「GitHub Actionsを使って指定した(だいたいの)日時にPull Requestをマージする」を実現したいと思います。
※GitHub Actionsが実行されるタイミングは保証されているわけではないので、「(だいたいの)日時」としています。

仕様としてはこんな感じ。

  • Pull Requestのマージ対象ブランチは「master」、「main」のみとする。
  • Draft状態のPull Reqeustはマージ対象外とする。
  • GitHub Actionsの「schedule」を利用して5分毎に実行する。
  • Pull Requestに「merge-at」というラベルを付与し、ラベルのDescriptionにマージ予定日時を「YYYY-MM-DD HH:mm」形式で入力する。
  • 指定したマージ予定日時を過ぎたらPull Requestをマージする。
  • マージ後に「merge-at」のラベルを削除する。
  • ワークフローを利用する場合はGitHubが公式提供しているものに絞る。(Marketplaceのワークフローは利用しない)

上記の仕様を元に作成したワークフローがこちら。

.github/workflows/merge-schedule.yml
name: Merge PRs at the specified datetime based on labels.

on:
  schedule:
    - cron: "*/5 * * * *"
  workflow_dispatch:

permissions:
  contents: write
  pull-requests: write

jobs:
  merge:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/github-script@v7
        with:
          script: |
            const { owner, repo } = context.repo;

            // JST を基準に現在時刻を取得
            const nowJst = new Date(
              new Date().toLocaleString("en-US", { timeZone: "Asia/Tokyo" })
            );

            // Open 状態の Pull Request を取得
            const prs = await github.paginate(
              github.rest.pulls.list,
              { owner, repo, state: "open" }
            );

            const allowedBaseBranches = ["master", "main"];
            for (const pr of prs) {
              // マージ対象ブランチかを判定
              if (!allowedBaseBranches.includes(pr.base.ref)) {
                console.log(`PR #${pr.number}: base is ${pr.base.ref}, skip`);
                continue;
              }

              // Draftは除外
              if (pr.draft) {
                console.log(`PR #${pr.number}: draft, skip`);
                continue;
              }

              // PRのラベルを取得
              const label = pr.labels.find(l => l.name === "merge-at");
              if (!label || !label.description) {
                continue;
              }

              // ラベルのDescriptionからマージ予定日時を取得する("YYYY-MM-DD HH:mm" 形式)
              const match = label.description.match(
                /^(\d{4}-\d{2}-\d{2} \d{2}:\d{2})$/
              );
              if (!match) {
                console.log(`PR #${pr.number}: merge-at ラベルの形式が不正です`);
                continue;
              }

              // マージ予定日時はJSTを基準とする
              const mergeAtJst = new Date(
                match[1].replace(" ", "T") + ":00+09:00"
              );

              if (nowJst < mergeAtJst) {
                console.log(`PR #${pr.number}: 指定した日時を経過していません`);
                continue;
              }

              console.log(`PR #${pr.number}: 指定した日時を経過しているためマージを実行します`);

              try {
                // マージ実行
                await github.rest.pulls.merge({
                  owner,
                  repo,
                  pull_number: pr.number,
                  merge_method: "merge",
                });
                console.log(`PR #${pr.number}: マージ成功`);
              } catch (e) {
                console.log(`PR #${pr.number}: マージ失敗 ${e.message}`);
              }
            
              try {
                // マージ後にラベルを外す(再実行防止)
                await github.rest.issues.removeLabel({
                  owner,
                  repo,
                  issue_number: pr.number,
                  name: "merge-at",
                });
                console.log(`PR #${pr.number}: ラベル削除成功`);
              } catch (e) {
                console.log(`PR #${pr.number}: ラベル削除失敗 ${e.message}`);
              }
            }

それでは動作確認です。

まずは、以下のようなPull Requestを作成します。

image.png

以下のようなラベルを準備します。

  • Name: merge-at
  • Description: 2025-12-13 16:32

image.png

先ほどのPull Requestに作成したラベルを付与します。

image.png

ワークフローが「2025-12-13 16:38」に実行されています。

image.png

マージ予定日時に「2025-12-13 16:32」を指定していたので、実行日時と比較して過去になっているためPull Requestがマージされました。
※「(だいたいの)日時」と説明していたとおりの動作ですね。

Screenshot from 2025-12-13 16-43-04.png

また、マージ完了後に「merge-at」のラベルが削除されていることがわかります。

Screenshot from 2025-12-13 17-20-53.png


これまではPodcastのwebサイトの更新のたびに手動でPull Requestのマージを行っていたため、この自動化の対応で随分楽になりそうです。
この機能を実現する場合はワークフローを自前で実装しようと考えていたので、今回対応できてとても満足しています。


関連記事

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?