Skip to content

Sending a Scheduled Report Email

A Docker environment can generate and send a report just as easily as it can back up a database or watch a website — query wherever your data lives, format a summary, and mail it out on a schedule, without a dedicated reporting tool. This guide builds a weekly digest of a GitHub repository's open issues, emailed every Monday morning.

The script below is ordinary Python — nothing about it is specific to Runner. If you already have a script like this sitting in a .py file somewhere, you don't need to adapt it: paste its full contents straight into the Command field as-is, and Runner runs it exactly as written.

Create the environment

  1. Go to Environments and click Add Environment.

  2. Set Type to Docker and Source to Image.

  3. Set Image to python:3.12-slim — the report script below only needs the standard library, so no extra dependencies to install.

  4. Save the environment.

Write the schedule

  1. Go to Schedules and click Add Schedule.

  2. Set Environment to the Docker environment created above.

  3. Under Environment Variables, add the repository to report on and the SMTP credentials to send with:

    NameValue
    GITHUB_REPOthe repository to report on, owner/repo
    SMTP_HOSTyour SMTP server hostname
    SMTP_PORTusually 587
    SMTP_USERthe sending account's username
    SMTP_PASSWORDthe sending account's password
    REPORT_TOthe address that should receive the report

    TIP

    These are separate from the SMTP server configured in Settings — that configuration is only used for Runner's own account and run-status emails, never from within a script. A schedule needs its own credentials to send mail.

  4. Set Command to the full script below, pasted in as one block:

    bash
    python3 -c "
    import json
    import os
    import smtplib
    import urllib.request
    from email.mime.text import MIMEText
    
    repo = os.environ['GITHUB_REPO']
    url = f'https://api.github.com/repos/{repo}/issues?state=open'
    req = urllib.request.Request(url, headers={'Accept': 'application/vnd.github+json'})
    
    with urllib.request.urlopen(req, timeout=10) as resp:
        issues = [i for i in json.load(resp) if 'pull_request' not in i]
    
    lines = [f'{len(issues)} open issue(s) in {repo}:', '']
    for issue in issues:
        number = issue['number']
        title = issue['title']
        link = issue['html_url']
        lines.append(f'- #{number} {title} ({link})')
    body = '\n'.join(lines)
    
    msg = MIMEText(body)
    msg['Subject'] = f'Weekly issue digest: {repo}'
    msg['From'] = os.environ['SMTP_USER']
    msg['To'] = os.environ['REPORT_TO']
    
    with smtplib.SMTP(os.environ['SMTP_HOST'], int(os.environ['SMTP_PORT'])) as smtp:
        smtp.starttls()
        smtp.login(os.environ['SMTP_USER'], os.environ['SMTP_PASSWORD'])
        smtp.send_message(msg)
    
    print('Sent digest with ' + str(len(issues)) + ' issue(s) to ' + os.environ['REPORT_TO'])
    "
  5. Set Schedule to 0 8 * * 1 for every Monday at 8am.

  6. Save the schedule and set it to Active.

Adjusting the example

  • Swap the GitHub API call for wherever your report's data actually lives — an internal API, a database query, a CSV export. The shape stays the same: fetch, format, send.
  • For an HTML-formatted report, build a MIMEMultipart message with an 'html'-subtype MIMEText part instead of plain text, and format the summary as a table.
  • Enable Notify on failed runs on the schedule so a broken query or SMTP error doesn't silently mean nobody gets the report — see Notifications.
  • If the report needs a package beyond the standard library, install it at the start of the Command with pip install --quiet <package> rather than switching to a Dockerfile source — see the Command examples in Schedules.
  • If you'd rather maintain the script as its own file — for version control, or to share it across schedules — upload it as an Additional File instead of pasting it into Command, and set Command to python3 /nodepit/report.py.
Was this page helpful?