Post

GHA Schedule Monthly Workflow

GitHub Actions Workflow for Identity and Access Management

GHA Schedule Monthly Workflow
Flow diagram of "Schedule Monthly" workflow

Schedule Monthly &
WR Schedule Monthly
Workflows

Summary

The schedule-monthly.yml and wr-schedule-monthly.yml workflows together are intended to monitor the individual activities of each member of the ‘website-write’ team. Members that have been inactive (as described below) for over two months are removed from the ‘website-write’ team. Members that have been inactive for over one month (and shy of two months) are notified that the bot will remove their team membership in the next month if the member does not resume activity. Specific details and functionalities are described below.

Trigger

  • Workflow runs at 11:00 UTC/ 4:00 PDT, on the 1st day of every month (except January and August)

Labels Used by Workflow

  • labelKey: ‘complexity2’, Complexity: Small
  • labelKey: ‘size025pt’,size: 0.25pt
  • labelKey: ‘readyForDevLead’,ready for dev lead
  • labelKey: ‘roleDevLeads’,role: dev leads
  • labelKey: ‘featureAdministrative’,Feature: Administrative

Tokens Used by Workflow

  • HACKFORLA_ADMIN_TOKEN ( used in schedule-monthly.yml )
  • HACKFORLA_BOT_PA_TOKEN ( used in wr-schedule-monthly.yml )

Workflow Files

Support File Folders:

Process:

  • The schedule-monthly.yml workflow is triggered on a cron schedule at 11:00 UTC / 4:00 am PDT on the first day of every month, except January and August. (January and August are omitted because the ‘website’ team takes off December and July. The time limits discussed below are adjusted to account for this expected inactivity during the break.) This workflow consists of the job “Trim_Contributors” with three main steps:
    • “Get Contributors” calls get-contributors-data.js:
      • The function fetchContributors() queries GitHub for data about all user contributions to the ‘hackforla/website’ repo:
        • If a user made any contribution due to 1. commits, 2. comments, or 3. issue assignments.
        • All user contributions within the last month (first run, allContributorsSinceOneMonthAgo) and within the last two months (second run, allContributorsSinceTwoMonthsAgo) are recorded.
        • Note that members of the ‘website-admin’ team as well as three Hack for LA bot accounts are considered ‘permanent contributors’ and are automatically included in these two lists regardless of contributions.
        • This function also records any open issue whose assignee does not show any activity within the last two months, and whether the issue is a “Skills Issue”- inactiveWithOpenIssue.
    • “Trim Inactive Members” calls trim-inactive-members.js:
      • The function readPreviousNotifyList() retrieves the list of ‘notified members’ from the previous month as previouslyNotified.
      • The function getTeamMembers() records all current ‘website-write’ team members as currentTeamMembers.
      • The function removeInactiveMembers() iterates through the list of current team members and checks whether the member is listed on the allContributorsSinceTwoMonthsAgo list. If the team member does not show any activity in the last two months, the function then checks:
        • Whether the team member is listed on the ‘website’ team; if not the person is added. (We want to make sure that the member is on the ‘website’ team before they are removed from ‘website-write’ team)
        • Whether the team member is on the list of inactiveWithOpenIssue and the issue is not a “Skills Issue” or “Pre-work Checklist”, if so their name and issue are added to cannotRemoveYet. (We want to review open assignments before the inactive member is removed)
        • Whether or not the team member was notified of their inactivity in the last month, i.e. whether their name is on the previouslyNotified list. If not, they will not be removed in the current month. (We want to give people a notification prior to removing them from the ‘website-write’ team)
        • Otherwise, the team member will be removed from the ‘website-write’ as well as the ‘website-merge’ teams if applicable.
        • If the member that was just removed has an open “Skills Issue” or “Pre-work Checklist”, this issue is closed by the bot via closePrework().
        • The lists of removedContributors and members we cannotRemoveYet are returned.
      • Next, the function getTeamMembers() runs a second time to update the list of updatedTeamMembers.
      • The function notifyInactiveMembers() iterates through the list of updated team members and checks whether the member is listed on the allContributorsSinceOneMonthAgo list. If the member does not show any activity within the last month, the function checks:
        • Whether the member cloned HfLA’s repo within last month. If so, then the member might be new and are still setting up, and they will not be notified of inactivity yet.
        • Otherwise, the member is added to the list of members to be notified.
        • The list of notifiedContributors is returned.
      • Using fs.writeFile(), the lists of removedContributors, notifiedContributors, and cannotRemoveYet are grouped as inactiveMemberLists then written to disk as inactive-members.json.
    • “Update Inactive Members JSON” uses stefanzweifel/git-auto-commit-action@v5.0.1 to commit the record of inactive-members.json to the repo to finish the “Schedule Monthly” workflow.
  • The “WR Schedule Monthly” post workflow file wr-schedule-monthly.yml runs if schedule-monthly.yml is successful. It includes:
    • The job “Create-New-Issue” and related Step call to create-new-issue.js:
      • The function createIssue() writes the lists of removed members and members to be notified to the template inactive-members.md and posts a new issue titled “Review Inactive Team Members” to the Project Board.
      • The function postComment() posts a comment to the Monday Dev Meeting Agenda issue #2607, informing that the workflow has run, linking to the issue that was created, and if applicable listing members with open issues (and issue numbers) via the post-issue-comment.js module.
    • Finally, the job “Close-New-Issue” gathers the pertinent data and then as a last step closes the just-generated “Review Inactive Team Members” issue.

