From e521d916bfcfed4000dbe28444f5a9b04850c021 Mon Sep 17 00:00:00 2001 From: avivkeller Date: Fri, 23 May 2025 11:14:23 -0400 Subject: [PATCH 1/9] feat(meta): require collaborators to me active --- .github/workflows/find-inactive-members.yml | 32 +++ CONTRIBUTING.md | 18 +- .../__tests__/index.test.mjs | 228 ++++++++++++++++++ .../scripts/find-inactive-members/index.mjs | 225 +++++++++++++++++ 4 files changed, 502 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/find-inactive-members.yml create mode 100644 apps/site/scripts/find-inactive-members/__tests__/index.test.mjs create mode 100644 apps/site/scripts/find-inactive-members/index.mjs diff --git a/.github/workflows/find-inactive-members.yml b/.github/workflows/find-inactive-members.yml new file mode 100644 index 0000000000000..829255e263890 --- /dev/null +++ b/.github/workflows/find-inactive-members.yml @@ -0,0 +1,32 @@ +name: Inactive Collaborators Report + +on: + workflow_dispatch: + schedule: + # Run once a week on Monday at 00:00 UTC + - cron: '0 0 * * 1' + +jobs: + check-inactive-collaborators: + runs-on: ubuntu-latest + permissions: + contents: read + issues: write + + steps: + - name: Harden Runner + uses: step-security/harden-runner@0634a2670c59f64b4a01f0f96f84700a4088b9f0 # v2.12.0 + with: + egress-policy: audit + + - name: Checkout repository + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + + - name: Create inactive collaborators report + uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 + with: + github-token: ${{ secrets.READ_ONLY_ORG_TOKEN }} + script: | + const { reportInactiveCollaborators } = await import("./apps/site/scripts/find-inactive-members.mjs"); + + await reportInactiveCollaborators(core, github); diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 47016c323f154..b68c20515a47d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -4,7 +4,8 @@ Thank you for your interest in contributing to the Node.js Website. Before you p - [Code of Conduct](https://github.com/nodejs/node/blob/HEAD/CODE_OF_CONDUCT.md) - [Contributing](#contributing) - - [Becoming a collaborator](#becoming-a-collaborator) + - [Becoming a Collaborator](#becoming-a-collaborator) + - [Maintaining Collaborator Status](#maintaining-collaborator-status) - [Getting started](#getting-started) - [CLI Commands](#cli-commands) - [Cloudflare Deployment](#cloudflare-deployment) @@ -54,6 +55,21 @@ If you're an active contributor seeking to become a member, we recommend you con +### Maintaining Collaborator Status + +Once you become a collaborator, you are expected to uphold certain responsibilities and standards to maintain your status: + +- **Adhere to Policies**: Collaborators must abide by the [Node.js Moderation Policy](https://github.com/nodejs/admin/blob/HEAD/Moderation-Policy.md) and [Code of Conduct](https://github.com/nodejs/node/blob/HEAD/CODE_OF_CONDUCT.md) at all times. + +- **Remain Active**: Collaborators are expected to interact with the repository at least once in the past twelve months. This can include: + - Reviewing pull requests + - Opening or commenting on issues + - Contributing commits + +If a collaborator becomes inactive for more than twelve months, they may be removed from the active collaborators list. They can be reinstated upon returning to active participation by going through the full nomination process again. + +Violations of the Code of Conduct or Moderation Policy may result in immediate removal of collaborator status, depending on the severity of the violation and the decision of the Technical Steering Committee and/or the OpenJS Foundation. + ## Getting started The steps below will give you a general idea of how to prepare your local environment for the Node.js Website and general steps diff --git a/apps/site/scripts/find-inactive-members/__tests__/index.test.mjs b/apps/site/scripts/find-inactive-members/__tests__/index.test.mjs new file mode 100644 index 0000000000000..717ade22d00ea --- /dev/null +++ b/apps/site/scripts/find-inactive-members/__tests__/index.test.mjs @@ -0,0 +1,228 @@ +import assert from 'node:assert/strict'; +import { beforeEach, describe, it, mock } from 'node:test'; + +import { + findInactiveMembers, + isActiveMember, + getDateMonthsAgo, + reportInactiveCollaborators, + createOrUpdateInactiveCollaboratorsIssue, + findInactiveCollaboratorsIssue, + formatIssueBody, +} from '../index.mjs'; + +// Test constants +const MOCK_DATE = new Date('2025-05-23T14:33:31Z'); +const CUTOFF_DATE = '2024-05-23'; +const TEST_MEMBERS = [ + { login: 'active-user' }, + { login: 'inactive-user' }, + { login: 'active-user-issues' }, +]; + +describe('Inactive Collaborators Tests', () => { + let core, github; + + mock.timers.enable({ apis: ['Date'], now: MOCK_DATE }); + + beforeEach(() => { + // Simplified mocks + const logs = [], + warnings = []; + core = { + info: msg => logs.push(msg), + warning: msg => warnings.push(msg), + getLogs: () => [...logs], + getWarnings: () => [...warnings], + clearLogs: () => { + logs.length = 0; + }, + }; + + github = { + rest: { + search: { + commits: async ({ q }) => ({ + data: { + total_count: q.includes('author:active-user') ? 5 : 0, + items: q.includes('author:active-user') + ? [{ sha: 'abc123' }] + : [], + }, + }), + issuesAndPullRequests: async ({ q }) => ({ + data: { + total_count: q.includes('involves:active-user-issues') ? 3 : 0, + items: q.includes('involves:active-user-issues') + ? [{ number: 123 }] + : [], + }, + }), + }, + teams: { + listMembersInOrg: async () => ({ data: TEST_MEMBERS }), + }, + issues: { + listForRepo: async ({ repo }) => ({ + data: + repo === 'repo-with-issue' + ? [ + { + number: 42, + title: 'Inactive Collaborators Report', + body: 'Previous report', + }, + ] + : [], + }), + create: async ({ title, body }) => ({ + data: { number: 99, title, body }, + }), + update: async ({ issue_number, body }) => ({ + data: { number: issue_number, body }, + }), + }, + }, + }; + }); + + describe('Utilities and core functionality', () => { + it('correctly formats dates with different month offsets', () => { + assert.equal(getDateMonthsAgo(12), CUTOFF_DATE); + assert.equal(getDateMonthsAgo(0), '2025-05-23'); + assert.equal(getDateMonthsAgo(6), '2024-11-23'); + }); + + it('correctly identifies active and inactive users', async () => { + assert.equal( + await isActiveMember('active-user', CUTOFF_DATE, github), + true + ); + assert.equal( + await isActiveMember('active-user-issues', CUTOFF_DATE, github), + true + ); + assert.equal( + await isActiveMember('inactive-user', CUTOFF_DATE, github), + false + ); + }); + + it('finds inactive members from the team list', async () => { + const inactiveMembers = await findInactiveMembers( + TEST_MEMBERS, + core, + github + ); + + assert.partialDeepStrictEqual(inactiveMembers, [ + { login: 'inactive-user' }, + ]); + }); + }); + + describe('Issue management', () => { + it('formats issue body correctly', () => { + const inactiveMembers = [ + { + login: 'inactive-user', + inactive_since: CUTOFF_DATE, + }, + ]; + + const body = formatIssueBody(inactiveMembers, CUTOFF_DATE); + + assert.ok(body.includes('# Inactive Collaborators Report')); + assert.ok(body.includes('## Inactive Collaborators (1)')); + assert.ok(body.includes('@inactive-user')); + }); + + it('handles empty inactive members list', () => { + const body = formatIssueBody([], CUTOFF_DATE); + assert.ok(body.includes('No inactive collaborators were found')); + assert.ok(!body.includes('| Login |')); + }); + + it('manages issue creation and updates', async () => { + const inactiveMembers = [ + { login: 'inactive-user', inactive_since: CUTOFF_DATE }, + ]; + + // Test finding issues + const existingIssue = await findInactiveCollaboratorsIssue( + github, + 'nodejs', + 'repo-with-issue' + ); + const nonExistingIssue = await findInactiveCollaboratorsIssue( + github, + 'nodejs', + 'repo-without-issue' + ); + + assert.equal(existingIssue?.number, 42); + assert.equal(nonExistingIssue, null); + + // Test updating existing issues + const updatedIssueNum = await createOrUpdateInactiveCollaboratorsIssue({ + github, + core, + org: 'nodejs', + repo: 'repo-with-issue', + inactiveMembers, + cutoffDate: CUTOFF_DATE, + }); + assert.equal(updatedIssueNum, 42); + + // Test creating new issues + const newIssueNum = await createOrUpdateInactiveCollaboratorsIssue({ + github, + core, + org: 'nodejs', + repo: 'repo-without-issue', + inactiveMembers, + cutoffDate: CUTOFF_DATE, + }); + assert.equal(newIssueNum, 99); + }); + }); + + describe('Complete workflow', () => { + it('correctly executes the full report generation workflow', async () => { + await reportInactiveCollaborators(core, github, { + org: 'nodejs', + teamSlug: 'team', + repo: 'repo', + monthsInactive: 12, + }); + + const logs = core.getLogs(); + assert.ok( + logs.some(log => log.includes('Checking inactive collaborators')) + ); + assert.ok( + logs.some(log => + log.includes('Inactive collaborators report available at:') + ) + ); + }); + + it('uses default parameters when not specified', async () => { + const customGithub = { + ...github, + rest: { + ...github.rest, + teams: { + listMembersInOrg: async ({ org, team_slug }) => { + assert.equal(org, 'nodejs'); + assert.equal(team_slug, 'nodejs-website'); + return { data: [] }; + }, + }, + }, + }; + + await reportInactiveCollaborators(core, customGithub); + }); + }); +}); diff --git a/apps/site/scripts/find-inactive-members/index.mjs b/apps/site/scripts/find-inactive-members/index.mjs new file mode 100644 index 0000000000000..0d40f2d721839 --- /dev/null +++ b/apps/site/scripts/find-inactive-members/index.mjs @@ -0,0 +1,225 @@ +// Configuration defaults +const DEFAULTS = { + ORG: 'nodejs', + TEAM_SLUG: 'nodejs-website', + REPO: 'nodejs.org', + MONTHS_INACTIVE: 12, + ISSUE: { + TITLE: 'Inactive Collaborators Report', + LABELS: ['inactive-collaborators', 'meta'], + }, +}; + +/** + * Gets a date string from X months ago + */ +function getDateMonthsAgo(months) { + const date = new Date(); + date.setMonth(date.getMonth() - months); + return date.toISOString().split('T')[0]; +} + +/** + * Checks if a member has had activity since the cutoff date + */ +async function isActiveMember(username, cutoffDate, github, repo) { + // Check for commits first + const commitQuery = `author:${username} repo:${repo} committer-date:>=${cutoffDate}`; + const { data: commitData } = await github.rest.search.commits({ + q: commitQuery, + per_page: 1, + }); + if (commitData.total_count > 0) return true; + + // Then check for issue/PR activity + const issueQuery = `involves:${username} repo:${repo} updated:>=${cutoffDate}`; + const { data: issueData } = await github.rest.search.issuesAndPullRequests({ + q: issueQuery, + per_page: 1, + advanced_search: true, + }); + return issueData.total_count > 0; +} + +/** + * Formats the issue body with inactive members information + */ +function formatIssueBody(inactiveMembers, cutoffDate) { + const now = getDateMonthsAgo(0); + let body = `# ${DEFAULTS.ISSUE.TITLE}\n\n`; + body += `Last updated: ${now}\n`; + body += `Checking for inactivity since: ${cutoffDate}\n\n`; + + if (inactiveMembers.length === 0) { + return false; + } + + body += `## Inactive Collaborators (${inactiveMembers.length})\n\n`; + body += `| Login | Inactive Since |\n`; + body += `| ----- | -------------- |\n`; + inactiveMembers.forEach(member => { + body += `| @${member.login} | ${member.inactive_since} |\n`; + }); + + body += `\n## What happens next?\n\n`; + body += `Team maintainers should review this list and contact inactive collaborators `; + body += `to confirm their continued interest in participating in the project.`; + + return body; +} + +/** + * Finds all inactive members from a team + */ +async function findInactiveMembers( + members = [], + core, + github, + repo, + monthsInactive = DEFAULTS.MONTHS_INACTIVE +) { + if (!members.length) { + core.info('No members found to check'); + return []; + } + + const cutoffDate = getDateMonthsAgo(monthsInactive); + core.info(`Checking for members inactive since ${cutoffDate}`); + const inactiveMembers = []; + + // Use concurrency control to avoid rate limiting + for (const member of members) { + const { login: username } = member; + try { + core.info(`Checking activity for ${username}...`); + const isActive = await isActiveMember(username, cutoffDate, github, repo); + + if (isActive) { + core.info(`${username} has recent activity - marked as active`); + } else { + core.info(`${username} has no recent activity - marked as inactive`); + inactiveMembers.push({ + login: username, + inactive_since: cutoffDate, + }); + } + } catch (error) { + core.warning(`Error checking activity for ${username}: ${error.message}`); + } + } + + core.info( + `Found ${inactiveMembers.length} inactive members out of ${members.length} total` + ); + return inactiveMembers; +} + +/** + * Finds an existing inactive collaborators issue + */ +async function findInactiveCollaboratorsIssue(github, org, repo) { + const { data: issues } = await github.rest.issues.listForRepo({ + owner: org, + repo, + state: 'open', + labels: DEFAULTS.ISSUE.LABELS[0], + per_page: 1, + }); + return issues.length > 0 ? issues[0] : null; +} + +/** + * Creates or updates the inactive collaborators issue + */ +async function createOrUpdateInactiveCollaboratorsIssue({ + github, + core, + org, + repo, + inactiveMembers, + cutoffDate, +}) { + const issueBody = formatIssueBody(inactiveMembers, cutoffDate); + if (!issueBody) return; + + const existingIssue = await findInactiveCollaboratorsIssue(github, org, repo); + + if (existingIssue) { + core.info( + `Updating existing inactive collaborators issue #${existingIssue.number}` + ); + await github.rest.issues.update({ + owner: org, + repo, + issue_number: existingIssue.number, + body: issueBody, + }); + return existingIssue.number; + } + + core.info('Creating new inactive collaborators issue'); + const { data: newIssue } = await github.rest.issues.create({ + owner: org, + repo, + title: DEFAULTS.ISSUE.TITLE, + body: issueBody, + labels: DEFAULTS.ISSUE.LABELS, + }); + return newIssue.number; +} + +/** + * Main function to report inactive collaborators + */ +async function reportInactiveCollaborators( + core, + github, + { + org = DEFAULTS.ORG, + teamSlug = DEFAULTS.TEAM_SLUG, + repo = DEFAULTS.REPO, + monthsInactive = DEFAULTS.MONTHS_INACTIVE, + } = {} +) { + core.info(`Checking inactive collaborators in ${org}/${teamSlug}`); + + const { data: members } = await github.rest.teams.listMembersInOrg({ + org, + team_slug: teamSlug, + per_page: 100, + }); + + const fullRepo = `${org}/${repo}`; + const inactiveMembers = await findInactiveMembers( + members, + core, + github, + fullRepo, + monthsInactive + ); + + const cutoffDate = getDateMonthsAgo(monthsInactive); + const issueNumber = await createOrUpdateInactiveCollaboratorsIssue({ + github, + core, + org, + repo, + inactiveMembers, + cutoffDate, + }); + + core.info( + `Inactive collaborators report available at: https://github.com/${org}/${repo}/issues/${issueNumber}` + ); + return issueNumber; +} + +export { + reportInactiveCollaborators, + createOrUpdateInactiveCollaboratorsIssue, + findInactiveCollaboratorsIssue, + findInactiveMembers, + isActiveMember, + getDateMonthsAgo, + formatIssueBody, +}; From 5c3d22ed156ee5f7837c77b57cabb1e68062044d Mon Sep 17 00:00:00 2001 From: avivkeller Date: Fri, 23 May 2025 15:35:01 -0400 Subject: [PATCH 2/9] fixup! --- ...rs.yml => find-inactive-collaborators.yml} | 27 ++++++++++--------- .../__tests__/index.test.mjs | 4 +-- .../index.mjs | 0 3 files changed, 16 insertions(+), 15 deletions(-) rename .github/workflows/{find-inactive-members.yml => find-inactive-collaborators.yml} (58%) rename apps/site/scripts/{find-inactive-members => find-inactive-collaborators}/__tests__/index.test.mjs (97%) rename apps/site/scripts/{find-inactive-members => find-inactive-collaborators}/index.mjs (100%) diff --git a/.github/workflows/find-inactive-members.yml b/.github/workflows/find-inactive-collaborators.yml similarity index 58% rename from .github/workflows/find-inactive-members.yml rename to .github/workflows/find-inactive-collaborators.yml index 829255e263890..46dcadc7394f7 100644 --- a/.github/workflows/find-inactive-members.yml +++ b/.github/workflows/find-inactive-collaborators.yml @@ -1,17 +1,21 @@ -name: Inactive Collaborators Report +name: Find inactive collaborators on: - workflow_dispatch: schedule: - # Run once a week on Monday at 00:00 UTC - - cron: '0 0 * * 1' + # Run every Monday at 4:05 AM UTC. + - cron: 5 4 * * 1 + + workflow_dispatch: + +env: + NODE_VERSION: lts/* + +permissions: {} jobs: - check-inactive-collaborators: + find: + if: github.repository == 'nodejs/node' runs-on: ubuntu-latest - permissions: - contents: read - issues: write steps: - name: Harden Runner @@ -19,14 +23,13 @@ jobs: with: egress-policy: audit - - name: Checkout repository - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: Create inactive collaborators report uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 with: - github-token: ${{ secrets.READ_ONLY_ORG_TOKEN }} + github-token: ${{ secrets.READ_ONLY_PUBLIC_REPO_TOKEN }} script: | - const { reportInactiveCollaborators } = await import("./apps/site/scripts/find-inactive-members.mjs"); + const { reportInactiveCollaborators } = await import("${{github.workspace}}/apps/site/scripts/find-inactive-collaborators/index.mjs"); await reportInactiveCollaborators(core, github); diff --git a/apps/site/scripts/find-inactive-members/__tests__/index.test.mjs b/apps/site/scripts/find-inactive-collaborators/__tests__/index.test.mjs similarity index 97% rename from apps/site/scripts/find-inactive-members/__tests__/index.test.mjs rename to apps/site/scripts/find-inactive-collaborators/__tests__/index.test.mjs index 717ade22d00ea..33c19460eb845 100644 --- a/apps/site/scripts/find-inactive-members/__tests__/index.test.mjs +++ b/apps/site/scripts/find-inactive-collaborators/__tests__/index.test.mjs @@ -138,9 +138,7 @@ describe('Inactive Collaborators Tests', () => { }); it('handles empty inactive members list', () => { - const body = formatIssueBody([], CUTOFF_DATE); - assert.ok(body.includes('No inactive collaborators were found')); - assert.ok(!body.includes('| Login |')); + assert.ok(!formatIssueBody([], CUTOFF_DATE)); }); it('manages issue creation and updates', async () => { diff --git a/apps/site/scripts/find-inactive-members/index.mjs b/apps/site/scripts/find-inactive-collaborators/index.mjs similarity index 100% rename from apps/site/scripts/find-inactive-members/index.mjs rename to apps/site/scripts/find-inactive-collaborators/index.mjs From 0ec595b9f28a531666d289e4688d8f1fb0d40a8a Mon Sep 17 00:00:00 2001 From: Aviv Keller Date: Fri, 23 May 2025 15:44:29 -0400 Subject: [PATCH 3/9] Update .github/workflows/find-inactive-collaborators.yml Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Aviv Keller --- .github/workflows/find-inactive-collaborators.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/find-inactive-collaborators.yml b/.github/workflows/find-inactive-collaborators.yml index 46dcadc7394f7..039c10cbefd27 100644 --- a/.github/workflows/find-inactive-collaborators.yml +++ b/.github/workflows/find-inactive-collaborators.yml @@ -14,7 +14,7 @@ permissions: {} jobs: find: - if: github.repository == 'nodejs/node' + if: github.repository == 'nodejs/nodejs.org' runs-on: ubuntu-latest steps: From de471821711fa954d37a2b892d4fa735ea5b5568 Mon Sep 17 00:00:00 2001 From: avivkeller Date: Fri, 23 May 2025 16:32:33 -0400 Subject: [PATCH 4/9] store workflow in .github --- .../scripts/__tests__/find-inactive-collaborators.test.mjs | 4 ++-- .../scripts/find-inactive-collaborators.mjs | 0 .github/workflows/find-inactive-collaborators.yml | 2 +- package.json | 3 ++- 4 files changed, 5 insertions(+), 4 deletions(-) rename apps/site/scripts/find-inactive-collaborators/__tests__/index.test.mjs => .github/scripts/__tests__/find-inactive-collaborators.test.mjs (98%) rename apps/site/scripts/find-inactive-collaborators/index.mjs => .github/scripts/find-inactive-collaborators.mjs (100%) diff --git a/apps/site/scripts/find-inactive-collaborators/__tests__/index.test.mjs b/.github/scripts/__tests__/find-inactive-collaborators.test.mjs similarity index 98% rename from apps/site/scripts/find-inactive-collaborators/__tests__/index.test.mjs rename to .github/scripts/__tests__/find-inactive-collaborators.test.mjs index 33c19460eb845..d999f8071146e 100644 --- a/apps/site/scripts/find-inactive-collaborators/__tests__/index.test.mjs +++ b/.github/scripts/__tests__/find-inactive-collaborators.test.mjs @@ -9,7 +9,7 @@ import { createOrUpdateInactiveCollaboratorsIssue, findInactiveCollaboratorsIssue, formatIssueBody, -} from '../index.mjs'; +} from '../find-inactive-collaborators.mjs'; // Test constants const MOCK_DATE = new Date('2025-05-23T14:33:31Z'); @@ -158,7 +158,7 @@ describe('Inactive Collaborators Tests', () => { 'repo-without-issue' ); - assert.equal(existingIssue?.number, 42); + assert.equal(existingIssue.number, 42); assert.equal(nonExistingIssue, null); // Test updating existing issues diff --git a/apps/site/scripts/find-inactive-collaborators/index.mjs b/.github/scripts/find-inactive-collaborators.mjs similarity index 100% rename from apps/site/scripts/find-inactive-collaborators/index.mjs rename to .github/scripts/find-inactive-collaborators.mjs diff --git a/.github/workflows/find-inactive-collaborators.yml b/.github/workflows/find-inactive-collaborators.yml index 039c10cbefd27..fbfb38f58f35f 100644 --- a/.github/workflows/find-inactive-collaborators.yml +++ b/.github/workflows/find-inactive-collaborators.yml @@ -30,6 +30,6 @@ jobs: with: github-token: ${{ secrets.READ_ONLY_PUBLIC_REPO_TOKEN }} script: | - const { reportInactiveCollaborators } = await import("${{github.workspace}}/apps/site/scripts/find-inactive-collaborators/index.mjs"); + const { reportInactiveCollaborators } = await import("${{github.workspace}}/.github/scripts/find-inactive-collaborators.mjs"); await reportInactiveCollaborators(core, github); diff --git a/package.json b/package.json index 808bcad2b1814..55fa4e31983e7 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,8 @@ "start": "turbo run start", "storybook": "turbo run storybook", "storybook:build": "turbo run storybook:build", - "test": "turbo test:unit", + "test:actions": "node --test \".github/**/*.test.mjs\"", + "test": "turbo test:unit && pnpm test:actions", "test:ci": "cross-env NODE_OPTIONS=\"--test-reporter=lcov --test-reporter-destination=lcov.info --test-reporter=junit --test-reporter-destination=junit.xml --test-reporter=@reporters/github --test-reporter-destination=stdout\" turbo test:unit", "cloudflare:preview": "turbo run cloudflare:preview", "cloudflare:deploy": "turbo run cloudflare:deploy" From c99039c7da14c2dc45ed773bc55b9774e0b4e761 Mon Sep 17 00:00:00 2001 From: avivkeller Date: Mon, 26 May 2025 10:39:10 -0400 Subject: [PATCH 5/9] use local list --- .../find-inactive-collaborators.test.mjs | 226 ------------------ .../scripts/find-inactive-collaborators.mjs | 225 ----------------- .../scripts/report-inactive-collaborators.mjs | 121 ++++++++++ .../workflows/find-inactive-collaborators.yml | 9 +- CONTRIBUTING.md | 5 +- GOVERNANCE.md | 40 +++- 6 files changed, 165 insertions(+), 461 deletions(-) delete mode 100644 .github/scripts/__tests__/find-inactive-collaborators.test.mjs delete mode 100644 .github/scripts/find-inactive-collaborators.mjs create mode 100644 .github/scripts/report-inactive-collaborators.mjs diff --git a/.github/scripts/__tests__/find-inactive-collaborators.test.mjs b/.github/scripts/__tests__/find-inactive-collaborators.test.mjs deleted file mode 100644 index d999f8071146e..0000000000000 --- a/.github/scripts/__tests__/find-inactive-collaborators.test.mjs +++ /dev/null @@ -1,226 +0,0 @@ -import assert from 'node:assert/strict'; -import { beforeEach, describe, it, mock } from 'node:test'; - -import { - findInactiveMembers, - isActiveMember, - getDateMonthsAgo, - reportInactiveCollaborators, - createOrUpdateInactiveCollaboratorsIssue, - findInactiveCollaboratorsIssue, - formatIssueBody, -} from '../find-inactive-collaborators.mjs'; - -// Test constants -const MOCK_DATE = new Date('2025-05-23T14:33:31Z'); -const CUTOFF_DATE = '2024-05-23'; -const TEST_MEMBERS = [ - { login: 'active-user' }, - { login: 'inactive-user' }, - { login: 'active-user-issues' }, -]; - -describe('Inactive Collaborators Tests', () => { - let core, github; - - mock.timers.enable({ apis: ['Date'], now: MOCK_DATE }); - - beforeEach(() => { - // Simplified mocks - const logs = [], - warnings = []; - core = { - info: msg => logs.push(msg), - warning: msg => warnings.push(msg), - getLogs: () => [...logs], - getWarnings: () => [...warnings], - clearLogs: () => { - logs.length = 0; - }, - }; - - github = { - rest: { - search: { - commits: async ({ q }) => ({ - data: { - total_count: q.includes('author:active-user') ? 5 : 0, - items: q.includes('author:active-user') - ? [{ sha: 'abc123' }] - : [], - }, - }), - issuesAndPullRequests: async ({ q }) => ({ - data: { - total_count: q.includes('involves:active-user-issues') ? 3 : 0, - items: q.includes('involves:active-user-issues') - ? [{ number: 123 }] - : [], - }, - }), - }, - teams: { - listMembersInOrg: async () => ({ data: TEST_MEMBERS }), - }, - issues: { - listForRepo: async ({ repo }) => ({ - data: - repo === 'repo-with-issue' - ? [ - { - number: 42, - title: 'Inactive Collaborators Report', - body: 'Previous report', - }, - ] - : [], - }), - create: async ({ title, body }) => ({ - data: { number: 99, title, body }, - }), - update: async ({ issue_number, body }) => ({ - data: { number: issue_number, body }, - }), - }, - }, - }; - }); - - describe('Utilities and core functionality', () => { - it('correctly formats dates with different month offsets', () => { - assert.equal(getDateMonthsAgo(12), CUTOFF_DATE); - assert.equal(getDateMonthsAgo(0), '2025-05-23'); - assert.equal(getDateMonthsAgo(6), '2024-11-23'); - }); - - it('correctly identifies active and inactive users', async () => { - assert.equal( - await isActiveMember('active-user', CUTOFF_DATE, github), - true - ); - assert.equal( - await isActiveMember('active-user-issues', CUTOFF_DATE, github), - true - ); - assert.equal( - await isActiveMember('inactive-user', CUTOFF_DATE, github), - false - ); - }); - - it('finds inactive members from the team list', async () => { - const inactiveMembers = await findInactiveMembers( - TEST_MEMBERS, - core, - github - ); - - assert.partialDeepStrictEqual(inactiveMembers, [ - { login: 'inactive-user' }, - ]); - }); - }); - - describe('Issue management', () => { - it('formats issue body correctly', () => { - const inactiveMembers = [ - { - login: 'inactive-user', - inactive_since: CUTOFF_DATE, - }, - ]; - - const body = formatIssueBody(inactiveMembers, CUTOFF_DATE); - - assert.ok(body.includes('# Inactive Collaborators Report')); - assert.ok(body.includes('## Inactive Collaborators (1)')); - assert.ok(body.includes('@inactive-user')); - }); - - it('handles empty inactive members list', () => { - assert.ok(!formatIssueBody([], CUTOFF_DATE)); - }); - - it('manages issue creation and updates', async () => { - const inactiveMembers = [ - { login: 'inactive-user', inactive_since: CUTOFF_DATE }, - ]; - - // Test finding issues - const existingIssue = await findInactiveCollaboratorsIssue( - github, - 'nodejs', - 'repo-with-issue' - ); - const nonExistingIssue = await findInactiveCollaboratorsIssue( - github, - 'nodejs', - 'repo-without-issue' - ); - - assert.equal(existingIssue.number, 42); - assert.equal(nonExistingIssue, null); - - // Test updating existing issues - const updatedIssueNum = await createOrUpdateInactiveCollaboratorsIssue({ - github, - core, - org: 'nodejs', - repo: 'repo-with-issue', - inactiveMembers, - cutoffDate: CUTOFF_DATE, - }); - assert.equal(updatedIssueNum, 42); - - // Test creating new issues - const newIssueNum = await createOrUpdateInactiveCollaboratorsIssue({ - github, - core, - org: 'nodejs', - repo: 'repo-without-issue', - inactiveMembers, - cutoffDate: CUTOFF_DATE, - }); - assert.equal(newIssueNum, 99); - }); - }); - - describe('Complete workflow', () => { - it('correctly executes the full report generation workflow', async () => { - await reportInactiveCollaborators(core, github, { - org: 'nodejs', - teamSlug: 'team', - repo: 'repo', - monthsInactive: 12, - }); - - const logs = core.getLogs(); - assert.ok( - logs.some(log => log.includes('Checking inactive collaborators')) - ); - assert.ok( - logs.some(log => - log.includes('Inactive collaborators report available at:') - ) - ); - }); - - it('uses default parameters when not specified', async () => { - const customGithub = { - ...github, - rest: { - ...github.rest, - teams: { - listMembersInOrg: async ({ org, team_slug }) => { - assert.equal(org, 'nodejs'); - assert.equal(team_slug, 'nodejs-website'); - return { data: [] }; - }, - }, - }, - }; - - await reportInactiveCollaborators(core, customGithub); - }); - }); -}); diff --git a/.github/scripts/find-inactive-collaborators.mjs b/.github/scripts/find-inactive-collaborators.mjs deleted file mode 100644 index 0d40f2d721839..0000000000000 --- a/.github/scripts/find-inactive-collaborators.mjs +++ /dev/null @@ -1,225 +0,0 @@ -// Configuration defaults -const DEFAULTS = { - ORG: 'nodejs', - TEAM_SLUG: 'nodejs-website', - REPO: 'nodejs.org', - MONTHS_INACTIVE: 12, - ISSUE: { - TITLE: 'Inactive Collaborators Report', - LABELS: ['inactive-collaborators', 'meta'], - }, -}; - -/** - * Gets a date string from X months ago - */ -function getDateMonthsAgo(months) { - const date = new Date(); - date.setMonth(date.getMonth() - months); - return date.toISOString().split('T')[0]; -} - -/** - * Checks if a member has had activity since the cutoff date - */ -async function isActiveMember(username, cutoffDate, github, repo) { - // Check for commits first - const commitQuery = `author:${username} repo:${repo} committer-date:>=${cutoffDate}`; - const { data: commitData } = await github.rest.search.commits({ - q: commitQuery, - per_page: 1, - }); - if (commitData.total_count > 0) return true; - - // Then check for issue/PR activity - const issueQuery = `involves:${username} repo:${repo} updated:>=${cutoffDate}`; - const { data: issueData } = await github.rest.search.issuesAndPullRequests({ - q: issueQuery, - per_page: 1, - advanced_search: true, - }); - return issueData.total_count > 0; -} - -/** - * Formats the issue body with inactive members information - */ -function formatIssueBody(inactiveMembers, cutoffDate) { - const now = getDateMonthsAgo(0); - let body = `# ${DEFAULTS.ISSUE.TITLE}\n\n`; - body += `Last updated: ${now}\n`; - body += `Checking for inactivity since: ${cutoffDate}\n\n`; - - if (inactiveMembers.length === 0) { - return false; - } - - body += `## Inactive Collaborators (${inactiveMembers.length})\n\n`; - body += `| Login | Inactive Since |\n`; - body += `| ----- | -------------- |\n`; - inactiveMembers.forEach(member => { - body += `| @${member.login} | ${member.inactive_since} |\n`; - }); - - body += `\n## What happens next?\n\n`; - body += `Team maintainers should review this list and contact inactive collaborators `; - body += `to confirm their continued interest in participating in the project.`; - - return body; -} - -/** - * Finds all inactive members from a team - */ -async function findInactiveMembers( - members = [], - core, - github, - repo, - monthsInactive = DEFAULTS.MONTHS_INACTIVE -) { - if (!members.length) { - core.info('No members found to check'); - return []; - } - - const cutoffDate = getDateMonthsAgo(monthsInactive); - core.info(`Checking for members inactive since ${cutoffDate}`); - const inactiveMembers = []; - - // Use concurrency control to avoid rate limiting - for (const member of members) { - const { login: username } = member; - try { - core.info(`Checking activity for ${username}...`); - const isActive = await isActiveMember(username, cutoffDate, github, repo); - - if (isActive) { - core.info(`${username} has recent activity - marked as active`); - } else { - core.info(`${username} has no recent activity - marked as inactive`); - inactiveMembers.push({ - login: username, - inactive_since: cutoffDate, - }); - } - } catch (error) { - core.warning(`Error checking activity for ${username}: ${error.message}`); - } - } - - core.info( - `Found ${inactiveMembers.length} inactive members out of ${members.length} total` - ); - return inactiveMembers; -} - -/** - * Finds an existing inactive collaborators issue - */ -async function findInactiveCollaboratorsIssue(github, org, repo) { - const { data: issues } = await github.rest.issues.listForRepo({ - owner: org, - repo, - state: 'open', - labels: DEFAULTS.ISSUE.LABELS[0], - per_page: 1, - }); - return issues.length > 0 ? issues[0] : null; -} - -/** - * Creates or updates the inactive collaborators issue - */ -async function createOrUpdateInactiveCollaboratorsIssue({ - github, - core, - org, - repo, - inactiveMembers, - cutoffDate, -}) { - const issueBody = formatIssueBody(inactiveMembers, cutoffDate); - if (!issueBody) return; - - const existingIssue = await findInactiveCollaboratorsIssue(github, org, repo); - - if (existingIssue) { - core.info( - `Updating existing inactive collaborators issue #${existingIssue.number}` - ); - await github.rest.issues.update({ - owner: org, - repo, - issue_number: existingIssue.number, - body: issueBody, - }); - return existingIssue.number; - } - - core.info('Creating new inactive collaborators issue'); - const { data: newIssue } = await github.rest.issues.create({ - owner: org, - repo, - title: DEFAULTS.ISSUE.TITLE, - body: issueBody, - labels: DEFAULTS.ISSUE.LABELS, - }); - return newIssue.number; -} - -/** - * Main function to report inactive collaborators - */ -async function reportInactiveCollaborators( - core, - github, - { - org = DEFAULTS.ORG, - teamSlug = DEFAULTS.TEAM_SLUG, - repo = DEFAULTS.REPO, - monthsInactive = DEFAULTS.MONTHS_INACTIVE, - } = {} -) { - core.info(`Checking inactive collaborators in ${org}/${teamSlug}`); - - const { data: members } = await github.rest.teams.listMembersInOrg({ - org, - team_slug: teamSlug, - per_page: 100, - }); - - const fullRepo = `${org}/${repo}`; - const inactiveMembers = await findInactiveMembers( - members, - core, - github, - fullRepo, - monthsInactive - ); - - const cutoffDate = getDateMonthsAgo(monthsInactive); - const issueNumber = await createOrUpdateInactiveCollaboratorsIssue({ - github, - core, - org, - repo, - inactiveMembers, - cutoffDate, - }); - - core.info( - `Inactive collaborators report available at: https://github.com/${org}/${repo}/issues/${issueNumber}` - ); - return issueNumber; -} - -export { - reportInactiveCollaborators, - createOrUpdateInactiveCollaboratorsIssue, - findInactiveCollaboratorsIssue, - findInactiveMembers, - isActiveMember, - getDateMonthsAgo, - formatIssueBody, -}; diff --git a/.github/scripts/report-inactive-collaborators.mjs b/.github/scripts/report-inactive-collaborators.mjs new file mode 100644 index 0000000000000..1921a4c237ba4 --- /dev/null +++ b/.github/scripts/report-inactive-collaborators.mjs @@ -0,0 +1,121 @@ +import { readFile } from 'node:fs/promises'; + +const CONFIG = { + GOVERNANCE_FILE: 'GOVERNANCE.md', + CURRENT_MEMBERS_HEADER: '#### Current Members', + INACTIVE_MONTHS: 12, + ISSUE_TITLE: 'Inactive Collaborator Report', + ISSUE_LABELS: ['meta', 'inactive-collaborator-report'], +}; + +// Get date N months ago in YYYY-MM-DD format +const getDateMonthsAgo = (months = CONFIG.INACTIVE_MONTHS) => { + const date = new Date(); + date.setMonth(date.getMonth() - months); + return date.toISOString().split('T')[0]; +}; + +// Parse collaborator usernames from governance file +async function parseCollaborators() { + const content = await readFile(CONFIG.GOVERNANCE_FILE, 'utf8'); + const lines = content.split('\n'); + const collaborators = []; + + const startIndex = lines.indexOf(CONFIG.CURRENT_MEMBERS_HEADER) + 1; + if (startIndex <= 0) return collaborators; + + for (let i = startIndex; i < lines.length; i++) { + const line = lines[i]; + if (line.startsWith('#')) break; + + const match = line.match(/^\s*-\s*\[([^\]]+)\]/); + if (match) collaborators.push(match[1]); + } + + return collaborators; +} + +// Check if users have been active since cutoff date +async function getInactiveUsers(github, usernames, repo, cutoffDate) { + const inactiveUsers = []; + + for (const username of usernames) { + const { data } = await github.rest.search.commits({ + q: `author:${username} repo:${repo} committer-date:>=${cutoffDate}`, + per_page: 1, + }); + + if (data.total_count === 0) { + inactiveUsers.push(username); + } + } + + return inactiveUsers; +} + +// Generate report for inactive members +function formatReport(inactiveMembers, cutoffDate) { + if (!inactiveMembers.length) return null; + + const today = getDateMonthsAgo(0); + return `# Inactive Collaborators Report + +Last updated: ${today} +Checking for inactivity since: ${cutoffDate} + +## Inactive Collaborators (${inactiveMembers.length}) + +| Login | +| ----- | +${inactiveMembers.map(m => `| @${m} |`).join('\n')} + +## What happens next? + +Team maintainers should review this list and contact inactive collaborators to confirm their continued interest in participating in the project.`; +} + +async function createOrUpdateIssue(github, context, report) { + if (!report) return; + + const { owner, repo } = context.repo; + const { data: issues } = await github.rest.issues.listForRepo({ + owner, + repo, + state: 'open', + labels: CONFIG.ISSUE_LABELS[1], + per_page: 1, + }); + + if (issues.total_count > 0) { + await github.rest.issues.update({ + owner, + repo, + issue_number: issues.items[0].number, + body: report, + }); + } else { + await github.rest.issues.create({ + owner, + repo, + title: CONFIG.ISSUE_TITLE, + body: report, + labels: CONFIG.ISSUE_LABELS, + }); + } +} + +export default async function (github, context) { + const repo = `${context.repo.owner}/${context.repo.repo}`; + const cutoffDate = getDateMonthsAgo(); + const collaborators = await parseCollaborators(); + + const inactiveMembers = await getInactiveUsers( + github, + collaborators, + `${context.repo.owner}/${context.repo.repo}`, + cutoffDate + ); + const report = formatReport(inactiveMembers, cutoffDate); + + await createOrUpdateIssue(github, context, report); +} diff --git a/.github/workflows/find-inactive-collaborators.yml b/.github/workflows/find-inactive-collaborators.yml index fbfb38f58f35f..5683ac4eff8e8 100644 --- a/.github/workflows/find-inactive-collaborators.yml +++ b/.github/workflows/find-inactive-collaborators.yml @@ -25,11 +25,10 @@ jobs: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - - name: Create inactive collaborators report + - name: Report inactive collaborators + id: inactive uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 with: - github-token: ${{ secrets.READ_ONLY_PUBLIC_REPO_TOKEN }} script: | - const { reportInactiveCollaborators } = await import("${{github.workspace}}/.github/scripts/find-inactive-collaborators.mjs"); - - await reportInactiveCollaborators(core, github); + const report = await import("${{github.workspace}}/.github/scripts/report-inactive-collaborators.mjs"); + report(github, exec) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b68c20515a47d..e1c5ce1bb82c0 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -61,10 +61,7 @@ Once you become a collaborator, you are expected to uphold certain responsibilit - **Adhere to Policies**: Collaborators must abide by the [Node.js Moderation Policy](https://github.com/nodejs/admin/blob/HEAD/Moderation-Policy.md) and [Code of Conduct](https://github.com/nodejs/node/blob/HEAD/CODE_OF_CONDUCT.md) at all times. -- **Remain Active**: Collaborators are expected to interact with the repository at least once in the past twelve months. This can include: - - Reviewing pull requests - - Opening or commenting on issues - - Contributing commits +- **Remain Active**: Collaborators are expected to author commits at least once in the past twelve months. If a collaborator becomes inactive for more than twelve months, they may be removed from the active collaborators list. They can be reinstated upon returning to active participation by going through the full nomination process again. diff --git a/GOVERNANCE.md b/GOVERNANCE.md index 6f6d463030dbc..e49a66cc96a4a 100644 --- a/GOVERNANCE.md +++ b/GOVERNANCE.md @@ -15,7 +15,45 @@ The Node.js Website Team is responsible for the day-to-day technical development The maintainers on the Node.js Website Team are responsible for steering the technical direction of the Node.js Website, and reserve the right to make final decisions on any issues or pull requests, in line with the Contribution Guidelines, Collaborator Guidelines, the Code of Conduct and the overall Governance premises of the Node.js project. -Members of this team are nominated through the guidelines provided in the Collaborator Guidelines within this repository. +Members of this team are nominated through the guidelines provided in the Collaborator Guidelines within this repository. After a passed nominations, members should submit a PR to add themselves to the list of current members, shown below. + +#### Current Members + +- [araujogui](https://github.com/araujogui) - **Guilherme Araújo** + +- [AugustinMauroy](https://github.com/AugustinMauroy) - **Augustin Mauroy** + +- [avivkeller](https://github.com/avivkeller) - **Aviv Keller** + +- [aymen94](https://github.com/aymen94) - **Aymen Naghmouchi** + +- [benhalverson](https://github.com/benhalverson) - **Ben Halverson** + +- [bjohansebas](https://github.com/bjohansebas) - **Sebastian Beltran** + +- [bmuenzenmeyer](https://github.com/bmuenzenmeyer) - **Brian Muenzenmeyer** + +- [bnb](https://github.com/bnb) - **Tierney Cyren** + +- [canerakdas](https://github.com/canerakdas) - **Caner Akdas** + +- [dario-piotrowicz](https://github.com/dario-piotrowicz) - **Dario Piotrowicz** + +- [Harkunwar](https://github.com/Harkunwar) - **Harkunwar Kochar** + +- [HinataKah0](https://github.com/HinataKah0) - **HinataKah0** + +- [manishprivet](https://github.com/manishprivet) - **Manish Kumar** + +- [mikeesto](https://github.com/mikeesto) - **Michael Esteban** + +- [ovflowd](https://github.com/ovflowd) - **Claudio Wunder** + +- [rodion-arr](https://github.com/rodion-arr) - **Rodion Abdurakhimov** + +- [SEWeiTung](https://github.com/SEWeiTung) - **Wei Tung** + +- [shanpriyan](https://github.com/shanpriyan) - **Shanmughapriyan S** ### Node.js Web Infra Team (`@nodejs/web-infra`) From d42745deca3e2f1ca01b78e8e2c76752b6dd4d7a Mon Sep 17 00:00:00 2001 From: avivkeller Date: Mon, 26 May 2025 10:40:48 -0400 Subject: [PATCH 6/9] remove unused tests --- package.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/package.json b/package.json index 55fa4e31983e7..808bcad2b1814 100644 --- a/package.json +++ b/package.json @@ -27,8 +27,7 @@ "start": "turbo run start", "storybook": "turbo run storybook", "storybook:build": "turbo run storybook:build", - "test:actions": "node --test \".github/**/*.test.mjs\"", - "test": "turbo test:unit && pnpm test:actions", + "test": "turbo test:unit", "test:ci": "cross-env NODE_OPTIONS=\"--test-reporter=lcov --test-reporter-destination=lcov.info --test-reporter=junit --test-reporter-destination=junit.xml --test-reporter=@reporters/github --test-reporter-destination=stdout\" turbo test:unit", "cloudflare:preview": "turbo run cloudflare:preview", "cloudflare:deploy": "turbo run cloudflare:deploy" From 5af6ebefa2af0a0ad83e8e4ac088b380a1f1fe15 Mon Sep 17 00:00:00 2001 From: Aviv Keller Date: Mon, 26 May 2025 18:16:54 -0400 Subject: [PATCH 7/9] use more lenient matching Signed-off-by: Aviv Keller --- .github/scripts/report-inactive-collaborators.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/scripts/report-inactive-collaborators.mjs b/.github/scripts/report-inactive-collaborators.mjs index 1921a4c237ba4..18ccfb77f14a1 100644 --- a/.github/scripts/report-inactive-collaborators.mjs +++ b/.github/scripts/report-inactive-collaborators.mjs @@ -21,7 +21,7 @@ async function parseCollaborators() { const lines = content.split('\n'); const collaborators = []; - const startIndex = lines.indexOf(CONFIG.CURRENT_MEMBERS_HEADER) + 1; + const startIndex = lines.findIndex(l => l.startsWith(CONFIG.CURRENT_MEMBERS_HEADER)) + 1; if (startIndex <= 0) return collaborators; for (let i = startIndex; i < lines.length; i++) { From babb4d4e807530d2e117ba392190bf670008d854 Mon Sep 17 00:00:00 2001 From: avivkeller Date: Tue, 27 May 2025 09:15:39 -0400 Subject: [PATCH 8/9] fixup! --- .github/scripts/report-inactive-collaborators.mjs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/scripts/report-inactive-collaborators.mjs b/.github/scripts/report-inactive-collaborators.mjs index 18ccfb77f14a1..658a1cd159fa9 100644 --- a/.github/scripts/report-inactive-collaborators.mjs +++ b/.github/scripts/report-inactive-collaborators.mjs @@ -21,7 +21,8 @@ async function parseCollaborators() { const lines = content.split('\n'); const collaborators = []; - const startIndex = lines.findIndex(l => l.startsWith(CONFIG.CURRENT_MEMBERS_HEADER)) + 1; + const startIndex = + lines.findIndex(l => l.startsWith(CONFIG.CURRENT_MEMBERS_HEADER)) + 1; if (startIndex <= 0) return collaborators; for (let i = startIndex; i < lines.length; i++) { @@ -105,7 +106,6 @@ async function createOrUpdateIssue(github, context, report) { } export default async function (github, context) { - const repo = `${context.repo.owner}/${context.repo.repo}`; const cutoffDate = getDateMonthsAgo(); const collaborators = await parseCollaborators(); From bf73db8e3f5b59b40174bf41302a2fe49f181aea Mon Sep 17 00:00:00 2001 From: avivkeller Date: Wed, 28 May 2025 12:55:04 -0400 Subject: [PATCH 9/9] code review --- .../scripts/report-inactive-collaborators.mjs | 2 +- .../workflows/find-inactive-collaborators.yml | 5 +--- GOVERNANCE.md | 24 +++++++++---------- 3 files changed, 14 insertions(+), 17 deletions(-) diff --git a/.github/scripts/report-inactive-collaborators.mjs b/.github/scripts/report-inactive-collaborators.mjs index 658a1cd159fa9..5f441474e2ce3 100644 --- a/.github/scripts/report-inactive-collaborators.mjs +++ b/.github/scripts/report-inactive-collaborators.mjs @@ -72,7 +72,7 @@ ${inactiveMembers.map(m => `| @${m} |`).join('\n')} ## What happens next? -Team maintainers should review this list and contact inactive collaborators to confirm their continued interest in participating in the project.`; +@nodejs/nodejs-website should review this list and contact inactive collaborators to confirm their continued interest in participating in the project.`; } async function createOrUpdateIssue(github, context, report) { diff --git a/.github/workflows/find-inactive-collaborators.yml b/.github/workflows/find-inactive-collaborators.yml index 5683ac4eff8e8..70d9ae441c90e 100644 --- a/.github/workflows/find-inactive-collaborators.yml +++ b/.github/workflows/find-inactive-collaborators.yml @@ -7,9 +7,6 @@ on: workflow_dispatch: -env: - NODE_VERSION: lts/* - permissions: {} jobs: @@ -31,4 +28,4 @@ jobs: with: script: | const report = await import("${{github.workspace}}/.github/scripts/report-inactive-collaborators.mjs"); - report(github, exec) + report(github, exec); diff --git a/GOVERNANCE.md b/GOVERNANCE.md index e49a66cc96a4a..247bb3eeb7bc7 100644 --- a/GOVERNANCE.md +++ b/GOVERNANCE.md @@ -15,39 +15,39 @@ The Node.js Website Team is responsible for the day-to-day technical development The maintainers on the Node.js Website Team are responsible for steering the technical direction of the Node.js Website, and reserve the right to make final decisions on any issues or pull requests, in line with the Contribution Guidelines, Collaborator Guidelines, the Code of Conduct and the overall Governance premises of the Node.js project. -Members of this team are nominated through the guidelines provided in the Collaborator Guidelines within this repository. After a passed nominations, members should submit a PR to add themselves to the list of current members, shown below. +Members of this team are nominated through the guidelines provided in the [Contributing Guidelines](https://github.com/nodejs/nodejs.org/blob/main/CONTRIBUTING.md#becoming-a-collaborator) within this repository. After a passed nomination, members should submit a PR to add themselves to the list of current members, shown below. #### Current Members -- [araujogui](https://github.com/araujogui) - **Guilherme Araújo** +- [araujogui](https://github.com/araujogui) - **Guilherme Araújo** (he/him) -- [AugustinMauroy](https://github.com/AugustinMauroy) - **Augustin Mauroy** +- [AugustinMauroy](https://github.com/AugustinMauroy) - **Augustin Mauroy** (he/him) -- [avivkeller](https://github.com/avivkeller) - **Aviv Keller** +- [avivkeller](https://github.com/avivkeller) - **Aviv Keller** (he/him) - [aymen94](https://github.com/aymen94) - **Aymen Naghmouchi** -- [benhalverson](https://github.com/benhalverson) - **Ben Halverson** +- [benhalverson](https://github.com/benhalverson) - **Ben Halverson** (he/him) - [bjohansebas](https://github.com/bjohansebas) - **Sebastian Beltran** -- [bmuenzenmeyer](https://github.com/bmuenzenmeyer) - **Brian Muenzenmeyer** +- [bmuenzenmeyer](https://github.com/bmuenzenmeyer) - **Brian Muenzenmeyer** (he/him) -- [bnb](https://github.com/bnb) - **Tierney Cyren** +- [bnb](https://github.com/bnb) - **Tierney Cyren** (they/them) - [canerakdas](https://github.com/canerakdas) - **Caner Akdas** - [dario-piotrowicz](https://github.com/dario-piotrowicz) - **Dario Piotrowicz** -- [Harkunwar](https://github.com/Harkunwar) - **Harkunwar Kochar** +- [Harkunwar](https://github.com/Harkunwar) - **Harkunwar Kochar** (he/him) -- [HinataKah0](https://github.com/HinataKah0) - **HinataKah0** +- [HinataKah0](https://github.com/HinataKah0) - **HinataKah0** (he/him) -- [manishprivet](https://github.com/manishprivet) - **Manish Kumar** +- [manishprivet](https://github.com/manishprivet) - **Manish Kumar** (he/him) -- [mikeesto](https://github.com/mikeesto) - **Michael Esteban** +- [mikeesto](https://github.com/mikeesto) - **Michael Esteban** (he/him) -- [ovflowd](https://github.com/ovflowd) - **Claudio Wunder** +- [ovflowd](https://github.com/ovflowd) - **Claudio Wunder** (they/them) - [rodion-arr](https://github.com/rodion-arr) - **Rodion Abdurakhimov**