Skip to content

fix(release): use GitHub API logins for contributors deduplication#205

Open
ymkiux wants to merge 2 commits into
mainfrom
fix/release-notes-contributors
Open

fix(release): use GitHub API logins for contributors deduplication#205
ymkiux wants to merge 2 commits into
mainfrom
fix/release-notes-contributors

Conversation

@ymkiux

@ymkiux ymkiux commented Jun 21, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Fix GitHub Release Contributors section to properly deduplicate by GitHub login instead of author name
  • Prevents same user (e.g., awsl/awsl233777) from appearing multiple times
  • Ensures circular avatars via wsrv.nl mask=circle (no CSS dependency)

Changes

  • Add "Fetch contributors from GitHub API" step in release workflow
    to get PR authors' GitHub logins via gh CLI
  • Modify listContributors to accept externalLogins from GitHub API
  • Change deduplication key from author name to GitHub login
  • Update formatContributorCard to support object parameter

Test plan

  • Verify workflow runs successfully in CI
  • Check generated release notes have deduplicated contributors
  • Confirm circular avatars (mask=circle in URL)

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Chores
    • Updated the release workflow to collect contributor data from GitHub during releases and feed it into changelog generation.
  • New Features
    • Enhanced release note generation to populate the “Contributors” section using external contributor logins, with sensible fallbacks.
  • Bug Fixes
    • Improved contributor deduplication and rendering so contributor cards display correctly with available profile details.
  • Tests
    • Updated unit test expectations for the contributor object format.
  • Documentation
    • Removed outdated Vietnamese i18n strings for two settings sections.

@coderabbitai

coderabbitai Bot commented Jun 21, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

A new GitHub Actions step fetches PR author logins via the gh CLI across the commit range between the latest and release tags, writing them to release-contributors.txt. The changelog.js script is updated to read that file and merge those logins—alongside commit-derived authors—into the release's Contributors section. Additionally, unused Vietnamese locale strings for sharePrefix and trashConfig settings are removed.

Changes

External Contributor Injection into Release Changelog

Layer / File(s) Summary
changelog.js contributor logic: accept objects, merge external logins
tools/release/changelog.js
formatContributorCard now accepts either a string or a { login, displayName } object, bypassing contributorProfile for objects. listContributors gains an externalLogins parameter, seeds contributors from it first (with deduping via a seen set), then merges commit authors. formatChangelog parameter destructuring now includes externalLogins = [] and forwards it into listContributors.
changelog.js main: read CONTRIBUTORS_FILE and pass logins
tools/release/changelog.js
main checks for env.CONTRIBUTORS_FILE, reads and trims newline-separated logins, warns on read errors, and passes the externalLogins array into formatChangelog.
Workflow: fetch PR author logins and wire CONTRIBUTORS_FILE
.github/workflows/release.yml
Adds a "Fetch contributors from GitHub API" step that skips on initial releases, fails fast if gh is unavailable, collects PR author logins via gh pr list or per-commit gh pr view, and writes them to release-contributors.txt. The subsequent changelog step gains a CONTRIBUTORS_FILE env var pointing at that file.
Test expectations: contributor list structure
tests/unit/release-changelog.test.mjs
Updates test assertion for listContributors(commits) to expect contributor objects with { login, displayName } fields.

Vietnamese Locale String Cleanup

Layer / File(s) Summary
Remove unused Vietnamese locale entries
web-ui/modules/i18n/locales/vi.mjs
Deletes settings.sharePrefix.* and settings.trashConfig.* translation entries that are no longer used by UI sections.

Sequence Diagram(s)

sequenceDiagram
  participant Job as release.yml job
  participant gh as gh CLI
  participant File as release-contributors.txt
  participant Script as changelog.js

  alt No LATEST_TAG
    Job->>Job: skip contributor fetch
  else Has LATEST_TAG
    Job->>gh: gh pr list (LATEST_TAG...RELEASE_TAG)
    gh-->>Job: PR author logins
    Job->>File: write logins (newline-separated)
  end
  Job->>Script: invoke with CONTRIBUTORS_FILE env var
  Script->>File: read & trim logins → externalLogins[]
  Script->>Script: listContributors(commits, externalLogins)
  Script->>Script: formatChangelog({ externalLogins })
  Script-->>Job: changelog with merged Contributors section
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • SakuraByteCore/codexmate#189: Directly related — also modifies formatContributorCard and contributor profile handling in tools/release/changelog.js, making it an important baseline for understanding the object-accepting signature change in this PR.

Poem

