Skip to content

The moment it happens

Update CHANGELOG.md when a GitHub release is published

When a GitHub release is published, its notes are added to the top of CHANGELOG.md in the repo with a commit.

Runs
The moment it happens
Apps
GitHub
AI
Not needed
Access
Contents: read and write

Send this to your assistant

Create a ClickRuntime automation: when I publish a release in acme/app, add its title, date and release notes to the top of CHANGELOG.md on main and commit it.

Paste it into your assistant. It tells you what it will send or change before it runs live.

First time? Adding ClickRuntime to your assistant takes about two minutes.

Why use it

Why teams use this automation

Many projects write release notes on GitHub and also keep a CHANGELOG.md in the repository for package registries, docs sites and people reading offline. Keeping both in step is a manual copy and paste that gets skipped when a release goes out in a hurry.

Here the release is the source. As soon as you publish one, GitHub calls the automation, which takes the release title, date and notes and inserts them as a new section at the top of CHANGELOG.md, then commits the file to your default branch.

Library maintainers and teams that ship packages to npm or PyPI use it so the changelog in the published package always matches the GitHub release page.

What you get out of it

  • One place to write notes

    You write the notes once on the release page. The file in the repository follows without another pull request.

  • Newest release on top

    The section is placed under the file heading, so the changelog keeps the usual newest-first order.

  • No duplicate sections

    If a section for the tag is already present, nothing is written, so re-publishing or retries are safe.

  • Pre-releases can be left out

    Betas and release candidates can be skipped so the changelog only lists versions people should install.

  • Signed webhook only

    Requests are checked against your webhook secret, so nobody else can write to your changelog by calling the URL.

How it works

What happens, step by step

  1. GitHub sends a webhook to the automation when a release is published.

  2. The automation checks the signature and skips drafts and pre-releases if you want.

  3. It reads the current CHANGELOG.md from the default branch and puts the new release section at the top, under the file heading.

  4. It commits the updated file with a message like "docs: changelog for v2.4.0".

Before it runs live

  • Connect GitHub
  • Paste your automation's link into the other app's webhook settings

What you get

RunsThe moment it happens
Update CHANGELOG.md when a GitHub release is publishedjust nowSucceeded
Commit · acme/app maindocs: changelog for v2.4.0 ## v2.4.0 (2026-11-10) ### Features - Export to Parquet (#812) ### Fixes - Retry on 429 from upstream (#809)

Build your own

Need it to work differently?

This template is a starting point. Copy the prompt and change it, or describe a different automation from scratch, with your own conditions, more apps or an AI step. Your assistant writes it, tests it and runs it on ClickRuntime.
  • Your own rules

    Conditions, thresholds, filters and exceptions, written the way you'd explain them to a colleague.

  • Any number of apps

    One automation can read from one app and update several others. Over 248 apps are ready, and anything with an API works too.

  • AI where it helps

    Summaries, classification, extraction and drafts use ClickRuntime's built-in AI, with no extra key.

  • On a schedule or on each event

    Runs as often as every 5 minutes, or the moment an app sends an event, and keeps track of what it already handled.

Changes you can ask for

  • Act only on the items that match your own rules, and skip the rest
  • Add an AI step, for example to summarize, sort or draft a reply
  • Add another app, for example also send it to Slack
  • Combine it with other GitHub steps into one automation

Under the hood

The code that runs this automation

This is the Python script that runs each time the automation starts. Your AI assistant writes it for you when you send the prompt, and you allow each step in your assistant. To change how the automation works, describe the change to your assistant and it updates the script for you. For every function it can use, see the script reference.
update_changelog_md_when_a_github_release_is_published.pyexample
import base64, datetime
from clickruntime import on_event, credentials, http, verify

SKIP_PRERELEASE = True

def gh(method, path, **kw):
    resp = http.request(method, f"https://api.github.com{path}", headers={
        "Authorization": f"Bearer {credentials.connection('github', 'work')}",
        "Accept": "application/vnd.github+json"}, **kw)
    resp.raise_for_status()
    return resp.json() if resp.content else None

@on_event
def handle(event):
    if not verify.github(event.body, event.header("X-Hub-Signature-256"), credentials.secret("GITHUB_WEBHOOK_SECRET")):
        return {"ignored": "bad signature"}
    body = event.json
    if event.header("X-GitHub-Event") != "release" or body.get("action") != "published":
        return {"ignored": True}
    rel, repo = body["release"], body["repository"]
    if SKIP_PRERELEASE and rel.get("prerelease"):
        return {"ignored": "prerelease"}
    path, branch = f"/repos/{repo['full_name']}/contents/CHANGELOG.md", repo["default_branch"]
    try:
        cur = gh("GET", path, params={"ref": branch})
        text, sha = base64.b64decode(cur["content"]).decode(), cur["sha"]
    except Exception:
        text, sha = "# Changelog\n", None
    if f"## {rel['tag_name']} " in text:
        return {"ignored": "already in changelog"}
    day = (rel.get("published_at") or datetime.datetime.utcnow().isoformat())[:10]
    section = f"## {rel['tag_name']} ({day})\n\n{(rel.get('body') or 'No notes.').strip()}\n\n"
    head, _, rest = text.partition("\n")
    new = f"{head}\n\n{section}{rest.lstrip()}" if head.startswith("# ") else section + text
    payload = {"message": f"docs: changelog for {rel['tag_name']}", "branch": branch,
               "content": base64.b64encode(new.encode()).decode()}
    if sha:
        payload["sha"] = sha
    gh("PUT", path, json=payload)
    return {"tag": rel["tag_name"]}

FAQ

Common questions

What if main is protected?
A protected branch that requires pull requests will reject the commit. Ask your assistant to commit to a new branch and open a pull request instead; the rest stays the same.
Will it add the same release twice if I edit it?
No. It only reacts to the "published" action, and it checks whether a section for that tag already exists before writing.
Can it use GitHub's generated release notes?
Yes. If you click "Generate release notes" when creating the release, that text is the release body, and that is what goes into the changelog.
What permissions does it need?
Contents: read and write. Nothing else in your accounts is used.
Does it need AI?
No. It runs without AI, so it doesn't use your plan's AI allowance.
How many runs does it use?
One run for each event the app sends.
Can I change how it works?
Yes. Describe the change to your AI assistant, for example a different schedule, channel or filter. It updates the automation, tests it and tells you what changed.

Set up “Update CHANGELOG.md when a GitHub release is published” from your assistant

Copy the prompt above, or connect your assistant first. Free plan, no credit card.