Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: modify workflow updateContributor to be trigger by a repository dispatch event and extract contrbuton of all public repo of frontendmu #248

Open
wants to merge 3 commits into
base: main
Choose a base branch
from

Conversation

derecklhw
Copy link
Contributor

@derecklhw derecklhw commented Oct 18, 2024

Summary by CodeRabbit

  • New Features

    • Added a new event trigger for the contributor update workflow, allowing it to be executed by a custom event.
    • Introduced a configuration file to specify included repositories and excluded contributors.
    • Enhanced contributor data fetching to aggregate contributions from all public repositories of the specified user.
  • Bug Fixes

    • Improved logic to exclude specified contributors while fetching and updating contributor data.
  • Documentation

    • Updated console logging for better clarity during the contributor update process.

Copy link
Contributor

coderabbitai bot commented Oct 18, 2024

Walkthrough

The pull request introduces modifications to three main files: a GitHub Actions workflow, a JavaScript script for updating contributors, and a new configuration file. The workflow file now includes an additional event trigger, repository_dispatch, allowing it to be activated by a custom event. The JavaScript function has been enhanced to fetch contributor data from all public repositories of a specified user, aggregating contributions while excluding certain repositories and contributors. A new configuration file specifies which repositories to include and which contributors to exclude.

Changes

File Path Change Summary
.github/workflows/update-contributors.yml Added repository_dispatch event trigger with type trigger-workflow.
packages/frontendmu-data/scripts/update-contributors.js Modified updateContributors function to fetch contributors from all public repositories instead of one. Updated logic to aggregate contributions and removed some console logging. Added async function loadConfig() and const configSource.
packages/frontendmu-data/scripts/update-contributors.config.json Introduced new configuration file defining includedRepositories and excludedContributors.

Poem

In the garden of code, we hop and we play,
New triggers and scripts brighten the day.
From all public fields, contributors we find,
A map of their efforts, all lovingly aligned.
With each little change, our project will grow,
Hooray for the updates, let the good times flow! 🐇✨


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Outside diff range and nitpick comments (4)
packages/frontendmu-data/scripts/update-contributors.js (4)

43-44: Externalize excluded contributors and repositories

The excludedContributors and excludedRepositories arrays are hardcoded within the script. For better maintainability and flexibility, consider moving them to a configuration file or allowing them to be passed as parameters.


85-85: Remove unnecessary console log statement

The console.log(contributorsData) statement seems to be for debugging purposes. If it's not needed in production, consider removing it to keep the console output clean.


89-90: Use consistent Git user configuration

The Git user configuration is set to "GitHub Action" and "action@github.com". If this script is run locally or in different environments, the commit attribution may not be accurate.

Consider parameterizing the Git user configuration or setting it to values that reflect the actual user or environment.

// Use environment variables or default to these values
const gitUserName = process.env.GIT_USER_NAME || "GitHub Action";
const gitUserEmail = process.env.GIT_USER_EMAIL || "action@github.com";

execSync(`git config user.name "${gitUserName}"`);
execSync(`git config user.email "${gitUserEmail}"`);

Line range hint 109-113: Ensure deep equality when comparing contributors

Using JSON.stringify for comparison may not account for differences in the order of properties or array elements. This could lead to unnecessary commits even when there are no actual changes.

Consider sorting the contributors before comparing or using a deep equality check:

function sortContributors(contributors) {
  return contributors.sort((a, b) => a.username.localeCompare(b.username));
}

const existingContributors = sortContributors(getExistingContributors());
const newContributors = sortContributors(updatedContributors);

if (JSON.stringify(newContributors) !== JSON.stringify(existingContributors)) {
  // Proceed to update and commit
}

Alternatively, use a deep equality library like lodash.isEqual for comparison.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 26365b9 and c8853d5.

📒 Files selected for processing (2)
  • .github/workflows/update-contributors.yml (1 hunks)
  • packages/frontendmu-data/scripts/update-contributors.js (1 hunks)
🧰 Additional context used
🔇 Additional comments (2)
.github/workflows/update-contributors.yml (1)

7-8: Approve new trigger, but suggest improvements

The addition of the repository_dispatch event trigger is a good improvement that adds flexibility to the workflow. However, there are a few points to consider:

  1. The current if condition github.event.pull_request.merged == true in the job might prevent the workflow from running on repository_dispatch events. Consider modifying it to accommodate both triggers:
if: (github.event_name == 'pull_request_target' && github.event.pull_request.merged == true) || github.event_name == 'repository_dispatch'
  1. You might want to consider passing additional data with the repository_dispatch event. This can be done by adding a client_payload to the event and accessing it in the workflow.

  2. The PR title mentions modifying the workflow to extract contributions from all public repos, but this change is not reflected in the workflow file. Ensure that the necessary changes to implement this functionality are included in the PR.

To verify the impact of the new trigger, you can run the following command:

This will help identify any additional files that might need to be updated to support the new functionality mentioned in the PR title.

✅ Verification successful

Approve new trigger with verification confirmed

