AlphornAlphorn Docs

Git & Repository Activity

Notify your team about pull requests, merges, releases, and code reviews.

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 to Alphorn. Set up a small proxy to transform GitHub's payload format into Alphorn's.

Proxy example (Node.js)

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/api/webhooks/wh_abc123", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ title, message, priority, tags }),
  });

  res.sendStatus(200);
});

Post-merge hook (local)

Notify after merging to main locally:

.git/hooks/post-merge
#!/bin/bash
BRANCH=$(git rev-parse --abbrev-ref HEAD)

if [ "$BRANCH" = "main" ]; then
  COMMIT=$(git log -1 --pretty=format:"%s (%an)")
  curl -s -X POST "https://app.alphorn.dev/api/webhooks/wh_abc123" \
    -H "Content-Type: application/json" \
    -d "{
      \"title\": \"Merged to main\",
      \"message\": \"$COMMIT\",
      \"priority\": 2,
      \"tags\": [\"git\", \"merge\", \"main\"]
    }"
fi

Release notifications (CI)

After creating a Git tag, notify the team:

VERSION=$(git describe --tags --exact-match 2>/dev/null)

if [ -n "$VERSION" ]; then
  CHANGELOG=$(git log --oneline "$(git describe --tags --abbrev=0 HEAD^)..HEAD" | head -10)
  curl -s -X POST "https://app.alphorn.dev/api/webhooks/wh_abc123" \
    -H "Content-Type: application/json" \
    -d "{
      \"title\": \"Release $VERSION\",
      \"message\": \"Changes:\n$CHANGELOG\",
      \"priority\": 3,
      \"tags\": [\"git\", \"release\", \"$VERSION\"]
    }"
fi

Routing examples

ChannelFilterPurpose
Slack (#dev)tags CONTAINS "pr" AND tags CONTAINS "merged"Merged PRs only
Slack (#releases)tags CONTAINS "release"New releases
Discordtags CONTAINS "git"All repository activity
Emailtags CONTAINS "release"Release archive

On this page