mirror of
https://github.com/tiennm99/claude-code-routine-trigger.git
synced 2026-06-08 18:16:09 +00:00
feat: scheduled GitHub Actions workflow to fire Claude Code routine 4x daily
This commit is contained in:
@@ -0,0 +1,72 @@
|
||||
name: Trigger Claude Code Routine
|
||||
|
||||
on:
|
||||
schedule:
|
||||
# UTC+7 (Asia/Ho_Chi_Minh) → UTC
|
||||
- cron: '0 22 * * *' # 05:00 UTC+7
|
||||
- cron: '0 3 * * *' # 10:00 UTC+7
|
||||
- cron: '0 8 * * *' # 15:00 UTC+7
|
||||
- cron: '0 13 * * *' # 20:00 UTC+7
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
text:
|
||||
description: 'Optional context text passed to the routine'
|
||||
required: false
|
||||
default: ''
|
||||
|
||||
concurrency:
|
||||
group: claude-code-routine-trigger
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
fire:
|
||||
name: Fire routine
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
steps:
|
||||
- name: POST /fire
|
||||
env:
|
||||
ROUTINE_FIRE_URL: ${{ secrets.ROUTINE_FIRE_URL }}
|
||||
ROUTINE_FIRE_TOKEN: ${{ secrets.ROUTINE_FIRE_TOKEN }}
|
||||
INPUT_TEXT: ${{ inputs.text }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
if [ -z "${ROUTINE_FIRE_URL:-}" ] || [ -z "${ROUTINE_FIRE_TOKEN:-}" ]; then
|
||||
echo "::error::Missing secrets ROUTINE_FIRE_URL and/or ROUTINE_FIRE_TOKEN"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
LOCAL_NOW=$(TZ=Asia/Ho_Chi_Minh date '+%Y-%m-%d %H:%M %Z')
|
||||
if [ -n "${INPUT_TEXT}" ]; then
|
||||
TEXT="${INPUT_TEXT}"
|
||||
else
|
||||
TEXT="Scheduled trigger at ${LOCAL_NOW} (run ${GITHUB_RUN_ID})"
|
||||
fi
|
||||
|
||||
PAYLOAD=$(jq -n --arg text "$TEXT" '{text: $text}')
|
||||
|
||||
RESPONSE=$(curl -sS -w "\n%{http_code}" -X POST "$ROUTINE_FIRE_URL" \
|
||||
-H "Authorization: Bearer $ROUTINE_FIRE_TOKEN" \
|
||||
-H "anthropic-version: 2023-06-01" \
|
||||
-H "anthropic-beta: experimental-cc-routine-2026-04-01" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "$PAYLOAD")
|
||||
|
||||
HTTP_CODE=$(printf '%s' "$RESPONSE" | tail -n1)
|
||||
BODY=$(printf '%s' "$RESPONSE" | sed '$d')
|
||||
|
||||
echo "HTTP ${HTTP_CODE}"
|
||||
echo "${BODY}"
|
||||
|
||||
if [ "$HTTP_CODE" != "200" ]; then
|
||||
echo "::error::Routine fire failed with HTTP ${HTTP_CODE}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
SESSION_URL=$(printf '%s' "$BODY" | jq -r '.claude_code_session_url // empty')
|
||||
if [ -n "$SESSION_URL" ]; then
|
||||
echo "### Claude Code session" >> "$GITHUB_STEP_SUMMARY"
|
||||
echo "- Triggered at: ${LOCAL_NOW}" >> "$GITHUB_STEP_SUMMARY"
|
||||
echo "- Session: ${SESSION_URL}" >> "$GITHUB_STEP_SUMMARY"
|
||||
fi
|
||||
@@ -0,0 +1,5 @@
|
||||
.DS_Store
|
||||
*.log
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2026 tiennm99
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -0,0 +1,51 @@
|
||||
# claude-code-routine-trigger
|
||||
|
||||
GitHub Actions workflow that fires a [Claude Code routine](https://code.claude.com/docs/en/routines) four times a day via the [`/fire` API](https://platform.claude.com/docs/en/api/claude-code/routines-fire).
|
||||
|
||||
Schedule (UTC+7, `Asia/Ho_Chi_Minh`):
|
||||
|
||||
| Local time | UTC cron |
|
||||
| ---------- | ------------- |
|
||||
| 05:00 | `0 22 * * *` |
|
||||
| 10:00 | `0 3 * * *` |
|
||||
| 15:00 | `0 8 * * *` |
|
||||
| 20:00 | `0 13 * * *` |
|
||||
|
||||
Manual `workflow_dispatch` is supported with an optional `text` input for ad-hoc runs.
|
||||
|
||||
## Setup
|
||||
|
||||
1. Create a routine at <https://claude.ai/code/routines> (requires a Pro/Max/Team/Enterprise plan with Claude Code on the web enabled).
|
||||
2. In the routine editor, add an **API** trigger and generate a token. Copy both the fire URL and the token — the token is shown once.
|
||||
3. Add two repository secrets (`Settings → Secrets and variables → Actions`):
|
||||
- `ROUTINE_FIRE_URL` — e.g. `https://api.anthropic.com/v1/claude_code/routines/trig_01ABo4hmfydBLFDgRMnKwEKy/fire`
|
||||
- `ROUTINE_FIRE_TOKEN` — e.g. `sk-ant-oat01-...`
|
||||
4. Enable Actions for the repo. Scheduled runs start on the next matching UTC tick.
|
||||
|
||||
## Manual run
|
||||
|
||||
`Actions → Trigger Claude Code Routine → Run workflow`. Leave `text` blank to get a timestamped default, or pass custom context (alert body, log line, etc.).
|
||||
|
||||
## Request shape
|
||||
|
||||
```bash
|
||||
curl -X POST "$ROUTINE_FIRE_URL" \
|
||||
-H "Authorization: Bearer $ROUTINE_FIRE_TOKEN" \
|
||||
-H "anthropic-version: 2023-06-01" \
|
||||
-H "anthropic-beta: experimental-cc-routine-2026-04-01" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"text": "Scheduled trigger ..."}'
|
||||
```
|
||||
|
||||
Beta header pinned to `experimental-cc-routine-2026-04-01`. Two previous dated beta versions keep working while migrating — bump when Anthropic ships a new one.
|
||||
|
||||
## Notes
|
||||
|
||||
- GitHub cron runs can lag 5–15 min under load and are best-effort — acceptable for housekeeping-style routines, not for precise timing.
|
||||
- Token is scoped to a single routine; a leak can only fire that one routine.
|
||||
- Each POST creates a new session (no idempotency). Avoid retry loops that would multiply sessions.
|
||||
- 429 responses include `Retry-After`; the workflow fails loud rather than retrying silently.
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
Reference in New Issue
Block a user