0
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?

Backlogのチケット→Codex CLIで実装→PR→Vercelプレビューまで自動化(サンプルコードあり)

Posted at

はじめに

Backlogに仕様を書いてチケット番号を渡すだけで、CodexCLIがコード実装→push、GitHub ActionsがPRとBacklogコメントを自動生成、Vercelがプレビューまで用意。テンプレとWorkflowを丸ごと公開します。
ディレクターやデザイナーがエンジニアへの依頼なしで、Backlogのチケットからフロントエンド(Next.js)の実装、プルリク出してプレビューまでできるようにすることで、デザインや機能の確認ができプロトタイプ作成までできるのではと思い試してみました。

全体フロー

  • Backlogに仕様を書いたチケット(例:DEV-自動採番)を作る
  • CodexCLIにテンプレを投げて、MCPでチケット番号のデータ取得、ローカルでコード修正 → ブランチ作成 → commit → push
  • GitHub ActionsがPR作成、BacklogにPRのURLを自動コメント
  • VercelがPRにプレビュー構築

準備

1. VercelとGithubのアカウントを紐づけておく

vercel.png

2. GithubにVercelのnextjs-boilerplate(用意されているテンプレート)をCloneして、ローカルにもCloneする

ファイル構成
├── app
│   ├── favicon.ico
│   ├── globals.css
│   ├── layout.tsx
│   └── page.tsx
├── next.config.ts
├── package-lock.json
├── package.json
├── postcss.config.mjs
├── pr_template.txt # プロンプト用のファイルを追加する
├── public
│   ├── file.svg
│   ├── globe.svg
│   ├── next.svg
│   ├── vercel.svg
│   └── window.svg
├── README.md
└── tsconfig.json

3. CodexCLIをインストールする(ChatGPT契約済み)

brew install codex
または
npm i -g @openai/codex

4. Backlogの個人設定 > APIからAPIキーを発行する

backlog-api-env.png

5. codexCLIの設定を行う(承認ループを減らす、MCP設定追加)

.codex/config.toml
approval_policy = "untrusted"
sandbox_mode = "workspace-write"

[sandbox_workspace_write]
network_access = true

[mcp_servers.backlog.env]
BACKLOG_DOMAIN = "your-domain.backlog.com"
BACKLOG_API_KEY = "your-api-key"

6. Githubの対象リポジトリに必要な設定をする
Repository secretsの設定
Setting > Actions secrets and variables
New repository secretからBACKLOG_DOMAINとBACKLOG_API_KEYを設定する

スクリーンショット 2025-10-05 12.34.39.png

Setting > ActionsからPermission変更する(プルリクを出すため)

github-actions-env.png

7. Github Actions:自動PR & Backlogコメント

  • DEV- を含むブランチpushで発火
  • Backlogに PR URL をコメント(HTTP 200/201 を成功扱い)
.github/workflows/auto-pr-on-dev.yml
name: Auto PR on DEV branches

on:
  push:
    branches:
      - 'DEV-*'
      - 'feat/DEV-*'
      - 'fix/DEV-*'
      - '**/DEV-*'
  workflow_dispatch: {}

permissions:
  contents: write
  pull-requests: write