Data auto-generated by Google Sheets worksheets (maintained in the “hackforla-bot@hackforla.org” account) complement this workflow:

  • On a daily trigger, a Google Apps Script queries the GitHub repo for the current ‘website-write’ team members and updates the team Roster with members’ statuses.
  • On a weekly trigger, an Google Apps Script compares the list of active team members with the list of Current Google Drive (Website) Registrants. If a member is not listed as active (or is not on a separate “Safe List”), they are removed from access to the Google Drive.

Test Procedure

Important note: line numbers and specific code references may have changed slightly since the time of this Wiki- verify with the actual files.

  • You will need to have a functioning test environment on your local repo. Refer to Hack for LA’s GitHub Actions, especially Tips 6, 7, & 8.
  • IMPORTANT: In addition to the ‘files changed’ in the PR, there are additional changes that you should make to help with testing. If you do not make these edits, you might delete or mis-edit Hack for LA data, delete team members, generate false or junk notifications, and/or create junk issues on HfLA’s live Project Board.
  • In schedule-monthly.yml, you will need to activate two personal tokens (see Hack for LA’s GitHub Actions)
    • HACKFORLA_BOT_PA_TOKEN scopes: admin:org_hook, public_repo
    • HACKFORLA_ADMIN_TOKEN scopes: admin:org_hook, repo, write:org
    • In the same file, around line 12, replace ‘hackforla’ with your personal repo.
    • Finally, add the line workflow_dispatch: under on: (in line with schedule:)
  • In get-contributors-data.js
    • Near line 84 replace with owner: 'hackforla',
    • Near line 85 replace with repo: 'website', (unless your repo is ‘website’)
  • In trim-inactive-members.js:
    • IMPORTANT: Disable the following by replacing:
      1
      2
      3
      4
      5
      6
      
      await github.request('DELETE /orgs/{org}/teams/{team_slug}/memberships/{username}', {
                org: context.repo.owner,
                team_slug: team,
                username: username,
              });
          }
      

      with:

      1
      2
      3
      4
      5
      6
      7
      
              console.log('Would be removed: ' + username);
              // await github.request('DELETE /orgs/{org}/teams/{team_slug}/memberships/{username}', {
              //   org: context.repo.owner,
              //   team_slug: team,
              //   username: username,
              // });
            }
      
    • IMPORTANT: Disable the entirety of the closePrework() function, starting around ln 110:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      
      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(`Would be closing "Skills Issue" 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.'
        // });
      }
      
  • In create-new-issue.js:
    • For const AGENDA_ISSUE_NUM = replace with an existing issue number in your repo
    • The variables for const owner and const repo are ok as is- do not replace.
    • IMPORTANT: replace the following lines exactly as shown:
      1
      2
      
      let removedList = removeList.map(x => "@ " + x).join("\n");    // important to add space
      let notifiedList = notifyList.map(x => "@ " + x).join("\n");   // important to add space
      
    • Towards the end of the files, comment out: // let milestone = parseInt(issueObject['milestone']);
    • And next: // milestone,
  • In /utils/get-team-members.js:
    • Ln 16, replace with org: 'hackforla',
  • In /utils/add-team-member.js:
    • Ln 11, replace with org: 'hackforla',
    • Ln 18, replace with org: 'hackforla',
  • In /utils/get-timeline.js:
    • Ln 16, replace with owner: 'hackforla',
    • Ln 17, replace with repo: 'website',
  • In /utils/post-issue-comment.js:
    • Ln 9, replace with owner: ' <your name> ', NOT ‘hackforla’
    • Ln 10, replace with repo: 'website', or however you named your repo

Testing Resources

This post is licensed under CC BY 4.0 by the author.