Skip to content

feat: add notify-on-release email pipeline to typescript-service-release - #76

Merged
Yan Xue (yanxue06) merged 1 commit into
mainfrom
feat/notify-on-release
May 22, 2026
Merged

feat: add notify-on-release email pipeline to typescript-service-release#76
Yan Xue (yanxue06) merged 1 commit into
mainfrom
feat/notify-on-release

Conversation

@yanxue06

@yanxue06 Yan Xue (yanxue06) commented May 21, 2026

Copy link
Copy Markdown
Contributor

Summary

Brings the email-monkey notify job into the public buildspace so PUBLIC caller repos (e.g. photon-hq/spectrum-ts) can get release-notification emails through plain uses: without needing the public→internal App-token + REST-dispatch workaround.

Background: GitHub blocks public → internal reusable-workflow uses: at the platform level (the internal repo's "Accessible from repositories in the organization" setting only opens it up to other private/internal repos). The notify pipeline was previously living inside buildspace-private for that reason, which forced spectrum-ts to either (a) move to internal visibility or (b) mint an App token in spectrum-ts and REST-dispatch a workflow in buildspace-private.

The notify pipeline itself isn't actually sensitive — it's a thin Tailscale-join + curl-JSON wrapper. The actual secrets (TS_OAUTH_*) stay org-secret regardless of where the workflow YAML lives, and the email-monkey hostname is a tailnet MagicDNS name unreachable from outside the tailnet. So the right call is to host it publicly and avoid the App-token dance.

Three coupled additions (atomic because notify needs release_type)

  1. .github/blocks/determine-publish-version/action.yaml — new release-type output classifying the bump (major/minor/patch) against the last stable (non-prerelease) release. Strips any -rc.N suffix before comparing so GA finalization of a prerelease (e.g. 2.0.0-rc.3 → 2.0.0) correctly classifies as major instead of patch.
  2. .github/blocks/generate-release-info/action.yaml — forwards release_type from the underlying version block.
  3. .github/workflows/typescript-service-release.yaml — new notify-on-release input (default true), new optional TS_OAUTH_CLIENT_ID / TS_OAUTH_SECRET secrets, new notify job (join tailnet → POST JSON to http://email-monkey/notify-release).

Notify gate

The notify job fires only when ALL of these hold:

  • github.repository == 'photon-hq/spectrum-ts' — only allow-listed caller today
  • (check-labels.release || inputs.release) — must actually be cutting a release
  • release-info, github-release all succeeded
  • npm-publish succeeded or was intentionally skipped
  • inputs.notify-on-release == true (defaults to true; opt-out per-release via the caller's with: block)
  • release_type ∈ {major, minor, patch} — defensive bound on the AI classifier output

Non-allow-listed callers and non-release runs get a clean skipped job, no error, no email.

Test plan

  • YAML parses cleanly (6 jobs total: check-labels, release-info, bump-version, github-release, npm-publish, new notify)
  • CI: check-readme, check-skills all SUCCESS
  • Gate-logic sim verified across 8 scenarios (spectrum-ts major/minor/patch, no release label, other callers, opt-out, upstream failure, unexpected release_type)
  • Body shape matches email-monkey's parseNotifyBody exactly ({repo, version, releaseNotes, releaseType, serviceName})
  • Target URL http://email-monkey/notify-release hits internal listener via tailnet K8s Service (:80 → :3001) — verified out-of-band
  • Before merge: confirm TS_OAUTH_CLIENT_ID / TS_OAUTH_SECRET org secrets exist (gh secret list --org photon-hq | grep TS_OAUTH)
  • After merge: next spectrum-ts release with release label triggers the notify job; check spectrum-ts Actions log + email-monkey pod logs

What's NOT in this PR

  • TS_OAUTH_CLIENT_ID / TS_OAUTH_SECRET setup (Tailscale OAuth client + org-level GitHub secret, Ryan's task)
  • Tailscale ACL allowing tag:ci → tag:internal-svc:80 (operator task)
  • email-monkey deployment with GITHUB_APP_* and RESEND_* env vars (separate, in email-monkey repo + K8s manifest)

Summary by CodeRabbit

  • New Features
    • Release bumps are now classified as major, minor, or patch and that bump type is emitted for consumers.
    • Classification compares against the most recent stable (non-prerelease) release, stripping prerelease suffixes and falling back to 0.0.0 when none exists.
    • CI exposes a release_type output for downstream steps and callers.
    • Workflows add a notify-on-release toggle and optional OAuth secrets to enable post-release notifications.
    • Post-release notifications run only when enabled and for classified major/minor/patch releases.

Review Change Stack

Copilot AI review requested due to automatic review settings May 21, 2026 03:48
@coderabbitai

coderabbitai Bot commented May 21, 2026

Copy link
Copy Markdown

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Classifies semantic-version bumps (major/minor/patch) against the last stable release, exposes that type through release-info outputs, and adds a conditional notify job that joins Tailscale and posts release metadata when eligible.

Changes

Release Notification with Type Classification

Layer / File(s) Summary
Release type determination
.github/blocks/determine-publish-version/action.yaml
Fetches recent releases, records stable_version, adds "Classify Bump" step that strips pre-release suffixes, compares semver components, emits kind, and exposes release-type.
Release info output wiring
.github/blocks/generate-release-info/action.yaml
Adds composite output release_type wired to steps.version-info.outputs.release-type.
Notification job and workflow interface
.github/workflows/typescript-service-release.yaml
Adds notify-on-release boolean input, optional TS_OAUTH_CLIENT_ID/TS_OAUTH_SECRET secrets, wires jobs.release-info.outputs.release_type, and implements notify job that joins Tailscale and POSTs release JSON to http://email-monkey/notify-release when caller/release/job conditions and release_type is `major

Sequence Diagrams

sequenceDiagram
  participant ParseVersion as Parse Version
  participant ClassifyBump as Classify Bump
  participant Output as release-type output
  ParseVersion->>ClassifyBump: previous-version, next-version
  ClassifyBump->>ClassifyBump: strip pre-release suffixes
  ClassifyBump->>ClassifyBump: compare major/minor/patch
  ClassifyBump->>Output: kind (major | minor | patch)
Loading
sequenceDiagram
  participant ReleaseInfo as release-info job
  participant Notify as notify job
  participant Tailscale as Tailscale Network
  participant EmailMonkey as email-monkey service
  ReleaseInfo->>Notify: outputs release_type, version, artifacts
  Notify->>Notify: evaluate if notify-on-release and release_type in major|minor|patch
  Notify->>Tailscale: join (TS_OAUTH_CLIENT_ID/TS_OAUTH_SECRET, tag:infra)
  Notify->>EmailMonkey: POST JSON payload (jq -> curl)
  EmailMonkey-->>Notify: HTTP response (status check)
Loading

Estimated Code Review Effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

I hop through YAML, sniff the version line,
Major, minor, patch — I sort them fine.
I join the tailnet with a tiny cheer,
Send JSON whispers when the release is near.
Email-monkey nods — the rabbit dances clear. 🐰📬

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately captures the main addition to the workflow: a notify-on-release email pipeline feature for the typescript-service-release workflow.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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 unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/notify-on-release

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

@github-actions

github-actions Bot commented May 21, 2026

Copy link
Copy Markdown

📄 README may need an update

This PR introduces changes that might not be reflected in README.md.

Reason: README.md does not fully match the current public release workflow API because .github/workflows/typescript-service-release.yaml now exposes additional documented-facing inputs/secrets (such as publish-command, use-blacksmith, notify-on-release, APP_ID, and APP_PRIVATE_KEY) and the release-info/version blocks now output release type data that the README omits.

This is an automated check powered by AI. If the README is intentionally unchanged, feel free to ignore this.

@github-actions

github-actions Bot commented May 21, 2026

Copy link
Copy Markdown

📚 Skills documentation may need an update

This PR introduces changes that might not be reflected in the skills documentation.

Reason: _skills-repo/skills/buildspace-ci-cd/SKILL.md does not reflect new public typescript-service-release.yaml workflow inputs and secrets such as publish-command, use-blacksmith, use-oidc, notify-on-release, APP_ID/APP_PRIVATE_KEY, and TS_OAUTH_CLIENT_ID/TS_OAUTH_SECRET, nor the updated release-type behavior exposed by the release-info blocks.

This is an automated check powered by AI. If the skills are intentionally unchanged, feel free to ignore this.

Copilot AI left a comment

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.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

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/blocks/determine-publish-version/action.yaml:
- Around line 109-135: The current Classify Bump logic strips prerelease
suffixes (prev_core/next_core) and recomputes kind, causing -rc → GA transitions
(e.g. 2.0.0-rc.3 → 2.0.0) to be misclassified as patch; change the step so it
either (A) compares NEXT against the last non-prerelease release (use a outputs
value that contains the last stable release instead of PREV with prerelease)
when computing kind, or (B) simply carry/propagate the original bump kind from
the prerelease flow instead of recomputing (read and forward the original kind
output from the earlier step into this job). Update references to PREV/NEXT,
prev_core/next_core and the produced kind variable so the classification
reflects the true bump rather than stripped cores.

In @.github/workflows/typescript-service-release.yaml:
- Around line 65-69: The notify job's gate currently only allows "major"
releases but the workflow input notify-on-release and docs promise notifications
for both major and minor bumps; update the notify job's conditional (the job
"notify" / its if-condition that currently checks for release == "major") to
also allow "minor" (e.g., change the equality check to a membership test for
["major","minor"] or alternatively change the logic to exclude only "patch"),
ensuring the notify-on-release input is still honored.
- Around line 221-227: The workflow step named "Join Tailnet" currently uses the
mutable reference tailscale/github-action@v3; update the uses field to pin the
action to a full commit SHA (e.g., tailscale/github-action@<full-commit-sha>) so
the executed code is immutable, keeping the existing inputs (oauth-client-id,
oauth-secret, tags, version) unchanged; locate the step by the "Join Tailnet"
name or the uses value tailscale/github-action@v3 and replace the tag with the
specific commit SHA.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f7473ac5-ae6c-4d59-a4a6-443ce347be76

📥 Commits

Reviewing files that changed from the base of the PR and between e585d60 and bf5470a.

📒 Files selected for processing (3)
  • .github/blocks/determine-publish-version/action.yaml
  • .github/blocks/generate-release-info/action.yaml
  • .github/workflows/typescript-service-release.yaml
📜 Review details
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: 2026-04-05T00:07:12.194Z
Learnt from: qwerzl
Repo: photon-hq/buildspace PR: 62
File: .github/workflows/update-docs.yaml:164-167
Timestamp: 2026-04-05T00:07:12.194Z
Learning: In this repo (photon-hq/buildspace), reusable workflow blocks referenced under photon-hq/buildspace (e.g., `photon-hq/buildspace/.github/blocks/<block>main`) are intentionally pinned to `main` because the blocks and workflows ship together and are kept in sync. During review, do not flag these references as version-pinning/supply-chain issues just because they use `main`; only require pinning to a release tag if there’s a separate reason (e.g., referencing an external repo or a non-controlled branch).

Applied to files:

  • .github/workflows/typescript-service-release.yaml
📚 Learning: 2026-04-27T01:30:22.893Z
Learnt from: yanxue06
Repo: photon-hq/buildspace PR: 73
File: .github/workflows/check-readme.yaml:18-18
Timestamp: 2026-04-27T01:30:22.893Z
Learning: When reviewing this repo’s GitHub Actions workflows, treat Blacksmith runner labels like `blacksmith-4vcpu-ubuntu-2404` and other `blacksmith-*vcpu-ubuntu-*` values as valid/intentional third-party runner labels (Blacksmith: blacksmith.sh). Do not flag them as unknown or non-standard runner labels—these are an intentional drop-in replacement for GitHub-hosted runners.

Applied to files:

  • .github/workflows/typescript-service-release.yaml
📚 Learning: 2026-04-27T01:30:22.893Z
Learnt from: yanxue06
Repo: photon-hq/buildspace PR: 73
File: .github/workflows/check-readme.yaml:18-18
Timestamp: 2026-04-27T01:30:22.893Z
Learning: In photon-hq/buildspace GitHub workflow YAML files, runner labels that match `blacksmith-*vcpu-ubuntu-*` (e.g., `blacksmith-4vcpu-ubuntu-2404`) are intentionally managed by the Blacksmith CI service (blacksmith.sh) and serve as drop-in replacements for GitHub-hosted ephemeral microVM runners. Do not flag these labels as unknown/non-standard, supply-chain concerns, or “unrecognized runner” issues; any linter/actionlint warnings about these specific labels are expected false positives.

Applied to files:

  • .github/workflows/typescript-service-release.yaml
🪛 zizmor (1.25.2)
.github/workflows/typescript-service-release.yaml

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

(unpinned-uses)

🔇 Additional comments (1)
.github/blocks/generate-release-info/action.yaml (1)

23-25: LGTM!

Comment thread .github/blocks/determine-publish-version/action.yaml
Comment thread .github/workflows/typescript-service-release.yaml Outdated
Comment thread .github/workflows/typescript-service-release.yaml
Copilot AI review requested due to automatic review settings May 21, 2026 04:13

Copilot AI left a comment

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.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 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/typescript-service-release.yaml:
- Around line 205-213: The notify job's gate currently checks
contains(fromJSON('["major","minor","patch"]'),
needs.release-info.outputs.release_type) which allows patch releases,
contradicting the PR objective; update that expression in the workflow 'if' to
restrict allowed types (e.g., change the array to only ["major"] to follow the
learnings or to ["major","minor"] if you want to exclude patch but keep minor),
ensuring the rest of the 'if' (inputs.notify-on-release and other needs checks)
remains unchanged and referencing the existing contains(fromJSON(...),
needs.release-info.outputs.release_type) expression to locate and modify the
code.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: d57b75a6-7d07-4e9f-a346-a128f05ddef5

📥 Commits

Reviewing files that changed from the base of the PR and between 448c0c1 and 064183e.

📒 Files selected for processing (3)
  • .github/blocks/determine-publish-version/action.yaml
  • .github/blocks/generate-release-info/action.yaml
  • .github/workflows/typescript-service-release.yaml
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: check-skills / check-skills
🧰 Additional context used
🧠 Learnings (5)
📚 Learning: 2026-04-05T00:07:12.194Z
Learnt from: qwerzl
Repo: photon-hq/buildspace PR: 62
File: .github/workflows/update-docs.yaml:164-167
Timestamp: 2026-04-05T00:07:12.194Z
Learning: In this repo (photon-hq/buildspace), reusable workflow blocks referenced under photon-hq/buildspace (e.g., `photon-hq/buildspace/.github/blocks/<block>main`) are intentionally pinned to `main` because the blocks and workflows ship together and are kept in sync. During review, do not flag these references as version-pinning/supply-chain issues just because they use `main`; only require pinning to a release tag if there’s a separate reason (e.g., referencing an external repo or a non-controlled branch).

Applied to files:

  • .github/workflows/typescript-service-release.yaml
📚 Learning: 2026-04-27T01:30:22.893Z
Learnt from: yanxue06
Repo: photon-hq/buildspace PR: 73
File: .github/workflows/check-readme.yaml:18-18
Timestamp: 2026-04-27T01:30:22.893Z
Learning: When reviewing this repo’s GitHub Actions workflows, treat Blacksmith runner labels like `blacksmith-4vcpu-ubuntu-2404` and other `blacksmith-*vcpu-ubuntu-*` values as valid/intentional third-party runner labels (Blacksmith: blacksmith.sh). Do not flag them as unknown or non-standard runner labels—these are an intentional drop-in replacement for GitHub-hosted runners.

Applied to files:

  • .github/workflows/typescript-service-release.yaml
📚 Learning: 2026-04-27T01:30:22.893Z
Learnt from: yanxue06
Repo: photon-hq/buildspace PR: 73
File: .github/workflows/check-readme.yaml:18-18
Timestamp: 2026-04-27T01:30:22.893Z
Learning: In photon-hq/buildspace GitHub workflow YAML files, runner labels that match `blacksmith-*vcpu-ubuntu-*` (e.g., `blacksmith-4vcpu-ubuntu-2404`) are intentionally managed by the Blacksmith CI service (blacksmith.sh) and serve as drop-in replacements for GitHub-hosted ephemeral microVM runners. Do not flag these labels as unknown/non-standard, supply-chain concerns, or “unrecognized runner” issues; any linter/actionlint warnings about these specific labels are expected false positives.

Applied to files:

  • .github/workflows/typescript-service-release.yaml
📚 Learning: 2026-05-21T04:14:41.715Z
Learnt from: yanxue06
Repo: photon-hq/buildspace PR: 76
File: .github/workflows/typescript-service-release.yaml:0-0
Timestamp: 2026-05-21T04:14:41.715Z
Learning: In photon-hq/buildspace’s `.github/workflows/typescript-service-release.yaml`, the `notify` job must remain intentionally gated to major releases only (e.g., `needs.release-info.outputs.release_type == 'major'`). Do not treat the absence of a `minor` (or broader) trigger as a bug, and do not recommend widening the condition (such as changing it to `!= 'patch'` or otherwise including non-major release types), since this is a deliberate noise-reduction decision to send announcement emails only for breaking-change (major) releases.

Applied to files:

  • .github/workflows/typescript-service-release.yaml
📚 Learning: 2026-05-21T04:14:41.715Z
Learnt from: yanxue06
Repo: photon-hq/buildspace PR: 76
File: .github/workflows/typescript-service-release.yaml:0-0
Timestamp: 2026-05-21T04:14:41.715Z
Learning: In `.github/workflows/typescript-service-release.yaml`, the `notify` job is intentionally gated so the email-monkey notification runs only when `needs.release-info.outputs.release_type == 'major'`. Minor and patch releases should not trigger this notification as a deliberate noise-reduction design. During code review, do not flag this as a missing `minor` handling case and do not suggest changing the condition (e.g., to `!= 'patch'`).

Applied to files:

  • .github/workflows/typescript-service-release.yaml
🪛 zizmor (1.25.2)
.github/workflows/typescript-service-release.yaml

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

(unpinned-uses)

🔇 Additional comments (5)
.github/workflows/typescript-service-release.yaml (3)

223-229: Pin tailscale/github-action to a full commit SHA.

This was flagged in a previous review and remains unaddressed. The response about "major-only" was for a different concern. Using @v3 is a mutable tag that could be retargeted upstream.


65-69: LGTM!

Also applies to: 83-88, 118-118


231-274: LGTM!

.github/blocks/determine-publish-version/action.yaml (1)

20-22: LGTM!

Also applies to: 27-77, 126-158

.github/blocks/generate-release-info/action.yaml (1)

23-25: LGTM!

Comment thread .github/workflows/typescript-service-release.yaml
Copilot AI review requested due to automatic review settings May 22, 2026 19:56

Copilot AI left a comment

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.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Brings the email-monkey notify job into the public buildspace so PUBLIC
caller repos (e.g. photon-hq/spectrum-ts) can get release-notification
emails through plain `uses:` without needing the public->internal
App-token + REST-dispatch workaround.

Three coupled additions, atomic because notify needs release_type:

1. .github/blocks/determine-publish-version/action.yaml
   - new `release-type` output classifying the bump (major|minor|patch).
   - Get-last-release step now fetches a page of recent releases and
     exposes BOTH `version` (latest of any kind — used for the AI commit
     range) and `stable_version` (latest non-prerelease — used for
     classification). Classifying against the last STABLE means a GA
     finalization of an -rc (e.g. 2.0.0-rc.3 -> 2.0.0) correctly emits
     `major` instead of silently downgrading to `patch`.
   - Classify Bump strips any -rc.N suffix on NEXT before comparing
     against PREV (which is already stable).

2. .github/blocks/generate-release-info/action.yaml
   - forwards `release_type` from the underlying version block.

3. .github/workflows/typescript-service-release.yaml
   - new `notify-on-release` input (default true). Description spells
     out the actual gate: fires on MAJOR releases of allow-listed callers
     only; minor/patch and non-allow-listed callers skip cleanly.
   - new optional `TS_OAUTH_CLIENT_ID` / `TS_OAUTH_SECRET` secrets
     (Tailscale OAuth for ephemeral tailnet join as tag:ci).
   - `release-info` job exposes `release_type` for downstream jobs.
   - new `notify` job: gates on caller == photon-hq/spectrum-ts AND
     release_type == 'major'. Body joins tailnet as tag:ci, POSTs
     {repo, version, releaseNotes, releaseType, serviceName} to
     http://email-monkey/notify-release. Onboard additional callers by
     extending the github.repository check.

Patch releases short-circuit; non-allowlisted callers short-circuit;
missing TS_OAUTH_* secrets fail loudly on the tailnet join step
(intentional, since you can't reach email-monkey without them).

Co-authored-by: Cursor <cursoragent@cursor.com>
@yanxue06
Yan Xue (yanxue06) merged commit cc21656 into main May 22, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants