> ## Documentation Index
> Fetch the complete documentation index at: https://herwaka.shesharp.org.nz/llms.txt
> Use this file to discover all available pages before exploring further.

# Deploy and Test Your Bot

> Set up GitHub Actions to run your bot automatically, configure secrets, and test everything end to end.

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.

<Accordion title="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.
</Accordion>

***

## Prompt 7: Create the GitHub Actions Workflow

Ask Claude Code to create the automation — say this or paste it:

```text title="Say this or copy this prompt" theme={null}
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.

<Note>
  **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.
</Note>

<Accordion title="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.
</Accordion>

<Accordion title="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.
</Accordion>

<Accordion title="View the generated workflow">
  ```yaml theme={null}
  # .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 "bot@users.noreply.github.com"
            git add state.json
            git diff --staged --quiet || git commit -m "chore: update commit bank state [skip ci]"
            git push
  ```
</Accordion>

***

## Configure GitHub Secrets

Your bot needs three secrets. Never commit these to your code — store them in GitHub's encrypted secrets.

<Warning>
  **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.
</Warning>

<Tabs>
  <Tab title="Using the command line (gh CLI)">
    If you have the [GitHub CLI](https://cli.github.com) installed:

    ```bash title="Copy this command" theme={null}
    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.
  </Tab>

  <Tab title="Using the GitHub website">
    <Steps>
      <Step title="Go to your repository settings">
        Open your repository on GitHub → **Settings** → **Secrets and variables** → **Actions**
      </Step>

      <Step title="Add OPENAI_API_KEY">
        Click **New repository secret**. Name: `OPENAI_API_KEY`. Value: paste your OpenAI API key. Click **Add secret**.
      </Step>

      <Step title="Add SLACK_WEBHOOK_TEST">
        Click **New repository secret**. Name: `SLACK_WEBHOOK_TEST`. Value: paste your test channel webhook URL. Click **Add secret**.
      </Step>

      <Step title="Add SLACK_WEBHOOK_PROD">
        Click **New repository secret**. Name: `SLACK_WEBHOOK_PROD`. Value: paste your production channel webhook URL. Click **Add secret**.
      </Step>
    </Steps>
  </Tab>
</Tabs>

***

## Push and Deploy

<Steps>
  <Step title="Stage and commit your code">
    ```bash title="Copy this command" theme={null}
    git add -A
    git commit -m "feat: add daily report bot"
    ```
  </Step>

  <Step title="Push to GitHub">
    ```bash title="Copy this command" theme={null}
    git push origin main
    ```
  </Step>

  <Step title="Verify 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.
  </Step>
</Steps>

***

## Test the Workflow

<Steps>
  <Step title="Dry run (no Slack post)">
    Trigger the workflow manually with a dry run to check everything works without posting to Slack:

    ```bash title="Copy this command" theme={null}
    gh workflow run daily-report.yml
    ```

    Or 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.
  </Step>

  <Step title="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_CHANNEL` environment variable is set to `test` in 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: None
    ```
  </Step>

  <Step title="Celebrate">
    Your bot works! It will now run automatically every weekday morning.
  </Step>
</Steps>

***

## Prompt 8: Troubleshoot

Things don't always work on the first try. Here's the pattern for debugging with Claude Code:

```text title="Say this or copy this prompt" theme={null}
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?
```

<Tip>
  **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.
</Tip>

### Common Issues

<AccordionGroup>
  <Accordion title="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.
  </Accordion>

  <Accordion title="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).
  </Accordion>

  <Accordion title="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.
  </Accordion>

  <Accordion title="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.
  </Accordion>

  <Accordion title="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: 0` is missing from the checkout step, so the git history isn't available.
  </Accordion>
</AccordionGroup>

***

## Switch to Production

Once you're happy with the test output, switch to the production channel:

1. In your workflow file (`.github/workflows/daily-report.yml`), change `SLACK_CHANNEL: test` to `SLACK_CHANNEL: prod`
2. Commit and push

Or ask Claude Code:

```text title="Say this or copy this prompt" theme={null}
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.

<Note>
  Your bot is deployed and running! Head to [What's next](/tutorial/vibe-coding/whats-next) for ideas to extend it, lessons learned, and reflection questions.
</Note>
