メインコンテンツへスキップ
← 記事一覧に戻る
·開発·8 min read

GitHub Actions cronが一度も動いていなかった:Node20×Supabase WebSocket不可とworking-directory罠

GitHub ActionsSupabaseNode.js個人開発自動化
GitHub Actions cronが一度も動いていなかった:Node20×Supabase WebSocket不可とworking-directory罠

ローカルで動くのに cron が「一度も走っていない」

gh run list --workflow=collect.yml
# → (0件)

自動収集パイプラインをリリースして数日後、確認のために叩いたコマンドが空を返しました。npm run collect はローカルで毎回 DB を更新していたので「動いてる」と思い込んでいたのです。cron は一度も成功していませんでした。

個人開発の localbiz-event-finder(商工会議所・connpassのイベントを自動収集するツール)で同じ問題を踏みました。ローカル実行では DB が更新されるため、GitHub Actions 側の停止に数日間気づかず。修正後は 1 分 15 秒で完走し、126 件登録・score≥60 のイベントで Slack 通知が発火しました。masatoman.net の記事用データ収集も自動化に依存しているため、パイプラインの死活監視は最優先で確認すべき事項です。

この記事でわかること:

  • working-directory の誤指定が cron を silent に止める仕組み
  • Node.js 20 × @supabase/supabase-js Realtime でなぜ WebSocket が落ちるのか
  • 今日試せる確認コマンド 3 つ

原因1: working-directory がネストしたサブディレクトリを指していた

# ❌ 問題のある設定
jobs:
  collect:
    runs-on: ubuntu-latest
    defaults:
      run:
        working-directory: localbiz-event-finder  # ← これが罠
    steps:
      - uses: actions/checkout@v4
      - run: npm install
      - run: npm run collect

リポジトリのルート自体が localbiz-event-finder/ でした。つまり working-directory: localbiz-event-finder は、存在しない localbiz-event-finder/localbiz-event-finder/ を探しにいきます。

期待: /home/runner/work/localbiz-event-finder/localbiz-event-finder/
実際: /home/runner/work/localbiz-event-finder/localbiz-event-finder/localbiz-event-finder/
                                                                       ^^^^^^^^^^^^^^^^^^^
                                                                       存在しないディレクトリ

GitHub Actions はこの設定ミスを push 時に検証しません。ジョブが実行されてはじめて Error: ENOENT: no such file or directory, chdir が出ます。かつ、cron トリガーではこのエラーが Slack 等に飛ばない限り気づけません。

修正

# ✅ 修正後(working-directory ごと削除)
jobs:
  collect:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm install
      - run: npm run collect

リポジトリがモノレポの場合は working-directory: packages/foo のように相対パスを起点にするか、cd を明示するほうが意図が明確です。


原因2: Node.js 20 × Supabase Realtime で UND_ERR_CONNECT_TIMEOUT

working-directory を修正しても、もう1つのエラーが出ました。

RealtimeClient error: UND_ERR_CONNECT_TIMEOUT
Error: connect ETIMEDOUT
    at RealtimeClient._connect (node_modules/@supabase/realtime-js/...)

@supabase/supabase-jsRealtimeClient は、Node.js 環境でネイティブ WebSocket(global.WebSocket)を要求します。Node.js 20 にはネイティブ WebSocket が実装されていません(実験的フラグが必要)。そのためタイムアウトで落ち続けます。

Node.js バージョンネイティブ WebSocketSupabase Realtime
18ws パッケージで代替可
20❌(実験的)UND_ERR_CONNECT_TIMEOUT 発生
22ネイティブで動作
24 LTSネイティブで動作

(Supabase 公式ドキュメントより。Node.js のバージョン別 WebSocket 対応状況)

修正

# ✅ Node.js 22 に上げる
- uses: actions/setup-node@v4
  with:
    node-version: '22'

これだけで WebSocket 問題は解消します。Realtime を使わず supabase.from(...).insert() だけなら Node 20 でも動きますが、createClient が内部で Realtime 接続を初期化するため影響を受けます。


なぜ「一度も動いていない」に気づかないのか

cron ジョブが失敗しても、以下の設定がないと誰にも通知されません:

  • Slack / Discord への通知ステップ
  • GitHub の「ワークフロー失敗メール通知」(デフォルトは ON だが迷惑メールに流れやすい)
  • gh run list の定期確認

ローカル実行が成功していると「動いてるはず」のバイアスが強くなります。特に個人開発で DB が別経路(手動実行)でも更新されている場合、cron の停止を数週間見逃すことがあります。


今すぐ確認できる 3 つのコマンド

# 1. ワークフローが最近走ったか確認
gh run list --workflow=your-workflow.yml --limit=10

# 2. 直近の実行のログを確認
gh run view <RUN_ID> --log

# 3. working-directory が正しいか確認(ローカル実行)
ls -la .  # リポジトリルートに package.json があるか確認

gh run list が空、または最終実行が数日前なら cron が止まっています。


で、どう稼ぐ?

自動収集パイプラインが止まると、収益機会も止まる

GitHub Actions の cron が機能していない状態は、目に見えないコスト損失です。

たとえばイベント収集ツールの場合:

  • cron が止まる → DB が古くなる → ユーザーに古いデータを表示 → 解約につながる
  • cron が動く → 毎日最新データを自動取得 → ユーザーが毎日戻ってくる → 継続率が上がる

個人開発 SaaS の収益を安定させるには「自動化の死活確認」がコードを書くのと同じくらい重要です。

確認コストを下げる Slack 通知の最小実装

# ワークフロー末尾に追加するだけ
- name: Notify success
  if: success()
  run: |
    curl -X POST ${{ secrets.SLACK_WEBHOOK_URL }} \
      -H 'Content-type: application/json' \
      --data '{"text":"✅ collect completed: ${{ steps.collect.outputs.count }} items"}'

- name: Notify failure
  if: failure()
  run: |
    curl -X POST ${{ secrets.SLACK_WEBHOOK_URL }} \
      -H 'Content-type: application/json' \
      --data '{"text":"❌ collect FAILED: ${{ github.workflow }} @ ${{ github.sha }}"}'

これを入れておくと失敗が即日わかります。Slack Webhook URL は無料で取得でき、設定は 5 分です。


まとめ:今日やること3つ

  1. gh run list で直近ワークフローの実行履歴を確認する — 空なら即アラート、最終実行が数日前なら要調査
  2. Node.js のバージョンを確認するnode-version: '22' 以上を指定していない場合は変更
  3. Slack 失敗通知を 1 ステップ追加する — cron の死活確認は人が見るより Slack が先に教えてくれる仕組みにする

Next Step

次に読むならこの導線です

すべての記事を見る
有料で次へ進む¥1,000

【第12回】夜寝てる間に Claude Code が記事を書き上げる構成 — 月 ¥5K で動く全コード

Claude Codeラボ全12話の集大成。Skills/MCP/サブエージェント/Hooks/リモート運用を統合した「自走する Claude 自動化」を、月 ¥5K の実コストで動かす全構成を公開。寝てる間に競合調査・記事下書き・PR まで自動化する 6 層アーキテクチャの完成版。

次の実験記録も追う

Claude Code × 個人開発の実験ログ、失敗、判断変更をまとめて追いたい人向けに、月次でLab Freeを届けます。

masatoman のメルマガ — 毎週月曜の朝に 1 通

masatoman.net で今週公開した記事の中から 1 本を、読者目線で深掘りした手紙が届きます。「自分も同じことやってる」「ここで詰まってた」が見つかる予告編。

この記事が役に立ったらシェア