Skip to content

fix: Sync WebGUI tags to OS Linear releases#2646

Merged
elibosley merged 1 commit into
masterfrom
codex/webgui-linear-release-sync
May 22, 2026
Merged

fix: Sync WebGUI tags to OS Linear releases#2646
elibosley merged 1 commit into
masterfrom
codex/webgui-linear-release-sync

Conversation

@elibosley
Copy link
Copy Markdown
Member

@elibosley elibosley commented May 22, 2026

Summary

  • Replaces the WebGUI Linear tag workflow's linear/linear-release-action@v0 usage with a direct Linear GraphQL sync.
  • Uses the shared OS Linear Release pipelines: prerelease tags sync to OS Prereleases, stable tags sync to OS Stable Releases.
  • Computes the previous semver tag, scans the tag range plus merged PR metadata, and attaches found Linear issues to the release.
  • Also resolves product.unraid.net/p/... links through Linear attachments so FeatureOS-linked Linear issues can be added to the release.
  • Preserves terminal release stages so a WebGUI rerun will not downgrade a release already marked Released by release-diff.
  • Updates checkout to actions/checkout@v6 to avoid the Node 20 action warning.

Validation

  • node --check .github/scripts/sync-linear-release.mjs
  • git diff --check
  • Verified previous-tag resolution locally for 7.3.1-rc.0.3, 7.3.1-rc.0.1, 7.3.0, and 7.2.7.
  • Ran the sync script locally against live Linear for 7.3.1-rc.0.3; it kept the release Released, updated the WebGUI commit SHA to the tag commit, and did not remove existing issue memberships.

Summary by CodeRabbit

Release Notes

  • Chores
    • Updated internal release automation and infrastructure tooling to improve the release synchronization process between GitHub and Linear.

Note: This release contains no user-facing changes.

Review Change Stack

Purpose of the change:
- Align the WebGUI tag workflow with the shared Unraid OS Linear release model.

Previous behavior:
- The workflow delegated to linear/linear-release-action with separate internal and public access keys.
- It created or updated Linear releases without syncing issue membership from the WebGUI release diff.
- The workflow used actions/checkout@v4, which now emits Node 20 deprecation warnings.

Why that was a problem:
- WebGUI tag automation could drift from release-diff and notification-worker release semantics.
- Linear tickets referenced by WebGUI PRs were not automatically attached to the OS Linear Release.
- The workflow warning obscured whether the run itself needed attention.

What this changes:
- Replaces the Linear release action with an explicit GraphQL sync script.
- Resolves the correct OS prerelease or stable release pipeline by tag channel.
- Syncs release metadata and preserves terminal Linear release stages instead of backtracking them.
- Scans the tag range and merged PR metadata for Linear issue IDs and FeatureOS links, then attaches linked Linear issues to the release.
- Updates checkout to actions/checkout@v6.

How it works:
- The workflow computes the previous semver tag and scans that git range.
- The script upserts the Linear Release in OS Prereleases or OS Stable Releases using LINEAR_API_KEY.
- Issue membership is added through Linear issueUpdate with addedReleaseIds; no issue state changes are made.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 22, 2026

Walkthrough

This PR introduces a new Node.js CLI script for syncing Linear issues to releases via GraphQL, and updates the GitHub Actions workflow to extract issue identifiers from commits and PRs, then invoke the script with computed metadata and generated issue file paths.

Changes

Linear Release Sync Integration

Layer / File(s) Summary
Sync script implementation
.github/scripts/sync-linear-release.mjs
New Node.js CLI script: loads release channel/metadata from environment, discovers Linear pipeline and stage, queries/creates/updates release with commit SHA and description, iterates issue identifiers and feature URL candidates, skips archived/missing issues, attaches each to release (deduplicated by issue ID), and outputs release metadata and synced/skipped identifier lists via GITHUB_OUTPUT.
Workflow integration
.github/workflows/linear-release.yml
Workflow now includes pull-requests: read permission, upgrades actions/checkout to v6, adds forced tag fetch, computes release channel/name and semver range from tag format, extracts PR numbers and issue keys/FeatureOS URLs from commit/PR data into files, invokes sync script with Linear credentials and release/range metadata, and extends summary reporting to include issue/link counts plus attached/skipped issue lists.

