Deploy and Test Your Bot
Your bot is built. Now let's make it run automatically every morning using GitHub Actions.
What Is GitHub Actions?
GitHub Actions is a built-in automation service that runs tasks for you. You tell it when to run (e.g. every weekday at 9 am) and what to do (e.g. run your bot script), and it handles the rest — no server needed.
How does it work under the hood?
When you push a workflow file (a YAML file in .github/workflows/), GitHub watches for the trigger you defined. When the trigger fires — a scheduled time, a push, or a manual button press — GitHub spins up a virtual machine, checks out your code, installs dependencies, and runs your commands. It's like having a free computer in the cloud that does one job and shuts down.
Prompt 7: Create the GitHub Actions Workflow
Ask Claude Code to create the automation — say this or paste it:
Create a GitHub Actions workflow file at .github/workflows/daily-report.yml
that:
1. Runs every weekday at 9:00 am NZST (that's around 8 PM or 9 PM UTC
depending on daylight saving — use 20:00 UTC as a reasonable default)
2. Can also be triggered manually using workflow_dispatch
3. Checks out the repository with full git history (fetch-depth: 0)
4. Sets up Node.js 20
5. Installs dependencies with npm ci
6. Runs the daily report script with these environment variables from
GitHub secrets: OPENAI_API_KEY, SLACK_WEBHOOK_TEST, SLACK_WEBHOOK_PROD
7. After the script runs, commits and pushes any changes to state.json
with the message "chore: update commit bank state [skip ci]"
8. The commit should use a bot identity for the git author
Add comments explaining each section of the workflow file.
What happens: Claude Code creates a complete workflow file with the cron schedule, checkout, setup, and run steps.
About cron timing: GitHub Actions cron uses UTC. New Zealand is UTC+12 (or UTC+13 during daylight saving). The schedule 0 20 * * 1-5 means "8 PM UTC, Monday to Friday" which is approximately 9 AM NZST. Cron triggers can be delayed by up to 15 minutes during busy periods — this is normal.
What does [skip ci] mean?
When the bot commits the updated state.json, the commit message includes [skip ci]. This tells GitHub Actions not to trigger another workflow run for that commit — otherwise you'd get an infinite loop of the bot triggering itself.
Why fetch-depth: 0?
By default, GitHub Actions checks out only the latest commit (shallow clone). Your bot needs to read the full git history to find recent commits. Setting fetch-depth: 0 ensures the entire history is available.
View the generated workflow
# .github/workflows/daily-report.yml
name: Daily Report Bot
on:
schedule:
# Run at 8 PM UTC (approx 9 AM NZST), Monday to Friday
- cron: '0 20 * * 1-5'
workflow_dispatch: # Allow manual triggering
jobs:
daily-report:
runs-on: ubuntu-latest
steps:
# Check out the full repository history
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}
# Set up Node.js
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
# Install dependencies
- name: Install dependencies
run: npm ci
# Run the daily report bot
- name: Run daily report
env:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
SLACK_WEBHOOK_TEST: ${{ secrets.SLACK_WEBHOOK_TEST }}
SLACK_WEBHOOK_PROD: ${{ secrets.SLACK_WEBHOOK_PROD }}
SLACK_CHANNEL: prod
run: node src/index.js
# Commit updated state.json so the commit bank persists
- name: Commit state changes
run: |
git config user.name "daily-report-bot"
git config user.email "[email protected]"
git add state.json
git diff --staged --quiet || git commit -m "chore: update commit bank state [skip ci]"
git push
Configure GitHub Secrets
Your bot needs three secrets. Never commit these to your code — store them in GitHub's encrypted secrets.
Never commit API keys, webhook URLs, or other secrets to your code. Anyone who can see your repository could steal them. GitHub Secrets encrypts them and makes them available only during workflow runs.
- Using the command line (gh CLI)
- Using the GitHub website
If you have the GitHub CLI installed:
gh secret set OPENAI_API_KEY
gh secret set SLACK_WEBHOOK_TEST
gh secret set SLACK_WEBHOOK_PROD
Each command will prompt you to paste the value.
Go to your repository settings
Open your repository on GitHub → Settings → Secrets and variables → Actions
Add OPENAI_API_KEY
Click New repository secret. Name:
OPENAI_API_KEY. Value: paste your OpenAI API key. Click Add secret.Add SLACK_WEBHOOK_TEST
Click New repository secret. Name:
SLACK_WEBHOOK_TEST. Value: paste your test channel webhook URL. Click Add secret.Add SLACK_WEBHOOK_PROD
Click New repository secret. Name:
SLACK_WEBHOOK_PROD. Value: paste your production channel webhook URL. Click Add secret.
Push and Deploy
Stage and commit your code
Copy this commandgit add -A
git commit -m "feat: add daily report bot"Push to GitHub
Copy this commandgit push origin mainVerify on GitHub
Go to your repository on GitHub → Actions tab. You should see the "Daily Report Bot" workflow listed. It will run automatically on the next scheduled time, or you can trigger it manually.
Test the Workflow
Dry run (no Slack post)
Trigger the workflow manually with a dry run to check everything works without posting to Slack:
Copy this commandgh workflow run daily-report.ymlOr on the GitHub website: Actions → Daily Report Bot → Run workflow.
Check the logs to see the output. You should see the commit collection, banking, and summary steps complete successfully.
Real test (post to test channel)
If the dry run looks good, trigger a real run that posts to your test Slack channel. Check the
SLACK_CHANNELenvironment variable is set totestin the workflow file, then trigger again.Go to your test Slack channel — you should see a message like this:
Daily Update — Tuesday, 11 March 2026
- Set up the daily report bot project structure and core modules
- Built the commit banking system to distribute updates across the week
- Integrated OpenAI for natural language summaries
Blockers: NoneCelebrate
Your bot works! It will now run automatically every weekday morning.
Prompt 8: Troubleshoot
Things don't always work on the first try. Here's the pattern for debugging with Claude Code:
I'm getting this error when the GitHub Actions workflow runs:
[paste the exact error message here]
The workflow is supposed to collect git commits, summarise them with
OpenAI, and post to Slack. Can you help me figure out what went wrong
and fix it?
Communication skill: Describing errors. Always paste the exact error message. Tell Claude Code what you expected to happen and what actually happened. The more context, the faster the fix. If you're using Wispr Flow, you can speak through the issue — "I'm getting an error that says..." — and then paste the actual error text.
Common Issues
Workflow not triggering
Possible causes:
- The workflow file isn't on the default branch (usually
main). Scheduled workflows only run from the default branch. - The cron schedule is in UTC, not your local time. Double-check the conversion.
- GitHub Actions can delay cron triggers by up to 15 minutes during busy periods.
Slack webhook returns 403 or 404
Possible causes:
- The webhook URL has been revoked or is incorrect. Go to your Slack app settings and check.
- The secret name in your workflow doesn't match what you set in GitHub Secrets.
- You're using the test webhook URL but the environment variable points to prod (or vice versa).
Empty commit messages or garbled output
Possible cause: The git log separator conflicts with shell operators. Using || or && as separators will break because the shell interprets them. Use a safe separator like <SEP> instead.
State push conflicts
Possible cause: If someone pushes to the repository at the same time as the bot tries to commit state.json, you'll get a conflict. The [skip ci] tag prevents cascading runs, but timing conflicts can still happen. This is rare in practice.
No commits found
Possible causes:
- The time window is too short. The default is 24 hours, but if the workflow runs at a slightly different time, some commits might fall outside the window.
fetch-depth: 0is missing from the checkout step, so the git history isn't available.
Switch to Production
Once you're happy with the test output, switch to the production channel:
- In your workflow file (
.github/workflows/daily-report.yml), changeSLACK_CHANNEL: testtoSLACK_CHANNEL: prod - Commit and push
Or ask Claude Code:
Change the default Slack channel in the GitHub Actions workflow from
"test" to "prod" so the daily reports go to the production channel.
That's it — your bot is live.
Your bot is deployed and running! Head to What's next for ideas to extend it, lessons learned, and reflection questions.