# CI/CD Pipeline Notifications

> Send CI/CD build notifications from GitHub Actions and GitLab CI when pipelines pass or fail, and route them to Slack, email, or other channels.

Source: https://docs.alphorn.dev/use-cases/ci-cd-pipelines/

CI/CD pipelines run in the background - you need to know when something fails without constantly watching dashboards. Add a curl step to your pipeline and Alphorn routes the result to the right people.

## GitHub Actions

Add a notification step at the end of your workflow. Values like the branch name are passed through `env:` and the JSON is built with `jq`, rather than pasting `${{ ... }}` expressions into the script, so a crafted branch name can't inject shell commands or break the JSON. Add `ALPHORN_WEBHOOK` and `ALPHORN_API_KEY` as repository secrets (**Settings → Secrets and variables → Actions**) first:

```yaml title=".github/workflows/deploy.yml"
- name: Notify on success
  if: success()
  env:
    ALPHORN_WEBHOOK: ${{ secrets.ALPHORN_WEBHOOK }}
    ALPHORN_API_KEY: ${{ secrets.ALPHORN_API_KEY }}
    REPO: ${{ github.repository }}
    REF_NAME: ${{ github.ref_name }}
    SHA: ${{ github.sha }}
  run: |
    jq -n --arg m "$REPO deployed from $REF_NAME (commit $SHA)" \
      '{title: "Deploy succeeded", message: $m, priority: 2, tags: ["ci", "deploy", "success"]}' |
    curl -fsS -X POST "$ALPHORN_WEBHOOK" \
      -H "Authorization: Bearer $ALPHORN_API_KEY" \
      -H "Content-Type: application/json" \
      --data-binary @-

- name: Notify on failure
  if: failure()
  env:
    ALPHORN_WEBHOOK: ${{ secrets.ALPHORN_WEBHOOK }}
    ALPHORN_API_KEY: ${{ secrets.ALPHORN_API_KEY }}
    REPO: ${{ github.repository }}
    REF_NAME: ${{ github.ref_name }}
    RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
  run: |
    jq -n --arg m "$REPO failed on $REF_NAME. See: $RUN_URL" \
      '{title: "Deploy FAILED", message: $m, priority: 5, tags: ["ci", "deploy", "failure"]}' |
    curl -fsS -X POST "$ALPHORN_WEBHOOK" \
      -H "Authorization: Bearer $ALPHORN_API_KEY" \
      -H "Content-Type: application/json" \
      --data-binary @-
```

## GitLab CI

`CI_JOB_STATUS` is only set to the job's result (`success`, `failed`, or `canceled`) in `after_script`, so send the notification from there. Define `ALPHORN_WEBHOOK` and `ALPHORN_API_KEY` as CI/CD variables (**Settings → CI/CD → Variables**, mark the key as masked), and make sure the job image has `jq` and `curl`:

```yaml title=".gitlab-ci.yml"
deploy:
  stage: deploy
  script:
    - ./deploy.sh
  after_script:
    - |
      if [ "$CI_JOB_STATUS" = "success" ]; then
        PRIORITY=2; TITLE="Deploy passed"; TAG="success"
      else
        PRIORITY=5; TITLE="Deploy FAILED"; TAG="failure"
      fi
      # jq escapes the branch name, so it can't break the JSON
      jq -n --arg t "$TITLE" --arg m "$CI_PROJECT_NAME on $CI_COMMIT_REF_NAME - $CI_PIPELINE_URL" \
        --argjson p "$PRIORITY" --arg tag "$TAG" \
        '{title: $t, message: $m, priority: $p, tags: ["ci", "deploy", $tag]}' |
      curl -fsS -X POST "$ALPHORN_WEBHOOK" \
        -H "Authorization: Bearer $ALPHORN_API_KEY" \
        -H "Content-Type: application/json" \
        --data-binary @-
```

## Routing examples

| Channel | Filter | Purpose |
|---|---|---|
| Slack (#deployments) | Tags has any of `deploy` | All deploy activity |
| Slack (#dev) | Tags has any of `failure` | Only failures, to avoid noise |
| PagerDuty | Priority greater than 3 and tags has any of `failure` | Page on-call for production failures |
| Email | Tags has any of `ci` | Archive all CI activity |
