Skip to content

fix: populate first-release editions and simplify URL#22

Merged
znat merged 5 commits intomainfrom
fix/release-edition-content-and-path
May 2, 2026
Merged

fix: populate first-release editions and simplify URL#22
znat merged 5 commits intomainfrom
fix/release-edition-content-and-path

Conversation

@znat
Copy link
Copy Markdown
Owner

@znat znat commented May 2, 2026

Summary

Two fixes to release editions, surfaced by v0.1.0 being deployed empty.

  • First releases were rendering empty. processOneRelease short-circuited the compare SHA list to [] whenever there was no predecessor release, so prCount=0, no top stories, no changelog — only the LLM hero quip survived. Walk the full history reachable from the release tag (git log <tag> --pretty=%H) instead, so the first edition spans every PR since the beginning of the repo. The cached inputsHash will mismatch on the next analyzer run, regenerating quip and releaseStory with real PR context.

  • URL collapsed from /releases/tag/<tag>/<slug>/ to /releases/<tag>/. The /tag/ infix mirrored github.com but added nothing here, and the slug was redundant — tags are already stable, distinctive identifiers (unlike PR numbers or commit SHAs). Matches gitsky's /[owner]/[repo]/releases/<tag>/ shape, scaled down for single-repo.

Test plan

  • yarn workspace @gitpulse/action test (54/54)
  • yarn workspace @gitpulse/site test (41/41)
  • yarn workspace @gitpulse/action typecheck
  • yarn workspace @gitpulse/site typecheck
  • After merge, confirm Self-deploy run logs releases: 1 fetched, 1 new/updated, 0 unchanged (was 0 new/updated, 1 unchanged)
  • Visit https://znat.github.io/gitpulse/releases/v0.1.0/ — confirm stat bar, top stories, and changelog are populated
  • Confirm homepage release-edition card links to the new path

Notes

  • Old /releases/tag/v0.1.0/v0-1-0/ URL will 404 after deploy. Was live for ~minutes; not adding a redirect.
  • Pre-existing local build issue with /pull/[number]/[slug] when public/data/stories/ is empty is unrelated — CI populates the data dir before building.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • First releases now scan the full commit history so stories can be matched across the entire repository.
    • Release URLs and Open Graph image routes simplified to use tag-only paths.
  • Bug Fixes

    • First-release matching no longer yields an empty commit list, improving story matching for initial releases.
    • Release pages no longer fail due to slug mismatches.
  • Tests

    • Clarified test description for the "no matched stories" case to reflect expected zero counts and empty lists.

The first release on a repo rendered an empty edition page because
processOneRelease short-circuited the SHA list to [] when there was no
predecessor, cascading to prCount=0 and empty top-stories/changelog.
Walk the full history reachable from the release tag instead, so the
first edition spans every PR/commit since the beginning of the repo.

Also collapse the route from /releases/tag/<tag>/<slug>/ to
/releases/<tag>/. The /tag/ infix and slug segment add no SEO value —
tags are already stable, distinctive identifiers.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 2, 2026

Caution

Review failed

Pull request was closed or merged during review

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

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: d30266e1-93df-4c78-897d-27b87375c4d6

📥 Commits

Reviewing files that changed from the base of the PR and between 3b142ee and 60626a7.

📒 Files selected for processing (1)
  • action/src/git.ts

📝 Walkthrough

Walkthrough

Adds a git helper to list commit SHAs reachable from a release tag and uses it for first-release SHA lists; removes the release URL slug segment across the site so release routes and URL helpers are tag-only (e.g., /releases//), and adds a legacy redirector for old slugged URLs.

Changes

Action: Git Log Enumeration for First Releases

Layer / File(s) Summary
Git Access
action/src/git.ts
Added exported listReachableShas(repoDir: string, ref: string): string[] that runs git log <ref> --pretty=format:%H via spawnSync, returns trimmed non-empty SHA lines on success, and returns [] with console.warn on error/non-zero exit or stderr.
Release Processing
action/src/index.ts
processOneRelease now uses listReachableShas(cfg.repoDir, release.tagName) when there is no previous release; the predecessor-comparison branch still uses GitHub compare fetch.
Tests
action/src/release-builder.test.ts
Renamed the zero-matched-stories test to “produces zero counters when no stories are matched”; inputs/assertions unchanged (still previousTag: null, matchedStories: []).

Site: Release URL Structure Simplification

Layer / File(s) Summary
Route Definitions
site/src/app/releases/[tag]/page.tsx, site/src/app/releases/[tag]/opengraph-image.tsx
Removed slug from route params and imports; generateStaticParams() emits { tag } only; handlers load releases by tag and no longer validate or use slugs.
Legacy Redirector
site/src/app/releases/tag/[tag]/[slug]/page.tsx
Replaced prior release-detail page with a legacy redirector that computes legacySlug() for static params and returns an HTML meta-refresh redirect to ../../../<tag>/ (plus a visible link).
URL Path Generation
site/src/lib/urls.ts
Removed exported releaseSlug(release); added releasePath(release) and updated releaseOgImagePath(release) to compose paths as /releases/<encoded-tag>/... using only release.tag.
Tests
site/src/lib/urls.test.ts
Removed releaseSlug tests and updated expectations to /releases/<tag>/ and /releases/<tag>/opengraph-image.png (tags with slashes URL-encoded).

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰 I nibble lines of git and tag,

I cleared the slug and skipped the lag,
First commits now march into view,
Tags lead the way—no slug to construe,
A hop, a patch, a carrot-coded cue.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 26.67% 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 title directly summarizes the two main changes: fixing first-release editions by populating them with full commit history, and simplifying release URLs from a nested tag/slug structure to a cleaner tag-only path.
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 fix/release-edition-content-and-path

Review rate limit: 9/10 reviews remaining, refill in 6 minutes.

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

@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented May 2, 2026

Greptile Summary

This PR fixes two bugs: first releases were producing empty editions because processOneRelease returned an empty SHA list when there was no predecessor; and the release URL shape was simplified from /releases/tag/<tag>/<slug>/ to /releases/<tag>/, with the old path converted to a meta-refresh legacy redirect. Both fixes are well-implemented and test-covered.

Confidence Score: 5/5

This PR is safe to merge — both fixes are correct, well-tested, and the previous security concern has been fully addressed.

No P0 or P1 issues found. The injection risk raised in the previous review is resolved by switching to spawnSync. The meta-refresh relative URL resolves correctly for all tag shapes. All 95 tests pass per the test plan.

No files require special attention.

Important Files Changed

Filename Overview
action/src/git.ts Adds listReachableShas using spawnSync (safe from injection) with proper error/stderr handling and a 10 MB buffer; addresses the previous security comment.
action/src/index.ts Calls listReachableShas for the first-release case instead of short-circuiting to [], fixing empty first editions.
site/src/app/releases/tag/[tag]/[slug]/page.tsx Converted from full content page to a meta-refresh legacy redirect; relative URL ../../../<tag>/ resolves correctly for all tag shapes including encoded slashes.
site/src/app/releases/[tag]/page.tsx New canonical release detail page at simplified /releases/<tag>/ path; logic is unchanged from the old page, just moved and slug segment removed.
site/src/lib/urls.ts Removed releaseSlug and simplified releasePath/releaseOgImagePath to a single encodeURIComponent(tag) segment.
site/src/app/releases/[tag]/opengraph-image.tsx Moved from the old slug-based path and simplified generateStaticParams to emit only { tag } without slug.
site/src/lib/urls.test.ts Updated tests for new URL shape; removed releaseSlug tests, added concise tests for releasePath and releaseOgImagePath.
action/src/release-builder.test.ts Minor test description rename to better reflect the zero-match case; no logic change.

Sequence Diagram

sequenceDiagram
    participant Browser
    participant LegacyPage as "Legacy /releases/tag/tag/slug/"
    participant NewPage as "/releases/tag/"
    participant Action as "GitHub Action"
    participant Git as "git log (spawnSync)"
    participant GH as "GitHub Compare API"

    Browser->>LegacyPage: "GET (old bookmarked URL)"
    LegacyPage-->>Browser: "200 + meta refresh to ../../../tag/"
    Browser->>NewPage: "GET /releases/tag/"
    NewPage-->>Browser: "200 Release Edition"

    Action->>GH: "fetchCompareShas (predecessor exists)"
    GH-->>Action: "SHA list"
    Action->>Git: "listReachableShas (first release, no predecessor)"
    Git-->>Action: "full history SHA list"
    Action->>Action: "matchStoriesForRelease(allStories, shaList)"
Loading

Reviews (4): Last reviewed commit: "fix: apply CodeRabbit auto-fixes" | Re-trigger Greptile

Comment thread action/src/git.ts Outdated
Comment thread action/src/git.ts Outdated
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 the current code and only fix it if needed.

Inline comments:
In `@action/src/git.ts`:
- Around line 36-37: The catch block currently swallows errors and returns []
silently; change it to catch the error (e.g., catch (err)) and emit a warning
that includes the ref value and the caught error before returning []; update the
catch in the same function/block (the try/catch around the reachable-SHA lookup)
to call the repository logger or GitHub Actions core warning with a message like
"reachable-SHA lookup failed for ref <ref>: <err>" so degraded behavior is
visible in deploy logs, then return [] as before.
- Around line 30-35: The code currently interpolates ref into a shell string
passed to execSync which opens a command-injection vector; replace the
string-based call to execSync with an argument-based child_process invocation
(e.g., execFileSync or spawnSync) so git is invoked as a command with an args
array (['log', ref, '--pretty=format:%H']) rather than a single interpolated
string; keep existing options (cwd: repoDir, encoding: 'utf8', stdio) and then
continue to split the returned out value as before.

In `@site/src/app/releases/`[tag]/page.tsx:
- Around line 1-3: Add a compatibility route component named LegacyReleasePage
that accepts params (tag and slug), extracts/awaits the tag, and issues a
server-side redirect to the new path; implement it using next/navigation's
redirect inside a new page component (export default async function
LegacyReleasePage({ params }) { const { tag } = await params;
redirect(`/releases/${encodeURIComponent(tag)}/`); }) so old
/releases/tag/<tag>/<slug>/ URLs are redirected to /releases/<tag>/.
🪄 Autofix (Beta)

❌ Autofix failed (check again to retry)

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: ASSERTIVE

Plan: Pro Plus

Run ID: ddfd6c06-954b-4db1-866c-f66a5c0a347c

📥 Commits

Reviewing files that changed from the base of the PR and between 0492f2b and 008e00c.

📒 Files selected for processing (7)
  • action/src/git.ts
  • action/src/index.ts
  • action/src/release-builder.test.ts
  • site/src/app/releases/[tag]/opengraph-image.tsx
  • site/src/app/releases/[tag]/page.tsx
  • site/src/lib/urls.test.ts
  • site/src/lib/urls.ts

Comment thread action/src/git.ts Outdated
Comment thread action/src/git.ts Outdated
Comment thread site/src/app/releases/[tag]/page.tsx
znat and others added 2 commits May 2, 2026 15:52
Use spawnSync with an args array instead of execSync with an
interpolated string, so a tag containing shell metacharacters can't be
re-interpreted as a command.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Shallow clones don't make `git log` fail — the command succeeds and
just returns fewer commits. The [] fallback only triggers on non-zero
exit (e.g. ref not fetched). Spell out both behaviors so a future
reader can tell them apart.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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.

♻️ Duplicate comments (1)
action/src/git.ts (1)

31-40: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Add warning logs when reachable-SHA enumeration fails.

listReachableShas still fails silently at Line 37 and Line 39-40. That makes degraded first-release behavior hard to detect in deploy logs.

Suggested patch
 export function listReachableShas(repoDir: string, ref: string): string[] {
   try {
     const result = spawnSync(
       'git',
       ['log', ref, '--pretty=format:%H'],
       { cwd: repoDir, encoding: 'utf8' },
     );
-    if (result.status !== 0) return [];
+    if (result.status !== 0) {
+      const stderr =
+        typeof result.stderr === 'string'
+          ? result.stderr.trim()
+          : String(result.stderr ?? '').trim();
+      console.warn(
+        `[gitpulse] unable to enumerate reachable SHAs for ref "${ref}"${stderr ? `: ${stderr}` : ''}`,
+      );
+      return [];
+    }
     return result.stdout.split('\n').map((s) => s.trim()).filter(Boolean);
-  } catch {
+  } catch (err) {
+    console.warn(
+      `[gitpulse] unable to enumerate reachable SHAs for ref "${ref}": ${err instanceof Error ? err.message : String(err)}`,
+    );
     return [];
   }
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@action/src/git.ts` around lines 31 - 40, The function listReachableShas
silently returns [] on failures; update it to log warnings instead: when
spawnSync (used as spawnSync('git', ['log', ref, '--pretty=format:%H'], { cwd:
repoDir, encoding: 'utf8' })) yields a non-zero result.status, emit a warning
(e.g., console.warn or your project logger) including repoDir, ref and
result.stderr, then return []; likewise, in the catch block log the thrown error
with context (repoDir/ref) before returning []; reference listReachableShas,
spawnSync, result, repoDir and ref to find where to add these warnings.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@action/src/git.ts`:
- Around line 31-40: The function listReachableShas silently returns [] on
failures; update it to log warnings instead: when spawnSync (used as
spawnSync('git', ['log', ref, '--pretty=format:%H'], { cwd: repoDir, encoding:
'utf8' })) yields a non-zero result.status, emit a warning (e.g., console.warn
or your project logger) including repoDir, ref and result.stderr, then return
[]; likewise, in the catch block log the thrown error with context (repoDir/ref)
before returning []; reference listReachableShas, spawnSync, result, repoDir and
ref to find where to add these warnings.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 470a5f2b-2ba5-4e46-b185-82ac02e01f58

📥 Commits

Reviewing files that changed from the base of the PR and between 008e00c and 8f429cd.

📒 Files selected for processing (1)
  • action/src/git.ts

- Surface degraded behavior in deploy logs: console.warn the ref + exit
  status / error before returning [], so a tag missing locally doesn't
  silently produce an empty edition again.
- Re-add /releases/tag/<tag>/<slug>/ as a static legacy redirect page.
  output: 'export' rules out next/navigation's redirect(), so emit a
  meta refresh with a URL relative to the legacy path (../../../<tag>/)
  — the relative form preserves basePath without hard-coding it.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@action/src/git.ts`:
- Around line 34-40: The spawnSync call that runs 'git log' (the invocation
using spawnSync and the result variable) can exceed the default stdout/stderr
buffer and silently fail; fix it by adding a larger maxBuffer (e.g. 10MB or
configurable) to the options object passed to spawnSync and also defensively
check result.error and result.stderr before assuming result.stdout, returning []
only when an actual error/status indicates failure; update the options passed to
the git log spawnSync invocation and the subsequent checks on
result/status/error/stderr to ensure large-history outputs are not truncated.
🪄 Autofix (Beta)

✅ Autofix completed


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 12d80a3f-6371-4739-a2f1-a8427679d4b1

📥 Commits

Reviewing files that changed from the base of the PR and between 8f429cd and 7e6d422.

📒 Files selected for processing (1)
  • action/src/git.ts

Comment thread action/src/git.ts
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 2, 2026

Note

Autofix is a beta feature. Expect some limitations and changes as we gather feedback and continue to improve it.

Autofix skipped. No unresolved CodeRabbit review comments with fix instructions found.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 2, 2026

Note

Autofix is a beta feature. Expect some limitations and changes as we gather feedback and continue to improve it.

Fixes Applied Successfully

Fixed 1 file(s) based on 1 unresolved review comment.

Files modified:

  • action/src/git.ts

Commit: 60626a7d0f96da181aacfcc98451e9766ac60c83

The changes have been pushed to the fix/release-edition-content-and-path branch.

Time taken: 3m 20s

Fixed 1 file(s) based on 1 unresolved review comment.

Co-authored-by: CodeRabbit <noreply@coderabbit.ai>
@znat znat merged commit 762cd86 into main May 2, 2026
2 of 3 checks passed
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