Skip to content

Conversation

@yujonglee
Copy link
Contributor

No description provided.

Fetch commit details for each desktop_v1 tag to capture the commit author date as createdAt. The change filters tags to desktop_v1, queries the commits API for each tag's SHA, and attaches commit.commit.author.date to the returned GithubTagInfo. This enables consumers to know when a tag was generated. The implementation also adds types for the commit response and logs the fetched tags for debugging.
Add changelog metadata utilities and UI diff link

Provide structured metadata for changelogs (beforeVersion, newerSlug, olderSlug) by adding a changelog utility that parses versions, handles pre-releases, and builds navigation + diff information. This replaces ad-hoc sorting in route loaders with getChangelogList/getChangelogBySlug and wires a GitHub diff link into the changelog view so users can quickly compare releases.
update contents
vv
Write versioning data into changelog frontmatter

Update versioning script to persist fetched version list into the project's changelog (create file if missing). This change adds fs helpers and path join imports and ensures the script updates or inserts the `created` field in the changelog frontmatter rather than overwriting the entire file, so release metadata is preserved and available for downstream processes.
v
v
v
@netlify
Copy link

netlify bot commented Nov 19, 2025

Deploy Preview for hyprnote ready!

Name Link
🔨 Latest commit c0bc517
🔍 Latest deploy log https://app.netlify.com/projects/hyprnote/deploys/691d1e4e9e5789000870f5bf
😎 Deploy Preview https://deploy-preview-1725--hyprnote.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 19, 2025

📝 Walkthrough

Walkthrough

The PR centralizes changelog metadata computation via a new changelog.ts module that enriches entries with navigation information and version relationships. Changelog MDX files are updated with ISO 8601 timestamps. The versioning script now fetches commit dates from GitHub and updates changelog files with proper created timestamps.

Changes

Cohort / File(s) Summary
Changelog content files
apps/web/content/changelog/1.0.0-nightly.1.mdx, apps/web/content/changelog/1.0.0-nightly.2.mdx, apps/web/content/changelog/1.0.0-nightly.3.mdx
Updated front matter created timestamps from date strings to ISO 8601 format with UTC timezone; added new nightly.3 changelog entry.
Changelog metadata module
apps/web/src/changelog.ts
New module providing enriched changelog metadata with navigation chains (beforeVersion, newerSlug, olderSlug); exports ChangelogWithMeta type and functions getChangelogList() and getChangelogBySlug().
Changelog view routes
apps/web/src/routes/_view/changelog/$slug.tsx, apps/web/src/routes/_view/changelog/index.tsx
Replaced content-collection-based lookups and manual sorting with centralized getChangelogList() and getChangelogBySlug() calls; added GitHub diff URL generation and conditional diff link rendering in route loader.
GitHub versioning script
apps/web/src/scripts/versioning.ts
Made fetchGithubDesktopTags non-exported and async; added GitHub commit API fetch to populate createdAt timestamps; added helper functions updateCreatedField() and updateChangelogFiles() to create/update changelog MDX files with generated timestamps.

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant Route as Changelog Route
    participant Module as changelog.ts
    participant Content as Changelog Files

    User->>Route: Request changelog slug
    Route->>Module: getChangelogBySlug(slug)
    Module->>Content: Load all changelogs (cached)
    activate Module
    Note over Module: Parse versions with semver<br/>Build navigation chains<br/>(beforeVersion, newerSlug, olderSlug)
    Module-->>Module: Cache results
    deactivate Module
    Module-->>Route: ChangelogWithMeta + diffUrl
    Route-->>User: Render changelog with navigation & diff link
Loading
sequenceDiagram
    participant Script as versioning.ts
    participant GitHub as GitHub API
    participant Disk as File System

    Script->>GitHub: Fetch all desktop tags
    activate GitHub
    Note over GitHub: Return tag list with SHA
    deactivate GitHub
    
    loop For each tag
        Script->>GitHub: Fetch commit date for SHA
        activate GitHub
        Note over GitHub: Return commit author date
        deactivate GitHub
        Script->>Script: Enrich GithubTagInfo with createdAt
    end
    
    Script->>Script: Build changelog content for each version
    Script->>Disk: Create/Update .mdx files<br/>with created timestamps
    activate Disk
    Note over Disk: Write to content/changelog/
    deactivate Disk
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • changelog.ts: New module with semver-based sorting, version classification (prerelease vs. stable), and multi-level navigation chain logic—verify correctness of version ordering, edge cases for pre-release chains, and memoization correctness.
  • versioning.ts: Async file I/O operations, GitHub API pagination and error handling, frontmatter parsing/updating logic—check error handling paths, API quota/rate limits, and file operation robustness.
  • Cross-module integration: Verify that changelog.ts and versioning.ts produce compatible data structures and that route loaders correctly consume the new metadata shape.
  • Edge cases: Test boundary conditions in navigation chain building (e.g., first/last versions, pre-release transitions, version sorting with malformed semver).

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Description check ❓ Inconclusive No pull request description was provided by the author, making it impossible to assess whether it relates to the changeset. Add a description explaining the purpose of the changelog script, metadata enrichment, and diff URL feature to help reviewers understand the changes.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main changes: adding changelog script logic and diff URL functionality to the changelog feature.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch changelog-meta-ui-diff-link

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Copy link
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: 0

