Git & Repository Activity
Notify your team about pull requests, merges, releases, and code reviews using GitHub webhooks, local git post-merge hooks, and CI release steps.
Keep your team in the loop on repository activity without everyone watching GitHub. Route important events - merged PRs, new releases, review requests - to the channels where your team already communicates.
GitHub Webhooks
GitHub can send webhook events straight to Alphorn. Pick the GitHub source preset when creating the webhook (under Advanced: field templates), or see Webhook templates for per-event templates. GitHub can't send an Authorization header, so create that webhook with No API key (URL is the secret).
In GitHub (Settings → Webhooks → Add webhook):
- Payload URL: your Alphorn webhook URL
- Content type:
application/json(GitHub's default,application/x-www-form-urlencoded, arrives as plain text, so the preset never applies) - Events: the events you care about
A small proxy is still useful if you want to format each event type differently. The example below skips signature verification to stay short; Alphorn doesn't check X-Hub-Signature-256, so for a proxy that verifies it, see the GitHub Webhooks proxy.
Proxy example (Node.js)
const express = require("express");
const app = express();
app.use(express.json()); // parses the JSON body into req.body
app.post("/github-webhook", (req, res) => {
const event = req.headers["x-github-event"];
const payload = req.body;
let title, message, priority, tags;
switch (event) {
case "pull_request":
if (payload.action === "opened") {
title = `PR opened: ${payload.pull_request.title}`;
message = `${payload.pull_request.user.login} opened #${payload.number} in ${payload.repository.full_name}`;
priority = 2;
tags = ["git", "pr", "opened"];
} else if (payload.action === "closed" && payload.pull_request.merged) {
title = `PR merged: ${payload.pull_request.title}`;
message = `#${payload.number} merged by ${payload.pull_request.merged_by.login}`;
priority = 2;
tags = ["git", "pr", "merged"];
} else {
return res.sendStatus(200);
}
break;
case "release":
title = `Release: ${payload.release.tag_name}`;
message = `${payload.repository.full_name} - ${payload.release.name || "New release"}`;
priority = 3;
tags = ["git", "release"];
break;
case "push":
if (payload.ref === "refs/heads/main") {
title = `Push to main: ${payload.repository.name}`;
message = `${payload.commits.length} commit(s) by ${payload.pusher.name}`;
priority = 2;
tags = ["git", "push", "main"];
} else {
return res.sendStatus(200);
}
break;
default:
return res.sendStatus(200);
}
fetch("https://app.alphorn.dev/n/V1StGXR8_Z5jdHi6B-myT", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${process.env.ALPHORN_API_KEY}`,
},
body: JSON.stringify({ title, message, priority, tags }),
}).catch((err) => console.error("Forwarding to Alphorn failed:", err));
res.sendStatus(200);
});
app.listen(8080);Post-merge hook (local)
Notify after merging to main locally. The hook needs jq and curl, and ALPHORN_API_KEY must be exported in the shell you run git from (or replace the alp_... fallback with your key):
#!/bin/bash
BRANCH=$(git rev-parse --abbrev-ref HEAD)
if [ "$BRANCH" = "main" ]; then
COMMIT=$(git log -1 --pretty=format:"%s (%an)")
jq -n --arg m "$COMMIT" \
'{title: "Merged to main", message: $m, priority: 2, tags: ["git", "merge", "main"]}' |
curl -fsS -X POST "https://app.alphorn.dev/n/V1StGXR8_Z5jdHi6B-myT" \
-H "Authorization: Bearer ${ALPHORN_API_KEY:-alp_...}" \
-H "Content-Type: application/json" \
--data-binary @-
fiGit only runs executable hooks, so make it executable with chmod +x .git/hooks/post-merge.
Release notifications (CI)
After creating a Git tag, notify the team:
VERSION=$(git describe --tags --exact-match 2>/dev/null)
if [ -n "$VERSION" ]; then
PREV=$(git describe --tags --abbrev=0 HEAD^ 2>/dev/null)
# First tag: no previous tag, so use the full history
CHANGELOG=$(git log --oneline ${PREV:+"$PREV..HEAD"} | head -10)
# jq escapes the multi-line changelog into valid JSON
jq -n --arg v "$VERSION" --arg c "$CHANGELOG" \
'{title: "Release \($v)", message: "Changes:\n\($c)", priority: 3, tags: ["git", "release", $v]}' |
curl -fsS -X POST "https://app.alphorn.dev/n/V1StGXR8_Z5jdHi6B-myT" \
-H "Authorization: Bearer $ALPHORN_API_KEY" \
-H "Content-Type: application/json" \
--data-binary @-
fiRouting examples
| Channel | Filter | Purpose |
|---|---|---|
| Slack (#dev) | Tags has all of pr, merged | Merged PRs only |
| Slack (#releases) | Tags has any of release | New releases |
| Discord | Tags has any of git | All repository activity |
Tags has any of release | Release archive |
IoT & Sensor Alerts
Route IoT and sensor alerts to any channel: temperature thresholds, device offline detection, ESP32 and Arduino HTTP requests, and an MQTT bridge.
GitHub Webhooks
Route GitHub webhook events - issues, workflow runs, build failures, discussions, security alerts - to Slack, Discord, PagerDuty, or any other channel.