diff --git a/README.md b/README.md index 4fe4fe4..1cf3f29 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/docs/rn-publish.md b/docs/rn-publish.md new file mode 100644 index 0000000..6a30481 --- /dev/null +++ b/docs/rn-publish.md @@ -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: []` 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 `` marker (idempotent re-publish). +6. **Edits the comment** to append a ✅ line with the published URL and the invisible `` 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` | `[]` | 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//pull/`) | 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: + +``` + +``` + +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` | diff --git a/workflows/rn-publish.yml b/workflows/rn-publish.yml index af2a5f5..7e9b19b 100644 --- a/workflows/rn-publish.yml +++ b/workflows/rn-publish.yml @@ -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}]') @@ -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" \ @@ -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" \