ci: sync published production releases to Linear scheduled pipeline#30622
Conversation
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 EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
There was a problem hiding this comment.
💡 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.') |
There was a problem hiding this comment.
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 👍 / 👎.
There was a problem hiding this comment.
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.
| with: | ||
| access_key: ${{ secrets.LINEAR_ACCESS_KEY }} |
There was a problem hiding this comment.
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 👍 / 👎.
There was a problem hiding this comment.
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>
There was a problem hiding this comment.
✦ 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 givenrelease.ymlcreates staging releases without--prerelease, makinggithub.event.release.prereleaseunreliable. - ✅
cli_version: v0.10.0pinned — 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:
- Linear Scheduled pipeline created (Settings → Releases → New pipeline)
- Pipeline access key added as
LINEAR_ACCESS_KEYrepo 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.
|
@vex-assistant-bot Thanks for the thorough review. Following up on the non-blocking nit re:
|
|
✦ Follow-up from original review Resolving the nit I flagged about the My APPROVE from 21:04 UTC stands on current HEAD ( |
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 byvellum-assistant.Summary
Adds
.github/workflows/linear-release-sync.ymlwhich runslinear/linear-release-action@v0on 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 asv0.7.4-staging.2are 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:v0.8.1, or is it still pending?"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
include_paths— that's not required today.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
LINEAR_ACCESS_KEYsecret and a configured Linear pipeline, thesynccommand 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.!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 samegh release createpath as production but encodes the channel in the tag suffix.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.github.repository == 'vellum-ai/vellum-assistant') prevents the job from running on fork releases where the secret isn't available.concurrency: group: linear-release-syncblock withcancel-in-progress: falseensures two back-to-back releases can't race the action into creating overlapping releases for the same commit window.linear/linear-release-action@755d50b5adb7dd42b976ee9334952745d62ceb2d(v0.6.0) andactions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd(v6.0.2) — both pinned per AGENTS.md GitHub Actions rules.ref: ${{ github.event.release.tag_name }}ensures the action scans the commit history that actually shipped, regardless of what's merged tomainby the time the event fires.Test plan
LINEAR_ACCESS_KEYis added to repo secrets.vX.Y.Ztag 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.vX.Y.Z-staging.N) will not run the sync job; the workflow'sifclause skips it.References
releaseeventAlternatives considered
workflow_run: Release completedinstead ofrelease: published. Rejected. The repo'srelease.ymlworkflow runsgh release createnear the end, so by the time therelease: publishedevent fires, the entire release pipeline is already done.release: publishedis 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.vellum-assistant-platform(deploys multiple times per day, no marketing versions), but wrong here.vellum-assistanthas real versioned releases that customers install on their own schedule; Scheduled pipelines preserve that mapping.-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.include_paths: clients/macos/**— but that's a refinement, not a v1 requirement.@v0tag instead of a SHA pin. Rejected per AGENTS.md GitHub Actions rules: all third-party actions must SHA-pin with a trailing# vX.Y.Zcomment. 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:
vellum-assistant→ Settings → Secrets and variables → Actions → New repo secret → nameLINEAR_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