Sequence Diagram

sequenceDiagram
  participant Workflow
  participant SyncScript as sync-linear-release.mjs
  participant LinearAPI as Linear GraphQL API
  
  Workflow->>SyncScript: node script with env vars<br/>(LINEAR_KEY, RELEASE_CHANNEL, issue file paths)
  SyncScript->>LinearAPI: query release pipeline by channel
  LinearAPI-->>SyncScript: pipeline metadata
  SyncScript->>LinearAPI: query existing release by tag/name
  LinearAPI-->>SyncScript: release (or none)
  SyncScript->>LinearAPI: create or update release<br/>(name, commit SHA, stage)
  LinearAPI-->>SyncScript: release object
  SyncScript->>SyncScript: read issue IDs and feature URLs from files
  SyncScript->>LinearAPI: query issue by ID / URL attachments
  LinearAPI-->>SyncScript: issue objects
  SyncScript->>LinearAPI: attach each issue to release<br/>(skip archived/missing)
  LinearAPI-->>SyncScript: success
  SyncScript->>Workflow: output release_id, release_url,<br/>synced/skipped lists
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • unraid/webgui#2644: The main PR updates .github/workflows/linear-release.yml to replace the tag-sync approach with a new commit-issue-to-release sync flow driven by .github/scripts/sync-linear-release.mjs, so the changes directly overlap in the Linear release workflow logic.

Poem

🐰 A script hops in to sync the way,
Issues now dance in Linear's display,
From GitHub PRs to release delight,
The workflow wires them all just right!
Commits and features in harmony stay,
Thump, thump! 🥕

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title describes the primary change: syncing WebGUI tags to OS Linear releases, which is the main objective of replacing the Linear release action with a GraphQL sync script.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/webgui-linear-release-sync

Comment @coderabbitai help to get the list of available commands and usage tips.

