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.
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
GitHub sends a webhook to the automation when a release is published.
The automation checks the signature and skips drafts and pre-releases if you want.
It reads the current CHANGELOG.md from the default branch and puts the new release section at the top, under the file heading.
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
Build your own
Need it to work differently?
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
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?
Will it add the same release twice if I edit it?
Can it use GitHub's generated release notes?
What permissions does it need?
Does it need AI?
How many runs does it use?
Can I change how it works?
Related
You might also like
Ask for missing details on new GitHub issues
New GitHub issues without steps to reproduce or a version number get a polite comment asking for them and a needs-info label.
See how it worksBack up your data to a GitHub repo every night
Your exports and configs are committed to a private repo each night, only when something changed, with a summary by email.
See how it worksGet weekly stats for your repos by email
Every Monday you see stars, forks, open issues and open pull requests for your repos, and how they changed since last week.
See how it worksKeep exploring
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.