feat: support get repository release by tag#162
Conversation
📝 WalkthroughWalkthroughA new API endpoint was added to fetch GitHub repository releases by tag. The implementation includes a Nitro route handler that retrieves release data from GitHub's API, normalizes the response structure, converts release markdown to HTML, and maps asset metadata into a standardized format. Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant Handler as Route Handler
participant GitHub as GitHub API
participant Markdown as Markdown Processor
Client->>Handler: GET /repos/{owner}/{repo}/releases/tags/{tag}
Handler->>GitHub: ghFetch(repos/{owner}/{repo}/releases/tags/{tag})
GitHub-->>Handler: Release data (id, tag, author, assets, body, etc.)
Handler->>Markdown: ghMarkdown(release.body, repo, context)
Markdown-->>Handler: Converted HTML
Handler->>Handler: Map assets to normalized format
Handler->>Handler: Construct GithubRelease object
Handler-->>Client: { release: GithubRelease }
Estimated code review effort🎯 2 (Simple) | ⏱️ ~15 minutes Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
|
@FliPPeDround is attempting to deploy a commit to the Nitro Team on Vercel. A member of the Team first needs to authorize it. |
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
README.md (1)
153-167: Includeassetsin this example payload for contract accuracy.The handler returns
release.assetsandGithubReleaserequires it, so documenting it here will keep the endpoint example aligned with runtime output.📝 Suggested doc update
"release": { @@ - "html": "..." + "html": "...", + "assets": [ + { + "contentType": "application/zip", + "size": 12345, + "createdAt": "2022-11-04T11:41:59Z", + "updatedAt": "2022-11-04T11:41:59Z", + "downloadCount": 42, + "downloadUrl": "https://github.com/owner/repo/releases/download/v3.0.0-rc.13/example.zip" + } + ] }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@README.md` around lines 153 - 167, The example JSON payload omits the required release.assets field used by the handler and declared on the GithubRelease type; update the example for the release object to include an assets array (release.assets) with at least the fields the handler/GithubRelease expect (e.g., asset id, name, content/type/size, and download URL or browser_download_url) so the documented payload matches runtime output.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@routes/repos/`[owner]/[repo]/releases/tags/[tag].ts:
- Line 46: The cache key passed to ghMarkdown is using the wrong release field —
change the key construction in the ghMarkdown call (where html: await
ghMarkdown(i.body, repo, "release-" + i.tag)) to use the release object's
tag_name property instead of i.tag (i.e., "release-" + i.tag_name) so markdown
caching is correctly namespaced per release tag; update any other ghMarkdown
usages that build the same key from i.tag to use i.tag_name as well.
- Around line 32-34: The code unsafely accesses route params via
event.context.params! and interpolates tag raw into the GitHub path causing
failures for tags containing slashes; update access to use safe checks (e.g.,
read params via event.context.params or destructure with optional chaining and
validate presence of owner/repo/tag) and handle missing params (return an error
or throw), and URL-encode the tag when calling ghFetch (use encodeURIComponent
on the tag) so the ghFetch call (repos/${repo}/releases/tags/${...}) is
constructed safely; reference the existing symbols event.context.params, tag,
repo, and ghFetch when making the fix.
---
Nitpick comments:
In `@README.md`:
- Around line 153-167: The example JSON payload omits the required
release.assets field used by the handler and declared on the GithubRelease type;
update the example for the release object to include an assets array
(release.assets) with at least the fields the handler/GithubRelease expect
(e.g., asset id, name, content/type/size, and download URL or
browser_download_url) so the documented payload matches runtime output.
🪄 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: defaults
Review profile: CHILL
Plan: Pro
Run ID: 95ef129e-f4c5-44ad-ad89-a58eb741640d
📒 Files selected for processing (2)
README.mdroutes/repos/[owner]/[repo]/releases/tags/[tag].ts
| const repo = `${event.context.params!.owner}/${event.context.params!.repo}`; | ||
| const tag = event.context.params!.tag; | ||
| const i = await ghFetch(`repos/${repo}/releases/tags/${tag}`); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify current param extraction and URL construction in this handler.
rg -n 'event\.context\.params|getRouterParam|releases/tags/\$\{tag\}|encodeURIComponent\(tag\)' -- 'routes/repos/[owner]/[repo]/releases/tags/[tag].ts'Repository: unjs/ungh
Length of output: 246
🏁 Script executed:
cat -n 'routes/repos/[owner]/[repo]/releases/tags/[tag].ts'Repository: unjs/ungh
Length of output: 2096
Use safe router-param access and URL-encode the tag in the GitHub path.
Interpolating tag raw can break lookups for valid tags containing / (for example, release/v1). Also, event.context.params! is unsafe and bypasses route-param guards.
💡 Suggested fix
import { defineRouteMeta, defineHandler } from "nitro";
+import { getRouterParam, HTTPError } from "nitro/h3";
import { ghFetch, ghMarkdown } from "~/utils/github";
import type { GithubRelease } from "~types";
@@
export default defineHandler(async (event) => {
- const repo = `${event.context.params!.owner}/${event.context.params!.repo}`;
- const tag = event.context.params!.tag;
- const i = await ghFetch(`repos/${repo}/releases/tags/${tag}`);
+ const owner = getRouterParam(event, "owner");
+ const repoName = getRouterParam(event, "repo");
+ const tag = getRouterParam(event, "tag");
+ if (!owner || !repoName || !tag) {
+ throw new HTTPError({ statusCode: 400, message: "Missing required route parameters" });
+ }
+ const repo = `${owner}/${repoName}`;
+ const i = await ghFetch(
+ `repos/${encodeURIComponent(owner)}/${encodeURIComponent(repoName)}/releases/tags/${encodeURIComponent(tag)}`,
+ );🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@routes/repos/`[owner]/[repo]/releases/tags/[tag].ts around lines 32 - 34, The
code unsafely accesses route params via event.context.params! and interpolates
tag raw into the GitHub path causing failures for tags containing slashes;
update access to use safe checks (e.g., read params via event.context.params or
destructure with optional chaining and validate presence of owner/repo/tag) and
handle missing params (return an error or throw), and URL-encode the tag when
calling ghFetch (use encodeURIComponent on the tag) so the ghFetch call
(repos/${repo}/releases/tags/${...}) is constructed safely; reference the
existing symbols event.context.params, tag, repo, and ghFetch when making the
fix.
| createdAt: i.created_at, | ||
| publishedAt: i.published_at, | ||
| markdown: i.body, | ||
| html: await ghMarkdown(i.body, repo, "release-" + i.tag), |
There was a problem hiding this comment.
Fix cache-key source field: use i.tag_name instead of i.tag.
i.tag is not the GitHub release field here, so this can become undefined and collapse markdown cache keys across tags in the same repo.
🐛 Suggested fix
- html: await ghMarkdown(i.body, repo, "release-" + i.tag),
+ html: await ghMarkdown(i.body, repo, `release-${i.tag_name}`),📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| html: await ghMarkdown(i.body, repo, "release-" + i.tag), | |
| html: await ghMarkdown(i.body, repo, `release-${i.tag_name}`), |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@routes/repos/`[owner]/[repo]/releases/tags/[tag].ts at line 46, The cache key
passed to ghMarkdown is using the wrong release field — change the key
construction in the ghMarkdown call (where html: await ghMarkdown(i.body, repo,
"release-" + i.tag)) to use the release object's tag_name property instead of
i.tag (i.e., "release-" + i.tag_name) so markdown caching is correctly
namespaced per release tag; update any other ghMarkdown usages that build the
same key from i.tag to use i.tag_name as well.
#161
Summary by CodeRabbit
New Features