The addition of the repository_dispatch event trigger is a beneficial enhancement that allows the workflow to be initiated both manually and programmatically without disrupting the existing pull_request_target trigger.

Upon reviewing the associated update-contributors.js scripts:

  • Compatibility: The scripts are designed to update the contributors.json file and handle commits independently of how they are triggered. Therefore, they are compatible with the new repository_dispatch event.

  • No Additional Changes Needed: Since the scripts do not rely on specific event data and are invoked by the workflow, no modifications to the scripts are necessary to support the new trigger.

Recommendations:

  1. Modify the if Condition: Ensure the workflow's if condition accommodates both the existing and new triggers. For example:

    if: (github.event_name == 'pull_request_target' && github.event.pull_request.merged == true) || github.event_name == 'repository_dispatch'
  2. Documentation: Update any relevant documentation to reflect the addition of the repository_dispatch trigger, ensuring team members are aware of the new capability.

  3. Validate Workflow Execution: After merging, perform a test dispatch to confirm that the workflow executes as expected with the new trigger.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check if there are any other files that might need updating for the new functionality

# Search for files that might be related to updating contributors
rg -l 'update.*contributors' --type js --type ts

Length of output: 166


Script:

#!/bin/bash
# Description: Display the contents of the identified update-contributors.js files for review

cat packages/frontendmu-astro/automation/update-contributors.js
cat packages/frontendmu-data/scripts/update-contributors.js

Length of output: 6690

packages/frontendmu-data/scripts/update-contributors.js (1)

4-4: Updated the 'owner' variable to 'frontendmu'

The 'owner' variable has been correctly updated to "frontendmu", ensuring that the script fetches repositories under the correct GitHub user.

Comment on lines +12 to +14
const allPublicRepositoriesList = await fetch(
`https://api.github.com/users/${owner}/repos`
).then((response) => response.json());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Handle potential pagination when fetching repositories

The GitHub API returns up to 30 repositories per request by default. If the user has more than 30 repositories, some repositories may be missed. Consider handling pagination or increasing the per_page parameter to ensure all repositories are fetched.

You can modify the fetch URL to include ?per_page=100:

- `https://api.github.com/users/${owner}/repos`
+ `https://api.github.com/users/${owner}/repos?per_page=100`

Or implement pagination as follows:

let allPublicRepositoriesList = [];
let page = 1;
let per_page = 100;
let hasMore = true;

while (hasMore) {
  const response = await fetch(
    `https://api.github.com/users/${owner}/repos?page=${page}&per_page=${per_page}`
  );
  const repos = await response.json();
  if (repos.length > 0) {
    allPublicRepositoriesList = allPublicRepositoriesList.concat(repos);
    page++;
  } else {
    hasMore = false;
  }
}

