website icon indicating copy to clipboard operation
website copied to clipboard

Edits to `contributors-data.js` and `schedule-monthly.yml`

Open t-will-gillis opened this issue 1 year ago • 3 comments

Dependents

  • [ ] #6459
  • [ ] #6460
  • [ ] #6396

Associated Files

  • [ ] #6395

Overview

There are several edits that we can make to contributors-data-.js and schedule-monthly.yml so that these files are more compatible with our other workflows, and to add other checks and features to the inactive members workflow.

Action Items

For the first change, refactor from the use of the octokit object to using the github context generated by actions/github-script. In this way, this workflow can more readily make use of, and contribute to, the reusable utility functions in the github-actions/utils/ folder.

  • [ ] At the beginning of the file, replace the Octokit object:

    const fs = require("fs");
    const { Octokit } = require("@octokit/rest");
    
    // Extend Octokit with new contributor endpoints and construct instance of class with Auth token 
    Object.assign(Octokit.prototype);
    const octokit = new Octokit({ auth: process.env.token });
    
    // Set variables to avoid hard-coding
    const org = 'hackforla';
    const repo = 'website';
    const team = 'website-write';
    const baseTeam = 'website';
    const maintTeam = 'website-maintain';
    

    with:

    // Import modules
    const fs = require("fs");
    const getTimeline = require('../../utils/get-timeline');
    
    // Global variables
    var github;
    var context;
    
    // Set variables to avoid hard-coding
    const org = context.repo.owner;
    const repo = context.repo.repo;
    const baseTeam = 'website';
    const writeTeam = 'website-write';
    const mergeTeam = 'website-merge';
    // const adminTeam = 'website-admins';   <-- Not used currently
    const maintTeam = 'website-maintain';
    
  • [ ] At the main() function declaration, replace:

    /**
     * Main function, immediately invoked
     */
    (async function main(){ 
    

    with:

    /**
     * Main function, immediately invoked
     * @param {Object} g   - github object from actions/github-script
     * @param {Object} c   - context object from actions/github-script
     */
    async function main({ g, c }) {
      github = g;
      context = c;  
    
    
  • [ ] Replace all references to octokit with github

  • [ ] Replace team with writeTeam, around 7 locations.

  • [ ] Remove the function getEventTimeline(issueNum) in its entirety, and replace:

    const timeline = await getEventTimeline(issueNum);
    

    with:

    const timeline = await getTimeline(issueNum, github, context);
    
  • [ ] Within the function removeInactiveMembers()`, add functionality to confirm that a member to-be-removed received notification in the prior month, and remove member from 'website-merge' if needed, and finally close a removed member's "Pre-work Checklist" if open:

    // Remove member from the team if they don't pass additional checks in `shouldRemoveOrNotify` function
          if(await shouldRemoveOrNotify(username)){
            // But if member has an open issue, don't remove
            if(username in inactiveWithOpenIssue){
              cannotRemoveYet[username] = inactiveWithOpenIssue[username];
            } else {
              await octokit.request('DELETE /orgs/{org}/teams/{team_slug}/memberships/{username}', {
                org: org,
                team_slug: team,
                username: username,
              })
              removedMembers.push(username);
            }
          } 
        }
    

    with:

          // Check if member is new/ created HfLA repo clone in last month
          if(await checkNotNewMember(username)){
            // But if member has an open issue or was not on the previously notified list, do not remove yet
            if(username in inactiveWithOpenIssue && inactiveWithOpenIssue[username][1] === false){
              cannotRemoveYet[username] = inactiveWithOpenIssue[username][0];
            } else if((previouslyNotifed.length > 0) && !(previouslyNotified.includes(username))){
              console.log('Member was not on last month\'s \'Inactive Members\' list, do not remove: ' + username);
            } else {
              // Remove member from all teams (except baseTeam)
              const teams = [writeTeam, mergeTeam];
              for(const team of teams){
                await github.request('DELETE /orgs/{org}/teams/{team_slug}/memberships/{username}', {
                  org: org,
                  team_slug: team,
                  username: username,
                });
              }
              removedMembers.push(username);
              // After removal, close member's "Pre-work checklist" if open
              if(username in inactiveWithOpenIssue && inactiveWithOpenIssue[username][1] === true){
                closePrework(username, inactiveWithOpenIssue[username][0]);
              }
            }
          } 
        }
    
  • [ ] Add function to automatically close an inactive member's Pre-work:

      /**
     * Function to close a just-removed inactive member's "Pre-work checklist" if open and add a comment
     * @param {String} member        - name of member whose "Pre-work checklist" will be closed
     * @param {Number} issueNum      - number of member's "Pre-work checklist"
     */
    async function closePrework(member, issueNum){ 
      // Close the assignee's "Pre-work Checklist" and add comment
      await github.request('PATCH /repos/{owner}/{repo}/issues/{issue_number}', {
        owner: org,
        repo: repo,
        issue_number: issueNum,
        state: 'closed'
      });
      console.log('Closing "Pre-work Checklist" issue number ' + issueNum + ' for ' + member);
      // Add comment to issue
      await github.request('POST /repos/{owner}/{repo}/issues/{issue_number}/comments', {
        owner: org,
        repo: repo,
        issue_number: issueNum,
        body: 'The Hack for LA Bot has closed this issue due to member inactivity.'
      });
    
  • [ ] Remove the entirety of function shouldRemoveOrNotify() since we are not checking whether a member is a Maintainer when determining if the member is Inactive. Replace with a new check for whether a member has recently cloned the HfLA repo, since we don't want to removed these people:

      /**
     * Function to check that the member did not recently clone HfLA repo, otherwise they are new
     * @param {String} member - Member's username 
     * @returns {Boolean}     - true/false 
     */
    async function checkNotNewMember(member){
      // Check if member cloned the HfLA repo within the last 30 days. If so, they are new members and they
      // may not have had time yet to get set up. Check the user's 10 most recent repos and see if any match 
      // "description":"Hack for LA's Website": if so then, check if "created_at" is less than 30 days.
      try {
        const memberRepoResults = await github.request('GET /users/{username}/repos', {
          username: member,
          direction: 'asc',
          sort: 'created',
          per_page: 10,
        });
        for(const memberRepo of memberRepoResults.data){
          if(memberRepo.description === "Hack for LA's website" && memberRepo.created_at > oneMonthAgo){
            console.log("Member created organization repo within last month: " + member);
            return false;
          }
        }
      } catch {}
    
      // Else this member is not new and should be notified or removed from team as appropriate
      return true;
    }
    
  • [ ] Finally, add function that will read the json file for the previous month's data to confirm that this month's to remove members received a notification the prior month:

    
    /**
     * Function to find the previous month's "Review Inactive Team Members" issue and extract the raw notified members list
     * @params {}              - none
     * @returns {Array}        - list of notified members (f. prev. month) 
     */
     async function readPreviousNotifyList(){
    
       try {
         // Retrieve previous month's inactive member list 
         const filepath = 'github-actions/utils/_data/inactive-members.json';
         const rawData = fs.readFileSync(filepath, 'utf8');
         const parsedData = JSON.parse(rawData);
         const notifiedMembers = parsedData['notifiedContributors'];
    
         return notifiedMembers;  
    
         // If error, return empty array
       } catch (err) {
         throw new Error(err);
         return [];
       }
    } 
    
  • [ ] In the schedule-monthly.yml file we will be using actions/github-script@v7, thus we do not need the steps to setup Node or install the npm dependencies. Note that the standard HfLA token is replaced by a token with extra ADMIN privileges to allow the workflow to run correctly. Replace:

        # Setup node 
      - name: Setup node
        uses: actions/setup-node@v4
        with:
          node-version: 18
          cache: 'npm'
    
      # Install dependencies to run js file
      - name: Install npm dependencies
        run: npm install
        working-directory: ./github-actions/trigger-schedule/github-data
    
      # Run js file: checks contributor activity logs, removes two-month inactive members from 
      # 'website-write' team, then compiles list of one-month inactive members for notification
      - name: Trim Members
        env:
          token: ${{ secrets.HACKFORLA_BOT_PA_TOKEN }}
        run: node github-actions/trigger-schedule/github-data/contributors-data.js
    

    with:

        # Run js file: checks contributor activity logs, removes two-month inactive members from 
      # 'website-write' team, then compiles list of one-month inactive members for notification
      - name: Trim Members
        uses: actions/github-script@v7
        with:
          github-token: ${{ secrets.HACKFORLA_ADMIN_TOKEN }}
          script: |
            const script = require('./github-actions/trigger-schedule/github-data/contributors-data.js')
            script({ g: github, c: context })
    
  • [ ] Remove steps to save artifact, and instead save json data to repo:

       # Upload artifact file to allow list sharing with next job "Create_New_Issue"
      - name: Upload artifact
        uses: actions/upload-artifact@v4
        with:
          name: trim_job_artifact
          path: inactive-Members.json 
    
    Create_New_Issue:
      needs: Trim_Contributors
      runs-on: ubuntu-latest     
      steps:
      - uses: actions/checkout@v4
    
      # Download artifact file from "Trim_Contributors"
      - name: Download artifact
        id: download-artifact
        uses: actions/download-artifact@v4
        with:
          name: trim_job_artifact
    
      # Extract and save artifact in usable form for next steps
      - name: Extract artifact
        id: extract-artifact
        run: |
          jq -c . inactive-Members.json > out-inactive-Members.json
          echo "TRIM_LISTS=$(cat out-inactive-Members.json)" >> $GITHUB_ENV
    

    with:

        # Commits list of inactive & removed members to repo for immediate and next month's use
      - name: Update Inactive Members JSON
        uses: stefanzweifel/[email protected]
        with:
         # Glob pattern of file which should be added to the commit
          file_pattern: github-actions/utils/_data/inactive-members.json
    
          # Optional commit message and author settings
          commit_message: Update Inactive Members JSON
          commit_author: GitHub Actions Bot <[email protected]> 
    
    Create_New_Issue:
      needs: Trim_Contributors
      runs-on: ubuntu-latest     
      steps:
      - uses: actions/checkout@v4
    

Resources/Instructions

Related issues:

schedule-monthly.yml contributors-data.js create-new-issue.js inactive-members.md

t-will-gillis avatar Jan 29 '24 06:01 t-will-gillis

This issue has exploded in complexity. I split out a couple smaller issues (#6395, #6396, #6459, #6460) to help bring the scope down.

  • #6395
  • #6396
  • #6459
  • #6460

t-will-gillis avatar Feb 29 '24 06:02 t-will-gillis

Hi @t-will-gillis, thank you for taking up this issue! Hfla appreciates you :)

Do let fellow developers know about your:- i. Availability: (When are you available to work on the issue/answer questions other programmers might have about your issue?) ii. ETA: (When do you expect this issue to be completed?)

You're awesome!

P.S. - You may not take up another issue until this issue gets merged (or closed). Thanks again :)

github-actions[bot] avatar Mar 13 '24 16:03 github-actions[bot]

Workflow ready to be implemented.

t-will-gillis avatar Mar 13 '24 22:03 t-will-gillis

@t-will-gillis Under resources, it says

Related issues:

schedule-monthly.yml contributors-data.js create-new-issue.js inactive-members.md

Is this the epic, or is there an epic that this all connects to?

ExperimentsInHonesty avatar Mar 15 '24 07:03 ExperimentsInHonesty

@ExperimentsInHonesty Under Resources, I removed the references to the other files and left the two that are being edited. There is not an epic that this connects to so this issue could be the epic, or I could create a separate epic if there should be one. However you think is best-

My mindset was that originally this issue would address everything, but then I split off the other issues since they can be edited mostly independently from this one, and this one was already very large by itself. I was planning to release a PR for this issue once the other four are done.

t-will-gillis avatar Mar 15 '24 20:03 t-will-gillis

@ExperimentsInHonesty Also, if it makes a difference, I am planning to submit the PR for this one assuming you approve of the changes.

t-will-gillis avatar Mar 15 '24 21:03 t-will-gillis

Hi @ExperimentsInHonesty I added the ready for product label in case you want to review this and/or edit anything. If the overall objectives look okay to you, I will post a PR.

t-will-gillis avatar Mar 21 '24 20:03 t-will-gillis

@t-will-gillis I don't think this matches the action items. Let's discuss at the merge team meeting

The current contributors-data.js has become unwieldly and much of its functionality can be modularized. There are also several additional edits that we can make so that these files are more compatible and exchangeable with other workflows, and there are other checks and features that should be added to the inactive members workflow as detailed following.

ExperimentsInHonesty avatar Mar 25 '24 21:03 ExperimentsInHonesty