From 9a78df193977534fe28ff42a79991e2e8a60f7ee Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Sat, 13 Dec 2025 19:58:31 +0900 Subject: [PATCH] feat(publish): add contributors section to release notes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Tag community contributors with @username in GitHub releases, following opencode's publish.ts pattern. 🤖 GENERATED WITH ASSISTANCE OF [OhMyOpenCode](https://github.com/code-yeongyu/oh-my-opencode) --- script/publish.ts | 68 ++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 56 insertions(+), 12 deletions(-) diff --git a/script/publish.ts b/script/publish.ts index ab22634..c13324b 100644 --- a/script/publish.ts +++ b/script/publish.ts @@ -41,7 +41,9 @@ async function updatePackageVersion(newVersion: string): Promise { console.log(`Updated: ${pkgPath}`) } -async function generateChangelog(previous: string): Promise { +async function generateChangelog(previous: string): Promise { + const notes: string[] = [] + try { const log = await $`git log v${previous}..HEAD --oneline --format="%h %s"`.text() const commits = log @@ -49,16 +51,59 @@ async function generateChangelog(previous: string): Promise { .filter((line) => line && !line.match(/^\w+ (ignore:|test:|chore:|ci:|release:)/i)) if (commits.length > 0) { - const changelog = commits.map((c) => `- ${c}`).join("\n") + for (const commit of commits) { + notes.push(`- ${commit}`) + } console.log("\n--- Changelog ---") - console.log(changelog) + console.log(notes.join("\n")) console.log("-----------------\n") - return changelog } } catch { console.log("No previous tags found, skipping changelog generation") } - return "" + + return notes +} + +async function getContributors(previous: string): Promise { + const notes: string[] = [] + + const team = ["actions-user", "github-actions[bot]", "code-yeongyu"] + + try { + const compare = + await $`gh api "/repos/code-yeongyu/oh-my-opencode/compare/v${previous}...HEAD" --jq '.commits[] | {login: .author.login, message: .commit.message}'`.text() + const contributors = new Map() + + for (const line of compare.split("\n").filter(Boolean)) { + const { login, message } = JSON.parse(line) as { login: string | null; message: string } + const title = message.split("\n")[0] ?? "" + if (title.match(/^(ignore:|test:|chore:|ci:|release:)/i)) continue + + if (login && !team.includes(login)) { + if (!contributors.has(login)) contributors.set(login, []) + contributors.get(login)?.push(title) + } + } + + if (contributors.size > 0) { + notes.push("") + notes.push(`**Thank you to ${contributors.size} community contributor${contributors.size > 1 ? "s" : ""}:**`) + for (const [username, userCommits] of contributors) { + notes.push(`- @${username}:`) + for (const commit of userCommits) { + notes.push(` - ${commit}`) + } + } + console.log("\n--- Contributors ---") + console.log(notes.join("\n")) + console.log("--------------------\n") + } + } catch (error) { + console.log("Failed to fetch contributors:", error) + } + + return notes } async function buildAndPublish(): Promise { @@ -71,7 +116,7 @@ async function buildAndPublish(): Promise { } } -async function gitTagAndRelease(newVersion: string, changelog: string): Promise { +async function gitTagAndRelease(newVersion: string, notes: string[]): Promise { if (!process.env.CI) return console.log("\nCommitting and tagging...") @@ -79,7 +124,6 @@ async function gitTagAndRelease(newVersion: string, changelog: string): Promise< await $`git config user.name "github-actions[bot]"` await $`git add package.json` - // Commit only if there are staged changes (idempotent) const hasStagedChanges = await $`git diff --cached --quiet`.nothrow() if (hasStagedChanges.exitCode !== 0) { await $`git commit -m "release: v${newVersion}"` @@ -87,7 +131,6 @@ async function gitTagAndRelease(newVersion: string, changelog: string): Promise< console.log("No changes to commit (version already updated)") } - // Tag only if it doesn't exist (idempotent) const tagExists = await $`git rev-parse v${newVersion}`.nothrow() if (tagExists.exitCode !== 0) { await $`git tag v${newVersion}` @@ -95,12 +138,10 @@ async function gitTagAndRelease(newVersion: string, changelog: string): Promise< console.log(`Tag v${newVersion} already exists`) } - // Push (idempotent - git push is already idempotent) await $`git push origin HEAD --tags` - // Create release only if it doesn't exist (idempotent) console.log("\nCreating GitHub release...") - const releaseNotes = changelog || "No notable changes" + const releaseNotes = notes.length > 0 ? notes.join("\n") : "No notable changes" const releaseExists = await $`gh release view v${newVersion}`.nothrow() if (releaseExists.exitCode !== 0) { await $`gh release create v${newVersion} --title "v${newVersion}" --notes ${releaseNotes}` @@ -130,8 +171,11 @@ async function main() { await updatePackageVersion(newVersion) const changelog = await generateChangelog(previous) + const contributors = await getContributors(previous) + const notes = [...changelog, ...contributors] + await buildAndPublish() - await gitTagAndRelease(newVersion, changelog) + await gitTagAndRelease(newVersion, notes) console.log(`\n=== Successfully published ${PACKAGE_NAME}@${newVersion} ===`) }