🐇 Hop hop, who wrote this code?
The rabbit checks each PR's road,
Collects the logins, writes them down,
No contributor left to drown.
The changelog blooms with every name—
Every coder gets their fame! 🌸

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% 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 pull request title clearly and accurately summarizes the main change: fixing deduplication of contributors in release notes by switching from name-based to GitHub login-based deduplication via the GitHub API.
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-notes-contributors

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.

@coderabbitai coderabbitai Bot 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.

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/workflows/release.yml:
- Around line 198-199: The LATEST_TAG environment variable is set to
steps.resolve.outputs.latest_tag, but this baseline differs from what the
changelog generation uses for deriving the target release, causing a mismatch in
the contributor data range. Update the LATEST_TAG assignment to use the same
baseline tag that the changelog generation process uses (the previous semver tag
for the target release) instead of steps.resolve.outputs.latest_tag, ensuring
both operations work with the same tag range.
- Around line 216-220: The fallback mechanism around lines 235-236 injects
unscoped contributors from the gh pr list command into the release contributors
list when the primary range-scoped extraction (from git log in lines 227-234)
finds no results. Instead of using the unscoped gh pr list output as a fallback,
modify the fallback logic to keep the contributor list empty or add a notice
indicating that no contributors were found within the intended range. This
ensures data accuracy by preventing contributors outside the target range from
being included in the release.

In `@tools/release/changelog.js`:
- Around line 143-154: The deduplication logic uses case-sensitive keys when
checking and adding to the seen Set, which allows the same GitHub login to
appear multiple times with different casing (e.g., "GitHub" vs "github").
Normalize the login values to lowercase before performing any deduplication
checks. In the first loop where externalLogins are processed, convert safeLogin
to lowercase before the seen.has() check and before seen.add(). In the second
loop where commits are processed, convert login to lowercase before the
seen.has() check and before seen.add(). Ensure the normalized lowercase value is
used consistently for deduplication while preserving the original casing for the
displayName property.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: f9dd0426-0ee5-422b-8cd9-4bfcd6a4a548

📥 Commits

Reviewing files that changed from the base of the PR and between 4d21e2e and 1f60739.

📒 Files selected for processing (2)
  • .github/workflows/release.yml
  • tools/release/changelog.js
📜 Review details
🔇 Additional comments (2)
tools/release/changelog.js (1)

120-130: LGTM!

Also applies to: 197-197, 235-235, 267-286

.github/workflows/release.yml (1)

241-246: LGTM!

Comment on lines +198 to +199
LATEST_TAG: ${{ steps.resolve.outputs.latest_tag }}
CONTRIBUTORS_FILE: release-contributors.txt

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.

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Use the same baseline tag as changelog generation.

Line 224 derives PRs from LATEST_TAG...RELEASE_TAG, but changelog generation uses the previous semver tag for the target release. This mismatch can produce contributor data for a different range.

Also applies to: 224-224

🤖 Prompt for 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.

In @.github/workflows/release.yml around lines 198 - 199, The LATEST_TAG
environment variable is set to steps.resolve.outputs.latest_tag, but this
baseline differs from what the changelog generation uses for deriving the target
release, causing a mismatch in the contributor data range. Update the LATEST_TAG
assignment to use the same baseline tag that the changelog generation process
uses (the previous semver tag for the target release) instead of
steps.resolve.outputs.latest_tag, ensuring both operations work with the same
tag range.

Comment on lines +216 to +220
gh pr list \
--repo "${GITHUB_REPOSITORY}" \
--limit 500 \
--json author \
--jq '.[].author.login' 2>/dev/null | sort -u > "${tmp_logins}" || true

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.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

What are the default filters for gh pr list (state, scope), and does this command support constraining results to commits between two git tags?

💡 Result:

The default state for the gh pr list command is open [1][2][3]. Regarding your specific questions: Default Filters: The gh pr list command filters by state, and its default value is open [1][2][4]. The available states you can filter by are open, closed, merged, and all [1][3][4]. There is no scope parameter in the command itself; however, you can constrain results to a specific repository using the -R or --repo flag [2][3]. Constraint by Git Tags: The gh pr list command does not natively support constraining results to commits between two git tags [1][2][4]. While you can use the --search flag to pass advanced GitHub search queries—such as searching by a specific commit SHA—it does not support range-based filtering using git tags directly [2][5][4]. To filter pull requests based on commits or time ranges associated with tags, you would typically need to identify the commit SHAs corresponding to your tags (using standard git commands like git rev-list or git show) and then use those SHAs or the associated dates within a --search query [2][4][6]. For example, if you have the commit SHAs, you might use the search syntax within the -S flag, though this requires manual translation of your git tags into inputs the search API accepts [2][4].

Citations:


🏁 Script executed:

sed -n '200,250p' .github/workflows/release.yml

Repository: SakuraByteCore/codexmate

Length of output: 2220


Fix the unscoped contributor fallback in release workflow.

The gh pr list call on line 216 lacks range filtering and defaults to all open PRs in the repository. When the primary range-scoped mechanism (lines 227-234, which extracts PR numbers from git log) finds no PRs, the fallback on lines 235-236 injects these unscoped contributors into the release contributors list, compromising data accuracy.

Replace the fallback to keep the contributor list empty with a notice instead:

Suggested patch
-          elif [ -s "${tmp_logins}" ]; then
-            cat "${tmp_logins}" > "${CONTRIBUTORS_FILE}"
+          elif [ -s "${tmp_logins}" ]; then
+            echo "::notice title=No range-scoped PR contributors::Using empty contributor list."
+            : > "${CONTRIBUTORS_FILE}"
🤖 Prompt for 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.

In @.github/workflows/release.yml around lines 216 - 220, The fallback mechanism
around lines 235-236 injects unscoped contributors from the gh pr list command
into the release contributors list when the primary range-scoped extraction
(from git log in lines 227-234) finds no results. Instead of using the unscoped
gh pr list output as a fallback, modify the fallback logic to keep the
contributor list empty or add a notice indicating that no contributors were
found within the intended range. This ensures data accuracy by preventing
contributors outside the target range from being included in the release.

Comment on lines +143 to +154
for (const login of externalLogins) {
const safeLogin = String(login || '').trim();
if (!safeLogin || seen.has(safeLogin)) continue;
seen.add(safeLogin);
contributors.push({ login: safeLogin, displayName: safeLogin });
}