Comment on lines +16 to +17
const allPublicRepositories = allPublicRepositoriesList.map(
(repo) => repo.name
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add error handling for fetching repositories

There's no check to verify if the repository fetch request was successful. If the API call fails or returns an error, the script may throw an exception when trying to process the response.

Consider adding error handling:

const response = await fetch(
  `https://api.github.com/users/${owner}/repos`
);
if (!response.ok) {
  throw new Error(`Error fetching repositories: ${response.status} ${response.statusText}`);
}
const allPublicRepositoriesList = await response.json();

Comment on lines 46 to 78
for (const repo of allPublicRepositories) {
if (excludedRepositories.includes(repo)) {
continue;
}
const contributorsList = await fetch(
`https://api.github.com/repos/${owner}/${repo}/contributors`
).then((response) => response.json());

// const contributorsListFiltered = contributorsList
// .map((contributor) => {
// return {
// username: contributor.login,
// contributions: contributor.contributions,
// };
// })
// .filter((contributor) => {
// return !excludedContributors.includes(contributor.username);
// });
// contributors.push(...contributorsListFiltered);
// console.log(`Contributors for ${repo}:`, contributorsListFiltered);
// }
// const updatedContributors = [...new Set(contributors)];

contributorsList.forEach((contributor) => {
if (!excludedContributors.includes(contributor.login)) {
if (contributorsMap[contributor.login]) {
contributorsMap[contributor.login] += contributor.contributions;
} else {
contributorsMap[contributor.login] = contributor.contributions;
}
}
});
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Handle API rate limiting and errors when fetching contributors

Fetching contributors for each repository in a loop may lead to hitting GitHub's API rate limits, especially for unauthenticated requests. Additionally, there's no error handling for these fetch requests, which could cause the script to fail silently.

To handle this:

  1. Authenticate API Requests: Use a GitHub personal access token to increase the rate limit.

    const GITHUB_TOKEN = process.env.GITHUB_TOKEN; // Ensure this environment variable is set
  2. Add Error Handling: Check if the fetch request was successful before processing the response.

    const contributorsResponse = await fetch(
      `https://api.github.com/repos/${owner}/${repo}/contributors`,
      {
        headers: {
          'Authorization': `token ${GITHUB_TOKEN}`
        }
      }
    );
    
    if (!contributorsResponse.ok) {
      console.error(`Error fetching contributors for ${repo}: ${contributorsResponse.status} ${contributorsResponse.statusText}`);
      continue; // Skip to the next repository
    }
    
    const contributorsList = await contributorsResponse.json();
  3. Implement Rate Limiting Handling: Detect when the rate limit is reached and wait before retrying.

    if (contributorsResponse.status === 403 && contributorsResponse.headers.get('X-RateLimit-Remaining') === '0') {
      const resetTime = contributorsResponse.headers.get('X-RateLimit-Reset');
      const currentTime = Math.floor(Date.now() / 1000);
      const waitTime = resetTime - currentTime;
      console.log(`Rate limit exceeded. Waiting for ${waitTime} seconds.`);
      await new Promise(resolve => setTimeout(resolve, waitTime * 1000));
      // Retry the request after waiting
    }

Comment on lines +69 to +76
contributorsList.forEach((contributor) => {
if (!excludedContributors.includes(contributor.login)) {
if (contributorsMap[contributor.login]) {
contributorsMap[contributor.login] += contributor.contributions;
} else {
contributorsMap[contributor.login] = contributor.contributions;
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Refactor contributor aggregation logic using a Map

While the current logic works, using a Map can make the code more efficient and cleaner when accumulating contributions.

Here's how you can implement it:

const contributorsMap = new Map();

contributorsList.forEach((contributor) => {
  if (!excludedContributors.includes(contributor.login)) {
    const currentContributions = contributorsMap.get(contributor.login) || 0;
    contributorsMap.set(contributor.login, currentContributions + contributor.contributions);
  }
});

Then, when creating updatedContributors:

const updatedContributors = Array.from(contributorsMap, ([username, contributions]) => ({
  username,
  contributions
}));

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Outside diff range and nitpick comments (1)
packages/frontendmu-data/scripts/update-contributors.js (1)

12-15: Validate the config structure.

While loading the config is a good practice, there's no validation of its structure or required fields. Consider adding checks to ensure includedRepositories and excludedContributors exist and are arrays.

Example:

if (!Array.isArray(config.includedRepositories) || !Array.isArray(config.excludedContributors)) {
  throw new Error('Invalid config structure: includedRepositories and excludedContributors must be arrays');
}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 209f2ce and 69efa77.

📒 Files selected for processing (1)
  • packages/frontendmu-data/scripts/update-contributors.js (2 hunks)
🧰 Additional context used
🔇 Additional comments (4)
packages/frontendmu-data/scripts/update-contributors.js (4)

4-4: LGTM: Owner variable updated.

The change of the owner variable to "frontendmu" aligns with the PR objectives.


16-21: Previous comments about pagination and error handling are still applicable.

The changes align with the PR objectives, but the concerns raised in previous comments about pagination and error handling when fetching repositories are still valid and should be addressed.


50-58: LGTM: Improved logging and file writing.

The addition of console logging and the use of formatted JSON stringification for file writing are good improvements. They enhance debugging capabilities and improve the readability of the output file.


8-9: Add error handling for config file fetching.

While the configSource URL construction looks correct, there's no error handling if the config file doesn't exist or can't be fetched. Consider adding a try-catch block in the loadConfig function to handle potential errors.

Comment on lines +24 to +47
const contributorsMap = {};

for (const repo of allPublicRepositories) {
if (!includedRepositories.includes(repo)) {
continue;
}
const contributorsList = await fetch(
`https://api.github.com/repos/${owner}/${repo}/contributors`
).then((response) => response.json());

contributorsList.forEach((contributor) => {
if (!excludedContributors.includes(contributor.login)) {
if (contributorsMap[contributor.login]) {
contributorsMap[contributor.login] += contributor.contributions;
} else {
contributorsMap[contributor.login] = contributor.contributions;
}
}
});
}
const updatedContributors = Object.entries(contributorsMap).map(([username, contributions]) => ({
username,
contributions
}));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add error handling when fetching contributors.

While the logic for aggregating contributions has been improved, there's still no error handling when fetching contributors for each repository. Consider adding try-catch blocks or checking the response status to handle potential API errors or rate limiting issues.

Example:

const contributorsResponse = await fetch(`https://api.github.com/repos/${owner}/${repo}/contributors`);
if (!contributorsResponse.ok) {
  console.error(`Failed to fetch contributors for ${repo}: ${contributorsResponse.status}`);
  continue;
}
const contributorsList = await contributorsResponse.json();

Comment on lines +92 to +94
async function loadConfig() {
return await fetch(configSource).then((response) => response.json());
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Enhance the loadConfig function with error handling and timeout.

While the loadConfig function is concise, it lacks error handling and a timeout setting. Consider adding these to improve robustness:

async function loadConfig() {
  try {
    const response = await fetch(configSource, { timeout: 5000 });
    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }
    return await response.json();
  } catch (error) {
    console.error('Failed to load config:', error);
    throw error;
  }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant