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
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ A collection of reusable [GitHub Agentic Workflows](https://github.github.io/gh-
### Release Note Workflows

- [📋 RN Write](docs/rn-write.md) - Write a plain-language release note entry when a pull request is merged
- [📤 RN Publish](docs/rn-publish.md) - Publish a reviewed release note comment to the Skyline Collaboration platform

### Documentation Workflows

Expand Down
137 changes: 137 additions & 0 deletions docs/rn-publish.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
# 📤 RN Publish

> For an overview of all available workflows, see the [main README](../README.md).

**Publishes a reviewed release note comment to the Skyline Collaboration platform**

The [RN Publish workflow](../workflows/rn-publish.yml) triggers when the `rn-publish` label is added to a pull request. It finds all `## 📋 Release Note` comments on the PR, converts the Markdown body to HTML, and posts each one to the Skyline Collaboration API (`api.skyline.be/api/ReleaseNotes`). Re-triggering the label updates existing entries rather than creating duplicates.

## Installation

### 1. Copy the workflow file

Copy [`workflows/rn-publish.yml`](../workflows/rn-publish.yml) into the `.github/workflows/` folder of the target repository.

### 2. Add the required secrets

Go to **Repository → Settings → Secrets and variables → Actions → Repository secrets** and add:

| Secret | Value |
|--------|-------|
| `COLLABORATION_USERNAME` | Your Skyline Collaboration platform username |
| `COLLABORATION_PASSWORD` | Your Skyline Collaboration platform password |

### 3. Create the `rn-publish` label

```bash
gh label create rn-publish --color 0075ca --description "Publish the release note to the collaboration platform"
```

Or via **GitHub → Repository → Issues → Labels → New label**.

### 4. (Optional but recommended) Set the `SOLUTION_NAME` variable

If this repository belongs to a named Solution (e.g. InfraOps), set a repository variable so the published entry is correctly categorised on the platform.

Go to **Repository → Settings → Secrets and variables → Actions → Repository variables** and add:

| Variable | Value |
|----------|-------|
| `SOLUTION_NAME` | The exact solution name as it appears in the collaboration platform (e.g. `InfraOps`) |

When `SOLUTION_NAME` is set, the workflow automatically populates `Application: "Solutions"` and `Functionality: [<SOLUTION_NAME>]` in the API payload. When absent, those fields are omitted and the entry is published without a solution category.

## How it works

### Activation

Add the `rn-publish` label to a pull request that has one or more `## 📋 Release Note` comments (produced by the [RN Write](rn-write.md) workflow or written manually). The label triggers the workflow; the label is removed again when publishing completes.

### Processing

For each `## 📋 Release Note` comment found on the PR:

1. **Validates** the required fields (`**Type:**`, `**Breaking Change:**`).
2. **Extracts** the description body (everything after the metadata header) and **converts it from Markdown to HTML** via `pandoc` so the collaboration platform's rich-text editor renders it correctly — paragraphs, bullet lists, `inline code`, **bold**, and *italic* are all preserved.
3. **Looks up `SolutionVersion`** — fetches the latest non-draft GitHub release whose `target_commitish` matches the PR's base branch and uses its tag name. Silently omitted when no matching release exists.
4. **Derives `Scope`** from the `Type` field automatically (see table below).
5. **Calls the API** — POST for a new entry, PATCH if the comment already contains an `<!-- rn-published: {id} -->` marker (idempotent re-publish).
6. **Edits the comment** to append a ✅ line with the published URL and the invisible `<!-- rn-published: {id} -->` marker.

After all entries are processed, the `rn-proposal` and `rn-publish` labels are removed and the `rn-published` label is applied to the PR.

### API fields

| Field | Source | Notes |
|-------|--------|-------|
| `Title` | `**Title:**` in the comment | Falls back to the PR title for comments that predate the Title field |
| `Description` | Comment body (below the metadata header) | Converted from GFM Markdown to HTML via `pandoc -f gfm -t html --wrap=none` |
| `Type` | `**Type:**` in the comment | Must be one of `New Feature/Enhancement`, `Bug Fix`, or `Release Notes` |
| `BreakingChange` | `**Breaking Change:**` in the comment | Must be `true` or `false` |
| `Scope` | Derived from `Type` — see table below | Always included |
| `Application` | Hardcoded `"Solutions"` | Only included when `SOLUTION_NAME` is set |
| `Functionality` | `[<SOLUTION_NAME>]` | Only included when `SOLUTION_NAME` is set |
| `SolutionVersion` | Tag name of the latest non-draft GitHub release for the PR's base branch | Omitted when no matching release exists |
| `InternalComments` | PR URL (`https://github.com/<repo>/pull/<number>`) | Always included |

### Scope mapping

The `Scope` field is derived automatically — no manual input needed:

| Type | Scope |
|------|-------|
| `Bug Fix` | `Regression only` |
| `New Feature/Enhancement` | `Functional` |
| `Release Notes` | `Functional` |

The logic here is that a bug fix restores previous behavior (regression scope), while a new feature or enhancement adds or changes functional behavior.

### Idempotency

Each published comment is edited to include an invisible HTML marker:

```
<!-- rn-published: 12345 -->
```

If the `rn-publish` label is added again later, the workflow detects this marker, looks up the existing entry by ID, and issues a PATCH request to update it — no duplicate entries are created.

## What it reads

- All PR comments matching `## 📋 Release Note`
- The PR's base branch (for `SolutionVersion` lookup)
- GitHub releases for the repository (for `SolutionVersion`)

## What it creates or updates

- **Entries on the Skyline Collaboration platform** — one per matching comment
- **Edits each comment** to append the published URL and idempotency marker
- **Removes** the `rn-proposal` and `rn-publish` labels
- **Applies** the `rn-published` label to the PR

## Manual release notes

The `## 📋 Release Note` heading is all the publish workflow looks for. This means anyone can manually write a release note comment on any PR using the structure below — the agentic `rn-write` workflow does not have to be active in the repository:

```markdown
## 📋 Release Note

**Title:** A short changelog title
**Type:** New Feature/Enhancement
**Breaking Change:** false

The description of the change, written in plain Markdown. Use paragraphs,
bullet lists, `inline code`, **bold**, and *italic* freely — they will be
converted to HTML before being sent to the platform.
```

## Troubleshooting

| Symptom | Cause | Fix |
|---------|-------|-----|
| `❌ Missing required secrets` | `COLLABORATION_USERNAME` or `COLLABORATION_PASSWORD` not set | Add them as repository secrets |
| `❌ No release note draft found` | No `## 📋 Release Note` comment on the PR | Run the `rn-write` workflow or add a comment manually |
| `❌ could not find **Type:**` | Comment is missing the `**Type:**` field | Edit the comment to add a valid Type |
| `❌ invalid Type` | Type is not one of the accepted values | Must be `New Feature/Enhancement`, `Bug Fix`, or `Release Notes` |
| `❌ Skyline API update failed` | Authentication or API error | Check secrets; inspect the HTTP status and message in the error comment |
| Description renders as one block on the platform | Old version of the workflow without pandoc | Update to the latest `rn-publish.yml` |
20 changes: 17 additions & 3 deletions workflows/rn-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,13 @@ jobs:

PR_URL="https://github.com/${REPO}/pull/${PR_NUMBER}"

# Resolve the base branch and latest release tag for SolutionVersion (silently omitted if none found)
BASE_BRANCH=$(gh pr view "$PR_NUMBER" --repo "$REPO" --json baseRefName --jq '.baseRefName')
SOLUTION_VERSION=$(gh api "repos/$REPO/releases" \
--jq '[.[] | select(.target_commitish == "'"$BASE_BRANCH"'" and .draft == false)] | first | .tag_name // empty' \
2>/dev/null || true)
if [ -n "$SOLUTION_VERSION" ]; then HAS_VERSION=true; else HAS_VERSION=false; fi

# Fetch all release note draft comments (id + body)
RN_COMMENTS_JSON=$(gh api "repos/$REPO/issues/$PR_NUMBER/comments" \
--jq '[.[] | select(.body | test("## 📋 Release Note")) | {id: .id, body: .body}]')
Expand Down Expand Up @@ -141,7 +148,9 @@ jobs:
/^[[:space:]]*$/ { if (!started) next; print; next }
{ started=1; print }
' \
| head -c 4000)
| head -c 4000 \
| pandoc -f gfm -t html --wrap=none)
echo "::debug::Converted description HTML: $DESCRIPTION"

if [ -z "$DESCRIPTION" ]; then
gh api "repos/$REPO/issues/$PR_NUMBER/comments" \
Expand All @@ -151,17 +160,22 @@ jobs:
fi

if [ "$BREAKING" = "true" ]; then BREAKING_JSON=true; else BREAKING_JSON=false; fi
if [ "$TYPE" = "Bug Fix" ]; then SCOPE="Regression only"; else SCOPE="Functional"; fi

PAYLOAD=$(jq -n \
--arg title "$TITLE" \
--arg desc "$DESCRIPTION" \
--arg type "$TYPE" \
--argjson breaking "$BREAKING_JSON" \
--arg scope "$SCOPE" \
--arg solution "$SOLUTION" \
--argjson has_solution "$HAS_SOLUTION" \
--arg version "$SOLUTION_VERSION" \
--argjson has_version "$HAS_VERSION" \
--arg pr_url "$PR_URL" \
'{Title: $title, Description: $desc, Type: $type, BreakingChange: $breaking, InternalComments: $pr_url}
+ (if $has_solution then {Application: "Solutions", Functionality: [$solution]} else {} end)')
'{Title: $title, Description: $desc, Type: $type, BreakingChange: $breaking, Scope: $scope, InternalComments: $pr_url}
+ (if $has_solution then {Application: "Solutions", Functionality: [$solution]} else {} end)
+ (if $has_version then {SolutionVersion: $version} else {} end)')

# Check for idempotency marker — if already published, update instead of creating a new entry
EXISTING_RN_ID=$(printf '%s' "$COMMENT_BODY" \
Expand Down