From d49acae4e992aa35deed2b3929e7aefc64751257 Mon Sep 17 00:00:00 2001 From: Eli <88557639+lishaduck@users.noreply.github.com> Date: Thu, 19 Mar 2026 12:42:44 -0500 Subject: [PATCH 1/4] fix: cherry-pick from forks, finally MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Dmitri Khokhlov <7530150+dkhokhlov@users.noreply.github.com> Co-authored-by: Austen Stone <22425467+austenstone@users.noreply.github.com> Co-authored-by: Federico Grandi <26386270+endbug@users.noreply.github.com> Co-authored-by: Odd Stråbø <638706+oddstr13@users.noreply.github.com> Co-authored-by: Felipe Santos <29582865+felipecrs@users.noreply.github.com> Co-authored-by: Fernando Fernández <10274099+ferferga@users.noreply.github.com> --- .../achievements/list/organizations.mjs | 4 +- source/plugins/achievements/list/users.mjs | 4 +- source/plugins/activity/index.mjs | 72 +++++++++++++------ source/plugins/habits/index.mjs | 10 ++- source/plugins/languages/analyzer/recent.mjs | 51 +++++++++++-- source/plugins/lines/index.mjs | 2 +- source/plugins/notable/index.mjs | 2 +- 7 files changed, 110 insertions(+), 35 deletions(-) diff --git a/source/plugins/achievements/list/organizations.mjs b/source/plugins/achievements/list/organizations.mjs index 70b22826460..6d70bb89c24 100644 --- a/source/plugins/achievements/list/organizations.mjs +++ b/source/plugins/achievements/list/organizations.mjs @@ -39,8 +39,8 @@ export default async function({list, login, data, computed, imports, graphql, qu //Managers { - const value = organization.projects.totalCount - const unlock = organization.projects.nodes?.shift() + const value = organization.projectsV2.totalCount + const unlock = organization.projectsV2.nodes?.shift() list.push({ title: "Managers", diff --git a/source/plugins/achievements/list/users.mjs b/source/plugins/achievements/list/users.mjs index 8b4ef03814e..de4877d3c87 100644 --- a/source/plugins/achievements/list/users.mjs +++ b/source/plugins/achievements/list/users.mjs @@ -55,8 +55,8 @@ export default async function({list, login, data, computed, imports, graphql, qu //Manager { - const value = user.projects.totalCount - const unlock = user.projects.nodes?.shift() + const value = user.projectsV2.totalCount + const unlock = user.projectsV2.nodes?.shift() list.push({ title: "Manager", diff --git a/source/plugins/activity/index.mjs b/source/plugins/activity/index.mjs index fcebc868404..ddaf505fd7e 100644 --- a/source/plugins/activity/index.mjs +++ b/source/plugins/activity/index.mjs @@ -37,17 +37,43 @@ export default async function({login, data, rest, q, account, imports}, {enabled } console.debug(`metrics/compute/${login}/plugins > activity > ${events.length} events loaded`) + const payloadTypesToCustomTypes = { + CommitCommentEvent:"comment", + CreateEvent:"ref/create", + DeleteEvent:"ref/delete", + ForkEvent:"fork", + GollumEvent:"wiki", + IssueCommentEvent:"comment", + IssuesEvent:"issue", + MemberEvent:"member", + PublicEvent:"public", + PullRequestEvent:"pr", + PullRequestReviewEvent:"review", + PullRequestReviewCommentEvent:"comment", + PushEvent:"push", + ReleaseEvent:"release", + WatchEvent:"star", + } + //Extract activity events const activity = (await Promise.all( events .filter(({actor}) => account === "organization" ? true : actor.login?.toLocaleLowerCase() === login.toLocaleLowerCase()) .filter(({created_at}) => Number.isFinite(days) ? new Date(created_at) > new Date(Date.now() - days * 24 * 60 * 60 * 1000) : true) .filter(event => visibility === "public" ? event.public : true) - .map(async ({type, payload, actor: {login: actor}, repo: {name: repo}, created_at}) => { - //See https://docs.github.com/en/free-pro-team@latest/developers/webhooks-and-events/github-event-types + .map(event => ({event, customType:payloadTypesToCustomTypes[event.type]})) + .filter(({customType}) => !!customType) //Ignore events with an unknown type + .filter(({customType}) => filter.includes("all") || filter.includes(customType)) //Filter events based on user preference + .map(({event}) => event) //Discard customType, it will be re-assigned + .map(async ({type, payload, actor:{login:actor}, repo:{name:repo}, created_at}) => { //See https://docs.github.com/en/free-pro-team@latest/developers/webhooks-and-events/github-event-types const timestamp = new Date(created_at) if (!imports.filters.repo(repo, skipped)) return null + + //Get custom type from the previously declared mapping, so that it acts as a single source of truth + const customType = payloadTypesToCustomTypes[type] + if (!customType) throw new Error(`Missing event mapping for type: ${type}`) + switch (type) { //Commented on a commit case "CommitCommentEvent": { @@ -56,27 +82,27 @@ export default async function({login, data, rest, q, account, imports}, {enabled const {comment: {user: {login: user}, commit_id: sha, body: content}} = payload if (!imports.filters.text(user, ignored)) return null - return {type: "comment", on: "commit", actor, timestamp, repo, content: await imports.markdown(content, {mode: markdown, codelines}), user, mobile: null, number: sha.substring(0, 7), title: ""} + return {type: customType, on: "commit", actor, timestamp, repo, content: await imports.markdown(content, {mode: markdown, codelines}), user, mobile: null, number: sha.substring(0, 7), title: ""} } //Created a git branch or tag case "CreateEvent": { const {ref: name, ref_type: type} = payload - return {type: "ref/create", actor, timestamp, repo, ref: {name, type}} + return {type: customType, actor, timestamp, repo, ref: {name, type}} } //Deleted a git branch or tag case "DeleteEvent": { const {ref: name, ref_type: type} = payload - return {type: "ref/delete", actor, timestamp, repo, ref: {name, type}} + return {type: customType, actor, timestamp, repo, ref: {name, type}} } //Forked repository case "ForkEvent": { const {forkee: {full_name: forked}} = payload - return {type: "fork", actor, timestamp, repo, forked} + return {type: customType, actor, timestamp, repo, forked} } //Wiki changes case "GollumEvent": { const {pages} = payload - return {type: "wiki", actor, timestamp, repo, pages: pages.map(({title}) => title)} + return {type: customType, actor, timestamp, repo, pages: pages.map(({title}) => title)} } //Commented on an issue case "IssueCommentEvent": { @@ -85,7 +111,7 @@ export default async function({login, data, rest, q, account, imports}, {enabled const {issue: {user: {login: user}, title, number}, comment: {body: content, performed_via_github_app: mobile}} = payload if (!imports.filters.text(user, ignored)) return null - return {type: "comment", on: "issue", actor, timestamp, repo, content: await imports.markdown(content, {mode: markdown, codelines}), user, mobile, number, title} + return {type: customType, on: "issue", actor, timestamp, repo, content: await imports.markdown(content, {mode: markdown, codelines}), user, mobile, number, title} } //Issue event case "IssuesEvent": { @@ -94,7 +120,7 @@ export default async function({login, data, rest, q, account, imports}, {enabled const {action, issue: {user: {login: user}, title, number, body: content}} = payload if (!imports.filters.text(user, ignored)) return null - return {type: "issue", actor, timestamp, repo, action, user, number, title, content: await imports.markdown(content, {mode: markdown, codelines})} + return {type: customType, actor, timestamp, repo, action, user, number, title, content: await imports.markdown(content, {mode: markdown, codelines})} } //Activity from repository collaborators case "MemberEvent": { @@ -103,11 +129,11 @@ export default async function({login, data, rest, q, account, imports}, {enabled const {member: {login: user}} = payload if (!imports.filters.text(user, ignored)) return null - return {type: "member", actor, timestamp, repo, user} + return {type: customType, actor, timestamp, repo, user} } //Made repository public case "PublicEvent": { - return {type: "public", actor, timestamp, repo} + return {type: customType, actor, timestamp, repo} } //Pull requests events case "PullRequestEvent": { @@ -116,14 +142,14 @@ export default async function({login, data, rest, q, account, imports}, {enabled const {action, pull_request: {user: {login: user}, title, number, body: content, additions: added, deletions: deleted, changed_files: changed, merged}} = payload if (!imports.filters.text(user, ignored)) return null - return {type: "pr", actor, timestamp, repo, action: (action === "closed") && (merged) ? "merged" : action, user, title, number, content: await imports.markdown(content, {mode: markdown, codelines}), lines: {added, deleted}, files: {changed}} + return {type: customType, actor, timestamp, repo, action: (action === "closed") && (merged) ? "merged" : action, user, title, number, content: await imports.markdown(content, {mode: markdown, codelines}), lines: {added, deleted}, files: {changed}} } //Reviewed a pull request case "PullRequestReviewEvent": { const {review: {state: review}, pull_request: {user: {login: user}, number, title}} = payload if (!imports.filters.text(user, ignored)) return null - return {type: "review", actor, timestamp, repo, review, user, number, title} + return {type: customType, actor, timestamp, repo, review, user, number, title} } //Commented on a pull request case "PullRequestReviewCommentEvent": { @@ -132,31 +158,36 @@ export default async function({login, data, rest, q, account, imports}, {enabled const {pull_request: {user: {login: user}, title, number}, comment: {body: content, performed_via_github_app: mobile}} = payload if (!imports.filters.text(user, ignored)) return null - return {type: "comment", on: "pr", actor, timestamp, repo, content: await imports.markdown(content, {mode: markdown, codelines}), user, mobile, number, title} + return {type: customType, on: "pr", actor, timestamp, repo, content: await imports.markdown(content, {mode: markdown, codelines}), user, mobile, number, title} } //Pushed commits case "PushEvent": { - let {size, commits, ref} = payload - commits = commits.filter(({author: {email}}) => imports.filters.text(email, ignored)) + let {size, ref, head, before} = payload + const [owner, repoName] = repo.split("/") + + const res = await rest.repos.compareCommitsWithBasehead({owner, repo:repoName, basehead:`${before}...${head}`}) + let {commits} = res.data + + commits = commits.filter(({author:{email}}) => imports.filters.text(email, ignored)) if (!commits.length) return null - if (commits.slice(-1).pop()?.message.startsWith("Merge branch ")) + if (commits.slice(-1).pop()?.commit.message.startsWith("Merge branch ")) commits = commits.slice(-1) - return {type: "push", actor, timestamp, repo, size, branch: ref.match(/refs.heads.(?.*)/)?.groups?.branch ?? null, commits: commits.reverse().map(({sha, message}) => ({sha: sha.substring(0, 7), message}))} + return {type:customType, actor, timestamp, repo, size, branch: ref.match(/refs.heads.(?.*)/)?.groups?.branch ?? null, commits: commits.reverse().map(({sha, message}) => ({sha: sha.substring(0, 7), message}))} } //Released case "ReleaseEvent": { if (!["published"].includes(payload.action)) return null const {action, release: {name, tag_name, prerelease, draft, body: content}} = payload - return {type: "release", actor, timestamp, repo, action, name: name || tag_name, prerelease, draft, content: await imports.markdown(content, {mode: markdown, codelines})} + return {type: customType, actor, timestamp, repo, action, name: name || tag_name, prerelease, draft, content: await imports.markdown(content, {mode: markdown, codelines})} } //Starred a repository case "WatchEvent": { if (!["started"].includes(payload.action)) return null const {action} = payload - return {type: "star", actor, timestamp, repo, action} + return {type: customType, actor, timestamp, repo, action} } //Unknown event default: { @@ -166,7 +197,6 @@ export default async function({login, data, rest, q, account, imports}, {enabled }), )) .filter(event => event) - .filter(event => filter.includes("all") || filter.includes(event.type)) .slice(0, limit) //Results diff --git a/source/plugins/habits/index.mjs b/source/plugins/habits/index.mjs index 3a1a437d602..d1f69aa6fe4 100644 --- a/source/plugins/habits/index.mjs +++ b/source/plugins/habits/index.mjs @@ -47,8 +47,14 @@ export default async function({login, data, rest, imports, q, account}, {enabled const patches = [ ...await Promise.allSettled( commits - .flatMap(({payload}) => payload.commits) - .filter(({author}) => data.shared["commits.authoring"].filter(authoring => author?.login?.toLocaleLowerCase().includes(authoring) || author?.email?.toLocaleLowerCase().includes(authoring) || author?.name?.toLocaleLowerCase().includes(authoring)).length) + .flatMap(({payload}) => payload?.commits ?? []) + .filter(commit => { + if (commit == null || typeof commit !== "object") return false + // Safely check author property + const author = commit?.author + if (!author) return false + return data.shared["commits.authoring"].filter(authoring => author?.login?.toLocaleLowerCase().includes(authoring) || author?.email?.toLocaleLowerCase().includes(authoring) || author?.name?.toLocaleLowerCase().includes(authoring)).length + }) .map(async commit => (await rest.request(commit)).data.files), ), ] diff --git a/source/plugins/languages/analyzer/recent.mjs b/source/plugins/languages/analyzer/recent.mjs index 927c91a7189..ebef67d292e 100644 --- a/source/plugins/languages/analyzer/recent.mjs +++ b/source/plugins/languages/analyzer/recent.mjs @@ -30,7 +30,7 @@ export class RecentAnalyzer extends Analyzer { async patches() { //Fetch commits from recent activity this.debug(`fetching patches from last ${this.days || ""} days up to ${this.load || "∞"} events`) - const commits = [], pages = Math.ceil((this.load || Infinity) / 100) + const pages = Math.ceil((this.load || Infinity) / 100) if (this.context.mode === "repository") { try { const {data: {default_branch: branch}} = await this.rest.repos.get(this.context) @@ -42,10 +42,11 @@ export class RecentAnalyzer extends Analyzer { this.debug(`failed to get default branch for ${this.context.owner}/${this.context.repo} (${error})`) } } + const events = [] try { for (let page = 1; page <= pages; page++) { this.debug(`fetching events page ${page}`) - commits.push( + events.push( ...(await (this.context.mode === "repository" ? this.rest.activity.listRepoEvents(this.context) : this.rest.activity.listEventsForAuthenticatedUser({username: this.login, per_page: 100, page}))).data .filter(({type, payload}) => (type === "PushEvent") && ((this.context.mode !== "repository") || ((this.context.mode === "repository") && (payload?.ref?.includes?.(`refs/heads/${this.context.branch}`))))) .filter(({actor}) => (this.account === "organization") || (this.context.mode === "repository") ? true : !filters.text(actor.login, [this.login], {debug: false})) @@ -57,8 +58,47 @@ export class RecentAnalyzer extends Analyzer { catch { this.debug("no more page to load") } + this.debug(`fetched ${events.length} events`) + + const wanted = new Map() + events.forEach(event => { + let key = `${event.repo.name}@${event.payload.ref}` + let item = wanted.get(key) ?? { commits: [] } + item.repo = event.repo.name + item.ref = event.payload.ref + item.commits.push(event.payload.before) + item.commits.push(event.payload.head) + wanted.set(key, item) + }) + + const commits = [] + for (const item of wanted.values()) { + try { + for (let page = 1; page <= pages; page++) { + this.debug(`fetching commits page ${page}`) + this.debug(`https://api.github.com/repos/${item.repo}/commits?sha=${item.ref}&per_page=20&page=${page}`) + commits.push( + ...(await this.rest.request(`https://api.github.com/repos/${item.repo}/commits?sha=${item.ref}&per_page=20&page=${page}`)).data + .map(x => { + item.commits = item.commits.filter(c => c !== x.sha) + return x + }) + .filter(({ committer }) => (this.account === "organization") || (this.context.mode === "repository") ? true : !filters.text(committer.login, [this.login], { debug: false })) + .filter(({ commit }) => ((!this.days) || (new Date(commit.committer.date) > new Date(Date.now() - this.days * 24 * 60 * 60 * 1000)))), + ) + if (item.commits < 1) { + this.debug("found expected commits") + break + } + } + } + catch { + this.debug("no more page to load") + } + } + this.debug(`fetched ${commits.length} commits`) - this.results.latest = Math.round((new Date().getTime() - new Date(commits.slice(-1).shift()?.created_at).getTime()) / (1000 * 60 * 60 * 24)) + this.results.latest = Math.round((new Date().getTime() - new Date(events.slice(-1).shift()?.created_at).getTime()) / (1000 * 60 * 60 * 24)) this.results.commits = commits.length //Retrieve edited files and filter edited lines (those starting with +/-) from patches @@ -66,7 +106,6 @@ export class RecentAnalyzer extends Analyzer { const patches = [ ...await Promise.allSettled( commits - .flatMap(({payload}) => payload.commits) .filter(({committer}) => filters.text(committer?.email, this.authoring, {debug: false})) .map(commit => commit.url) .map(async commit => (await this.rest.request(commit)).data), @@ -75,9 +114,9 @@ export class RecentAnalyzer extends Analyzer { .filter(({status}) => status === "fulfilled") .map(({value}) => value) .filter(({parents}) => parents.length <= 1) - .map(({sha, commit: {message, committer}, verification, files}) => ({ + .map(({sha, commit: {message, author}, verification, files}) => ({ sha, - name: `${message} (authored by ${committer.name} on ${committer.date})`, + name: `${message} (authored by ${author.name} on ${author.date})`, verified: verification?.verified ?? null, editions: files.map(({filename, patch = ""}) => { const edition = { diff --git a/source/plugins/lines/index.mjs b/source/plugins/lines/index.mjs index 33000a75fcb..aef7c14e651 100644 --- a/source/plugins/lines/index.mjs +++ b/source/plugins/lines/index.mjs @@ -44,7 +44,7 @@ export default async function({login, data, imports, rest, q, account}, {enabled return //Compute changes repos[handle] = {added: 0, deleted: 0, changed: 0} - const contributors = stats.filter(({author}) => (context.mode === "repository") || (context.mode === "organization") ? true : author?.login?.toLocaleLowerCase() === login.toLocaleLowerCase()) + const contributors = stats.filter(({author} = {}) => (context.mode === "repository") || (context.mode === "organization") ? true : author?.login?.toLocaleLowerCase() === login.toLocaleLowerCase()) for (const contributor of contributors) { let added = 0, changed = 0, deleted = 0 contributor.weeks.forEach(({a = 0, d = 0, c = 0, w}) => { diff --git a/source/plugins/notable/index.mjs b/source/plugins/notable/index.mjs index c9adc3b7d0f..0b97f24b4d2 100644 --- a/source/plugins/notable/index.mjs +++ b/source/plugins/notable/index.mjs @@ -109,7 +109,7 @@ export default async function({login, q, imports, rest, graphql, data, account, console.debug(`metrics/compute/${login}/plugins > notable > aggregating results`) const aggregated = new Map() for (const {name, handle, avatar, organization = handle.split("/").shift() ?? "", stars, ..._extras} of contributions) { - const key = repositories ? handle : name + const key = !organization || repositories ? handle : name; if (aggregated.has(key)) { const aggregate = aggregated.get(key) aggregate.aggregated++ From be30840a6b4dc5585a582afe5f286c60efdfad27 Mon Sep 17 00:00:00 2001 From: Eli <88557639+lishaduck@users.noreply.github.com> Date: Thu, 19 Mar 2026 12:57:13 -0500 Subject: [PATCH 2/4] apply spellings --- .github/actions/spelling/expect.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/spelling/expect.txt b/.github/actions/spelling/expect.txt index 6592b42b5fb..39093a586fc 100644 --- a/.github/actions/spelling/expect.txt +++ b/.github/actions/spelling/expect.txt @@ -12,6 +12,7 @@ audioscrobbler Autoloaded automator backreference +basehead Bizau blankslate blog @@ -32,7 +33,6 @@ datapoints dded dependabot deployer -dessant destructuring Dexcom dflag From 6384971990eaf4860e4e6565544fa333be0024cc Mon Sep 17 00:00:00 2001 From: Eli <88557639+lishaduck@users.noreply.github.com> Date: Thu, 19 Mar 2026 12:59:19 -0500 Subject: [PATCH 3/4] chore: code formatting --- source/plugins/activity/index.mjs | 43 ++++++++++--------- source/plugins/habits/index.mjs | 8 ++-- .../plugins/languages/analyzer/analyzer.mjs | 7 ++- source/plugins/languages/analyzer/recent.mjs | 6 +-- source/plugins/notable/index.mjs | 2 +- source/plugins/rss/index.mjs | 4 +- 6 files changed, 36 insertions(+), 34 deletions(-) diff --git a/source/plugins/activity/index.mjs b/source/plugins/activity/index.mjs index ddaf505fd7e..4df26a3cffc 100644 --- a/source/plugins/activity/index.mjs +++ b/source/plugins/activity/index.mjs @@ -38,21 +38,21 @@ export default async function({login, data, rest, q, account, imports}, {enabled console.debug(`metrics/compute/${login}/plugins > activity > ${events.length} events loaded`) const payloadTypesToCustomTypes = { - CommitCommentEvent:"comment", - CreateEvent:"ref/create", - DeleteEvent:"ref/delete", - ForkEvent:"fork", - GollumEvent:"wiki", - IssueCommentEvent:"comment", - IssuesEvent:"issue", - MemberEvent:"member", - PublicEvent:"public", - PullRequestEvent:"pr", - PullRequestReviewEvent:"review", - PullRequestReviewCommentEvent:"comment", - PushEvent:"push", - ReleaseEvent:"release", - WatchEvent:"star", + CommitCommentEvent: "comment", + CreateEvent: "ref/create", + DeleteEvent: "ref/delete", + ForkEvent: "fork", + GollumEvent: "wiki", + IssueCommentEvent: "comment", + IssuesEvent: "issue", + MemberEvent: "member", + PublicEvent: "public", + PullRequestEvent: "pr", + PullRequestReviewEvent: "review", + PullRequestReviewCommentEvent: "comment", + PushEvent: "push", + ReleaseEvent: "release", + WatchEvent: "star", } //Extract activity events @@ -61,18 +61,19 @@ export default async function({login, data, rest, q, account, imports}, {enabled .filter(({actor}) => account === "organization" ? true : actor.login?.toLocaleLowerCase() === login.toLocaleLowerCase()) .filter(({created_at}) => Number.isFinite(days) ? new Date(created_at) > new Date(Date.now() - days * 24 * 60 * 60 * 1000) : true) .filter(event => visibility === "public" ? event.public : true) - .map(event => ({event, customType:payloadTypesToCustomTypes[event.type]})) + .map(event => ({event, customType: payloadTypesToCustomTypes[event.type]})) .filter(({customType}) => !!customType) //Ignore events with an unknown type .filter(({customType}) => filter.includes("all") || filter.includes(customType)) //Filter events based on user preference .map(({event}) => event) //Discard customType, it will be re-assigned - .map(async ({type, payload, actor:{login:actor}, repo:{name:repo}, created_at}) => { //See https://docs.github.com/en/free-pro-team@latest/developers/webhooks-and-events/github-event-types + .map(async ({type, payload, actor: {login: actor}, repo: {name: repo}, created_at}) => { //See https://docs.github.com/en/free-pro-team@latest/developers/webhooks-and-events/github-event-types const timestamp = new Date(created_at) if (!imports.filters.repo(repo, skipped)) return null //Get custom type from the previously declared mapping, so that it acts as a single source of truth const customType = payloadTypesToCustomTypes[type] - if (!customType) throw new Error(`Missing event mapping for type: ${type}`) + if (!customType) + throw new Error(`Missing event mapping for type: ${type}`) switch (type) { //Commented on a commit @@ -165,15 +166,15 @@ export default async function({login, data, rest, q, account, imports}, {enabled let {size, ref, head, before} = payload const [owner, repoName] = repo.split("/") - const res = await rest.repos.compareCommitsWithBasehead({owner, repo:repoName, basehead:`${before}...${head}`}) + const res = await rest.repos.compareCommitsWithBasehead({owner, repo: repoName, basehead: `${before}...${head}`}) let {commits} = res.data - commits = commits.filter(({author:{email}}) => imports.filters.text(email, ignored)) + commits = commits.filter(({author: {email}}) => imports.filters.text(email, ignored)) if (!commits.length) return null if (commits.slice(-1).pop()?.commit.message.startsWith("Merge branch ")) commits = commits.slice(-1) - return {type:customType, actor, timestamp, repo, size, branch: ref.match(/refs.heads.(?.*)/)?.groups?.branch ?? null, commits: commits.reverse().map(({sha, message}) => ({sha: sha.substring(0, 7), message}))} + return {type: customType, actor, timestamp, repo, size, branch: ref.match(/refs.heads.(?.*)/)?.groups?.branch ?? null, commits: commits.reverse().map(({sha, message}) => ({sha: sha.substring(0, 7), message}))} } //Released case "ReleaseEvent": { diff --git a/source/plugins/habits/index.mjs b/source/plugins/habits/index.mjs index d1f69aa6fe4..25694bd6421 100644 --- a/source/plugins/habits/index.mjs +++ b/source/plugins/habits/index.mjs @@ -49,12 +49,14 @@ export default async function({login, data, rest, imports, q, account}, {enabled commits .flatMap(({payload}) => payload?.commits ?? []) .filter(commit => { - if (commit == null || typeof commit !== "object") return false + if (commit == null || typeof commit !== "object") + return false // Safely check author property const author = commit?.author - if (!author) return false + if (!author) + return false return data.shared["commits.authoring"].filter(authoring => author?.login?.toLocaleLowerCase().includes(authoring) || author?.email?.toLocaleLowerCase().includes(authoring) || author?.name?.toLocaleLowerCase().includes(authoring)).length - }) + }) .map(async commit => (await rest.request(commit)).data.files), ), ] diff --git a/source/plugins/languages/analyzer/analyzer.mjs b/source/plugins/languages/analyzer/analyzer.mjs index 4cd81b4c7e4..1437b8f90e6 100644 --- a/source/plugins/languages/analyzer/analyzer.mjs +++ b/source/plugins/languages/analyzer/analyzer.mjs @@ -1,10 +1,10 @@ //Imports +import core from "@actions/core" import fs from "fs/promises" import os from "os" import paths from "path" import git from "simple-git" import { filters } from "../../../app/metrics/utils.mjs" -import core from "@actions/core" /**Analyzer */ export class Analyzer { @@ -89,9 +89,8 @@ export class Analyzer { const {repo, branch, path} = this.parse(repository) let token - if (process.env.GITHUB_ACTIONS) { + if (process.env.GITHUB_ACTIONS) token = core.getInput("token") - } let url = /^https?:\/\//.test(repo) ? repo : `https://${token}@github.com/${repo}` try { @@ -183,4 +182,4 @@ export class Analyzer { debug(message) { return console.debug(`metrics/compute/${this.login}/plugins > languages > ${this.constructor.name.replace(/([a-z])([A-Z])/, (_, a, b) => `${a} ${b.toLocaleLowerCase()}`).toLocaleLowerCase()} > ${message}`) } -} \ No newline at end of file +} diff --git a/source/plugins/languages/analyzer/recent.mjs b/source/plugins/languages/analyzer/recent.mjs index ebef67d292e..31e1331fb35 100644 --- a/source/plugins/languages/analyzer/recent.mjs +++ b/source/plugins/languages/analyzer/recent.mjs @@ -63,7 +63,7 @@ export class RecentAnalyzer extends Analyzer { const wanted = new Map() events.forEach(event => { let key = `${event.repo.name}@${event.payload.ref}` - let item = wanted.get(key) ?? { commits: [] } + let item = wanted.get(key) ?? {commits: []} item.repo = event.repo.name item.ref = event.payload.ref item.commits.push(event.payload.before) @@ -83,8 +83,8 @@ export class RecentAnalyzer extends Analyzer { item.commits = item.commits.filter(c => c !== x.sha) return x }) - .filter(({ committer }) => (this.account === "organization") || (this.context.mode === "repository") ? true : !filters.text(committer.login, [this.login], { debug: false })) - .filter(({ commit }) => ((!this.days) || (new Date(commit.committer.date) > new Date(Date.now() - this.days * 24 * 60 * 60 * 1000)))), + .filter(({committer}) => (this.account === "organization") || (this.context.mode === "repository") ? true : !filters.text(committer.login, [this.login], {debug: false})) + .filter(({commit}) => ((!this.days) || (new Date(commit.committer.date) > new Date(Date.now() - this.days * 24 * 60 * 60 * 1000)))), ) if (item.commits < 1) { this.debug("found expected commits") diff --git a/source/plugins/notable/index.mjs b/source/plugins/notable/index.mjs index 0b97f24b4d2..3fe91dd68f6 100644 --- a/source/plugins/notable/index.mjs +++ b/source/plugins/notable/index.mjs @@ -109,7 +109,7 @@ export default async function({login, q, imports, rest, graphql, data, account, console.debug(`metrics/compute/${login}/plugins > notable > aggregating results`) const aggregated = new Map() for (const {name, handle, avatar, organization = handle.split("/").shift() ?? "", stars, ..._extras} of contributions) { - const key = !organization || repositories ? handle : name; + const key = !organization || repositories ? handle : name if (aggregated.has(key)) { const aggregate = aggregated.get(key) aggregate.aggregated++ diff --git a/source/plugins/rss/index.mjs b/source/plugins/rss/index.mjs index da5e9abbb05..677851e6952 100644 --- a/source/plugins/rss/index.mjs +++ b/source/plugins/rss/index.mjs @@ -17,8 +17,8 @@ export default async function({login, q, imports, data, account}, {enabled = fal //Set User-Agent header const parser = new rss({ //eslint-disable-line new-cap headers: { - "User-Agent": "gh-metrics/metrics (+https://github.com/gh-metrics/metrics)" - } + "User-Agent": "gh-metrics/metrics (+https://github.com/gh-metrics/metrics)", + }, }) //Load rss feed From cff092cf55ef907d826e20169d98f5154e69bde7 Mon Sep 17 00:00:00 2001 From: Eli <88557639+lishaduck@users.noreply.github.com> Date: Thu, 19 Mar 2026 16:14:09 -0500 Subject: [PATCH 4/4] cleanup --- .github/workflows/ci.yml | 3 +-- .github/workflows/test.yml | 5 ----- .gitignore | 2 +- .vscode/extensions.json | 5 +++++ .vscode/settings.json | 3 +++ source/.eslintrc.yml | 2 +- source/plugins/habits/index.mjs | 1 - 7 files changed, 11 insertions(+), 10 deletions(-) create mode 100644 .vscode/extensions.json create mode 100644 .vscode/settings.json diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2a2d2c5001e..a18ac7c44db 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -27,7 +27,7 @@ jobs: ref: master - name: Format code run: | - npm install -g dprint + npm i mkdir -v -p /home/runner/.cache/dprint/cache npx dprint fmt --config .github/config/dprint.json - name: Publish formatted code @@ -38,7 +38,6 @@ jobs: git add --all git commit -m "chore: code formatting" git push - set -e # Update plugins and template indexes, along with README.md update-indexes: diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 9af5f8a5db1..387fb43be2e 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -36,11 +36,6 @@ jobs: steps: - name: Checkout repository uses: actions/checkout@v4 - - name: Format code with dprint - run: | - npm install -g dprint - mkdir -v -p /home/runner/.cache/dprint/cache - npx dprint fmt --config .github/config/dprint.json - name: Build gh-metrics/metrics:${{ github.head_ref || 'master' }} env: GIT_REF: ${{ github.head_ref || 'master' }} diff --git a/.gitignore b/.gitignore index dffcc2b2530..2917007b312 100644 --- a/.gitignore +++ b/.gitignore @@ -104,7 +104,7 @@ dist .tern-port # User settings -settings.json +/settings.json # Community templates source/templates/.community diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 00000000000..c5d06d00db3 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,5 @@ +{ + "recommendations": [ + "dprint.dprint" + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000000..88af5fab072 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "editor.defaultFormatter": "dprint.dprint" +} diff --git a/source/.eslintrc.yml b/source/.eslintrc.yml index 3d160bdc089..ee4b80dcf48 100644 --- a/source/.eslintrc.yml +++ b/source/.eslintrc.yml @@ -63,7 +63,7 @@ rules: # Code integrity no-unsafe-optional-chaining: error no-duplicate-imports: error - eqeqeq: error + eqeqeq: [error, "always", {null: "ignore"}] # Code simplicity max-depth: [error, 10] diff --git a/source/plugins/habits/index.mjs b/source/plugins/habits/index.mjs index 25694bd6421..58fe482b57b 100644 --- a/source/plugins/habits/index.mjs +++ b/source/plugins/habits/index.mjs @@ -51,7 +51,6 @@ export default async function({login, data, rest, imports, q, account}, {enabled .filter(commit => { if (commit == null || typeof commit !== "object") return false - // Safely check author property const author = commit?.author if (!author) return false