🧹 Nitpick comments (7)
apps/web/content/changelog/1.0.0-nightly.3.mdx (1)

1-5: Changelog entry and timestamp look good; check possible typo

Frontmatter and entry structure look correct. Consider double-checking the wording in Line 5 — if this refers to the Tiptap editor, titap is likely a typo.

apps/web/src/routes/_view/changelog/$slug.tsx (1)

6-29: Slug-based lookup and diff URL wiring look solid

The loader’s switch to getChangelogBySlug, newerSlug/olderSlug navigation, and the computed GitHub compare URL (beforeVersionversion) is cohesive and keeps route logic simple. The conditional “View diff on GitHub” link correctly guards on diffUrl and targets a new tab. If you want to harden the external link further, you could change rel="noreferrer" to rel="noreferrer noopener" for extra safety, but it’s optional.

Also applies to: 34-35, 78-89

apps/web/src/scripts/versioning.ts (2)

30-37: GitHub tag + commit fetch pipeline is correct but consider rate limits and options usage

The enriched GithubTagInfo with createdAt and the commit-fetch loop look correct and match the GitHub API shapes. A couple of non-blocking points to consider:

  • You make 1 (tags) + N (commits) GitHub requests in parallel via Promise.all. With many tags and no token, this can quickly hit unauthenticated rate limits.
  • The options?.token parameter is wired into headers but the only call site at Line 145 does not pass a token, so the token support may never be exercised.

If this script is intended for CI or repeated local use, you may want to pass a GitHub token from the environment and/or limit concurrency (e.g., with a small pool) to make it more robust against API throttling.

Also applies to: 46-108


110-125: Frontmatter update logic works; refine behavior for missing files and error handling

updateCreatedField correctly detects/creates frontmatter and rewrites the created line while preserving the rest of the header and body. updateChangelogFiles then:

  • Resolves apps/web/content/changelog from the script directory (relative path looks correct for this file).
  • Reads each <version>.mdx, defaulting to "" if it doesn’t exist, and writes updated content.

Two recommended tweaks:

  1. Avoid silently creating empty MDX files
    When readFile fails, the code currently generates a new file containing only frontmatter. If you only want to annotate existing changelogs, you could skip writes when readFile fails, or at least log that the changelog file is missing instead of creating an empty stub.

  2. Propagate failures from the top-level promise
    fetchGithubDesktopTags().then(updateChangelogFiles); does not attach a .catch, so network or I/O errors may surface as an unhandled rejection rather than a clear non‑zero exit. Wrapping this in an async main with try/catch (or adding .catch(err => { console.error(err); process.exit(1); })) would make failures explicit for CI or scripting.

These are behavior/operational improvements; the current implementation is functionally correct for existing files.

Also applies to: 127-145

apps/web/src/changelog.ts (3)

20-27: Consider simplifying the filter predicate.

The type predicate is correct but verbose. You can simplify it slightly:

-    .filter(
-      (
-        entry,
-      ): entry is {
-        doc: Changelog;
-        version: semver.SemVer;
-      } => entry !== null,
-    );
+    .filter((entry): entry is { doc: Changelog; version: semver.SemVer } =>
+      entry !== null
+    );

63-66: Consider using if/else instead of early return in forEach.

Early returns in forEach work but reduce readability. An if/else structure would be clearer:

     indices.forEach((idx, j) => {
-      if (j === 0) {
-        withMeta[idx].beforeVersion = null;
-        return;
-      }
-
-      const prevIdx = indices[j - 1];
-      withMeta[idx].beforeVersion = parsed[prevIdx].doc.version;
+      if (j === 0) {
+        withMeta[idx].beforeVersion = null;
+      } else {
+        const prevIdx = indices[j - 1];
+        withMeta[idx].beforeVersion = parsed[prevIdx].doc.version;
+      }
     });

