Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .github/workflows/find-inactive-collaborators.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,14 @@ jobs:

- name: Find inactive collaborators
run: tools/find-inactive-collaborators.mjs ${{ env.NUM_COMMITS }}

- name: Open pull request
uses: gr2m/create-or-update-pull-request-action@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
author: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
body: This PR was generated by tools/find-inactive-collaborators.yml.
commit-message: "meta: move one or more collaborators to emeritus"
labels: meta
title: "meta: move one or more collaborators to emeritus"
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,8 @@ For information on reporting security vulnerabilities in Node.js, see
For information about the governance of the Node.js project, see
[GOVERNANCE.md](./GOVERNANCE.md).

<!-- node-core-utils depends on the format of the TSC list. If the
format changes, those utilities need to be tested and updated. -->
### TSC (Technical Steering Committee)

<!--lint disable prohibited-strings-->
Expand Down Expand Up @@ -249,6 +251,9 @@ For information about the governance of the Node.js project, see

</details>

<!-- node-core-utils and find-inactive-collaborators.mjs depend on the format
of the collaborator list. If the format changes, those utilities need to be
tested and updated. -->
### Collaborators

* [addaleax](https://github.com/addaleax) -
Expand Down Expand Up @@ -462,6 +467,8 @@ For information about the governance of the Node.js project, see

<summary>Emeriti</summary>

<!-- find-inactive-collaborators.mjs depends on the format of the emeriti list.
If the format changes, those utilities need to be tested and updated. -->
### Collaborator emeriti

* [andrasq](https://github.com/andrasq) -
Expand Down
87 changes: 85 additions & 2 deletions tools/find-inactive-collaborators.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,85 @@ async function getCollaboratorsFromReadme() {
return returnedArray;
}

async function moveCollaboratorToEmeritus(peopleToMove) {
const readmeText = readline.createInterface({
input: fs.createReadStream(new URL('../README.md', import.meta.url)),
crlfDelay: Infinity,
});
let fileContents = '';
let inCollaboratorsSection = false;
let inCollaboratorEmeritusSection = false;
let collaboratorFirstLine = '';
const textToMove = [];
for await (const line of readmeText) {
// If we've been processing collaborator emeriti and we reach the end of
// the list, print out the remaining entries to be moved because they come
// alphabetically after the last item.
if (inCollaboratorEmeritusSection && line === '' &&
fileContents.endsWith('&gt;\n')) {
while (textToMove.length) {
fileContents += textToMove.pop();
}
}

// If we've found the collaborator heading already, stop processing at the
// next heading.
if (line.startsWith('#')) {
inCollaboratorsSection = false;
inCollaboratorEmeritusSection = false;
}

const isCollaborator = inCollaboratorsSection && line.length;
const isCollaboratorEmeritus = inCollaboratorEmeritusSection && line.length;

if (line === '### Collaborators') {
inCollaboratorsSection = true;
}
if (line === '### Collaborator emeriti') {
inCollaboratorEmeritusSection = true;
}

if (isCollaborator) {
if (line.startsWith('* ')) {
collaboratorFirstLine = line;
} else if (line.startsWith('**')) {
const [, name, email] = /^\*\*([^*]+)\*\* &lt;(.+)&gt;/.exec(line);
if (peopleToMove.some((entry) => {
return entry.name === name && entry.email === email;
})) {
textToMove.push(`${collaboratorFirstLine}\n${line}\n`);
} else {
fileContents += `${collaboratorFirstLine}\n${line}\n`;
}
} else {
fileContents += `${line}\n`;
}
}

if (isCollaboratorEmeritus) {
if (line.startsWith('* ')) {
collaboratorFirstLine = line;
} else if (line.startsWith('**')) {
const currentLine = `${collaboratorFirstLine}\n${line}\n`;
// If textToMove is empty, this still works because when undefined is
// used in a comparison with <, the result is always false.
while (textToMove[0] < currentLine) {
fileContents += textToMove.shift();
}
fileContents += currentLine;
} else {
fileContents += `${line}\n`;
}
}

if (!isCollaborator && !isCollaboratorEmeritus) {
fileContents += `${line}\n`;
}
}

return fileContents;
}

// Get list of current collaborators from README.md.
const collaborators = await getCollaboratorsFromReadme();

Expand All @@ -113,9 +192,13 @@ const inactive = collaborators.filter((collaborator) =>
!authors.has(collaborator.mailmap) &&
!landers.has(collaborator.mailmap) &&
!approvingReviewers.has(collaborator.name)
).map((collaborator) => collaborator.name);
);

if (inactive.length) {
console.log('\nInactive collaborators:\n');
console.log(inactive.map((name) => `* ${name}`).join('\n'));
console.log(inactive.map((entry) => `* ${entry.name}`).join('\n'));
console.log('\nGenerating new README.md file...');
const newReadmeText = await moveCollaboratorToEmeritus(inactive);
fs.writeFileSync(new URL('../README.md', import.meta.url), newReadmeText);
console.log('Updated README.md generated. Please commit these changes.');
}