> ## Documentation Index
> Fetch the complete documentation index at: https://docs.devin.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# ステージング環境で毎晩E2Eテストを実行する

export const UseCaseHero = ({title, description, prompt, category, features, devinUrl, agent, intent, playbookId, type}) => {
  const encodedPrompt = encodeURIComponent(prompt || '');
  const tag = 'docs-use-case-gallery';
  const utm = 'utm_source=docs&utm_medium=use-case-gallery&utm_campaign=hero-cta';
  const agentParams = (agent ? '&agent=' + agent : '') + (intent ? '&intent=' + intent : '') + (playbookId ? '&playbookId=' + playbookId : '');
  const devinHref = type === 'schedule' ? 'https://app.devin.ai/settings/schedules/create?' + utm + agentParams + (prompt ? '&prompt=' + encodedPrompt : '') : type === 'review' ? 'https://app.devin.ai/review?' + utm : agent === 'ada' ? 'https://app.devin.ai/search?' + utm + '&noSubmit=true' + (prompt ? '&prompt=' + encodedPrompt : '') : devinUrl ? devinUrl.includes('?') ? devinUrl + '&' + utm + agentParams : devinUrl + '?' + utm + agentParams : prompt ? 'https://app.devin.ai/?tags=' + tag + '&' + utm + agentParams + '&prompt=' + encodedPrompt : 'https://app.devin.ai/?' + utm + agentParams;
  const buttonLabel = type === 'schedule' ? 'Schedule in Devin ↗' : type === 'review' ? 'Set Up Devin Review ↗' : agent === 'advanced' ? 'Try in Devin ↗' : agent === 'dana' ? 'Try in Dana ↗' : agent === 'ada' ? 'Try in Ask Devin ↗' : 'Try in Devin ↗';
  const featureList = features ? features.split(',').map(f => f.trim()) : [];
  return <div className="uc-hero">
      <div className="uc-hero-inner">
        <div className="uc-hero-left">
          <h1 className="uc-hero-title">{title}</h1>
          <p className="uc-hero-desc">{description}</p>
          <div>
            <a href={devinHref} target="_blank" rel="noopener noreferrer" className="try-in-devin-btn">
              {buttonLabel}
            </a>
          </div>
        </div>
        <div className="uc-hero-meta">
          <div className="uc-meta-item">
            <span className="uc-meta-label">Author</span>
            <span className="uc-meta-value">Cognition</span>
          </div>
          <div className="uc-meta-item">
            <span className="uc-meta-label">Category</span>
            <span className="uc-meta-value">{category}</span>
          </div>
          {featureList.length > 0 && <div className="uc-meta-item">
              <span className="uc-meta-label">Features</span>
              <span className="uc-meta-value">{featureList.join(', ')}</span>
            </div>}
        </div>
      </div>
    </div>;
};

export const PromptBlock = ({children, type, agent, intent, playbookId}) => {
  var utm = 'utm_source=docs&utm_medium=use-case-gallery&utm_campaign=prompt-block';
  var tag = 'docs-use-case-gallery';
  var agentParams = (agent ? '&agent=' + agent : '') + (intent ? '&intent=' + intent : '') + (playbookId ? '&playbookId=' + playbookId : '');
  var label = type === 'schedule' ? 'Schedule in Devin' : type === 'playbook' ? 'Create Playbook' : type === 'knowledge' ? 'Add to Knowledge' : agent === 'advanced' ? 'Try in Devin' : agent === 'dana' ? 'Try in Dana' : agent === 'ada' ? 'Try in Ask Devin' : 'Try in Devin';
  var buildUrl = function (text) {
    var encoded = encodeURIComponent(text);
    if (type === 'schedule') return 'https://app.devin.ai/settings/schedules/create?' + utm + agentParams + '&prompt=' + encoded;
    if (type === 'playbook') return 'https://app.devin.ai/settings/playbooks/create?' + utm + '&body=' + encoded;
    if (type === 'knowledge') return 'https://app.devin.ai/knowledge?' + utm + '&body=' + encoded;
    if (agent === 'ada') return 'https://app.devin.ai/search?' + utm + '&noSubmit=true&prompt=' + encoded;
    return 'https://app.devin.ai/?tags=' + tag + '&' + utm + agentParams + '&prompt=' + encoded;
  };
  const ref = React.useRef(null);
  const [href, setHref] = React.useState('#');
  React.useEffect(() => {
    if (!ref.current) return;
    var codeEl = ref.current.querySelector('pre code');
    if (codeEl) {
      var text = codeEl.textContent.trim();
      if (text) setHref(buildUrl(text));
    }
    var header = ref.current.querySelector('[data-component-part="code-block-header"]');
    if (header && !header.querySelector('.prompt-block-devin-link')) {
      var link = document.createElement('a');
      link.href = href;
      link.target = '_blank';
      link.rel = 'noopener noreferrer';
      link.className = 'prompt-block-devin-link';
      link.style.cssText = 'display:inline-flex;align-items:center;gap:6px;text-decoration:none;color:#fff;font-size:11px;font-weight:500;padding:4px 10px;border-radius:6px;white-space:nowrap;background:#317CFF;transition:background 0.2s;margin-left:8px;';
      link.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></svg> ' + label;
      link.onmouseenter = function () {
        link.style.background = '#2968D9';
      };
      link.onmouseleave = function () {
        link.style.background = '#317CFF';
      };
      header.appendChild(link);
    }
    var existingLink = ref.current.querySelector('.prompt-block-devin-link');
    if (existingLink && href !== '#') existingLink.href = href;
  });
  return <div className="prompt-block" ref={ref}>{children}</div>;
};

