Skip to content

discord-release

Posts a release announcement to a Discord channel when a vX.Y.Z tag pipeline runs. Parses the tag, decides whether the release is worth announcing, pulls the notes from the GitLab Releases API and posts a Discord embed.

Pairs with releaser-pleaser and release-plz: those cut the tag and the release, this one tells people about it.

The filter is the point

An announcements channel fed by every patch bump across the estate stops being read within a week, and a channel nobody reads is worse than no channel. The default is minor and major cuts only — any tag whose patch component is 0. Headline projects opt into patch announcements with announce_patches: true; everything else stays quiet until its next feature release.

Jobs

Job What it runs
discord-release (rename with job_name) Parses $CI_COMMIT_TAG, applies the filter, fetches release notes, and POSTs a Discord embed. Writes the payload to discord-release/<job_name>.json as an artifact whether or not it posts, so an announcement can be inspected after the fact. interruptible: false — a tag pipeline should not be cancelled by a later push.

Inputs

Input Type Default Description
stage string notify GitLab CI stage. Consumers must declare it in stages:.
image_version string "v0.1.1" ci-base tag to run in. Needs curl and jq — 48 MB, against the 611 MB this job used to pull for them.
job_name string discord-release Name of the generated job. Override to include the component more than once, e.g. a second channel.
webhook_url string "$DISCORD_RELEASE_WEBHOOK" Discord webhook URL. Defaults to the group-level variable so a consumer needs no per-project config. Empty means "skip quietly".
announce_patches boolean false Announce patch releases (vX.Y.Z where Z is not 0). Reserved for headline projects.
announce_prereleases boolean false Announce prerelease tags (v1.3.0-rc.1 and friends).
tag string "$CI_COMMIT_TAG" The tag to announce. Override to re-announce an earlier release, or to exercise the filter.
if string "$CI_COMMIT_TAG" Gating rules:if expression — by default, "this pipeline has a tag". The schedule-never guard is always applied ahead of it. Mirrors the same input on releaser-pleaser.
project_name string "$CI_PROJECT_TITLE" Human name used in the embed title.
notes_limit number 1200 Characters of release notes to include. Discord's ceiling is 4096; the lower default keeps an announcement scannable.
notes_retries number 5 Polls of the Releases API before announcing without notes.
token string "$CI_JOB_TOKEN" Token used to read the Releases API. Public projects need none.
dry_run boolean false Build and print the payload without posting.
allow_failure boolean true Whether a failed announcement fails the pipeline. Leave true in consumers.

What gets announced

Tag announce_patches Result Embed colour
v1.0.0 either announced, major amber #F8C272
v1.2.0 either announced, minor teal #1F6E70
v0.4.0 either announced, minor teal #1F6E70
v1.2.3 false quiet
v1.2.3 true announced, patch deep #0C292E
v1.3.0-rc.1 either quiet unless announce_prereleases teal
nightly-2026-08-09 either quiet (not semver)

"Major" here means MINOR and PATCH are both 0, so on a pre-1.0 project v0.4.0 reads as a minor cut, which is what it is.

Minimal usage

stages:
  - notify

include:
  - component: gitlab.com/phpboyscout/cicd/[email protected]

A headline project that wants its patches announced too:

include:
  - component: gitlab.com/phpboyscout/cicd/[email protected]
    inputs:
      announce_patches: true

Failure behaviour

Nothing here is allowed to break a release.

  • allow_failure: true by default. A Discord outage, a rotated webhook or a rate-limit must not fail the pipeline that just cut a release.
  • A missing webhook is a skip, not a failure. The job warns and exits 0. This is deliberate: the component can be rolled out across the estate before the webhook variable exists, and starts working the moment it does.
  • Release notes are best-effort. The tag pipeline can start before releaser-pleaser has created the Release object, so the lookup retries (notes_retries) and then degrades to an embed with no body rather than failing or announcing nothing.
  • An unrecognised tag is a skip. Anything that is not vMAJOR.MINOR.PATCH[-PRERELEASE] exits 0 without posting.

Setting up the webhook

The webhook is created in Discord, not here: Server Settings → Integrations → Webhooks → New Webhook, pointed at the announcements channel. Copy the URL and store it as a masked group-level CI variable named DISCORD_RELEASE_WEBHOOK.

The webhook URL is a credential

Anyone holding it can post to the channel as that webhook. Keep the variable masked. Rotate it in Discord if it ever reaches a log or a public MR.

v* must be a protected tag in the consuming project

DISCORD_RELEASE_WEBHOOK is a protected group variable, so GitLab only exposes it to jobs running on protected refs. Every phpboyscout project protects v* at Maintainer level, which is what makes a release tag a protected ref and lets the variable through.

A project that does not protect its v* tags gets an empty webhook, and the component treats an empty webhook as "skip quietly" — so the job goes green and nothing is ever announced. That is silent by design (it is what allows rollout before the webhook exists), which makes this the one setting worth checking when a new project's releases never show up in the channel.

Set it with:

glab api -X POST "projects/<path%2Fencoded>/protected_tags" \
  -H "Content-Type: application/json" \
  --input - <<<'{"name":"v*","create_access_level":40}'