@elibosley elibosley merged commit 460bdb7 into master May 22, 2026
4 of 5 checks passed
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/linear-release.yml:
- Around line 120-134: The PR extraction only captures "Merge pull request `#123`"
commits and misses squash-merge notes like "(`#123`)"; update the PR_NUMBERS
assignment (the grep pipeline that reads LOG_PATH and populates PR_NUMBERS) to
also match squash-merge patterns (e.g., "(`#123`)" or just "`#123`" inside
parentheses) so those PR numbers are included, then keep the existing
dedupe/sort and the for-loop that queries each PR and appends to PR_TEXT_PATH
unchanged. Ensure the updated grep/regex still ignores non-numeric matches and
preserves the sort -u || true behavior.
- Around line 85-89: When PREVIOUS_TAG is empty the script sets
RANGE_SPEC="$TAG_NAME" which makes git log walk the entire tag ancestry; replace
that branch so RANGE_SPEC is set to the single-tag range like
"${TAG_NAME}^..${TAG_NAME}" (i.e., use TAG_NAME^..TAG_NAME) to limit git log to
commits introduced by the tag, and ensure the later git log "$RANGE_SPEC" call
uses this new range; if you want to be defensive, fall back to "$TAG_NAME" only
if the "^" parent ref does not exist.
- Line 34: Pin the GitHub Action checkout step by replacing uses:
actions/checkout@v6 with a specific immutable commit SHA for actions/checkout to
prevent upstream retags; also adjust the tag-range logic around PREVIOUS_TAG and
RANGE_SPEC so that when PREVIOUS_TAG is empty you compute a delta (e.g., use git
rev-list or explicitly set RANGE_SPEC to "$PREVIOUS_TAG..$TAG_NAME" only when
PREVIOUS_TAG is set) to avoid including all history, and broaden the PR parsing
used for git log output (the code that inspects git log "$RANGE_SPEC") to also
detect squash-merge and commit-message patterns (not just "Merge pull request
#...") so PR/Linear metadata is captured reliably.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: b36b9307-2a55-49bf-82e0-116169078f79

📥 Commits

Reviewing files that changed from the base of the PR and between 43a661a and ab518eb.

📒 Files selected for processing (2)
  • .github/scripts/sync-linear-release.mjs
  • .github/workflows/linear-release.yml

steps:
- name: Checkout tag
uses: actions/checkout@v4
uses: actions/checkout@v6
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate and inspect the workflow
FILE=".github/workflows/linear-release.yml"
if [ ! -f "$FILE" ]; then
  echo "Missing $FILE"
  exit 1
fi

echo "=== File: $FILE ==="
nl -ba "$FILE" | sed -n '1,140p'

Repository: unraid/webgui

Length of output: 170


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE=".github/workflows/linear-release.yml"

echo "=== Sanity: does $FILE exist? ==="
ls -la "$FILE"

echo "=== Line context around 34 (25-45) ==="
awk 'NR>=25 && NR<=45 {printf "%5d  %s\n", NR, $0}' "$FILE"

echo "=== All actions/checkout usages in this workflow file ==="
rg -n --fixed-string "actions/checkout@" "$FILE" || true

echo "=== Any checkout pinned elsewhere in the repo? ==="
rg -n --fixed-string "actions/checkout@" .github/workflows || true

Repository: unraid/webgui

Length of output: 1917


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE=".github/workflows/linear-release.yml"

echo "=== All actions/checkout usages in this workflow file ==="
rg -n --fixed-strings "actions/checkout@" "$FILE" || true

echo "=== Any actions/checkout usages elsewhere (to see if pinned) ==="
rg -n --fixed-strings "actions/checkout@" .github/workflows || true

Repository: unraid/webgui

Length of output: 599


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE=".github/workflows/linear-release.yml"

echo "=== Line range 70-110 ==="
awk 'NR>=70 && NR<=110 {printf "%5d  %s\n", NR, $0}' "$FILE"

echo "=== Line range 110-160 ==="
awk 'NR>=110 && NR<=160 {printf "%5d  %s\n", NR, $0}' "$FILE"

echo "=== Full file (for any other uses of RANGE_SPEC / PR parsing) ==="
cat "$FILE"

Repository: unraid/webgui

Length of output: 12174


Pin actions/checkout to an immutable commit SHA (line 34).

  • actions/checkout@v6 is still using a mutable tag; pin to a specific SHA to avoid upstream retags/supply-chain drift.
  • When PREVIOUS_TAG is empty, RANGE_SPEC="$TAG_NAME" makes git log "$RANGE_SPEC" include all history up to the tag rather than a tag delta.
  • PR number extraction only matches Merge pull request #..., so squash merges can miss PR/Linear metadata.
🧰 Tools
🪛 zizmor (1.25.2)

[error] 34-34: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/linear-release.yml at line 34, Pin the GitHub Action
checkout step by replacing uses: actions/checkout@v6 with a specific immutable
commit SHA for actions/checkout to prevent upstream retags; also adjust the
tag-range logic around PREVIOUS_TAG and RANGE_SPEC so that when PREVIOUS_TAG is
empty you compute a delta (e.g., use git rev-list or explicitly set RANGE_SPEC
to "$PREVIOUS_TAG..$TAG_NAME" only when PREVIOUS_TAG is set) to avoid including
all history, and broaden the PR parsing used for git log output (the code that
inspects git log "$RANGE_SPEC") to also detect squash-merge and commit-message
patterns (not just "Merge pull request #...") so PR/Linear metadata is captured
reliably.

Comment on lines +85 to +89
if [ -n "$PREVIOUS_TAG" ]; then
RANGE_SPEC="${PREVIOUS_TAG}..${TAG_NAME}"
else
RANGE_SPEC="$TAG_NAME"
fi
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Avoid full-history scans when no previous tag exists.

Line 88 sets RANGE_SPEC to just ${TAG_NAME}. The later git log "$RANGE_SPEC" then walks the entire ancestry of that tag, so syncing the earliest semver tag would attach every historical issue/PR in the repository to a single Linear release.

Suggested fix
           if [ -n "$PREVIOUS_TAG" ]; then
             RANGE_SPEC="${PREVIOUS_TAG}..${TAG_NAME}"
           else
-            RANGE_SPEC="$TAG_NAME"
+            RANGE_SPEC="${TAG_NAME}^!"
           fi
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/linear-release.yml around lines 85 - 89, When PREVIOUS_TAG
is empty the script sets RANGE_SPEC="$TAG_NAME" which makes git log walk the
entire tag ancestry; replace that branch so RANGE_SPEC is set to the single-tag
range like "${TAG_NAME}^..${TAG_NAME}" (i.e., use TAG_NAME^..TAG_NAME) to limit
git log to commits introduced by the tag, and ensure the later git log
"$RANGE_SPEC" call uses this new range; if you want to be defensive, fall back
to "$TAG_NAME" only if the "^" parent ref does not exist.

Comment on lines +120 to +134
PR_NUMBERS="$(
grep -Eo 'Merge pull request #[0-9]+' "$LOG_PATH" \
| grep -Eo '[0-9]+' \
| sort -u || true
)"