<UseCaseHero title="ステージング環境で毎晩 E2E テストを実行する" description="Devin に、毎晩ステージング環境に対してエンドツーエンドテストスイートを実行させ、失敗時にはチケットを自動で起票するようスケジュールします。" prompt="ステージング環境に対して毎晩 E2E テストスイートを実行する Devin のナイトリースケジュール設定を手伝ってください。https://docs.devin.ai/use-cases/gallery/api-scheduled-sessions にあるガイドに従い、サービスユーザーの作成、テストプレイブックの作成、API によるスケジュール作成という各ステップを順に案内してください。" category="自動化" features="API、スケジュール、プレイブック" />

<div className="uc-detail-wrapper">
  <Tip>手動でのセットアップが面倒ですか？このページのリンクを Devin のセッションに貼り付けて、すべてのセットアップを依頼してください。</Tip>

  このガイドでは、[v3 API](/ja/api-reference/v3/overview) を使用したスケジュール管理について説明します。これは自動化やInfrastructure as Codeワークフローに役立ちます。APIの設定なしに、[Devin UI](https://app.devin.ai/settings/schedules?utm_source=docs\&utm_medium=use-case-gallery) から直接スケジュールを作成・管理することもできます。

  <Steps>
    <Step title="APIアクセス用のサービスユーザーをセットアップする">
      API 経由で作成されたスケジュール済みセッションには、適切な権限を持つサービスユーザーが必要です。最初に 1 つ設定しておけば、以降のすべての呼び出しでその APIキー を使用できます。

      1. [app.devin.ai](https://app.devin.ai/?utm_source=docs\&utm_medium=use-case-gallery) > **Settings** > **Service Users** に移動し、**Create Service User** をクリックします
      2. `ManageOrgSchedules` 権限を含むロールを割り当てます
      3. 作成後に表示される APIキー を保存しておきます。表示されるのは一度きりで、その後は `Bearer` トークンとして使用します

      **organization ID** を取得するには、サービスユーザーのトークンを使って [List Organizations](/ja/api-reference/v3/organizations/enterprise-organizations) エンドポイントを呼び出します。

      ```bash theme={null}
      curl "https://api.devin.ai/v3/enterprise/organizations" \
        -H "Authorization: Bearer $DEVIN_API_KEY"
      ```

      このガイドに記載されているコマンドがそのまま動作するように、両方の値をエクスポートしてください:

      ```bash theme={null}
      export DEVIN_API_KEY="sk-your-service-user-key"
      export ORG_ID="your-org-id"
      ```

      サービスユーザーおよび権限の詳細については、[API 認証に関するドキュメント](/ja/api-reference/authentication)を参照してください。
    </Step>

    <Step title="テスト実行のためのプレイブックを作成します">
      スケジュールを作成する前に、E2E スイートをどのように実行し、結果をどのように扱うかを Devin に正確に指示する[プレイブック](/ja/product-guides/creating-playbooks)を用意します。[**Settings > Playbooks**](https://app.devin.ai/settings/playbooks?utm_source=docs\&utm_medium=use-case-gallery) に移動して新しいプレイブックを作成するか、テストワークフローの説明から Devin に生成を依頼してください。以下は Playwright スイート向けの例です:

      <PromptBlock type="playbook">
        ```txt Nightly E2E test playbook theme={null}
        You are running the nightly E2E test suite for acme/web-app against staging.

        Steps:
        1. Run the Playwright E2E suite targeting staging:
           `PLAYWRIGHT_BASE_URL=https://staging.acme.dev npx playwright test --reporter=json`
        2. Parse the JSON results. For each failing test:
           - Create a Linear ticket in the "QA" team with:
             - Title: "[Nightly E2E] <test name>"
             - Description: the full error message, stack trace, and the test file path
             - Label: "nightly-e2e-failure"
             - Priority: High if the test is in tests/critical/*, otherwise Medium
           - Attach any failure screenshots from the Playwright report
        3. If all tests pass, post a summary to the #qa-results Slack channel:
           "Nightly E2E: all <N> tests passed against staging (<date>)"
        4. If any tests fail, post to #qa-results:
           "Nightly E2E: <N> of <total> tests failed — <count> Linear tickets created"
           Include a list of the failing test names.
        ```
      </PromptBlock>

      保存後はプレイブック ID を控えておいてください。API 呼び出しで必要になります。プレイブックを表示しているときの URL (`app.devin.ai/.../playbooks/{playbook_id}`) から確認できます。

      <Tip>Devin がプレイブックの一部としてチケットを作成できるように、[Linear integration](/ja/integrations/linear) をインストールしてください。[スケジュールを作成するとき](https://app.devin.ai/settings/schedules/create?utm_source=docs\&utm_medium=use-case-gallery)に、チームに自動で通知が届くように **Slack チャンネル** (例: `#qa-results`) も指定できます。テストで必要な場合は、[organization secrets](/ja/product-guides/secrets) を通じて、ステージング環境のシークレット (データベース URL、APIキー など) への読み取り専用アクセスを Devin に付与してください。</Tip>
    </Step>

    <Step title="API でナイトリースケジュールを作成する">
      次に、`POST /v3/organizations/{org_id}/schedules` エンドポイントを使ってスケジュールを登録します。次の例では、毎晩午前 2 時 (UTC) に実行されます。

      ```bash theme={null}
      curl -X POST "https://api.devin.ai/v3/organizations/$ORG_ID/schedules" \
        -H "Authorization: Bearer $DEVIN_API_KEY" \
        -H "Content-Type: application/json" \
        -d '{
          "title": "Nightly E2E — staging",
          "prompt": "Run the nightly E2E test suite against staging. Follow the playbook exactly.",
          "schedule_type": "recurring",
          "frequency": "0 2 * * *",
          "playbook_id": "your-playbook-id"
        }'
      ```

      レスポンスには、このスケジュールを後で管理する際に使用する `schedule_id` が含まれています。保存しておいてください:

      ```bash theme={null}
      export SCHEDULE_ID="a1b2c3d4-e5f6-7890-abcd-ef1234567890"  # レスポンスから取得
      ```

      `frequency` フィールドは標準的な cron 構文を使用します。以下にいくつか便利な例を示します:

      | Pattern       | When it runs       |
      | ------------- | ------------------ |
      | `0 2 * * *`   | 毎日午前 2 時 (UTC)     |
      | `0 2 * * 1-5` | 平日のみ (月〜金)         |
      | `0 6 * * 1`   | 毎週月曜日の午前 6 時 (UTC) |

      なぜ午前 2 時なのでしょうか？テストは、その日の最後のデプロイがステージング環境で安定した後で、かつエンジニアが業務を開始したときに不具合が見えるよう、十分早い時間に実行したいからです。チームのタイムゾーンとデプロイのサイクルに合わせて調整してください。

      利用可能なすべてのフィールドについては、[Create schedule エンドポイントのドキュメント](/ja/api-reference/v3/schedules/post-organizations-schedules)を参照してください。
    </Step>

    <Step title="初回実行を検証し、プロンプトを最適化する">
      スケジュールが最初に実行されたら、セッションを確認して、Devin がテストを正しく実行し、出力が期待どおりになっていることを確かめます。

      1. Devin のダッシュボードを開き、**Past Sessions** の中から対象のセッションを探します — スケジュール名のタグが付いています
      2. Playwright のテストスイートは実行されましたか？ 実際の不具合 (フレークテストではないもの) に対して Linear チケットが作成されましたか？
      3. `#qa-results` Slack チャンネルでサマリーメッセージを確認します

      初回実行時によくある問題と、その対処方法:

      * **Devin がステージングにアクセスできない**: ステージング環境変数 (`STAGING_API_KEY` や `DATABASE_URL` など) を [organization secrets](/ja/product-guides/secrets) として追加し、すべてのスケジュールされたセッションで利用できるようにします
      * **フレークテストからのチケットが多すぎる**: プレイブックにリトライを追加します: 「チケットを起票する前に、失敗したテストはすべて 1 回再実行する。2 回失敗したテストに対してのみチケットを起票する。」
      * **テストに時間がかかりすぎる**: スイートの対象範囲を絞ります — 例: 「`tests/critical/` と `tests/smoke/` のテストだけを実行する」— またはセッションのタイムアウトを延長します

      <PromptBlock agent="advanced">
        ```txt theme={null}
        プロンプトの調整: フレークテストを除外する
        ナイトリーの E2E プレイブックを更新します。失敗したテストに対して Linear チケットを作成する前に、
        `npx playwright test <test-file> --retries=1` で 1 回だけ再実行してください。
        リトライでもテストが失敗した場合にのみチケットを作成します。
        リトライで成功したテストには "flaky" ラベルを付け、Slack のサマリー内で言及します。
        ```
      </PromptBlock>
    </Step>

    <Step title="スケジュールをコードで管理する">
      ナイトリーの実行が安定したら、他のスケジュールと同様に管理したくなるはずです。デプロイフリーズ中は一時停止したり、テストスイートが変わったらプロンプトを更新したり、別の環境用に 2 つ目のスケジュールを作成したりできます。

      デプロイフリーズやメンテナンスウィンドウの間は、**スケジュールを一時停止** します:

      ```bash theme={null}
      curl -X PATCH "https://api.devin.ai/v3/organizations/$ORG_ID/schedules/$SCHEDULE_ID" \
        -H "Authorization: Bearer $DEVIN_API_KEY" \
        -H "Content-Type: application/json" \
        -d '{"enabled": false}'
      ```

      凍結期間が終わったら、**再度有効化して**ください:

      ```bash theme={null}
      curl -X PATCH "https://api.devin.ai/v3/organizations/$ORG_ID/schedules/$SCHEDULE_ID" \
        -H "Authorization: Bearer $DEVIN_API_KEY" \
        -H "Content-Type: application/json" \
        -d '{"enabled": true}'
      ```

      稼働中のものを監査するには、**すべてのスケジュールを一覧表示**します：

      ```bash theme={null}
      curl "https://api.devin.ai/v3/organizations/$ORG_ID/schedules" \
        -H "Authorization: Bearer $DEVIN_API_KEY"
      ```

      複数のスケジュールを管理しているチームは、YAML 設定ファイルからスケジュール定義を同期する CLI の作成を Devin に依頼することで、テスト設定と同様にスケジュールもバージョン管理できます。

      <PromptBlock>
        ```txt Schedules-as-code CLI theme={null}
        Write a Python script that reads schedule definitions from a file called
        devin-schedules.yaml and syncs them to the Devin API.

        The YAML file should look like:
          schedules:
            - title: "Nightly E2E — staging"
              prompt: "Run the nightly E2E suite against staging."
              frequency: "0 2 * * *"
              playbook_id: "a1b2c3d4e5f6789012345678"
            - title: "Nightly E2E — preview"
              prompt: "Run the E2E suite against the preview environment."
              frequency: "0 3 * * 1-5"
              playbook_id: "a1b2c3d4e5f6789012345678"

        The script should:
        1. Compare the YAML definitions against existing schedules from the API
        2. Create new schedules, update changed ones, and disable schedules
           removed from the file
        3. Support a --dry-run flag that shows what would change
        4. Use the endpoints at /v3/organizations/{org_id}/schedules
        ```
      </PromptBlock>
    </Step>
  </Steps>
</div>