// Fallback to commit authors for missing entries
for (const commit of commits) {
const contributor = formatContributorName(commit.author);
const key = contributor.toLowerCase();
if (seen.has(key)) continue;
seen.add(key);
contributors.push(contributor);
const { login, displayName } = contributorProfile(commit.author);
if (!login || seen.has(login)) continue;
seen.add(login);

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.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Normalize login keys before deduplication.

On Line 145 and Line 153, dedupe uses case-sensitive keys. This can still emit duplicates when the same GitHub login appears with different casing across sources.

Suggested patch
 for (const login of externalLogins) {
     const safeLogin = String(login || '').trim();
-    if (!safeLogin || seen.has(safeLogin)) continue;
-    seen.add(safeLogin);
+    const loginKey = safeLogin.toLowerCase();
+    if (!safeLogin || seen.has(loginKey)) continue;
+    seen.add(loginKey);
     contributors.push({ login: safeLogin, displayName: safeLogin });
 }
 ...
 for (const commit of commits) {
     const { login, displayName } = contributorProfile(commit.author);
-    if (!login || seen.has(login)) continue;
-    seen.add(login);
+    const loginKey = String(login || '').toLowerCase();
+    if (!login || seen.has(loginKey)) continue;
+    seen.add(loginKey);
     contributors.push({ login, displayName });
 }
🤖 Prompt for 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.

In `@tools/release/changelog.js` around lines 143 - 154, The deduplication logic
uses case-sensitive keys when checking and adding to the seen Set, which allows
the same GitHub login to appear multiple times with different casing (e.g.,
"GitHub" vs "github"). Normalize the login values to lowercase before performing
any deduplication checks. In the first loop where externalLogins are processed,
convert safeLogin to lowercase before the seen.has() check and before
seen.add(). In the second loop where commits are processed, convert login to
lowercase before the seen.has() check and before seen.add(). Ensure the
normalized lowercase value is used consistently for deduplication while
preserving the original casing for the displayName property.

ymkiux added 2 commits June 21, 2026 17:55
- Add "Fetch contributors from GitHub API" step in release workflow
  to get PR authors' GitHub logins via gh CLI
- Modify listContributors to accept externalLogins from GitHub API
- Change deduplication key from author name to GitHub login
  (prevents awsl/awsl233777 from appearing as separate contributors)
- Update formatContributorCard to support object parameter
- Ensure circular avatars via wsrv.nl mask=circle (no CSS dependency)
…icate keys

- Update listContributors test to expect { login, displayName } objects
- Remove duplicate settings.sharePrefix.* keys in vi locale
- Remove duplicate settings.trashConfig.* keys in vi locale
@ymkiux ymkiux force-pushed the fix/release-notes-contributors branch from 0df9e7b to de36813 Compare June 21, 2026 09:56

@coderabbitai coderabbitai Bot 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.

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/release.yml:
- Around line 224-231: The git log command using the commit range
"${LATEST_TAG}...${RELEASE_TAG}" fails when RELEASE_TAG hasn't been created yet
in a new release flow. Since the failure is silently handled by the || true
operator, tmp_merged becomes empty, causing incorrect contributor data
downstream. Replace ${RELEASE_TAG} in the git log command with an existing ref
that is guaranteed to be present (such as HEAD or the target branch reference)
to ensure the command succeeds regardless of whether the release tag has been
created yet.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: efbd84b6-d125-4870-a2f9-41de947ba1b2

📥 Commits

Reviewing files that changed from the base of the PR and between 0df9e7b and de36813.

📒 Files selected for processing (4)
  • .github/workflows/release.yml
  • tests/unit/release-changelog.test.mjs
  • tools/release/changelog.js
  • web-ui/modules/i18n/locales/vi.mjs
💤 Files with no reviewable changes (1)
  • web-ui/modules/i18n/locales/vi.mjs
🚧 Files skipped from review as they are similar to previous changes (2)
  • tests/unit/release-changelog.test.mjs
  • tools/release/changelog.js
📜 Review details
🔇 Additional comments (1)
.github/workflows/release.yml (1)

216-220: Unscoped gh pr list fallback remains in place.

This still falls back to unscoped gh pr list authors when tmp_merged is empty, which can inject contributors outside the intended release range.

Also applies to: 235-236

Comment on lines +224 to +231
git log "${LATEST_TAG}...${RELEASE_TAG}" --pretty=format:%s \
| grep -oE '#[0-9]+' \
| sed 's/^#//' \
| sort -u \
| while read -r pr_number; do
gh pr view "${pr_number}" --repo "${GITHUB_REPOSITORY}" --json author --jq '.author.login' 2>/dev/null || true
done \
| sort -u > "${tmp_merged}" || true

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.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Use an existing ref for the commit-range upper bound.

On Line 224, git log "${LATEST_TAG}...${RELEASE_TAG}" can fail when ${RELEASE_TAG} hasn’t been created yet (new release flow). Because the pipeline ends with || true, this silently empties tmp_merged and skews contributors downstream.

Suggested fix
+          range_head="${RELEASE_TAG}"
+          if ! git rev-parse --verify --quiet "refs/tags/${RELEASE_TAG}" >/dev/null; then
+            range_head="HEAD"
+          fi
+
-          git log "${LATEST_TAG}...${RELEASE_TAG}" --pretty=format:%s \
+          git log "${LATEST_TAG}...${range_head}" --pretty=format:%s \
             | grep -oE '#[0-9]+' \
             | sed 's/^`#//`' \
             | sort -u \
             | while read -r pr_number; do
                 gh pr view "${pr_number}" --repo "${GITHUB_REPOSITORY}" --json author --jq '.author.login' 2>/dev/null || true
               done \
             | sort -u > "${tmp_merged}" || true
📝 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.

Suggested change
git log "${LATEST_TAG}...${RELEASE_TAG}" --pretty=format:%s \
| grep -oE '#[0-9]+' \
| sed 's/^#//' \
| sort -u \
| while read -r pr_number; do
gh pr view "${pr_number}" --repo "${GITHUB_REPOSITORY}" --json author --jq '.author.login' 2>/dev/null || true
done \
| sort -u > "${tmp_merged}" || true
range_head="${RELEASE_TAG}"
if ! git rev-parse --verify --quiet "refs/tags/${RELEASE_TAG}" >/dev/null; then
range_head="HEAD"
fi
git log "${LATEST_TAG}...${range_head}" --pretty=format:%s \
| grep -oE '#[0-9]+' \
| sed 's/^`#//`' \
| sort -u \
| while read -r pr_number; do
gh pr view "${pr_number}" --repo "${GITHUB_REPOSITORY}" --json author --jq '.author.login' 2>/dev/null || true
done \
| sort -u > "${tmp_merged}" || true
🤖 Prompt for 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.

In @.github/workflows/release.yml around lines 224 - 231, The git log command
using the commit range "${LATEST_TAG}...${RELEASE_TAG}" fails when RELEASE_TAG
hasn't been created yet in a new release flow. Since the failure is silently
handled by the || true operator, tmp_merged becomes empty, causing incorrect
contributor data downstream. Replace ${RELEASE_TAG} in the git log command with
an existing ref that is guaranteed to be present (such as HEAD or the target
branch reference) to ensure the command succeeds regardless of whether the
release tag has been created yet.

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