index.php

https://個人の見解.jp

当ブログではアフィリエイト広告を使用しております。

GitLab REST API で MR を作成直後に merge しようとするとエラーが返却される

前提

Pipeline 実行状態や Approval 状態、権限とか該当プロジェクトにおいて merge できる条件をクリアしているのに以下のいずれかのエラーが発生する

  • 422 Unprocessable Entity (Branch cannot be merged)
  • 405 Method Not Allowed (405 Method Not Allowed)

原因

MR 作成直後は GitLab の内部処理が完了していない場合がある(らしい)

結論

Create MR のレスポンスもしくは、Get single MR のレスポンスに含まれる detailed_merge_statusmergeable になるまでリトライする1

コード

最小限のコードなのでエラーハンドリングなどは別途行う必要があります。

<?php

try {
    // MRの作成
    $createMergeRequest = (new \GuzzleHttp\Client())
        ->post("{$_ENV['GITLAB_API_BASE_URL']}projects/{$_ENV['GITLAB_PROJECT_ID']}/merge_requests", [
            'headers' => [
                'PRIVATE-TOKEN' => $_ENV['GITLAB_BOT_TOKEN'],
                'Content-Type' => 'application/x-www-form-urlencoded'
            ],
            'form_params' => [
                'source_branch' => 'branch name',
                'target_branch' => 'main', // 任意のmerge先
                'title' => 'MRのタイトル',
                // MR作成時に行うその他任意のパラメータ定義
            ]
        ]);
    $content = json_decode($createMergeRequest->getBody()->getContents());

    // マージチェック通過後にマージする
    if (mergeable($content->iid)) {
        (new \GuzzleHttp\Client())
            ->put("{$_ENV['GITLAB_API_BASE_URL']}projects/{$_ENV['GITLAB_PROJECT_ID']}/merge_requests/{$content->iid}/merge", [
                'headers' => [
                    'PRIVATE-TOKEN' => $_ENV['GITLAB_BOT_TOKEN'],
                    'Content-Type' => 'application/x-www-form-urlencoded',
                ],
                'form_params' => [
                    'should_remove_source_branch' => true,
                    // mergeの際に行う任意のパラメータ
                ],
            ]);
    }
} catch (\Throwable $th) {
    echo "エラー: {$th->getMessage()}";
}

/**
 * detailed_merge_status が mergeable になるまでリトライする
 *
 * @param int $iid
 * @return bool
 */
function mergeable(int $iid): bool
{
    $start = time();
    $maxWaitTime = 30;
    while (time() - $start < $maxWaitTime) {
        $getMergeRequest = (new \GuzzleHttp\Client())
            ->get("{$_ENV['GITLAB_API_BASE_URL']}projects/{$_ENV['GITLAB_PROJECT_ID']}/merge_requests/{$iid}", [
                'headers' => [
                    'PRIVATE-TOKEN' => $_ENV['GITLAB_BOT_TOKEN'],
                    'Content-Type' => 'application/json',
                ],
            ]);

        $content = json_decode($getMergeRequest->getBody()->getContents());
        if ($content->detailed_merge_status === 'mergeable') {
            echo "mergeable になりました。\n";
            return true;
        }
        // 次のリクエストまで3秒待機
        echo "current status -> {$content->detailed_merge_status}\n";
        sleep(3);
    }

    throw new RuntimeException('タイムアウト');
}

最後に

お前... まさか feature ブランチでの作業直後にセルフマージしようとしてないよな?

参考

docs.gitlab.com


  1. GitLab の Version が 15.6 未満の場合は merge_statuscan_be_merged になるまで