for PR_NUMBER in $PR_NUMBERS; do
curl -fsSL \
-H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer ${GH_TOKEN}" \
-H "X-GitHub-Api-Version: 2022-11-28" \
"${GITHUB_API_URL:-https://api.github.com}/repos/${GITHUB_REPOSITORY}/pulls/${PR_NUMBER}" \
| jq -r '[.title, .body, .head.ref, .base.ref] | map(select(. != null and . != "")) | .[]' \
>> "$PR_TEXT_PATH"
done
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Include squash-merge PRs in the metadata scan.

Lines 120-123 only recognize Merge pull request #123`` commits. Squash merges land as ... (#123`)`, so their PR title/body never gets queried and any Linear IDs or FeatureOS URLs that only exist there are silently omitted from the release sync.

Suggested fix
           PR_NUMBERS="$(
-            grep -Eo 'Merge pull request #[0-9]+' "$LOG_PATH" \
+            grep -Eo 'Merge pull request #[0-9]+|\(#[0-9]+\)' "$LOG_PATH" \
               | grep -Eo '[0-9]+' \
               | sort -u || true
           )"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
PR_NUMBERS="$(
grep -Eo 'Merge pull request #[0-9]+' "$LOG_PATH" \
| grep -Eo '[0-9]+' \
| sort -u || true
)"
for PR_NUMBER in $PR_NUMBERS; do
curl -fsSL \
-H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer ${GH_TOKEN}" \
-H "X-GitHub-Api-Version: 2022-11-28" \
"${GITHUB_API_URL:-https://api.github.com}/repos/${GITHUB_REPOSITORY}/pulls/${PR_NUMBER}" \
| jq -r '[.title, .body, .head.ref, .base.ref] | map(select(. != null and . != "")) | .[]' \
>> "$PR_TEXT_PATH"
done
PR_NUMBERS="$(
grep -Eo 'Merge pull request #[0-9]+|\(#[0-9]+\)' "$LOG_PATH" \
| grep -Eo '[0-9]+' \
| sort -u || true
)"
for PR_NUMBER in $PR_NUMBERS; do
curl -fsSL \
-H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer ${GH_TOKEN}" \
-H "X-GitHub-Api-Version: 2022-11-28" \
"${GITHUB_API_URL:-https://api.github.com}/repos/${GITHUB_REPOSITORY}/pulls/${PR_NUMBER}" \
| jq -r '[.title, .body, .head.ref, .base.ref] | map(select(. != null and . != "")) | .[]' \
>> "$PR_TEXT_PATH"
done
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/linear-release.yml around lines 120 - 134, The PR
extraction only captures "Merge pull request `#123`" commits and misses
squash-merge notes like "(`#123`)"; update the PR_NUMBERS assignment (the grep
pipeline that reads LOG_PATH and populates PR_NUMBERS) to also match
squash-merge patterns (e.g., "(`#123`)" or just "`#123`" inside parentheses) so
those PR numbers are included, then keep the existing dedupe/sort and the
for-loop that queries each PR and appends to PR_TEXT_PATH unchanged. Ensure the
updated grep/regex still ignores non-numeric matches and preserves the sort -u
|| true behavior.

@elibosley elibosley deleted the codex/webgui-linear-release-sync branch May 22, 2026 16:10
elibosley added a commit that referenced this pull request May 22, 2026
## Summary

- Pins `actions/checkout` to the immutable commit currently referenced
by `v6.0.2`.
- Bounds the no-previous-tag path to `TAG^..TAG` when possible so the
workflow does not scan full tag ancestry.
- Restricts PR number extraction to explicit merge commit messages like
`Merge pull request #123`, avoiding accidental issue references like
`(#123)`.
- Adds Linear-side GitHub PR link resolution: discovered PR URLs are
passed to the sync script, which resolves Linear attachments for those
URLs and attaches the linked issues to the release.
- Fixes manual `workflow_dispatch` testing against historical tags by
checking out the workflow branch while still resolving the requested
`tag_name`.
- Carries active QA work across OS prereleases: internal tag sync now
attaches active issues to the exact prerelease, the stable companion
release, and the next planned prerelease. It removes next-prerelease
membership only after the issue reaches an internal/completed state.
- Sweeps existing issues already attached to the exact prerelease
through the same policy, so tickets are carried forward even when they
were not mentioned by the latest WebGUI tag diff.

## Review Notes

- Valid: mutable checkout tag. Fixed by pinning to
`de0fac2e4500dabe0009e67214ff5f5447ce83dd`.
- Valid: empty `PREVIOUS_TAG` range walked too much history. Fixed with
parent-bounded range and a parentless fallback.
- Valid: broad parenthetical PR extraction could treat issue references
as PR numbers. Fixed by restricting extraction to `Merge pull request
#[0-9]+` while preserving numeric extraction, `sort -u`, and `|| true`
behavior.
- Follow-up support: if the GitHub PR only links to Linear from the
Linear side, `attachmentsForURL` now resolves the issue from the PR URL.
- Manual test finding: checking out `tag_name` during
`workflow_dispatch` failed for historical tags because the tag did not
contain the new script. Fixed by checking out `github.ref` for manual
runs.
- Skipped: none. All reported findings applied to the current code.

## Validation

- `git diff --check`
- `node --check .github/scripts/sync-linear-release.mjs`
- `ruby -e 'require "yaml";
YAML.load_file(".github/workflows/linear-release.yml"); puts "yaml ok"'`
- Shell probe confirmed PR extraction returns `2645` from `Merge pull
request #2645 ...` and ignores parenthetical `(#2646)` / `(#123)`
references.
- Shell probe confirmed the first-tag fallback uses a single-commit
style range such as `6.2.2^..6.2.2` when a parent exists.
- Live Linear validation:
`attachmentsForURL(https://github.com/unraid/webgui/pull/2645)` resolved
`OS-239`, and the sync script attached `OS-239` to `7.3.1-rc.0.3`
without changing its `QA Ready` state.
- Manual workflow test passed:
https://github.com/unraid/webgui/actions/runs/26299583415 for
`tag_name=7.3.1-rc.0.3`; it synced the existing Linear Release and
attached `OS-239` idempotently.
- Local Linear validation after carry-forward change: `OS-239` is `QA
Ready` and attached to `7.3.1-rc.0.3`, `7.3.1-rc.0.4`, and `Unraid OS
7.3.1 Stable`.


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

## Chores
* Improved release automation and synchronization between GitHub PRs and
Linear issues, ensuring PR links are properly captured and tracked
throughout the release cycle
* Strengthened issue state management across multiple release versions,
providing better visibility into which changes are included in each
release
* Enhanced overall reliability of the release workflow

<!-- review_stack_entry_start -->

[![Review Change
Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/unraid/webgui/pull/2647?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack)

<!-- review_stack_entry_end -->

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant