Skip to content

ci: sync published production releases to Linear scheduled pipeline#30622

Merged
ashleeradka merged 2 commits into
mainfrom
devin/1778704043-linear-release-sync
May 13, 2026
Merged

ci: sync published production releases to Linear scheduled pipeline#30622
ashleeradka merged 2 commits into
mainfrom
devin/1778704043-linear-release-sync

Conversation

@ashleeradka
Copy link
Copy Markdown
Contributor

Prompt / plan

Wire up Linear's Releases integration so issues referenced in commits between two consecutive v* releases of this repo are automatically grouped under the new release in Linear. Companion change for the platform monorepo: vellum-ai/vellum-assistant-platform#6723 (Continuous pipeline). This PR wires the equivalent for the tagged-release model used by vellum-assistant.

Summary

Adds .github/workflows/linear-release-sync.yml which runs linear/linear-release-action@v0 on every published production release of this repo. The action scans commits between the previous Linear release and the tagged commit, extracts Linear issue identifiers (e.g. LUM-1234, JARVIS-123) from commit and PR titles, and creates or updates a release in the configured Scheduled pipeline.

The trigger is release: types: [published], gated to clean semver tags (v0.5.3); pre-release tags such as v0.7.4-staging.2 are filtered out so Linear issues aren't marked as shipped to production before they actually are.

Why needed

Today, when an issue's PR merges, Linear automation marks it "Merged" but there's no signal back into Linear when the merge actually ships in a tagged release. For a tagged-release repo like vellum-assistant (macOS DMG, brain daemon, CLI), the gap between merge and tagged release can mask real questions:

  • "Did this fix actually ship in v0.8.1, or is it still pending?"
  • "Which set of issues went out in the last production release?"
  • "If a customer reports a regression after upgrading to v0.8.x, which release introduced it (in Linear terms, not just git terms)?"

Linear's Releases feature answers those questions by maintaining a first-class "Release" property on every issue, populated automatically from CI. Combined with workflow automations ("when this pipeline's release ships, move included issues to Done"), the manual "did this go out yet?" loop disappears.

Benefits

  • Automatic "did this ship?" answer. Every Linear issue gains a Release property in the sidebar, computed without human intervention.
  • Status automation. Linear can auto-move "Merged" issues to "Done" on release completion, replacing manual triage.
  • Release notes generation. Linear can synthesize plain-language release notes from issues in a release range — useful for changelogs and customer-facing release announcements.
  • Single Linear release covers both surfaces. The macOS DMG and brain daemon ship in lockstep from the same release tag, so a single Scheduled pipeline maps cleanly to both. If we ever want to split per-surface later, we can add a second pipeline with include_paths — that's not required today.
  • Complementary to existing Sentry releases. Sentry releases group errors by deploy (vellum-macos@0.8.1-staging.2, vellum-assistant@0.8.1); Linear releases group issues by deploy. They answer different questions and can coexist without conflict.

Safety

  • No effect until prerequisites are met in the Linear/GitHub UIs. Without the LINEAR_ACCESS_KEY secret and a configured Linear pipeline, the sync command will fail loudly (the action exits non-zero). It will not silently corrupt or modify any Linear data. The first intentionally-broken run after merge can be ignored; subsequent runs after setup will succeed.
  • Pre-release tags never mark issues as shipped. The !contains(github.event.release.tag_name, '-staging.') guard ensures only true production releases advance the Linear pipeline. The repo's release workflow publishes staging tags through the same gh release create path as production but encodes the channel in the tag suffix.
  • Read-only on the repo. The job requests only permissions: contents: read. No write access to the repo, no write access to issues, no PR or comment manipulation. The action's writes go to Linear over HTTPS via the access key.
  • Forks are skipped. Repository-name guard (github.repository == 'vellum-ai/vellum-assistant') prevents the job from running on fork releases where the secret isn't available.
  • Concurrency-serialized. A concurrency: group: linear-release-sync block with cancel-in-progress: false ensures two back-to-back releases can't race the action into creating overlapping releases for the same commit window.
  • Third-party action SHA-pinned. linear/linear-release-action@755d50b5adb7dd42b976ee9334952745d62ceb2d (v0.6.0) and actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd (v6.0.2) — both pinned per AGENTS.md GitHub Actions rules.
  • Checkout pins to the published tag, not the default branch. ref: ${{ github.event.release.tag_name }} ensures the action scans the commit history that actually shipped, regardless of what's merged to main by the time the event fires.

Test plan

  • CI green on this PR (workflow file is syntactically valid; lint/type checks unaffected since it's an isolated workflow file).
  • Post-merge, no-op until the Linear pipeline is created and LINEAR_ACCESS_KEY is added to repo secrets.
  • After setup, the next production release (vX.Y.Z tag without -staging.) will trigger the workflow. Verify by opening a recently-merged Linear issue from that release range → its sidebar should show a populated Release property.
  • Pre-release tags (vX.Y.Z-staging.N) will not run the sync job; the workflow's if clause skips it.

References

Alternatives considered

  • Trigger on workflow_run: Release completed instead of release: published. Rejected. The repo's release.yml workflow runs gh release create near the end, so by the time the release: published event fires, the entire release pipeline is already done. release: published is also more semantic ("a release exists") and naturally provides the release tag and SHA in the event payload, removing the need for a separate "find the deployed SHA" step.
  • Use a Continuous pipeline instead of Scheduled. Rejected. Continuous pipelines model "every deploy is a release" — appropriate for vellum-assistant-platform (deploys multiple times per day, no marketing versions), but wrong here. vellum-assistant has real versioned releases that customers install on their own schedule; Scheduled pipelines preserve that mapping.
  • Track staging releases (-staging.N) under a separate pipeline. Deferred. Could be valuable to know which issues shipped to staging vs. production, but starts to dilute the "shipped to production" signal that's the core point of this integration. Easy to add later with a second pipeline.
  • Split per-surface (macOS vs brain) pipelines on day one. Deferred. Both surfaces ship in lockstep from the same release tag, so a single pipeline correctly captures every shipped issue. If we ever want to filter "which release did the macOS DMG actually deliver this fix in" separately from the brain daemon, we'd add a second Scheduled pipeline with include_paths: clients/macos/** — but that's a refinement, not a v1 requirement.
  • Use the floating @v0 tag instead of a SHA pin. Rejected per AGENTS.md GitHub Actions rules: all third-party actions must SHA-pin with a trailing # vX.Y.Z comment. The cost of bumping the SHA on each upstream release is small.

Manual setup required after merge

These steps cannot be automated in CI — they require the Linear and GitHub UIs:

  1. Linear: Settings → Releases → New pipeline → Type: Scheduled → Teams: Illuminati (+ JARVIS if relevant).
  2. Linear: Copy the pipeline's access key from its settings page.
  3. GitHub: vellum-assistant → Settings → Secrets and variables → Actions → New repo secret → name LINEAR_ACCESS_KEY, value = the key from step 2.

Once configured, the next published production release auto-creates the first Linear release.

Link to Devin session: https://app.devin.ai/sessions/8a3ce390e6e24deba0fac01d1b4a001e
Requested by: @ashleeradka

Adds a workflow that runs linear/linear-release-action whenever a
production release is published on this repo. The action scans commit
history between the previous release tag and the new one, extracts
Linear issue identifiers from commit and PR titles, and creates or
updates a release in the configured Scheduled pipeline.

Pre-release tags (e.g. v0.7.4-staging.2) are skipped so Linear issues
aren't marked as shipped to production before they actually are.

Co-Authored-By: ashlee@vellum.ai <ashlee@vellum.ai>
@devin-ai-integration
Copy link
Copy Markdown
Contributor

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment and CI monitoring

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 62fe714ff8

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

# Scheduled-pipeline releases aligned with actual production launches.
if: >-
github.repository == 'vellum-ai/vellum-assistant' &&
!contains(github.event.release.tag_name, '-staging.')
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Restrict sync to clean semver tags

Because this job subscribes to release: published, GitHub's release-event docs say published fires for pre-releases too; this guard only excludes tags containing -staging., so a published prerelease/manual test tag such as v0.8.1-rc.1 or v0.8.1-beta.1 would still sync to the Scheduled Linear pipeline and mark issues as shipped. I checked .github/workflows/release.yml and the production path only creates clean vMAJOR.MINOR.PATCH tags, so enforce that same tag shape (or also skip github.event.release.prerelease) before running the sync.

Useful? React with 👍 / 👎.

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.

Fixed in 60798a7: tightened the filter from !contains(tag_name, '-staging.') to !contains(tag_name, '-').

Couldn't use github.event.release.prerelease as a secondary guard because release.yml creates all releases (staging and production) with gh release create --latest, without --prerelease. So release.prerelease is false for both channels and can't distinguish them. The hyphen guard works because every plausible pre-release tag shape (-staging.N, -rc.N, -beta.N, -alpha.N) is required by semver to contain a hyphen between the version core and the pre-release identifier. Resolved.

Comment on lines +80 to +81
with:
access_key: ${{ secrets.LINEAR_ACCESS_KEY }}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Pin the Linear CLI version

I checked linear/linear-release-action v0.6.0: its cli_version input defaults to latest, and the README recommends pinning an exact tag for reproducible builds. Since this step pins the action SHA but omits cli_version, every production release can download a newer Linear Release CLI with changed behavior or flags without any repo change, which can break or alter the Linear sync unexpectedly; pass an explicit CLI version in this with: block.

Useful? React with 👍 / 👎.

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.

Fixed in 60798a7: pinned cli_version: v0.10.0 (the current default in the upstream README) so a future change to the action's latest resolution can't silently alter the sync's behavior in production. Added an inline comment with the upgrade path (look up the new tag at https://github.com/linear/linear-release/releases, bump in the same PR as the action SHA bump). Resolved.

Two defensive hardening changes:

1. Pin the underlying Linear Release CLI to v0.10.0. The action's
   cli_version input defaults to 'latest', which would let every
   production run pull a newer CLI build with potentially-changed
   behavior or flags. Pinning keeps the sync reproducible.

2. Tighten the production-tag filter from a literal '-staging.' match
   to any tag containing '-'. The current release workflow only emits
   clean semver and '-staging.N' tags, but if a future workflow ever
   publishes '-rc.N', '-beta.N', or '-alpha.N' tags those would have
   leaked through and marked Linear issues as shipped to production
   prematurely. The 'gh release create' calls don't pass --prerelease,
   so we can't rely on github.event.release.prerelease — match on the
   hyphen in the tag instead.

Co-Authored-By: ashlee@vellum.ai <ashlee@vellum.ai>
Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration Bot left a comment

Choose a reason for hiding this comment

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

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no potential bugs to report.

View in Devin Review to see 1 additional finding.

Open in Devin Review

Copy link
Copy Markdown
Contributor

@vex-assistant-bot vex-assistant-bot Bot left a comment

Choose a reason for hiding this comment

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

APPROVE

Value: Linear's "did this ship?" becomes automatic — every tagged vellum-assistant release auto-populates issue Release properties and can trigger state transitions (Merged → Done), eliminating the manual loop after each production tag.

What this does: Adds .github/workflows/linear-release-sync.yml — fires on release: published, skips any tag containing - (catches -staging.N, -rc.N, -beta.N, etc.), checks out the exact release ref with full history, and runs linear/linear-release-action against the Scheduled pipeline.


Codex P2s verified resolved in HEAD (60798a7):

  • Pre-release filter tightened to !contains(tag_name, '-') — correctly excludes all pre-release variants, not just -staging.. Correct call given release.yml creates staging releases without --prerelease, making github.event.release.prerelease unreliable.
  • cli_version: v0.10.0 pinned — eliminates silent CLI behavior changes on future production runs. Upgrade path documented inline. Good.

Security posture looks right:

  • permissions: contents: read — minimal, no write access to repo, issues, or PRs ✅
  • Both third-party actions SHA-pinned with version comments per AGENTS.md ✅
  • Fork guard (github.repository == 'vellum-ai/vellum-assistant') ✅
  • Concurrency serialized, cancel-in-progress: false — correct for release ordering ✅
  • fetch-depth: 0 — required for history walk, correct ✅
  • ref: github.event.release.tag_name — scans the shipped commit, not a racing HEAD ✅

Non-blocking nit: The actions/checkout comment reads # v6.0.2. As of current releases, actions/checkout is at v4.x — there is no v6. If de0fac2e corresponds to a v4.x tag, the comment is a documentation inaccuracy (doesn't affect runtime since the SHA is what runs, not the comment). Worth verifying and correcting in a follow-up if so.

Pre-merge reminder: The workflow is intentionally a no-op until:

  1. Linear Scheduled pipeline created (Settings → Releases → New pipeline)
  2. Pipeline access key added as LINEAR_ACCESS_KEY repo secret

First run after merge will fail loudly (non-zero exit) until those are configured — that's the expected and documented behavior.

Vellum Constitution — Trust-seeking: the workflow is read-only, SHA-pinned, concurrency-serialized, and fails loudly when unconfigured — behavior is fully auditable and the system earns trust before asking for it.

@devin-ai-integration
Copy link
Copy Markdown
Contributor

@vex-assistant-bot Thanks for the thorough review.

Following up on the non-blocking nit re: actions/checkout's # v6.0.2 comment — verified the SHA matches the tag:

$ gh api repos/actions/checkout/commits/v6.0.2 --jq '.sha'
de0fac2e4500dabe0009e67214ff5f5447ce83dd

actions/checkout is in fact at the v6.x major now (v5 landed in March, v6 in October), so the comment is accurate. This pin also matches the existing convention in release.yml and other workflows in this repo — keeping the new file consistent with the rest of the CI. No change needed.

@vex-assistant-bot
Copy link
Copy Markdown
Contributor

Follow-up from original review

Resolving the nit I flagged about the # v6.0.2 comment on actions/checkout. Confirmed via GitHub API after investigating a companion PR: actions/checkout@v6.0.2 is real — released 2026-01-09, SHA de0fac2e is correct. My concern was a knowledge-cutoff gap on my end. The SHA pin is accurate and the comment is valid. Nothing to fix.

My APPROVE from 21:04 UTC stands on current HEAD (60798a75). No new commits.

@ashleeradka ashleeradka merged commit 467658d into main May 13, 2026
2 checks passed
@ashleeradka ashleeradka deleted the devin/1778704043-linear-release-sync branch May 13, 2026 21:26
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