107-116: Navigation logic is correct.

The position-based approach for computing newer/older slugs works well. Consider extracting the index lookups for slightly better readability:

   descOrder.forEach((idxInParsed, position) => {
-    const newerIdx = position > 0 ? descOrder[position - 1] : undefined;
-    const olderIdx =
-      position < descOrder.length - 1 ? descOrder[position + 1] : undefined;
+    const hasNewer = position > 0;
+    const hasOlder = position < descOrder.length - 1;
+    const newerIdx = hasNewer ? descOrder[position - 1] : undefined;
+    const olderIdx = hasOlder ? descOrder[position + 1] : undefined;
 
     withMeta[idxInParsed].newerSlug =
       newerIdx !== undefined ? parsed[newerIdx].doc.slug : null;
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9387537 and c0bc517.

📒 Files selected for processing (7)
  • apps/web/content/changelog/1.0.0-nightly.1.mdx (1 hunks)
  • apps/web/content/changelog/1.0.0-nightly.2.mdx (1 hunks)
  • apps/web/content/changelog/1.0.0-nightly.3.mdx (1 hunks)
  • apps/web/src/changelog.ts (1 hunks)
  • apps/web/src/routes/_view/changelog/$slug.tsx (2 hunks)
  • apps/web/src/routes/_view/changelog/index.tsx (2 hunks)
  • apps/web/src/scripts/versioning.ts (3 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
apps/web/src/routes/_view/changelog/index.tsx (2)
apps/web/src/routes/_view/changelog/$slug.tsx (1)
  • Route (8-31)
apps/web/src/changelog.ts (2)
  • getChangelogList (132-134)
  • ChangelogWithMeta (4-8)
apps/web/src/routes/_view/changelog/$slug.tsx (1)
apps/web/src/changelog.ts (1)
  • getChangelogBySlug (136-140)
⏰ 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). (2)
  • GitHub Check: fmt
  • GitHub Check: ci (macos, macos-14)
🔇 Additional comments (7)
apps/web/content/changelog/1.0.0-nightly.1.mdx (1)

2-2: ISO timestamp frontmatter looks good

The created field now uses a full ISO 8601 UTC timestamp, which is consistent with the new changelog metadata workflow.

apps/web/content/changelog/1.0.0-nightly.2.mdx (1)

2-2: Consistent ISO created timestamp

This created value matches the new ISO 8601 format and keeps nightly entries consistent.

apps/web/src/routes/_view/changelog/index.tsx (1)

4-11: Clean switch to centralized changelog metadata

Using getChangelogList() in the loader and typing changelog as ChangelogWithMeta keeps this route aligned with the new shared changelog meta utilities and removes local sorting/derivation logic. No issues from this side.

Also applies to: 67-68

apps/web/src/changelog.ts (4)

1-8: LGTM!

The imports and type definition are clean. The ChangelogWithMeta type appropriately extends Changelog with navigation and versioning metadata.


73-99: Verify the beforeVersion computation logic with comprehensive tests.

This logic handles three fallback strategies for stable releases, which is complex:

  1. Link to previous stable within same major/minor (lines 80-84)
  2. Link to first pre-release with same base (lines 86-91)
  3. Link to previous stable globally (lines 93-97)

Please ensure this logic is covered by tests, especially for edge cases like:

  • First stable release ever (no previous version)
  • First stable in a major/minor with and without pre-releases
  • Multiple major/minor versions with varying pre-release patterns

You can add unit tests for buildChangelogMeta() or integration tests that verify the beforeVersion field for various version sequences.


136-140: LGTM! Clean slug lookup.

The .find() approach is simple and correct. For large changelog collections (100+), you could optimize with a Map, but this is likely premature optimization for typical use cases.


122-130: Verify cache invalidation behavior with content-collections in development.

The module-level cache is correct for production (static content), but in development with the @content-collections/vite plugin, cache staleness is a legitimate concern. While the plugin likely watches and regenerates content, there's no explicit cache invalidation in the code. If the generated "content-collections" module reloads but changelog.ts doesn't, the cache becomes stale.

Recommended actions:

  1. Verify if @content-collections/vite triggers module reload on content changes in dev mode
  2. If not guaranteed, add explicit cache invalidation: if (import.meta.hot) { import.meta.hot.accept(() => { cache = null; }); }
  3. Or document that server restart is required after content changes in development

@yujonglee yujonglee merged commit 9cee851 into main Nov 19, 2025
11 checks passed
@yujonglee yujonglee deleted the changelog-meta-ui-diff-link branch November 19, 2025 01:45
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.

2 participants