jobs:
  create-pr:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Set base & extract vars
        run: |
          echo "BRANCH=${GITHUB_REF_NAME}" >> $GITHUB_ENV
          echo "BASE=${{ github.event.repository.default_branch }}" >> $GITHUB_ENV
          ISSUE_KEY=$(echo "${GITHUB_REF_NAME}" | grep -oE 'DEV-[0-9]+' || true)
          echo "ISSUE_KEY=${ISSUE_KEY}" >> $GITHUB_ENV

      - name: Ensure PR exists (create or reuse)
        id: pr
        uses: actions/github-script@v7
        with:
          github-token: ${{ github.token }}   # ポリシーで禁止なら PAT Secret に差し替え
          script: |
            const { owner, repo } = context.repo;
            const branch   = process.env.BRANCH;
            const base     = process.env.BASE || context.payload.repository.default_branch;
            const issueKey = process.env.ISSUE_KEY || branch;
            const title    = `${issueKey}: auto PR`;
            const body     = `Backlog: https://wondercoms.backlog.com/view/${issueKey}\n\n変更点:\n- 叩き台PR`;

            const openPrs = await github.paginate(github.rest.pulls.list, { owner, repo, state: 'open', head: `${owner}:${branch}`, base, per_page: 100 });
            if (openPrs.length) { core.setOutput('url', openPrs[0].html_url); return; }

            const closedPrs = await github.paginate(github.rest.pulls.list, { owner, repo, state: 'closed', head: `${owner}:${branch}`, base, per_page: 100 });
            if (closedPrs.length) {
              const reopened = await github.rest.pulls.update({ owner, repo, pull_number: closedPrs[0].number, state: 'open' });
              core.setOutput('url', reopened.data.html_url); return;
            }

            const pr = await github.rest.pulls.create({ owner, repo, title, head: branch, base, body });
            core.setOutput('url', pr.data.html_url);

      - name: Echo PR URL
        run: echo "PR URL = ${{ steps.pr.outputs.url }}"

      # (必要なら一時的に)認証デバッグ
      - name: Debug Backlog credentials (temporary)
        if: env.ISSUE_KEY != ''
        env:
          BACKLOG_DOMAIN: ${{ secrets.BACKLOG_DOMAIN }}
          BACKLOG_API_KEY: ${{ secrets.BACKLOG_API_KEY }}
        run: |
          set -euo pipefail
          DOMAIN="$(echo -n "$BACKLOG_DOMAIN" | sed -e 's#^https\?://##' -e 's#/*$##' | tr -d ' \r\n')"
          KEY="$(echo -n "$BACKLOG_API_KEY" | tr -d ' \r\n')"
          HTTP=$(curl -s -o /tmp/me.json -w '%{http_code}' "https://${DOMAIN}/api/v2/users/myself?apiKey=${KEY}" || true)
          echo "Auth /users/myself HTTP=${HTTP}"; test "$HTTP" = "200"

      - name: Comment PR URL to Backlog
        if: env.ISSUE_KEY != '' && steps.pr.outputs.url != ''
        env:
          BACKLOG_DOMAIN: ${{ secrets.BACKLOG_DOMAIN }}
          BACKLOG_API_KEY: ${{ secrets.BACKLOG_API_KEY }}
        run: |
          set -euo pipefail
          DOMAIN="$(echo -n "$BACKLOG_DOMAIN" | sed -e 's#^https\?://##' -e 's#/*$##' | tr -d ' \r\n')"
          KEY="$(echo -n "$BACKLOG_API_KEY" | tr -d ' \r\n')"
          ISSUE="${ISSUE_KEY}"
          PR_URL='${{ steps.pr.outputs.url }}'

          STATUS="$(curl -sS -o /tmp/backlog_comment.json -w '%{http_code}' -X POST \
            "https://${DOMAIN}/api/v2/issues/${ISSUE}/comments?apiKey=${KEY}" \
            --data-urlencode "content=自動生成PR: ${PR_URL}")"

          echo "HTTP=${STATUS}"; cat /tmp/backlog_comment.json || true
          if [ "$STATUS" = "200" ] || [ "$STATUS" = "201" ]; then
            echo "OK"
          else
            echo "Backlog comment failed"; exit 1
          fi

8. Codex用テンプレ(pr_template.txt)

pr_template.txt
タスク:
Backlog の課題 {ISSUE_KEY}(https://wondercoms.backlog.com/view/{ISSUE_KEY})を MCP で取得し、記載の新仕様に基づいて Next.js リポに最小限の修正を加え、ブランチ作成→コミット→プッシュまで行う(PR作成とBacklogコメントは GitHub Actions が自動で実施)。

前提・重要事項(探索禁止の明示):
- 依存追加は禁止。書き込みは app/**, components/**, content/**, public/** のみ。
- lint / typecheck / build は実行しない。
- すべてのコミットは main から新規ブランチを切って行う。
- git の履歴要約は不要。必要な git は --no-pager で最小限。

手順(あなた=Codexが順に実行する):
1) Backlog 課題取得と要約
   - MCP backlog.get_issue("{ISSUE_KEY}") と、必要に応じて backlog.get_issue_comments("{ISSUE_KEY}") を呼び、要約を1〜2行で作成。
   - 仕様から「URL/ルート/対象ファイル/修正内容」を 1箇所 に限定して抽出。曖昧なら課題本文を優先。

2) 変更計画(最小差分)
   - 変更ファイルは最大1〜3個に抑える。新規画像は極力避ける(必要な場合のみ public/backlog/{ISSUE_KEY}/ を使う)。

3) 実装(直接編集)
   - 対象ファイルのみ直接編集して実装する(新規作成は必要最小限)。
   - 許可外パスの混入ガード:
     CHANGED="$(git --no-pager diff --name-only || true)"
     BAD="$(echo "$CHANGED" | grep -Ev '^(app/|components/|content/|public/)' || true)"
     if [ -n "$BAD" ]; then echo "非許可パス変更:"; echo "$BAD"; exit 1; fi

4) ブランチ作成・コミット・プッシュ(必ず main 起点)
   - git checkout main && git pull
   - git checkout -b feat/{ISSUE_KEY}-auto
   - git add -A
   - git commit -m "{ISSUE_KEY}: <短い要約>"
   - git push -u origin feat/{ISSUE_KEY}-auto

5) PR作成とBacklogコメントは GitHub Actions に任せる
   - push 後、Actions の "Auto PR on DEV branches" 実行を待ち、PR URL を確認(BacklogへのPR URLコメントも自動)

出力フォーマット:
- {ISSUE_KEY} 要約(12行)
- 変更ファイル一覧(最大3件)
- PR URL(確認できた場合)
- 成否と要点(1行)

リポ直下に保存。{ISSUE_KEY} だけ差し替えれば使い回しOK。ローカルは push まで、PRとBacklogは Actions が自動。

9. Backlogチケット記述テンプレ(今回はNext.js初期状態からヘッダーサイドバーありの管理画面を準備するイメージでChatGPTに生成してもらいました)

# タイトル
DEV-7 ダッシュボードレイアウト追加(Header/Sidebar/Footer + 3セクション)

# 目的
ヘッダー・フッター・サイドバーを持つダッシュボードレイアウトを /dashboard に追加する。

# スコープ
- app/**, components/**, public/** のみ編集。依存追加なし。

# 期待ファイル
- app/dashboard/layout.tsx
- app/dashboard/page.tsx
- app/dashboard/reports/page.tsx
- app/dashboard/settings/page.tsx
- components/ui/Header.tsx, Footer.tsx, Sidebar.tsx

# サイドバー
- Overview → /dashboard
- Reports  → /dashboard/reports
- Settings → /dashboard/settings

# 受入基準
- /dashboard で Header/Sidebar/Footer 表示
- 3リンクでページ切替
- アクティブスタイルあり
- 変更は許可パスのみ

# Authoritative Spec (YAML)
route_group: "/dashboard"
pages:
  - { id: overview, path: "/dashboard", title: "Overview" }
  - { id: reports,  path: "/dashboard/reports", title: "Reports" }
  - { id: settings, path: "/dashboard/settings", title: "Settings" }
layout:
  header: true
  footer: true
  sidebar:
    items:
      - { label: "Overview", href: "/dashboard" }
      - { label: "Reports",  href: "/dashboard/reports" }
      - { label: "Settings", href: "/dashboard/settings" }
allowed_paths: ["app/**","components/**","public/**"]

backlog1.png

実行する

  • ターミナルで対象のフォルダにて以下のコマンドを実行する(CodexCLIで実行する)
codex
/approvals

codex-approval.png

3のFull Accessを選択する(コマンド実行を許可する)

pr_template.txtをプロンプト実行するために

pr_template.txt を読み込み、{ISSUE_KEY} を DEV-7 に置換して実行。
backlogからMCPでデータを取得して、その後の処理はpr_template.txtの内容にしたがってあなたが実行して下さい。

をコンソールで入力して実行する(DEV-7は都度チケット番号に変更する)

codex1.png

codex2.png

  • GithubでActionsが実行されていることを確認する

actions.png

  • PRが出ていることを確認する

pull-request.png

pr-file-changed.png

  • Backlogの対象チケットにPRのURL記載されたコメントがあることを確認する

backlog2.png

  • Vercelの対象プロジェクトのActive Branchesでプレビューが構成されていることを確認する

vercel2.png

  • Previewをクリックして実装されたサイトを確認する(チケットの指示に近い内容)

dashboard.png

よくあるハマりどころと対処

  • PR作成が403:「ActionsはPR作成不可」
    → Settingの設定を確認
  • Backlogコメントが認証エラー(code:11)
    → BACKLOG_DOMAIN 形式(backlogのURLのドメイン部分: hoge.backlog.com)・個人APIキーの権限・改行混入を確認
  • on: push が発火しない
    → そのブランチにもYAMLが入っているか。main を取り込み、空コミットで試してみる
  • Codexが“Summarize recent commits”で止まる
    → ページャ無効(export GIT_PAGER=cat PAGER=cat)+テンプレに「履歴要約は不要」と明記

公開リポジトリ

サンプルコード: プロンプトファイル(pr_template.txt)、github actionsファイル追加済み

おわりに

「チケット番号だけでプロトタイプのPRとプレビュー」まで用意できるとエンジニアの作業なしでデザインや仕様の確認まで持っていけます。
現状はあくまでもプロトタイプとしてですが、Next.jsで準備することによりその後のエンジニアの作業がやりやすくなります。また、簡単な管理画面程度であればデザイナー、ディレクターだけでNext.jsサイトが構築できるのではと思います。
反響があれば、今後はチケットの添付画像からレイアウトを実装、コメントから同一ブランチに対して修正コミットを入れる、WEB版Codexでの対応などを追加していこうと考えています。

株式会社ワンダーコムズではデータ基盤構築からAIエージェント機能実装など幅広いシステム開発を行っています。システム開発、AIの導入、AIエージェント実装などお気軽にご相談・お問い合わせください。

0
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
0
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?