<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Release on AI2CORE - AI技術ブログ</title>
    <link>https://www.ai2core.com/tags/release/</link>
    <description>Recent content in Release on AI2CORE - AI技術ブログ</description>
    <generator>Hugo -- 0.146.4</generator>
    <language>ja</language>
    <lastBuildDate>Sat, 07 Mar 2026 13:00:00 +0900</lastBuildDate>
    <atom:link href="https://www.ai2core.com/tags/release/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>GitHub Actionsでモノレポを安全に自動リリースする設計: 変更検知・段階配布・失敗復旧</title>
      <link>https://www.ai2core.com/posts/2026-03-07-github-actions-monorepo-release-automation-guide/</link>
      <pubDate>Sat, 07 Mar 2026 13:00:00 +0900</pubDate>
      <guid>https://www.ai2core.com/posts/2026-03-07-github-actions-monorepo-release-automation-guide/</guid>
      <description>モノレポ運用でGitHub Actionsを使い、変更検知から段階リリース、ロールバック、監査まで実装する具体手順を解説。</description>
      <content:encoded><![CDATA[<h1 id="github-actionsでモノレポを安全に自動リリースする設計-変更検知段階配布失敗復旧">GitHub Actionsでモノレポを安全に自動リリースする設計: 変更検知・段階配布・失敗復旧</h1>
<p>モノレポのCI/CDは、単一リポジトリだから楽になる一方で、リリース設計を誤ると一気に難しくなります。</p>
<ul>
<li>1つの変更で全サービスを再デプロイしてしまう</li>
<li>並列ジョブが増えてキュー渋滞する</li>
<li>どのコミットがどのサービスへ反映されたか追跡できない</li>
<li>一部失敗時のロールバックが曖昧</li>
</ul>
<p>本記事では、GitHub Actionsでモノレポを運用しているチーム向けに、実務で耐えるリリース自動化の構成を具体的に説明します。</p>
<h2 id="1-モノレポcicdで先に決める設計原則">1. モノレポCI/CDで先に決める設計原則</h2>
<p>最初に次の原則を明文化します。</p>
<ol>
<li>変更のないサービスはデプロイしない</li>
<li>リリース対象は機械的に決定する</li>
<li>本番反映は段階的（canary/割合配布）</li>
<li>失敗時の復旧手順を自動化する</li>
<li>監査ログ（誰が何をいつ）を残す</li>
</ol>
<p>この5つがないと、運用が属人化し、障害時対応が遅れます。</p>
<h2 id="2-変更検知をワークフローの入口に置く">2. 変更検知をワークフローの入口に置く</h2>
<p>モノレポでは「どのディレクトリが変わったか」を最初に判定し、対象サービスだけを処理します。</p>
<h3 id="21-changed-filesの例">2.1 changed-filesの例</h3>
<div class="highlight"><div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">11
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">12
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">13
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">14
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">15
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">16
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">17
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">18
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">19
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">20
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#f92672">jobs</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">detect</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">runs-on</span>: <span style="color:#ae81ff">ubuntu-latest</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">outputs</span>:
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">matrix</span>: <span style="color:#ae81ff">${{ steps.set-matrix.outputs.matrix }}</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">steps</span>:
</span></span><span style="display:flex;"><span>      - <span style="color:#f92672">uses</span>: <span style="color:#ae81ff">actions/checkout@v4</span>
</span></span><span style="display:flex;"><span>      - <span style="color:#f92672">id</span>: <span style="color:#ae81ff">changed</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">uses</span>: <span style="color:#ae81ff">tj-actions/changed-files@v45</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">with</span>:
</span></span><span style="display:flex;"><span>          <span style="color:#f92672">files_yaml</span>: |<span style="color:#e6db74">
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">            api:
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">              - services/api/**
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">            web:
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">              - services/web/**
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">            worker:
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">              - services/worker/**</span>
</span></span><span style="display:flex;"><span>      - <span style="color:#f92672">id</span>: <span style="color:#ae81ff">set-matrix</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">run</span>: |<span style="color:#e6db74">
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">          python .github/scripts/build_matrix.py &#39;${{ toJson(steps.changed.outputs) }}&#39;</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>ここでmatrixを作り、後続ジョブを <code>fromJson</code> で動的展開します。</p>
<h2 id="3-reusable-workflowで共通化する">3. reusable workflowで共通化する</h2>
<p>各サービスごとにworkflowを複製すると、保守コストが急上昇します。<code>workflow_call</code> で共通化します。</p>
<div class="highlight"><div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">11
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">12
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">13
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">14
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">15
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">16
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">17
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#75715e"># .github/workflows/release-service.yml</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">on</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">workflow_call</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">inputs</span>:
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">service</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">required</span>: <span style="color:#66d9ef">true</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">type</span>: <span style="color:#ae81ff">string</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">environment</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">required</span>: <span style="color:#66d9ef">true</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">type</span>: <span style="color:#ae81ff">string</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">jobs</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">release</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">runs-on</span>: <span style="color:#ae81ff">ubuntu-latest</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">steps</span>:
</span></span><span style="display:flex;"><span>      - <span style="color:#f92672">uses</span>: <span style="color:#ae81ff">actions/checkout@v4</span>
</span></span><span style="display:flex;"><span>      - <span style="color:#f92672">run</span>: <span style="color:#ae81ff">./scripts/release.sh ${{ inputs.service }} ${{ inputs.environment }}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>呼び出し側はサービス名だけ渡せばよくなり、ガバナンスが効きます。</p>
<h2 id="4-同時実行制御concurrencyで事故防止">4. 同時実行制御（concurrency）で事故防止</h2>
<p>同一サービスへの並列デプロイは事故のもとです。<code>concurrency</code> を必ず設定します。</p>
<div class="highlight"><div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">2
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#f92672">concurrency</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">group</span>: <span style="color:#ae81ff">release-${{ github.ref }}-${{ matrix.service }}</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">cancel-in-progress</span>: <span style="color:#66d9ef">false</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p><code>cancel-in-progress</code> は本番では通常 <code>false</code> が安全です。途中キャンセルで半端状態を作らないためです。</p>
<h2 id="5-環境ごとの保護ルールを使う">5. 環境ごとの保護ルールを使う</h2>
<p>GitHub Environmentsを使えば、本番前レビューやシークレット分離を標準機能で実装できます。</p>
<ul>
<li>staging: 自動デプロイ</li>
<li>production: 承認必須</li>
<li>緊急時のみ管理者がoverride</li>
</ul>
<p>加えて、OIDCでクラウド認証し、長期鍵（固定アクセスキー）を排除します。</p>
<h2 id="6-段階リリースcanaryを組み込む">6. 段階リリース（Canary）を組み込む</h2>
<p>本番へ一気に100%展開は避け、段階配布をワークフローに組み込みます。</p>
<p>例:</p>
<ol>
<li>10%トラフィックへ5分</li>
<li>エラーレート確認</li>
<li>問題なければ50%</li>
<li>最終的に100%</li>
</ol>
<p>疑似コード例:</p>
<div class="highlight"><div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">2
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">4
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">5
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">6
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">7
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">8
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">9
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>deploy --service api --traffic <span style="color:#ae81ff">10</span>
</span></span><span style="display:flex;"><span>sleep <span style="color:#ae81ff">300</span>
</span></span><span style="display:flex;"><span>check_slo api <span style="color:#f92672">||</span> rollback api
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>deploy --service api --traffic <span style="color:#ae81ff">50</span>
</span></span><span style="display:flex;"><span>sleep <span style="color:#ae81ff">300</span>
</span></span><span style="display:flex;"><span>check_slo api <span style="color:#f92672">||</span> rollback api
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>deploy --service api --traffic <span style="color:#ae81ff">100</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p><code>check_slo</code> はp95レイテンシ・5xx率・ビジネスKPIを含めるのが理想です。</p>
<h2 id="7-失敗復旧を手順ではなく機能にする">7. 失敗復旧を「手順」ではなく「機能」にする</h2>
<p>運用で強いチームは、ロールバックを手作業Runbookではなくワークフロー化しています。</p>
<h3 id="71-rollback-workflow例">7.1 rollback workflow例</h3>
<div class="highlight"><div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">11
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">12
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">13
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">14
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#f92672">on</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">workflow_dispatch</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">inputs</span>:
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">service</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">required</span>: <span style="color:#66d9ef">true</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">target_sha</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">required</span>: <span style="color:#66d9ef">true</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">jobs</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">rollback</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">runs-on</span>: <span style="color:#ae81ff">ubuntu-latest</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">steps</span>:
</span></span><span style="display:flex;"><span>      - <span style="color:#f92672">uses</span>: <span style="color:#ae81ff">actions/checkout@v4</span>
</span></span><span style="display:flex;"><span>      - <span style="color:#f92672">run</span>: <span style="color:#ae81ff">./scripts/rollback.sh ${{ github.event.inputs.service }} ${{ github.event.inputs.target_sha }}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>「事故時はこのボタンを押す」で復旧できる状態を目指してください。</p>
<h2 id="8-リリース証跡の自動生成">8. リリース証跡の自動生成</h2>
<p>監査対応やトラブル解析で必須になるのが証跡です。</p>
<p>最低限、以下をアーティファクト化します。</p>
<ul>
<li>対象サービス一覧</li>
<li>対象コミットSHA</li>
<li>実行者</li>
<li>実行時刻</li>
<li>結果（成功/失敗）</li>
<li>ロールバック有無</li>
</ul>
<p>さらにSlack/Discord通知で、サービス単位の反映結果を即時共有すると現場が安定します。</p>
<h2 id="9-コスト最適化も同時に行う">9. コスト最適化も同時に行う</h2>
<p>モノレポCIは規模が大きくなるほどコストが効いてきます。</p>
<ul>
<li>キャッシュ（依存/ビルド成果物）を徹底</li>
<li>変更のないサービスはskip</li>
<li>夜間バッチと競合しない時間帯に重いjobを寄せる</li>
<li>自己ホストランナーはautoscaling前提で運用</li>
</ul>
<p>「全サービス毎回build」は最初は簡単ですが、半年後に必ず負債化します。</p>
<h2 id="10-導入チェックリスト">10. 導入チェックリスト</h2>
<ul>
<li><input disabled="" type="checkbox"> 変更検知のmatrix生成がある</li>
<li><input disabled="" type="checkbox"> reusable workflowに統一されている</li>
<li><input disabled="" type="checkbox"> concurrency制御がサービス単位で設定済み</li>
<li><input disabled="" type="checkbox"> production環境に承認ルールあり</li>
<li><input disabled="" type="checkbox"> canary + 自動SLO判定がある</li>
<li><input disabled="" type="checkbox"> rollback workflowが存在する</li>
<li><input disabled="" type="checkbox"> リリース証跡を保存している</li>
<li><input disabled="" type="checkbox"> 通知連携（Slack/Discord）が整備されている</li>
</ul>
<h2 id="まとめ">まとめ</h2>
<p>GitHub Actionsでのモノレポ自動リリースは、単にworkflowを書く作業ではありません。<strong>変更検知・段階配布・復旧自動化・監査</strong> を含む運用設計です。</p>
<ul>
<li>デプロイ対象を最小化する</li>
<li>本番反映は必ず段階化する</li>
<li>失敗時の復旧を自動化する</li>
<li>証跡を残して再現性を確保する</li>
</ul>
<p>この設計を最初に固めると、サービス数が増えても運用は破綻しません。まずは「変更検知 + reusable workflow + rollback workflow」の3点から着手するのが実装コストと効果のバランスが良いです。</p>
<h2 id="付録-失敗しにくい運用設計テンプレート">付録: 失敗しにくい運用設計テンプレート</h2>
<p>最後に、運用へ落とし込むときに有効なテンプレートを示します。実際にはこの3点をチーム標準にするだけで、リリース事故率が下がります。</p>
<ul>
<li><strong>PRテンプレートに「影響サービス」欄を必須化</strong>
<ul>
<li><code>service-a, service-b</code> のように宣言させ、変更検知結果と突合する</li>
</ul>
</li>
<li><strong>デプロイ承認時の確認項目を固定化</strong>
<ul>
<li>監視グラフURL、ロールバック手順URL、オンコール担当を毎回入力</li>
</ul>
</li>
<li><strong>失敗後レビューのフォーマット統一</strong>
<ul>
<li>失敗原因、検知時刻、復旧時刻、再発防止策を必ず残す</li>
</ul>
</li>
</ul>
<p>特にモノレポでは「誰がどこに責任を持つか」が曖昧になりやすいです。workflowの技術実装だけでなく、<strong>承認と振り返りの運用ルール</strong>をセットにして初めて、自動リリースは安定運用に乗ります。</p>
]]></content:encoded>
      <category>Tech</category>
      <category>GitHub Actions</category>
      <category>Monorepo</category>
      <category>CI/CD</category>
      <category>Release</category>
      <category>DevOps</category>
    </item>
  </channel>
</rss>
