Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
110 changes: 110 additions & 0 deletions .github/blocks/notify-discord/action.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
name: "Notify Discord"
description: "Post a release announcement embed to a Discord channel webhook"

inputs:
webhook-url:
description: "[secret] Discord channel webhook URL. Empty disables the block."
required: false
default: ""
service-name:
description: "[string] Display name of the released service"
required: true
version:
description: "[string] Version being released (e.g., 1.2.3)"
required: true
release-url:
description: "[string] URL to the GitHub Release"
required: true
release-notes:
description: "[string] Markdown release notes (truncated to 4096 chars)"
required: false
default: ""
prerelease:
description: "[boolean: 'true'/'false'] Whether this is a prerelease"
required: false
default: "false"
dry-run:
description: "[boolean: 'true'/'false'] Log payload without sending"
required: false
default: "false"

runs:
using: composite
steps:
- name: Send Discord announcement
uses: actions/github-script@v7
env:
WEBHOOK_URL: ${{ inputs.webhook-url }}
SERVICE_NAME: ${{ inputs.service-name }}
VERSION: ${{ inputs.version }}
RELEASE_URL: ${{ inputs.release-url }}
RELEASE_NOTES: ${{ inputs.release-notes }}
PRERELEASE: ${{ inputs.prerelease }}
DRY_RUN: ${{ inputs.dry-run }}
with:
script: |
const webhookUrl = process.env.WEBHOOK_URL;
if (!webhookUrl) {
core.info('Discord webhook not configured, skipping.');
return;
}
core.setSecret(webhookUrl);

const serviceName = process.env.SERVICE_NAME;
const version = process.env.VERSION;
const releaseUrl = process.env.RELEASE_URL;
const rawNotes = process.env.RELEASE_NOTES || '';
const isPrerelease = process.env.PRERELEASE === 'true';
const isDryRun = process.env.DRY_RUN === 'true';

const MAX_DESCRIPTION = 4096;
const description = rawNotes.length > MAX_DESCRIPTION
? rawNotes.slice(0, MAX_DESCRIPTION - 1) + '\u2026'
: rawNotes;

const titlePrefix = isPrerelease ? '[Pre-release] ' : '';
const color = isPrerelease ? 0xF1C40F : 0x2ECC71;

const payload = {
embeds: [
{
title: `${titlePrefix}${serviceName} v${version}`,
url: releaseUrl,
description,
color,
timestamp: new Date().toISOString(),
footer: { text: context.payload.repository?.full_name || `${context.repo.owner}/${context.repo.repo}` },
},
],
};

if (isDryRun) {
core.info('Dry run enabled, not sending. Payload:');
core.info(JSON.stringify(payload, null, 2));
return;
}

const WEBHOOK_TIMEOUT_MS = 10000;
let response;
try {
response = await fetch(webhookUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
signal: AbortSignal.timeout(WEBHOOK_TIMEOUT_MS),
});
} catch (error) {
if (error.name === 'AbortError' || error.name === 'TimeoutError') {
core.setFailed(`Discord webhook timed out after ${WEBHOOK_TIMEOUT_MS}ms.`);
return;
}
throw error;
}

if (!response.ok) {
const body = await response.text();
core.setFailed(`Discord webhook returned ${response.status} ${response.statusText}: ${body}`);
return;
}

core.info(`Discord announcement posted (HTTP ${response.status}).`);
53 changes: 53 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1273,6 +1273,59 @@ Posts or updates a single comment on a pull request. When a `comment-key` is pro
```


---

### notify-discord

**Path:** `.github/blocks/notify-discord/action.yaml`

Posts a release announcement embed to a Discord channel webhook. The block no-ops when `webhook-url` is empty, so it can be wired into a pipeline unconditionally and stays dormant for repos that haven't configured `DISCORD_WEBHOOK_URL`.

#### Setup (one-time, in Discord)

1. Open the Discord server, then **Server Settings → Integrations → Webhooks → New Webhook**.
2. Pick the channel where releases should be announced.
3. Copy the webhook URL (looks like `https://discord.com/api/webhooks/<id>/<token>`).
4. Add it to GitHub as a repo or org Actions secret named `DISCORD_WEBHOOK_URL`.

The webhook URL is bound to the channel selected in step 2 — that is how "specific channel" is targeted; no channel ID is needed in the workflow.

#### Inputs

| Input | Type | Required | Default | Description |
|-------|------|----------|---------|-------------|
| `webhook-url` | secret | No | `""` | Discord channel webhook URL. Empty disables the block. |
| `service-name` | string | Yes | — | Display name of the released service |
| `version` | string | Yes | — | Version being released (e.g., `1.2.3`) |
| `release-url` | string | Yes | — | URL to the GitHub Release (use the `url` output of `create-github-release`) |
| `release-notes` | string | No | `""` | Markdown release notes (auto-truncated to Discord's 4096-char embed limit) |
| `prerelease` | boolean | No | `false` | Tags the embed as a prerelease (amber color, `[Pre-release]` title prefix) |
Comment thread
yanxue06 marked this conversation as resolved.
| `dry-run` | boolean | No | `false` | Logs the JSON payload without sending |

#### Usage

```yaml
- name: Create GitHub Release
id: gh-release
uses: photon-hq/buildspace/.github/blocks/create-github-release@main
with:
version: ${{ steps.release-info.outputs.version }}
title: "${{ inputs.service-name }} v${{ steps.release-info.outputs.version }}"
notes: ${{ steps.release-info.outputs.release_notes }}
prerelease: ${{ inputs.prerelease }}

- name: Announce release on Discord
uses: photon-hq/buildspace/.github/blocks/notify-discord@main
with:
webhook-url: ${{ secrets.DISCORD_WEBHOOK_URL }}
service-name: ${{ inputs.service-name }}
version: ${{ steps.release-info.outputs.version }}
release-url: ${{ steps.gh-release.outputs.url }}
release-notes: ${{ steps.release-info.outputs.release_notes }}
prerelease: ${{ inputs.prerelease }}
```
Comment thread
yanxue06 marked this conversation as resolved.


---

### update-docs
Expand Down
Loading