{"id":113,"date":"2026-03-09T00:44:26","date_gmt":"2026-03-09T00:44:26","guid":{"rendered":"https:\/\/blog.rebalai.com\/ja\/2026\/03\/09\/pythongithub-actions\/"},"modified":"2026-03-09T04:36:59","modified_gmt":"2026-03-09T04:36:59","slug":"pythongithub-actions","status":"publish","type":"post","link":"https:\/\/blog.rebalai.com\/ja\/2026\/03\/09\/pythongithub-actions\/","title":{"rendered":"Python\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u5411\u3051GitHub Actions\u8a2d\u5b9a: \u5b9f\u52d9\u3067\u5b66\u3093\u3060\u5b8c\u5168\u30ac\u30a4\u30c9"},"content":{"rendered":"<p>\u53bb\u5e74\u306e11\u6708\u30013\u4eba\u30c1\u30fc\u30e0\u3067\u904b\u7528\u3057\u3066\u3044\u305fFastAPI\u306e\u30b5\u30fc\u30d3\u30b9\u304c\u91d1\u66dc\u306e\u5915\u65b9\u306b\u58ca\u308c\u305f\u3002\u539f\u56e0\u306fPython\u306e\u30d0\u30fc\u30b8\u30e7\u30f3\u975e\u4e92\u63db\u3002\u30ed\u30fc\u30ab\u30eb\u306f3.11\u3001\u672c\u756a\u306f3.10\u3067\u3001\u7279\u5b9a\u306ef-string\u69cb\u6587\u304c3.10\u3067\u306f\u52d5\u304b\u306a\u304b\u3063\u305f\u3002\u300c\u6b21\u304b\u3089\u6c17\u3092\u3064\u3051\u3088\u3046\u300d\u3067\u7d42\u308f\u3089\u305b\u3088\u3046\u3068\u3057\u305f\u3089\u30012\u9031\u9593\u5f8c\u306b\u307e\u3063\u305f\u304f\u540c\u3058\u7406\u7531\u3067\u5225\u306e\u30b5\u30fc\u30d3\u30b9\u304c\u843d\u3061\u305f\u3002\u305d\u3053\u3067\u3088\u3046\u3084\u304fCI\/CD\u3092\u3061\u3083\u3093\u3068\u3084\u308d\u3046\u3068\u6c7a\u3081\u305f\u3002<\/p>\n<p>\u305d\u308c\u307e\u3067\u306fJenkins\uff08\u524d\u8077\u304b\u3089\u5f15\u304d\u7d99\u3044\u3060\u3084\u3064\uff09\u3092\u306a\u3093\u3068\u306a\u304f\u4f7f\u3063\u3066\u3044\u305f\u3002\u8a2d\u5b9a\u304cXML\u3067\u3001\u8ab0\u3082\u30e1\u30f3\u30c6\u3057\u305f\u304f\u306a\u3044\u30aa\u30fc\u30e9\u3092\u91b8\u3057\u51fa\u3057\u3066\u3044\u305f\u3002GitHub Actions\u306b\u79fb\u884c\u3057\u305f\u4e3b\u306a\u7406\u7531\u306f\u300c\u8a2d\u5b9a\u30d5\u30a1\u30a4\u30eb\u3092\u30b3\u30fc\u30c9\u3068\u540c\u3058\u30ea\u30dd\u30b8\u30c8\u30ea\u3067\u7ba1\u7406\u3057\u305f\u3044\u300d\u3068\u3044\u3046\u4e00\u70b9\u3002YAML\u304c\u597d\u304d\u3068\u3044\u3046\u308f\u3051\u3067\u306f\u306a\u3044\u3051\u3069\u3001Git\u3067\u5dee\u5206\u304c\u898b\u3048\u308b\u306e\u306f\u78ba\u5b9f\u306b\u52a9\u304b\u308b\u3002<\/p>\n<p>\u3053\u306e\u8a18\u4e8b\u306f\u300cGitHub Actions\u306e\u6982\u8981\u300d\u3067\u306f\u306a\u3044\u3002\u57fa\u672c\u6982\u5ff5\u306f\u308f\u304b\u3063\u3066\u3044\u308b\u3068\u3044\u3046\u524d\u63d0\u3067\u3001\u5b9f\u969b\u306bPython\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u306b\u7d44\u307f\u8fbc\u3080\u3068\u304d\u306b\u30cf\u30de\u308b\u30dd\u30a4\u30f3\u30c8 \u2014 \u30ad\u30e3\u30c3\u30b7\u30e5\u6226\u7565\u3001\u30de\u30c8\u30ea\u30c3\u30af\u30b9\u30d3\u30eb\u30c9\u3001\u74b0\u5883\u5225<a href=\"https:\/\/m.do.co\/c\/06956e5e2802\" title=\"DigitalOcean\u3067\u30c7\u30d7\u30ed\u30a4\" rel=\"nofollow sponsored\" target=\"_blank\">\u30c7\u30d7\u30ed\u30a4<\/a>\u306e\u5206\u96e2 \u2014 \u3092\u4e2d\u5fc3\u306b\u66f8\u304f\u3002<\/p>\n<h2>\u30ef\u30fc\u30af\u30d5\u30ed\u30fc\u30d5\u30a1\u30a4\u30eb\u306e\u69cb\u6210: \u6700\u521d\u306e\u8a2d\u8a08\u304c\u5f8c\u3067\u52b9\u3044\u3066\u304f\u308b<\/h2>\n<p><code>.github\/workflows\/<\/code>\u306bYAML\u3092\u7f6e\u304f\u3060\u3051\u3001\u3068\u3044\u3046\u306e\u306f\u8ab0\u3067\u3082\u77e5\u3063\u3066\u3044\u308b\u3002\u554f\u984c\u306f\u305d\u306e\u4e2d\u8eab\u306e\u8a2d\u8a08\u3060\u3002\u79c1\u304c\u6700\u521d\u306b\u3084\u3063\u305f\u30df\u30b9\u306f\u3001\u30c6\u30b9\u30c8\u30fb\u30ea\u30f3\u30c8\u30fb<a href=\"https:\/\/m.do.co\/c\/06956e5e2802\" title=\"DigitalOcean\u3067\u30c7\u30d7\u30ed\u30a4\" rel=\"nofollow sponsored\" target=\"_blank\">\u30c7\u30d7\u30ed\u30a4<\/a>\u3092\u3059\u3079\u3066<code>ci.yml<\/code>\u4e00\u679a\u306b\u8a70\u3081\u8fbc\u3093\u3060\u3053\u3068\u3002\u6700\u521d\u306f\u300c\u30b7\u30f3\u30d7\u30eb\u3067\u3044\u3044\u300d\u3068\u601d\u3063\u3066\u3044\u305f\u3051\u3069\u3001\u672c\u756a<a href=\"https:\/\/m.do.co\/c\/06956e5e2802\" title=\"DigitalOcean\u3067\u30c7\u30d7\u30ed\u30a4\" rel=\"nofollow sponsored\" target=\"_blank\">\u30c7\u30d7\u30ed\u30a4<\/a>\u306e\u6761\u4ef6\u3060\u3051\u5909\u3048\u305f\u3044\u3068\u304d\u306b\u5168\u4f53\u3092\u8aad\u307f\u76f4\u3059\u7fbd\u76ee\u306b\u306a\u3063\u3066\u3001\u30c1\u30fc\u30e0\u306e\u8ab0\u304b\u304c\u300c\u3053\u306e\u30d5\u30a1\u30a4\u30eb\u3001\u3061\u3087\u3063\u3068\u6016\u3044\u3093\u3067\u3059\u3051\u3069\u300d\u3068\u8a00\u3044\u51fa\u3057\u305f\u3002<\/p>\n<p>\u4eca\u306f3\u30d5\u30a1\u30a4\u30eb\u306b\u5206\u3051\u3066\u3044\u308b:<\/p>\n<pre><code>.github\/\n  workflows\/\n    ci.yml        # PR\u3054\u3068\u306b\u5b9f\u884c: \u30c6\u30b9\u30c8\u3001\u30ea\u30f3\u30c8\u3001\u578b\u30c1\u30a7\u30c3\u30af\n    <a href=\"https:\/\/m.do.co\/c\/06956e5e2802\" title=\"Deploy on DigitalOcean Cloud\" rel=\"nofollow sponsored\" target=\"_blank\">deploy<\/a>.yml    # main\u30de\u30fc\u30b8\u6642: \u30b9\u30c6\u30fc\u30b8\u30f3\u30b0\u81ea\u52d5\u2192\u672c\u756a\u624b\u52d5\u627f\u8a8d\n    scheduled.yml # cron\u30b8\u30e7\u30d6: DB \u30d0\u30c3\u30af\u30a2\u30c3\u30d7\u3001\u5b9a\u671f\u30ec\u30dd\u30fc\u30c8\u306a\u3069\n<\/code><\/pre>\n<p>\u30d5\u30a1\u30a4\u30eb\u3092\u5206\u3051\u308b\u3068<code>workflow_call<\/code>\u3067\u306e\u518d\u5229\u7528\u3082\u3067\u304d\u308b\u3057\u3001Actions \u30bf\u30d6\u3067\u3069\u306e\u30ef\u30fc\u30af\u30d5\u30ed\u30fc\u304c\u5931\u6557\u3057\u305f\u304b\u4e00\u76ee\u3067\u308f\u304b\u308b\u3002\u5730\u5473\u3060\u3051\u3069\u3001\u30c1\u30fc\u30e0\u304c\u300cCI\u306e\u72b6\u614b\u3092\u78ba\u8a8d\u3059\u308b\u300d\u7fd2\u6163\u304c\u660e\u3089\u304b\u306b\u5897\u3048\u305f\u3002<\/p>\n<p>\u57fa\u672c\u306eCI\u8a2d\u5b9a\u304b\u3089\u59cb\u3081\u308b:<\/p>\n<pre><code class=\"language-yaml\"># .github\/workflows\/ci.yml\nname: CI\n\non:\n  push:\n    branches: [main, develop]\n  pull_request:\n    branches: [main]\n\njobs:\n  test:\n    runs-on: ubuntu-latest\n    strategy:\n      matrix:\n        python-version: [&quot;3.10&quot;, &quot;3.11&quot;, &quot;3.12&quot;]\n      fail-fast: false  # 1\u3064\u5931\u6557\u3057\u3066\u3082\u4ed6\u306e\u30d0\u30fc\u30b8\u30e7\u30f3\u306e\u30c6\u30b9\u30c8\u306f\u7d9a\u3051\u308b\n\n    steps:\n      - uses: actions\/checkout@v4\n\n      - name: Python ${{ matrix.python-version }} \u3092\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7\n        uses: actions\/setup-python@v5\n        with:\n          python-version: ${{ matrix.python-version }}\n          cache: &quot;pip&quot;\n\n      - name: \u4f9d\u5b58\u95a2\u4fc2\u3092\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\n        run: |\n          python -m pip install --upgrade pip\n          pip install -r requirements.txt\n          pip install -r requirements-dev.txt\n\n      - name: \u30c6\u30b9\u30c8\u5b9f\u884c\n        run: pytest --tb=short -q\n\n      - name: \u30ea\u30f3\u30c8\u3068\u578b\u30c1\u30a7\u30c3\u30af\n        run: |\n          ruff check .\n          mypy src\/\n<\/code><\/pre>\n<p><code>fail-fast: false<\/code>\u306f\u6700\u521d\u306b\u4ed8\u3051\u5fd8\u308c\u305f\u3002\u30c7\u30d5\u30a9\u30eb\u30c8\u306f<code>true<\/code>\u306a\u306e\u3067\u3001Python 3.10\u3067\u30c6\u30b9\u30c8\u304c\u5931\u6557\u3059\u308b\u30683.11\u30683.12\u306f\u5b9f\u884c\u3055\u308c\u306a\u3044\u307e\u307e\u7d42\u308f\u308b\u3002\u3069\u306e\u30d0\u30fc\u30b8\u30e7\u30f3\u3067\u3069\u3053\u304c\u58ca\u308c\u3066\u3044\u308b\u304b\u3092\u4e00\u5ea6\u306b\u628a\u63e1\u3057\u305f\u3044\u3068\u304d \u2014 \u7279\u306b\u30e9\u30a4\u30d6\u30e9\u30ea\u3092\u4f5c\u3063\u3066\u3044\u308b\u306a\u3089 \u2014 \u3053\u308c\u306f\u5fc5\u9808\u3060\u3068\u601d\u3046\u3002<\/p>\n<h2>Python\u30ad\u30e3\u30c3\u30b7\u30e5: <code>cache: 'pip'<\/code>\u3060\u3051\u3067\u306f\u4e0d\u5341\u5206\u306a\u7406\u7531<\/h2>\n<p>\u6b63\u76f4\u306b\u8a00\u3046\u3002\u6700\u521d\u306e1\u30f6\u6708\u306f\u30ad\u30e3\u30c3\u30b7\u30e5\u8a2d\u5b9a\u3092\u96d1\u306b\u3084\u3063\u3066\u3044\u305f\u3002<code>actions\/setup-python@v5<\/code>\u306e<code>cache: 'pip'<\/code>\u3060\u3051\u8a2d\u5b9a\u3057\u3066\u300c\u30ad\u30e3\u30c3\u30b7\u30e5\u3057\u3066\u308b\u304b\u3089\u5927\u4e08\u592b\u300d\u3068\u601d\u3063\u3066\u3044\u305f\u3002\u3042\u308b\u65e5\u30c1\u30fc\u30e0\u30e1\u30f3\u30d0\u30fc\u304c\u300cPR\u51fa\u3059\u305f\u3073\u306b3\u5206\u4ee5\u4e0a\u304b\u304b\u308b\u3093\u3067\u3059\u3051\u3069\u3001\u3053\u308c\u666e\u901a\u3067\u3059\u304b?\u300d\u3068\u805e\u3044\u3066\u304d\u3066\u3001\u521d\u3081\u3066\u3061\u3083\u3093\u3068\u8abf\u3079\u305f\u3002<\/p>\n<p><code>cache: 'pip'<\/code>\u306fpip\u306e\u30b0\u30ed\u30fc\u30d0\u30eb\u30c0\u30a6\u30f3\u30ed\u30fc\u30c9\u30ad\u30e3\u30c3\u30b7\u30e5\u3092\u4fdd\u5b58\u3059\u308b\u3060\u3051\u3067\u3001\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u6e08\u307f\u30d1\u30c3\u30b1\u30fc\u30b8\u3092\u30ad\u30e3\u30c3\u30b7\u30e5\u3059\u308b\u308f\u3051\u3067\u306f\u306a\u3044\u3002\u3064\u307e\u308a<code>requirements.txt<\/code>\u304c\u5909\u308f\u3063\u3066\u3044\u306a\u304f\u3066\u3082\u3001\u6bce\u56de\u30d1\u30c3\u30b1\u30fc\u30b8\u3092\u518d\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u3059\u308b\u3002\u30cd\u30c3\u30c8\u30ef\u30fc\u30af\u5f80\u5fa9\u306f\u6e1b\u308b\u304c\u3001\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u6642\u9593\u305d\u306e\u3082\u306e\u306f\u307b\u307c\u5909\u308f\u3089\u306a\u3044\u3002<\/p>\n<p>\u4eee\u60f3\u74b0\u5883\u3054\u3068\u30ad\u30e3\u30c3\u30b7\u30e5\u3059\u308b\u306e\u304c\u305a\u3063\u3068\u3088\u3044:<\/p>\n<pre><code class=\"language-yaml\">- name: \u4f9d\u5b58\u95a2\u4fc2\u30ad\u30e3\u30c3\u30b7\u30e5\n  uses: actions\/cache@v4\n  id: cache-venv\n  with:\n    path: ~\/.venv\n    key: venv-${{ runner.os }}-${{ matrix.python-version }}-${{ hashFiles('requirements*.txt') }}\n    restore-keys: |\n      venv-${{ runner.os }}-${{ matrix.python-version }}-\n\n- name: \u4eee\u60f3\u74b0\u5883\u3092\u4f5c\u6210\u3057\u3066\u4f9d\u5b58\u95a2\u4fc2\u3092\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\n  if: steps.cache-venv.outputs.cache-hit != 'true'\n  run: |\n    python -m venv ~\/.venv\n    ~\/.venv\/bin\/pip install --upgrade pip\n    ~\/.venv\/bin\/pip install -r requirements.txt\n    ~\/.venv\/bin\/pip install -r requirements-dev.txt\n\n- name: \u4eee\u60f3\u74b0\u5883\u3092PATH\u306b\u8ffd\u52a0\n  run: echo &quot;$HOME\/.venv\/bin&quot; &gt;&gt; $GITHUB_PATH\n<\/code><\/pre>\n<p><code>hashFiles('requirements*.txt')<\/code>\u3092\u30ad\u30e3\u30c3\u30b7\u30e5\u30ad\u30fc\u306b\u542b\u3081\u308b\u3053\u3068\u3067\u3001<code>requirements.txt<\/code>\u304b<code>requirements-dev.txt<\/code>\u304c\u5909\u308f\u3063\u305f\u3068\u304d\u3060\u3051\u30ad\u30e3\u30c3\u30b7\u30e5\u304c\u7121\u52b9\u5316\u3055\u308c\u308b\u3002\u3053\u306e\u5909\u66f4\u5f8c\u3001\u5e73\u5747\u5b9f\u884c\u6642\u9593\u304c3\u520610\u79d2\u304b\u308950\u79d2\u304f\u3089\u3044\u307e\u3067\u843d\u3061\u305f\u3002\u3046\u3061\u306e\u30ea\u30dd\u30b8\u30c8\u30ea\u306fPR\u304c1\u65e5\u306b\u5341\u6570\u56de\u8d70\u308b\u306e\u3067\u3001Actions\u306e\u6d88\u8cbb\u5206\u6570\u304c\u4f53\u611f\u3067\u304d\u308b\u304f\u3089\u3044\u6e1b\u3063\u305f\u3002<\/p>\n<p><code>restore-keys<\/code>\u306e\u66f8\u304d\u65b9\u3082\u91cd\u8981\u3002\u5b8c\u5168\u4e00\u81f4\u3059\u308b\u30ad\u30e3\u30c3\u30b7\u30e5\u304c\u306a\u3044\u5834\u5408\u3001\u90e8\u5206\u30de\u30c3\u30c1\u3067\u4ee5\u524d\u306e\u30ad\u30e3\u30c3\u30b7\u30e5\u3092\u4f7f\u3046\u3002\u30d1\u30c3\u30b1\u30fc\u30b8\u30921\u500b\u8ffd\u52a0\u3057\u305f\u5834\u5408\u3067\u3082\u3001\u65e2\u5b58\u306e\u4eee\u60f3\u74b0\u5883\u3092\u518d\u5229\u7528\u3057\u3066diff\u3060\u3051\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u3059\u308b\u306e\u3067\u3001\u5b8c\u5168\u306a\u518d\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u3088\u308a\u305a\u3063\u3068\u901f\u3044\u3002<\/p>\n<p>Poetry\u3084PDM\u3092\u4f7f\u3063\u3066\u3044\u308b\u306a\u3089\u3001<code>pyproject.toml<\/code>\u3068lock\u30d5\u30a1\u30a4\u30eb\u3092\u30cf\u30c3\u30b7\u30e5\u306b\u542b\u3081\u308b\u3002<code>hashFiles('pyproject.toml', 'poetry.lock')<\/code>\u306e\u3088\u3046\u306b\u3002lock\u30d5\u30a1\u30a4\u30eb\u3092\u542b\u3081\u306a\u3044\u3068\u3001\u4f9d\u5b58\u95a2\u4fc2\u306e\u30d0\u30fc\u30b8\u30e7\u30f3\u304c\u5909\u308f\u3063\u3066\u3082\u30ad\u30e3\u30c3\u30b7\u30e5\u304c\u66f4\u65b0\u3055\u308c\u306a\u3044\u30ea\u30b9\u30af\u304c\u3042\u308b\u3002<\/p>\n<h2>\u30c6\u30b9\u30c8\u30ab\u30d0\u30ec\u30c3\u30b8\u3068PR\u3078\u306e\u81ea\u52d5\u30b3\u30e1\u30f3\u30c8<\/h2>\n<p>\u30c6\u30b9\u30c8\u304c\u901a\u3063\u3066\u3044\u308b\u3060\u3051\u3067\u306f\u4e0d\u5341\u5206\u3060\u3068\u6c17\u3065\u3044\u305f\u306e\u306f\u3001\u30ab\u30d0\u30ec\u30c3\u30b8\u304c1\u30f6\u6708\u3067\u3058\u308f\u3058\u308f\u4e0b\u304c\u3063\u3066\u3044\u304f\u306e\u3092\u5f8c\u304b\u3089\u767a\u898b\u3057\u305f\u3068\u304d\u3060\u3063\u305f\u3002\u30b3\u30fc\u30c9\u306f\u5897\u3048\u3066\u3044\u308b\u306e\u306b\u30c6\u30b9\u30c8\u304c\u8ffd\u3044\u3064\u3044\u3066\u3044\u306a\u3044\u3001\u3088\u304f\u3042\u308b\u30d1\u30bf\u30fc\u30f3\u3002\u30ed\u30fc\u30ab\u30eb\u3067<code>pytest --cov<\/code>\u3092\u81ea\u5206\u304b\u3089\u5b9f\u884c\u3059\u308b\u4eba\u306f\u5c11\u306a\u3044\u3002<\/p>\n<p>PR\u306b\u30ab\u30d0\u30ec\u30c3\u30b8\u30ec\u30dd\u30fc\u30c8\u3092\u81ea\u52d5\u3067\u30b3\u30e1\u30f3\u30c8\u3068\u3057\u3066\u8ffd\u52a0\u3059\u308b\u3068\u3001\u30ec\u30d3\u30e5\u30fc\u6642\u306b\u5acc\u3067\u3082\u76ee\u306b\u5165\u308b:<\/p>\n<pre><code class=\"language-yaml\">- name: \u30ab\u30d0\u30ec\u30c3\u30b8\u4ed8\u304d\u3067\u30c6\u30b9\u30c8\u5b9f\u884c\n  run: |\n    pytest tests\/ \\\n      --cov=src \\\n      --cov-report=xml \\\n      --cov-report=term-missing \\\n      --cov-fail-under=75\n\n- name: \u30ab\u30d0\u30ec\u30c3\u30b8\u3092PR\u306b\u30b3\u30e1\u30f3\u30c8\n  uses: MishaKav\/pytest-coverage-comment@v1.3.2\n  if: github.event_name == 'pull_request'\n  with:\n    pytest-xml-coverage-path: .\/coverage.xml\n    title: \u30c6\u30b9\u30c8\u30ab\u30d0\u30ec\u30c3\u30b8\n    create-new-comment: false       # \u65e2\u5b58\u30b3\u30e1\u30f3\u30c8\u3092\u66f4\u65b0\uff08\u65b0\u898f\u4f5c\u6210\u3057\u306a\u3044\uff09\n    report-only-changed-files: true # \u3053\u306ePR\u3067\u5909\u66f4\u3057\u305f\u30d5\u30a1\u30a4\u30eb\u3060\u3051\u8868\u793a\n<\/code><\/pre>\n<p><code>report-only-changed-files: true<\/code>\u304c\u5730\u5473\u306b\u4fbf\u5229\u3067\u3001PR\u5168\u4f53\u306e\u30ab\u30d0\u30ec\u30c3\u30b8\u3067\u306f\u306a\u304f\u300c\u3053\u306ePR\u3067\u5909\u66f4\u3057\u305f\u30b3\u30fc\u30c9\u306e\u30ab\u30d0\u30ec\u30c3\u30b8\u300d\u3092\u8868\u793a\u3059\u308b\u3002\u30ec\u30d3\u30e5\u30a2\u30fc\u304c\u300c\u3053\u306e\u30b3\u30fc\u30c9\u8ffd\u52a0\u3057\u305f\u3051\u3069\u30c6\u30b9\u30c8\u306f?\u300d\u3092\u4e00\u76ee\u3067\u78ba\u8a8d\u3067\u304d\u308b\u3002<\/p>\n<p><code>--cov-fail-under<\/code>\u306e\u6570\u5024\u306b\u3064\u3044\u3066: \u3046\u3061\u306f75%\u306b\u8a2d\u5b9a\u3057\u3066\u3044\u308b\u3051\u3069\u3001\u9069\u5207\u306a\u5024\u306f\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u306b\u3088\u308b\u3002\u30ec\u30ac\u30b7\u30fc\u30b3\u30fc\u30c9\u3092\u62b1\u3048\u3066\u3044\u308b\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u3067\u3044\u304d\u306a\u308a80%\u3092\u8a2d\u5b9a\u3059\u308b\u3068\u8ab0\u3082PR\u3092\u51fa\u305b\u306a\u304f\u306a\u308b\u300260%\u304f\u3089\u3044\u304b\u3089\u59cb\u3081\u3066\u3001\u30c6\u30b9\u30c8\u3092\u66f8\u304f\u6587\u5316\u304c\u5b9a\u7740\u3057\u305f\u3089\u4e0a\u3052\u3066\u3044\u304f\u65b9\u304c\u73fe\u5b9f\u7684\u3060\u3068\u601d\u3046\u3002<\/p>\n<p>\u3042\u3068\u3001\u5730\u5473\u306b\u91cd\u8981\u306a\u30dd\u30a4\u30f3\u30c8: \u30b5\u30fc\u30c9\u30d1\u30fc\u30c6\u30a3\u306eAction\u306f<code>@main<\/code>\u3067\u56fa\u5b9a\u3057\u306a\u3044\u3053\u3068\u3002<code>@v1.3.2<\/code>\u306e\u3088\u3046\u306b\u5177\u4f53\u7684\u306a\u30d0\u30fc\u30b8\u30e7\u30f3\u30bf\u30b0\u3092\u4f7f\u3046\u3002<code>@main<\/code>\u306b\u3057\u3066\u304a\u304f\u3068\u30a2\u30c3\u30d7\u30b9\u30c8\u30ea\u30fc\u30e0\u306e\u5909\u66f4\u3067\u7a81\u7136\u30d1\u30a4\u30d7\u30e9\u30a4\u30f3\u304c\u58ca\u308c\u308b\u3002\u5b9f\u969b\u306b\u4e00\u5ea6\u3084\u3089\u308c\u3066\u3001\u539f\u56e0\u3092\u7279\u5b9a\u3059\u308b\u306e\u306b30\u5206\u304b\u304b\u3063\u305f\u3002<\/p>\n<h2>\u74b0\u5883\u5225\u30c7\u30d7\u30ed\u30a4: \u30b9\u30c6\u30fc\u30b8\u30f3\u30b0\u81ea\u52d5\u2192\u672c\u756a\u624b\u52d5\u627f\u8a8d<\/h2>\n<p>\u30c7\u30d7\u30ed\u30a4\u30d1\u30a4\u30d7\u30e9\u30a4\u30f3\u3067\u4e00\u756a\u60a9\u3093\u3060\u306e\u304c\u300c\u3069\u3053\u307e\u3067\u81ea\u52d5\u5316\u3059\u308b\u304b\u300d\u3060\u3063\u305f\u3002\u30b9\u30c6\u30fc\u30b8\u30f3\u30b0\u306f\u5168\u81ea\u52d5\u3001\u672c\u756a\u306f\u4eba\u9593\u304c\u78ba\u8a8d\u3057\u3066\u304b\u3089 \u2014 \u3053\u306e\u69cb\u6210\u304c\u3046\u3061\u306e\u30c1\u30fc\u30e0\u306b\u306f\u5408\u3063\u3066\u3044\u305f\u3002GitHub Environments\u304c\u307e\u3055\u306b\u3053\u306e\u7528\u9014\u306b\u5411\u3044\u3066\u3044\u308b\u3002<\/p>\n<p>\u30ea\u30dd\u30b8\u30c8\u30ea\u306eSettings \u2192 Environments\u3067<code>staging<\/code>\u3068<code>production<\/code>\u3092\u4f5c\u6210\u3057\u3001<code>production<\/code>\u306b\u3060\u3051Required reviewers\u3092\u8a2d\u5b9a\u3059\u308b\u3002<a href=\"https:\/\/m.do.co\/c\/06956e5e2802\" title=\"DigitalOcean for Production Workloads\" rel=\"nofollow sponsored\" target=\"_blank\">production<\/a> deploy\u306e\u30b8\u30e7\u30d6\u304c\u30c8\u30ea\u30ac\u30fc\u3055\u308c\u308b\u3068\u3001GitHub UI\u3067\u30c1\u30fc\u30e0\u30e1\u30f3\u30d0\u30fc\u304c\u300cApprove\u300d\u3092\u62bc\u3059\u307e\u3067\u5f85\u6a5f\u72b6\u614b\u306b\u306a\u308b\u3002<\/p>\n<pre><code class=\"language-yaml\"># .github\/workflows\/deploy.yml\nname: <a href=\"https:\/\/m.do.co\/c\/06956e5e2802\" title=\"Deploy on DigitalOcean Cloud\" rel=\"nofollow sponsored\" target=\"_blank\">Deploy<\/a>\n\non:\n  push:\n    branches: [main]\n\nenv:\n  REGISTRY: ghcr.io\n  IMAGE_NAME: ${{ github.repository }}\n\njobs:\n  build-image:\n    runs-on: ubuntu-latest\n    permissions:\n      contents: read\n      packages: write\n    outputs:\n      image-tag: ${{ steps.meta.outputs.version }}\n    steps:\n      - uses: actions\/checkout@v4\n\n      - name: Container Registry\u306b\u30ed\u30b0\u30a4\u30f3\n        uses: docker\/login-action@v3\n        with:\n          registry: ${{ env.REGISTRY }}\n          username: ${{ github.actor }}\n          password: ${{ secrets.GITHUB_TOKEN }}\n\n      - name: \u30e1\u30bf\u30c7\u30fc\u30bf\u3092\u53d6\u5f97\n        id: meta\n        uses: docker\/metadata-action@v5\n        with:\n          images: ${{ env.REGISTRY }}\/${{ env.IMAGE_NAME }}\n          tags: |\n            type=sha,prefix=sha-\n\n      - name: Docker\u30a4\u30e1\u30fc\u30b8\u3092\u30d3\u30eb\u30c9\u3057\u3066\u30d7\u30c3\u30b7\u30e5\n        uses: docker\/build-push-action@v6\n        with:\n          context: .\n          push: true\n          tags: ${{ steps.meta.outputs.tags }}\n          cache-from: type=gha\n          cache-to: type=gha,mode=max\n\n  <a href=\"https:\/\/m.do.co\/c\/06956e5e2802\" title=\"Deploy on DigitalOcean Cloud\" rel=\"nofollow sponsored\" target=\"_blank\">deploy<\/a>-staging:\n    needs: build-image\n    runs-on: ubuntu-latest\n    environment: staging\n    steps:\n      - name: \u30b9\u30c6\u30fc\u30b8\u30f3\u30b0\u306b<a href=\"https:\/\/m.do.co\/c\/06956e5e2802\" title=\"DigitalOcean\u3067\u30c7\u30d7\u30ed\u30a4\" rel=\"nofollow sponsored\" target=\"_blank\">\u30c7\u30d7\u30ed\u30a4<\/a>\n        env:\n          IMAGE_TAG: ${{ needs.build-image.outputs.image-tag }}\n        run: |\n          # <a href=\"https:\/\/m.do.co\/c\/06956e5e2802\" title=\"DigitalOcean\u3067\u30c7\u30d7\u30ed\u30a4\" rel=\"nofollow sponsored\" target=\"_blank\">\u30c7\u30d7\u30ed\u30a4<\/a>\u30b9\u30af\u30ea\u30d7\u30c8\u306f\u3053\u3053\u306b\n          echo &quot;Deploying $IMAGE_TAG to staging&quot;\n\n  <a href=\"https:\/\/m.do.co\/c\/06956e5e2802\" title=\"Deploy on DigitalOcean Cloud\" rel=\"nofollow sponsored\" target=\"_blank\">deploy<\/a>-<a href=\"https:\/\/m.do.co\/c\/06956e5e2802\" title=\"DigitalOcean for <a href=\"https:\/\/m.do.co\/c\/06956e5e2802\" title=\"DigitalOcean for <a href=\"https:\/\/m.do.co\/c\/06956e5e2802\" title=\"DigitalOcean for Production Workloads\" rel=\"nofollow sponsored\" target=\"_blank\">Production<\/a> Workloads\" rel=\"nofollow sponsored\" target=\"_blank\">Production<\/a> Workloads\" rel=\"nofollow sponsored\" target=\"_blank\">production<\/a>:\n    needs: <a href=\"https:\/\/m.do.co\/c\/06956e5e2802\" title=\"Deploy on DigitalOcean Cloud\" rel=\"nofollow sponsored\" target=\"_blank\">deploy<\/a>-staging\n    runs-on: ubuntu-latest\n    environment: <a href=\"https:\/\/m.do.co\/c\/06956e5e2802\" title=\"DigitalOcean for Production Workloads\" rel=\"nofollow sponsored\" target=\"_blank\">production<\/a>  # \u3053\u3053\u3067\u624b\u52d5\u627f\u8a8d\u5f85\u3061\n    steps:\n      - name: \u672c\u756a\u306b<a href=\"https:\/\/m.do.co\/c\/06956e5e2802\" title=\"DigitalOcean\u3067\u30c7\u30d7\u30ed\u30a4\" rel=\"nofollow sponsored\" target=\"_blank\">\u30c7\u30d7\u30ed\u30a4<\/a>\n        env:\n          IMAGE_TAG: ${{ needs.build-image.outputs.image-tag }}\n        run: |\n          echo &quot;Deploying $IMAGE_TAG to <a href=\"https:\/\/m.do.co\/c\/06956e5e2802\" title=\"DigitalOcean for Production Workloads\" rel=\"nofollow sponsored\" target=\"_blank\">production<\/a>&quot;\n<\/code><\/pre>\n<p><code>cache-from: type=gha<\/code>\u3068<code>cache-to: type=gha,mode=max<\/code>\u306fGitHub Actions\u306e\u30ad\u30e3\u30c3\u30b7\u30e5\u3092Docker\u306e\u30ec\u30a4\u30e4\u30fc\u30ad\u30e3\u30c3\u30b7\u30e5\u3068\u3057\u3066\u4f7f\u3046\u8a2d\u5b9a\u3002\u4f9d\u5b58\u95a2\u4fc2\u304c\u5909\u308f\u3063\u3066\u3044\u306a\u3044\u5834\u5408\u3001Docker\u30d3\u30eb\u30c9\u304c3\u5206\u304b\u308940\u79d2\u304f\u3089\u3044\u306b\u77ed\u7e2e\u3055\u308c\u305f\u3002<\/p>\n<p>GitHub Container Registry\uff08ghcr.io\uff09\u306f\u540c\u3058\u30ea\u30dd\u30b8\u30c8\u30ea\u306eActions\u304b\u3089<code>GITHUB_TOKEN<\/code>\u3067\u8a8d\u8a3c\u3067\u304d\u308b\u306e\u3067\u3001\u5225\u9014\u30b7\u30fc\u30af\u30ec\u30c3\u30c8\u3092\u8a2d\u5b9a\u3057\u306a\u304f\u3066\u3044\u3044\u3002Docker Hub\u3092\u4f7f\u3046\u5834\u5408\u306f<code>DOCKERHUB_USERNAME<\/code>\u3068<code>DOCKERHUB_TOKEN<\/code>\u304c\u5fc5\u8981\u306b\u306a\u308b\u3002<\/p>\n<p>\u5404\u74b0\u5883\u3054\u3068\u306b\u5225\u306eSecrets\u3092\u6301\u3066\u308b\u306e\u3082\u4fbf\u5229\u3067\u3001<code>staging<\/code>\u3068<code>production<\/code>\u3067\u540c\u3058\u30ad\u30fc\u540d\uff08<code>DATABASE_URL<\/code>\u3068\u304b\uff09\u3092\u5225\u306e\u5024\u3067\u7ba1\u7406\u3067\u304d\u308b\u3002<\/p>\n<h2>\u5b9f\u969b\u306b\u3084\u3089\u304b\u3057\u305f\u30df\u30b9\u3068\u3001\u305d\u3053\u304b\u3089\u5b66\u3093\u3060\u3053\u3068<\/h2>\n<p>\u30d1\u30a4\u30d7\u30e9\u30a4\u30f3\u306e\u30c7\u30d0\u30c3\u30b0\u306f\u30ed\u30fc\u30ab\u30eb\u3067\u307b\u307c\u3067\u304d\u306a\u3044\u3002<code>act<\/code>\u3068\u3044\u3046\u30c4\u30fc\u30eb\u3092\u4f7f\u3048\u3070\u90e8\u5206\u7684\u306b\u306f\u3067\u304d\u308b\u3051\u3069\u3001GitHub<a href=\"https:\/\/m.do.co\/c\/06956e5e2802\" title=\"DigitalOcean <a href=\"https:\/\/m.do.co\/c\/06956e5e2802\" title=\"DigitalOcean <a href=\"https:\/\/m.do.co\/c\/06956e5e2802\" title=\"DigitalOcean <a href=\"https:\/\/m.do.co\/c\/06956e5e2802\" title=\"DigitalOcean <a href=\"https:\/\/m.do.co\/c\/06956e5e2802\" title=\"DigitalOcean <a href=\"https:\/\/m.do.co\/c\/06956e5e2802\" title=\"DigitalOcean \u672c\u756a\u74b0\u5883\u30af\u30e9\u30a6\u30c9\" rel=\"nofollow sponsored\" target=\"_blank\">\u672c\u756a\u74b0\u5883<\/a>\u30af\u30e9\u30a6\u30c9&#8221; rel=&#8221;nofollow sponsored&#8221; target=&#8221;_blank&#8221;>\u672c\u756a\u74b0\u5883<\/a>\u30af\u30e9\u30a6\u30c9&#8221; rel=&#8221;nofollow sponsored&#8221; target=&#8221;_blank&#8221;>\u672c\u756a\u74b0\u5883<\/a>\u30af\u30e9\u30a6\u30c9&#8221; rel=&#8221;nofollow sponsored&#8221; target=&#8221;_blank&#8221;>\u672c\u756a\u74b0\u5883<\/a>\u30af\u30e9\u30a6\u30c9&#8221; rel=&#8221;nofollow sponsored&#8221; target=&#8221;_blank&#8221;>\u672c\u756a\u74b0\u5883<\/a>\u30af\u30e9\u30a6\u30c9&#8221; rel=&#8221;nofollow sponsored&#8221; target=&#8221;_blank&#8221;>\u672c\u756a\u74b0\u5883<\/a>\u3068\u306e\u5dee\u7570\u304c\u3042\u308b\u306e\u3067\u3001\u6700\u7d42\u7684\u306b\u306f\u30b3\u30df\u30c3\u30c8\u3057\u3066CI\u3092\u56de\u3059\u3057\u304b\u306a\u3044\u3002\u3053\u308c\u304c\u5730\u5473\u306b\u3064\u3089\u304f\u3066\u3001\u30c7\u30d0\u30c3\u30b0\u306e\u30b3\u30df\u30c3\u30c8\u304cGit\u5c65\u6b74\u306b\u6b8b\u308a\u7d9a\u3051\u308b\u3002<\/p>\n<p><strong>\u30bf\u30a4\u30e0\u30a2\u30a6\u30c8\u8a2d\u5b9a\u3092\u6700\u521d\u304b\u3089\u5165\u308c\u308b<\/strong><\/p>\n<p>GitHub Actions\u306e\u30c7\u30d5\u30a9\u30eb\u30c8\u30bf\u30a4\u30e0\u30a2\u30a6\u30c8\u306f6\u6642\u9593\u3002\u3042\u308b\u3068\u304d\u7d71\u5408\u30c6\u30b9\u30c8\u304cflaky\u72b6\u614b\u306b\u306a\u3063\u3066\u3001\u5916\u90e8API\u306e\u5fdc\u7b54\u5f85\u3061\u3067\u6b62\u307e\u3063\u305f\u30b8\u30e7\u30d6\u304c6\u6642\u9593\u56de\u308a\u7d9a\u3051\u305f\u3002\u7fcc\u671dActions\u306e\u6d88\u8cbb\u5206\u6570\u3092\u898b\u3066\u6c17\u3065\u3044\u305f\u3002\u305d\u308c\u4ee5\u6765\u3001\u3059\u3079\u3066\u306e\u30b8\u30e7\u30d6\u306b<code>timeout-minutes<\/code>\u3092\u8a2d\u5b9a\u3057\u3066\u3044\u308b:<\/p>\n<pre><code class=\"language-yaml\">jobs:\n  test:\n    runs-on: ubuntu-latest\n    timeout-minutes: 20  # \u30c6\u30b9\u30c8\u304c20\u5206\u3092\u8d85\u3048\u305f\u3089\u4f55\u304b\u304c\u304a\u304b\u3057\u3044\n<\/code><\/pre>\n<p><strong><code>if<\/code>\u5f0f\u3067\u30b7\u30fc\u30af\u30ec\u30c3\u30c8\u3092\u4f7f\u304a\u3046\u3068\u3057\u305f<\/strong><\/p>\n<p>\u30b8\u30e7\u30d6\u306e\u5b9f\u884c\u6761\u4ef6\u306b\u30b7\u30fc\u30af\u30ec\u30c3\u30c8\u3092\u4f7f\u304a\u3046\u3068\u3057\u305f\u3053\u3068\u304c\u3042\u308b:<\/p>\n<pre><code class=\"language-yaml\"># \u3053\u308c\u306f\u52d5\u304b\u306a\u3044\nif: ${{ secrets.DEPLOY_TOKEN != '' }}\n<\/code><\/pre>\n<p>\u30bb\u30ad\u30e5\u30ea\u30c6\u30a3\u4e0a\u306e\u7406\u7531\u3067\u30b7\u30fc\u30af\u30ec\u30c3\u30c8\u306f<code>if<\/code>\u5f0f\u306e\u4e2d\u3067\u8a55\u4fa1\u3055\u308c\u306a\u3044\u3002\u4ee3\u308f\u308a\u306b<code>vars<\/code>\uff08\u30b7\u30fc\u30af\u30ec\u30c3\u30c8\u3067\u306f\u306a\u3044\u5909\u6570\uff09\u3092\u4f7f\u3046\u304b\u3001\u30b8\u30e7\u30d6\u306e\u8a2d\u8a08\u3092\u898b\u76f4\u3059\u5fc5\u8981\u304c\u3042\u308b\u3002\u3053\u308c\u306b\u6c17\u3065\u304b\u305a\u306b30\u5206\u304f\u3089\u3044\u300c\u306a\u305c\u6761\u4ef6\u5206\u5c90\u304c\u52b9\u304b\u306a\u3044\u3093\u3060\u300d\u3068\u60a9\u3093\u3060\u3002<\/p>\n<p><strong><code>GITHUB_TOKEN<\/code>\u306e\u6a29\u9650\u306f\u6700\u5c0f\u5316\u3059\u308b<\/strong><\/p>\n<p>\u30c7\u30d5\u30a9\u30eb\u30c8\u306e<code>GITHUB_TOKEN<\/code>\u306f\u601d\u3063\u305f\u3088\u308a\u5e83\u3044\u6a29\u9650\u3092\u6301\u3063\u3066\u3044\u308b\u3002\u30ef\u30fc\u30af\u30d5\u30ed\u30fc\u30d5\u30a1\u30a4\u30eb\u306b\u660e\u793a\u7684\u306b\u66f8\u3044\u3066\u304a\u304f:<\/p>\n<pre><code class=\"language-yaml\">permissions:\n  contents: read\n  pull-requests: write  # PR\u30b3\u30e1\u30f3\u30c8\u306e\u305f\u3081\u306b\u5fc5\u8981\n  packages: write       # ghcr.io\u30d7\u30c3\u30b7\u30e5\u306e\u305f\u3081\u306b\u5fc5\u8981\n<\/code><\/pre>\n<p>\u5fc5\u8981\u306a\u6a29\u9650\u3060\u3051\u3092\u5ba3\u8a00\u3059\u308b\u3053\u3068\u3067\u3001\u4e07\u304c\u4e00\u306e\u30b5\u30d7\u30e9\u30a4\u30c1\u30a7\u30fc\u30f3\u653b\u6483\u306e\u30ea\u30b9\u30af\u3082\u5c11\u3057\u4e0b\u304c\u308b\u3002\u3053\u308c\u306f\u6700\u521d\u304b\u3089\u7fd2\u6163\u306b\u3057\u305f\u65b9\u304c\u3044\u3044\u3002<\/p>\n<p><strong>\u30b5\u30d6\u30e2\u30b8\u30e5\u30fc\u30eb\u306e\u8a2d\u5b9a\u6f0f\u308c<\/strong><\/p>\n<p>\u3053\u308c\u306f\u5c11\u3057\u6065\u305a\u304b\u3057\u3044\u8a71\u306a\u3093\u3060\u3051\u3069 \u2014 submodule\u3092\u542b\u3080\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u3067<code>actions\/checkout@v4<\/code>\u3092\u4f7f\u3063\u305f\u3068\u304d\u3001<code>submodules: recursive<\/code>\u3092\u4ed8\u3051\u5fd8\u308c\u305f\u3002\u30ed\u30fc\u30ab\u30eb\u3067\u306f\u6b63\u5e38\u306b\u52d5\u304f\u306e\u306b\u3001CI\u4e0a\u3067\u306e\u307f<code>ModuleNotFoundError<\/code>\u304c\u51fa\u7d9a\u3051\u305f\u3002\u30a8\u30e9\u30fc\u30e1\u30c3\u30bb\u30fc\u30b8\u304c\u30e2\u30b8\u30e5\u30fc\u30eb\u306e\u554f\u984c\u3092\u793a\u3057\u3066\u3044\u308b\u306e\u3067\u3001\u5168\u7136\u95a2\u4fc2\u306a\u3044\u65b9\u5411\u30671\u6642\u9593\u4ee5\u4e0a\u8abf\u67fb\u3057\u3066\u3044\u305f\u3002\u5b9f\u969b\u306e\u4fee\u6b63\u306f1\u884c\u3060\u3063\u305f:<\/p>\n<pre><code class=\"language-yaml\">- uses: actions\/checkout@v4\n  with:\n    submodules: recursive\n<\/code><\/pre>\n<p>Right, so \u2014 \u3053\u3046\u3044\u3046\u30df\u30b9\u306f\u8a2d\u5b9a\u3057\u305f\u76f4\u5f8c\u3067\u306f\u306a\u304f\u3001\u3057\u3070\u3089\u304f\u3057\u3066\u304b\u3089\u300c\u3042\u308c\u3001\u3053\u308c\u305a\u3063\u3068\u304a\u304b\u3057\u304f\u306a\u304b\u3063\u305f?\u300d\u3068\u6c17\u3065\u304f\u30d1\u30bf\u30fc\u30f3\u304c\u591a\u3044\u3002\u30ef\u30fc\u30af\u30d5\u30ed\u30fc\u306e\u30ed\u30b0\u3092\u5b9a\u671f\u7684\u306b\u898b\u308b\u7fd2\u6163\u304c\u5927\u4e8b\u3060\u3068\u601d\u3046\u3002<\/p>\n<h2>\u7d50\u5c40\u3001\u79c1\u304c\u4eca\u3084\u3063\u3066\u3044\u308b\u69cb\u6210<\/h2>\n<p>3\u30f6\u6708\u307b\u3069\u3053\u306e\u8a2d\u5b9a\u3067\u904b\u7528\u3057\u3066\u3001\u843d\u3061\u7740\u3044\u305f\u69cb\u6210\u3092\u307e\u3068\u3081\u308b\u3002<\/p>\n<p>\u30c6\u30b9\u30c8\u306f3\u30d0\u30fc\u30b8\u30e7\u30f3\uff083.10\u30013.11\u30013.12\uff09\u306e\u30de\u30c8\u30ea\u30c3\u30af\u30b9\u5b9f\u884c\u3002\u305f\u3060\u3057\u3001\u3053\u308c\u306f\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3067\u306f\u306a\u304f\u30e9\u30a4\u30d6\u30e9\u30ea\u3092\u4f5c\u3063\u3066\u3044\u308b\u5834\u5408\u306e\u8a71\u3002\u30b5\u30fc\u30d3\u30b9\u306a\u3089<a href=\"https:\/\/m.do.co\/c\/06956e5e2802\" title=\"DigitalOcean \u672c\u756a\u74b0\u5883\u30af\u30e9\u30a6\u30c9\" rel=\"nofollow sponsored\" target=\"_blank\">\u672c\u756a\u74b0\u5883<\/a>\u306b\u5408\u308f\u305b\u305f1\u30d0\u30fc\u30b8\u30e7\u30f3\u3060\u3051\u3067\u3044\u3044\u3002\u79c1\u306f\u6700\u521d\u3001\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306a\u306e\u306b3\u30d0\u30fc\u30b8\u30e7\u30f3\u5168\u90e8\u30c6\u30b9\u30c8\u3057\u3066\u3001Actions\u306e\u6d88\u8cbb\u5206\u6570\u3092\u7121\u99c4\u306b\u3057\u3066\u3044\u305f\u3002<\/p>\n<p>\u30ab\u30d0\u30ec\u30c3\u30b8\u306e\u4e0b\u9650\u306f75%\u3002\u7406\u60f3\u3092\u8a00\u3048\u307080%\u307e\u3067\u4e0a\u3052\u305f\u3044\u3051\u3069\u3001\u4eca\u306e\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u3067\u306f\u305d\u3053\u307e\u3067\u9054\u3057\u3066\u3044\u306a\u3044\u3002\u7126\u3063\u3066\u6570\u5b57\u3092\u4e0a\u3052\u308b\u3088\u308a\u3001\u30c6\u30b9\u30c8\u3092\u66f8\u304f\u6587\u5316\u3092\u5b9a\u7740\u3055\u305b\u308b\u65b9\u304c\u9577\u671f\u7684\u306b\u610f\u5473\u304c\u3042\u308b\u3002<\/p>\n<p>\u30c7\u30d7\u30ed\u30a4\u306f\u30b9\u30c6\u30fc\u30b8\u30f3\u30b0\u81ea\u52d5\u3001\u672c\u756a\u624b\u52d5\u627f\u8a8d\u306e2\u6bb5\u69cb\u3048\u3002\u305f\u3060\u3001\u3053\u308c\u304c10\u4eba\u8d85\u3048\u306e\u30c1\u30fc\u30e0\u306b\u901a\u7528\u3059\u308b\u304b\u3068\u3044\u3046\u3068\u81ea\u4fe1\u304c\u306a\u3044 \u2014 \u4eba\u6570\u304c\u5897\u3048\u3066<a href=\"https:\/\/m.do.co\/c\/06956e5e2802\" title=\"DigitalOcean\u3067\u30c7\u30d7\u30ed\u30a4\" rel=\"nofollow sponsored\" target=\"_blank\">\u30c7\u30d7\u30ed\u30a4<\/a>\u983b\u5ea6\u304c\u4e0a\u304c\u3063\u305f\u3068\u304d\u306b\u624b\u52d5\u627f\u8a8d\u304c\u30dc\u30c8\u30eb\u30cd\u30c3\u30af\u306b\u306a\u308b\u53ef\u80fd\u6027\u306f\u3042\u308b\u3002\u4eca\u306e\u3046\u3061\u306e\u30c1\u30fc\u30e0\u30b5\u30a4\u30ba\uff083\u4eba\uff09\u3067\u306f\u3001\u300c\u8ab0\u304b\u304c\u78ba\u8a8d\u3057\u305f\u300d\u3068\u3044\u3046\u5b89\u5fc3\u611f\u306e\u65b9\u304c\u30b9\u30d4\u30fc\u30c9\u3088\u308a\u4fa1\u5024\u304c\u3042\u308b\u3002<\/p>\n<p>pytest\u306e\u30aa\u30d7\u30b7\u30e7\u30f3\u306f<code>pyproject.toml<\/code>\u306b\u66f8\u3044\u3066\u304a\u304f\u3053\u3068\u3092\u5f37\u304f\u3059\u3059\u3081\u308b:<\/p>\n<pre><code class=\"language-toml\">[tool.pytest.ini_options]\ntestpaths = [&quot;tests&quot;]\naddopts = &quot;-v --tb=short&quot;\n<\/code><\/pre>\n<p>\u3053\u3046\u3059\u308b\u3068\u30ef\u30fc\u30af\u30d5\u30ed\u30fc\u5185\u306f<code>pytest<\/code>\u3060\u3051\u3067\u6e08\u3080\u3002\u30ed\u30fc\u30ab\u30eb\u5b9f\u884c\u3082\u540c\u3058\u30aa\u30d7\u30b7\u30e7\u30f3\u306b\u306a\u308b\u306e\u3067\u3001\u300cCI\u3067\u306f\u901a\u3063\u3066\u30ed\u30fc\u30ab\u30eb\u3067\u306f\u843d\u3061\u308b\u300d\u3068\u3044\u3046\u8b0e\u306e\u73fe\u8c61\u304c\u6e1b\u308b\u3002<\/p>\n<p>Jenkins\u304b\u3089\u79fb\u884c\u3057\u30663\u30f6\u6708\u3001\u623b\u308a\u305f\u3044\u3068\u601d\u3063\u305f\u3053\u3068\u306f\u4e00\u5ea6\u3082\u306a\u3044\u3002\u8a2d\u5b9a\u3092\u30b3\u30fc\u30c9\u3068\u3057\u3066\u7ba1\u7406\u3067\u304d\u308b\u3053\u3068\u3001PR\u30ec\u30d3\u30e5\u30fc\u306e\u6d41\u308c\u306b\u81ea\u7136\u306b\u7d44\u307f\u8fbc\u3081\u308b\u3053\u3068 \u2014 \u3053\u306e2\u70b9\u3060\u3051\u3067\u5341\u5206\u306b\u5143\u304c\u53d6\u308c\u3066\u3044\u308b\u3002\u6700\u521d\u306e\u8a2d\u5b9a\u304c\u5b8c\u74a7\u3058\u3083\u306a\u304f\u3066\u3082\u554f\u984c\u306a\u3044\u3002\u52d5\u304b\u3059\u3053\u3068\u304c\u5148\u3067\u3001\u6539\u5584\u306f\u5f8c\u304b\u3089\u3067\u304d\u308b\u3002<\/p>\n<p><!-- Reviewed: 2026-03-09 | Status: ready_to_publish | Changes: meta_description expanded with staging\/prod detail; \"One thing I noticed:\" localized to natural Japanese; English mid-sentence \"I am not 100% sure this scales beyond a 10-person team\" replaced with Japanese-native phrasing --><\/p>\n","protected":false},"excerpt":{"rendered":"<p>\u53bb\u5e74\u306e11\u6708\u30013\u4eba\u30c1\u30fc\u30e0\u3067\u904b\u7528\u3057\u3066\u3044\u305fFastAPI\u306e\u30b5\u30fc\u30d3\u30b9\u304c\u91d1\u66dc\u306e\u5915\u65b9\u306b\u58ca\u308c\u305f\u3002\u539f\u56e0\u306fPython\u306e\u30d0\u30fc\u30b8\u30e7\u30f3\u975e\u4e92\u63db\u3002\u30ed\u30fc\u30ab\u30eb\u306f3.11\u3001\u672c\u756a\u306f3.10\u3067\u3001\u7279\u5b9a\u306ef-string\u69cb\u6587\u304c3.10\u3067\u306f\u52d5\u304b\u306a\u304b\u3063\u305f\u3002\u300c\u6b21\u304b\u3089\u6c17\u3092\u3064\u3051\u3088\u3046\u300d\u3067\u7d42\u308f\u3089\u305b\u3088\u3046\u3068\u3057\u305f\u3089\u30012\u9031\u9593\u5f8c\u306b\u307e\u3063\u305f\u304f\u540c\u3058\u7406\u7531\u3067\u5225\u306e\u30b5\u30fc\u30d3\u30b9\u304c\u843d\u3061\u305f\u3002<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"site-sidebar-layout":"default","site-content-layout":"","ast-site-content-layout":"default","site-content-style":"default","site-sidebar-style":"default","ast-global-header-display":"","ast-banner-title-visibility":"","ast-main-header-display":"","ast-hfb-above-header-display":"","ast-hfb-below-header-display":"","ast-hfb-mobile-header-display":"","site-post-title":"","ast-breadcrumbs-content":"","ast-featured-img":"","footer-sml-layout":"","ast-disable-related-posts":"","theme-transparent-header-meta":"","adv-header-id-meta":"","stick-header-meta":"","header-above-stick-meta":"","header-main-stick-meta":"","header-below-stick-meta":"","astra-migrate-meta-layouts":"default","ast-page-background-enabled":"default","ast-page-background-meta":{"desktop":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"ast-content-background-meta":{"desktop":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"footnotes":""},"categories":[1],"tags":[],"class_list":["post-113","post","type-post","status-publish","format-standard","hentry","category-general"],"_links":{"self":[{"href":"https:\/\/blog.rebalai.com\/ja\/wp-json\/wp\/v2\/posts\/113","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.rebalai.com\/ja\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.rebalai.com\/ja\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.rebalai.com\/ja\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.rebalai.com\/ja\/wp-json\/wp\/v2\/comments?post=113"}],"version-history":[{"count":8,"href":"https:\/\/blog.rebalai.com\/ja\/wp-json\/wp\/v2\/posts\/113\/revisions"}],"predecessor-version":[{"id":161,"href":"https:\/\/blog.rebalai.com\/ja\/wp-json\/wp\/v2\/posts\/113\/revisions\/161"}],"wp:attachment":[{"href":"https:\/\/blog.rebalai.com\/ja\/wp-json\/wp\/v2\/media?parent=113"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.rebalai.com\/ja\/wp-json\/wp\/v2\/categories?post=113"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.rebalai.com\/ja\/wp-json\/wp\/v2\/tags?post=113"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}