From b2d9079347879b10285aa5e33077680d6ec751d5 Mon Sep 17 00:00:00 2001 From: Will Gillis <40799239+t-will-gillis@users.noreply.github.com> Date: Mon, 21 Jul 2025 19:58:51 -0700 Subject: [PATCH 01/25] Create member-activity-trigger.yml initial commit of yml --- .github/workflows/member-activity-trigger.yml | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 .github/workflows/member-activity-trigger.yml diff --git a/.github/workflows/member-activity-trigger.yml b/.github/workflows/member-activity-trigger.yml new file mode 100644 index 0000000000..476067436c --- /dev/null +++ b/.github/workflows/member-activity-trigger.yml @@ -0,0 +1,40 @@ +name: Member Activity Trigger + +on: + workflow_call: + issues: + types: [opened, assigned, unassigned, closed] + issue_comment: + types: [created] + pull_request: + types: [opened, closed] + pull_request_review: + types: [submitted] + +jobs: + Gather-Activity-Event-Information: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Gather Event Details + id: gather-event-details + uses: actions/github-script@v7 + with: + github-token: ${{ secrets.HACKFORLA_GRAPHQL_TOKEN }} + script: | + const script = require('./github-actions/activity-trigger/activity-trigger.js') + const activity = script({g: github, c: context}) + return activity + + - if: ${{ steps.gather-event-details.outputs.result != '[]' }} + name: Post to Skills Issue + id: post-to-skills-issue + uses: actions/github-script@v7 + with: + github-token: ${{ secrets.HACKFORLA_GRAPHQL_TOKEN }} + script: | + const activity = ${{ steps.gather-event-details.outputs.result }} + const script = require('./github-actions/activity-trigger/post-to-skills-issue.js') + script({g: github, c: context}, activity) + From d108995bbc19bb8fb0034412908507d5a47a29a5 Mon Sep 17 00:00:00 2001 From: Will Gillis <40799239+t-will-gillis@users.noreply.github.com> Date: Mon, 21 Jul 2025 19:59:51 -0700 Subject: [PATCH 02/25] Rename member-activity-trigger.yml to activity-trigger.yml --- .../{member-activity-trigger.yml => activity-trigger.yml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .github/workflows/{member-activity-trigger.yml => activity-trigger.yml} (100%) diff --git a/.github/workflows/member-activity-trigger.yml b/.github/workflows/activity-trigger.yml similarity index 100% rename from .github/workflows/member-activity-trigger.yml rename to .github/workflows/activity-trigger.yml From 9f912c6ac42b895084c30152d11cde7c002e238d Mon Sep 17 00:00:00 2001 From: Will Gillis <40799239+t-will-gillis@users.noreply.github.com> Date: Mon, 21 Jul 2025 20:13:43 -0700 Subject: [PATCH 03/25] Create activity-trigger.js --- .../activity-trigger/activity-trigger.js | 123 ++++++++++++++++++ 1 file changed, 123 insertions(+) create mode 100644 github-actions/activity-trigger/activity-trigger.js diff --git a/github-actions/activity-trigger/activity-trigger.js b/github-actions/activity-trigger/activity-trigger.js new file mode 100644 index 0000000000..6a67cd2dc0 --- /dev/null +++ b/github-actions/activity-trigger/activity-trigger.js @@ -0,0 +1,123 @@ +/** + * This function is triggered by member activities, which include: + * the eventName (i.e. "issues", "pull_request", "pull_request_review", etc. ), + * the eventAction (i.e. "opened", "assigned", "submitted", etc.), and + * the eventActor (user who is credited for the event). + * @param {Object} github - GitHub object from function calling activity-trigger.js + * @param {Object} context - Context of the function calling activity-trigger.js + * @returns {Object} - An object containing the eventActor and a message + */ +async function activityTrigger({g, c}) { + + github = g; + context = c; + + let issueNum = ''; + let assignee = ''; + let timeline = ''; + + let eventName = context.eventName; + let eventAction = context.payload.action; + let eventActor = context.actor; + let activity = []; + + const excludedActors = ['HackforLABot', 'elizabethhonest']; + + if (eventName === 'issues') { + issueNum = context.payload.issue.number; + eventUrl = context.payload.issue.html_url; + timeline = context.payload.issue.updated_at; + // If issue action is not opened and an assignee exists, then change + // the eventActor to the issue assignee, else retain issue author + assignee = context.payload.assignee?.login; + if (eventAction != 'opened' && assignee != null ) { + console.log(`Issue is ${eventAction}. Change eventActor => ${assignee}`); + eventActor = assignee; + } else { + eventActor = context.payload.issue.user.login; + } + if (eventAction === 'closed') { + let reason = context.payload.issue.state_reason; + eventAction = reason; + } + } else if (eventName === 'issue_comment') { + issueNum = context.payload.issue.number; + eventUrl = context.payload.comment.html_url; + timeline = context.payload.comment.updated_at; + } else if (eventName === 'pull_request') { + issueNum = context.payload.pull_request.number; + eventUrl = context.payload.pull_request.html_url; + timeline = context.payload.pull_request.updated_at; + // If PR closed, check if merged and change eventActor to the original pr author + if (eventAction === 'closed') { + eventAction = context.payload.pull_request.merged ? 'merged' : 'closed'; + eventActor = context.payload.pull_request.user.login; + } + } else if (eventName === 'pull_request_review') { + issueNum = context.payload.pull_request.number; + eventUrl = context.payload.review.html_url; + timeline = context.payload.review.updated_at; + } + + // Following are for confirmation, can be removed + console.log(`eventName = ${eventName}`); + console.log(`eventAction = ${eventAction}`); + console.log(`eventActor = ${eventActor}`); + console.log(`issueNum = ${issueNum}`); + console.log(`eventUrl = ${eventUrl}`); + console.log(`eventTime = ${timeline}`); + + // Return immediately if the issueNum is a Skills Issue- to discourage + // infinite loop (recording comment, recording the recording of comment, etc.) + const isSkillsIssue = await checkIfSkillsIssue(issueNum); + if (isSkillsIssue) { + console.log(`issueNum: ${issueNum} identified as Skills Issue`); + return activity; + } + // Return immediately if the eventActor is a bot- same reason + if (eventActor in excludedActors) { + return activity; + } + + // Message templates to post on Skills Issue + const actionMap = { + 'issues.opened': 'opened an issue', + 'issues.completed': 'closed an issue as completed', + 'issues.not_planned': 'closed an issue as not planned', + 'issues.duplicate': 'closed an issue as duplicate', + 'issues.assigned': 'been assigned to an issue', + 'issues.unassigned': 'been unassigned from an issue', + 'issue_comment.created': 'commented on an issue or pr', + 'pull_request.opened': 'opened a pull request', + 'pull_request.closed': 'had a pull request closed w/o merging', + 'pull_request.merged': 'had a pull request merged', + 'pull_request_review.submitted': 'submitted a pull request review' + }; + const action = actionMap[`${eventName}.${eventAction}`]; + let message = `@ ${eventActor} has ${action}: #[${issueNum}](${eventUrl}) at ${timeline}`; + console.log(message); + + activity = [eventActor, message]; + return activity; + + + + /** + * Helper function to check if issueNum references a Skills Issue + * @param {Number} issueNum - issue number to check + * @returns {Boolean} - true if Skills Issue, false if not + */ + async function checkIfSkillsIssue(issueNum) { + // https://docs.github.com/en/rest/issues/labels?apiVersion=2022-11-28#list-labels-for-an-issue + const labelData = await github.request('GET /repos/{owner}/{repo}/issues/{issue_number}/labels', { + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: issueNum + }); + const isSkillsIssue = labelData.data.some(label => label.name === "Complexity: Prework"); + + return isSkillsIssue; + } +} + +module.exports = activityTrigger; From 9557aef8565a3206ebf296faa64de6534a64deee Mon Sep 17 00:00:00 2001 From: Will Gillis <40799239+t-will-gillis@users.noreply.github.com> Date: Mon, 21 Jul 2025 20:22:37 -0700 Subject: [PATCH 04/25] Create post-to-skills-issue.js --- .../activity-trigger/post-to-skills-issue.js | 85 +++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 github-actions/activity-trigger/post-to-skills-issue.js diff --git a/github-actions/activity-trigger/post-to-skills-issue.js b/github-actions/activity-trigger/post-to-skills-issue.js new file mode 100644 index 0000000000..fb391da410 --- /dev/null +++ b/github-actions/activity-trigger/post-to-skills-issue.js @@ -0,0 +1,85 @@ +// Import modules +const retrieveSkillsIssue = require('../utils/retrieve-skills-issue'); +const postComment = require('../utils/post-issue-comment'); +const checkTeamMembership = require('../utils/check-team-membership'); +const statusFieldIds = require('../utils/_data/status-field-ids'); +const mutateIssueStatus = require('../utils/mutate-issue-status'); + + + +/** + * Function to retrieve Skills Issue and post message + * @param {Object} github - GitHub object + * @param {Object} context - Context object + * @param {Object} activity - eventActor and message + * + */ +async function postToSkillsIssue({g, c}, activity) { + + github = g; + context = c; + + const owner = context.repo.owner; + const repo = context.repo.repo; + const TEAM = 'website-write'; + + const username = activity[0]; + const message = activity[1]; + const MARKER = ''; + + // Retrieve user's Skills Issue + const { skillsIssueNum, skillsIssueNodeId } = await retrieveSkillsIssue(username); + // Return immediately if Skills Issue not found + if (skillsIssueNum) { + console.log(`Found Skills Issue for ${username}: ${skillsIssueNum}`); + } else { + console.log(`Did not find Skills Issue for ${username}. Cannot post message.`); + return; + } + + // Retrieve all comments from the Skills Issue + // https://docs.github.com/en/rest/issues/comments?apiVersion=2022-11-28#list-issue-comments + const commentData = await github.request('GET /repos/{owner}/{repo}/issues/{issueNum}/comments', { + owner, + repo, + issueNum: skillsIssueNum, + }); + + // Find the comment that includes the MARKER text and append message + const commentFound = commentData.data.find(comment => comment.body.includes(MARKER)) + const commentFoundId = commentFound ? commentFound.id : null; + + if (commentFound) { + const commentId = commentFoundId; + const originalBody = commentFound.body; + const updatedBody = `${originalBody}\n${message}`; + // https://docs.github.com/en/rest/issues/comments?apiVersion=2022-11-28#update-an-issue-comment + const patchSkillsIssue = await github.request('PATCH /repos/{owner}/{repo}/issues/comments/{commentId}', { + owner, + repo, + commentId, + body: updatedBody + }); + } else { + const body = `${MARKER}\n## Activity Log: ${username}\n${message}`; + await postComment(github, context, skillsIssueNum, body); + } + + // Check whether eventActor is team member; if so open issue and move to "In progress" + const isActiveMember = await checkTeamMembership(github, username, team); + + if (isActiveMember) { + // Make sure Skills Issue is open + await github.request('PATCH /repos/{owner}/{repo}/issues/{issueNum}', { + owner, + repo, + issueNum: skillsIssueNum, + state: "open", + }); + // Update item's status to "In progress (actively working)" + let statusValue = statusFieldIds('In_Progress'); + await mutateIssueStatus(github, context, skillsIssueNodeId, statusValue); + } +} + +module.exports = postToSkillsIssue; From 297b97ce2a5613a1dc7e7c9d6830683b3adf594c Mon Sep 17 00:00:00 2001 From: Will Gillis <40799239+t-will-gillis@users.noreply.github.com> Date: Mon, 21 Jul 2025 20:30:53 -0700 Subject: [PATCH 05/25] Create get-skills-issue.js --- github-actions/utils/get-skills-issue.js | 29 ++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 github-actions/utils/get-skills-issue.js diff --git a/github-actions/utils/get-skills-issue.js b/github-actions/utils/get-skills-issue.js new file mode 100644 index 0000000000..b72f0ce48c --- /dev/null +++ b/github-actions/utils/get-skills-issue.js @@ -0,0 +1,29 @@ +/** + * Helper function to retrieve eventActor's Skills Issue + * @param {String} eventActor - Key reference to look up user's Skill Issue + * @return {Object} - eventActor's skillsIssueNum and skillsIssueNodeId + */ +async function retrieveSkillsIssue(eventActor) { + // Note that this returns the first 10 issues assigned to the eventActor- which is presumed + // to include that person's Skills Issue- this might need to be changed if Skill's Issues not found + // https://docs.github.com/en/rest/issues/issues?apiVersion=2022-11-28#list-repository-issues + const issueData = await github.request('GET /repos/{owner}/{repo}/issues', { + owner: context.repo.owner, + repo: context.repo.repo, + assignee: eventActor, + state: 'all', + direction: 'asc', + per_page: 10, + }); + + // Find issue with the `Complexity: Prework` label, then extract skillsIssueNum and skillsIssueNodeId + const skillsIssue = issueData.data.find(issue => issue.labels.some(label => label.name === "Complexity: Prework")); + const skillsIssueNum = skillsIssue ? skillsIssue.number : null; + const skillsIssueNodeId = skillsIssue ? skillsIssue.node_id : null; + + console.log(`Found skills issue ${skillsIssueNum}: ${skillsIssueNodeId}`); + + return {skillsIssueNum, skillsIssueNodeId}; +} + +module.exports = retrieveSkillsIssue; From d1983144c6817a6e49f93c0713470152ab576c17 Mon Sep 17 00:00:00 2001 From: Will Gillis <40799239+t-will-gillis@users.noreply.github.com> Date: Mon, 21 Jul 2025 20:36:50 -0700 Subject: [PATCH 06/25] change 'retrieve' --> 'get' --- .../activity-trigger/post-to-skills-issue.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/github-actions/activity-trigger/post-to-skills-issue.js b/github-actions/activity-trigger/post-to-skills-issue.js index fb391da410..f10480d8cb 100644 --- a/github-actions/activity-trigger/post-to-skills-issue.js +++ b/github-actions/activity-trigger/post-to-skills-issue.js @@ -1,5 +1,5 @@ // Import modules -const retrieveSkillsIssue = require('../utils/retrieve-skills-issue'); +const getSkillsIssue = require('../utils/get-skills-issue'); const postComment = require('../utils/post-issue-comment'); const checkTeamMembership = require('../utils/check-team-membership'); const statusFieldIds = require('../utils/_data/status-field-ids'); @@ -8,7 +8,7 @@ const mutateIssueStatus = require('../utils/mutate-issue-status'); /** - * Function to retrieve Skills Issue and post message + * Function to get eventActor's Skills Issue and post message * @param {Object} github - GitHub object * @param {Object} context - Context object * @param {Object} activity - eventActor and message @@ -27,8 +27,8 @@ async function postToSkillsIssue({g, c}, activity) { const message = activity[1]; const MARKER = ''; - // Retrieve user's Skills Issue - const { skillsIssueNum, skillsIssueNodeId } = await retrieveSkillsIssue(username); + // Get eventActor's Skills Issue + const { skillsIssueNum, skillsIssueNodeId } = await getSkillsIssue(username); // Return immediately if Skills Issue not found if (skillsIssueNum) { console.log(`Found Skills Issue for ${username}: ${skillsIssueNum}`); @@ -37,7 +37,7 @@ async function postToSkillsIssue({g, c}, activity) { return; } - // Retrieve all comments from the Skills Issue + // Get all comments from the Skills Issue // https://docs.github.com/en/rest/issues/comments?apiVersion=2022-11-28#list-issue-comments const commentData = await github.request('GET /repos/{owner}/{repo}/issues/{issueNum}/comments', { owner, From f99e6043977ea1d6b2fccd77adaa4bd78ecbf80f Mon Sep 17 00:00:00 2001 From: Will Gillis <40799239+t-will-gillis@users.noreply.github.com> Date: Mon, 21 Jul 2025 20:40:18 -0700 Subject: [PATCH 07/25] revise comments --- github-actions/activity-trigger/activity-trigger.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/github-actions/activity-trigger/activity-trigger.js b/github-actions/activity-trigger/activity-trigger.js index 6a67cd2dc0..e1bac26865 100644 --- a/github-actions/activity-trigger/activity-trigger.js +++ b/github-actions/activity-trigger/activity-trigger.js @@ -1,8 +1,6 @@ /** - * This function is triggered by member activities, which include: - * the eventName (i.e. "issues", "pull_request", "pull_request_review", etc. ), - * the eventAction (i.e. "opened", "assigned", "submitted", etc.), and - * the eventActor (user who is credited for the event). + * This function parses the triggered event to determine the trigger eventName and eventAction + * and from this information decide the eventActor (user who is credited for the event). * @param {Object} github - GitHub object from function calling activity-trigger.js * @param {Object} context - Context of the function calling activity-trigger.js * @returns {Object} - An object containing the eventActor and a message From 01154d10ac6518665d2eaf00970ff99b8817f75f Mon Sep 17 00:00:00 2001 From: Will Gillis <40799239+t-will-gillis@users.noreply.github.com> Date: Mon, 28 Jul 2025 13:35:10 -0700 Subject: [PATCH 08/25] Declare variables --- github-actions/activity-trigger/activity-trigger.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/github-actions/activity-trigger/activity-trigger.js b/github-actions/activity-trigger/activity-trigger.js index e1bac26865..2b6ad4e131 100644 --- a/github-actions/activity-trigger/activity-trigger.js +++ b/github-actions/activity-trigger/activity-trigger.js @@ -1,3 +1,9 @@ +// Global variables +var github; +var context; + + + /** * This function parses the triggered event to determine the trigger eventName and eventAction * and from this information decide the eventActor (user who is credited for the event). From 190f7bb3cfde6ad98dfa78932f7d91b42bc0f458 Mon Sep 17 00:00:00 2001 From: Will Gillis <40799239+t-will-gillis@users.noreply.github.com> Date: Mon, 28 Jul 2025 13:48:30 -0700 Subject: [PATCH 09/25] declared variables; team --> TEAM; add semicolon; remove variable declaration --- .../activity-trigger/post-to-skills-issue.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/github-actions/activity-trigger/post-to-skills-issue.js b/github-actions/activity-trigger/post-to-skills-issue.js index f10480d8cb..e39c2e6d8c 100644 --- a/github-actions/activity-trigger/post-to-skills-issue.js +++ b/github-actions/activity-trigger/post-to-skills-issue.js @@ -5,6 +5,10 @@ const checkTeamMembership = require('../utils/check-team-membership'); const statusFieldIds = require('../utils/_data/status-field-ids'); const mutateIssueStatus = require('../utils/mutate-issue-status'); +// Global variables +var github; +var context; + /** @@ -46,7 +50,7 @@ async function postToSkillsIssue({g, c}, activity) { }); // Find the comment that includes the MARKER text and append message - const commentFound = commentData.data.find(comment => comment.body.includes(MARKER)) + const commentFound = commentData.data.find(comment => comment.body.includes(MARKER)); const commentFoundId = commentFound ? commentFound.id : null; if (commentFound) { @@ -54,7 +58,7 @@ async function postToSkillsIssue({g, c}, activity) { const originalBody = commentFound.body; const updatedBody = `${originalBody}\n${message}`; // https://docs.github.com/en/rest/issues/comments?apiVersion=2022-11-28#update-an-issue-comment - const patchSkillsIssue = await github.request('PATCH /repos/{owner}/{repo}/issues/comments/{commentId}', { + await github.request('PATCH /repos/{owner}/{repo}/issues/comments/{commentId}', { owner, repo, commentId, @@ -66,7 +70,7 @@ async function postToSkillsIssue({g, c}, activity) { } // Check whether eventActor is team member; if so open issue and move to "In progress" - const isActiveMember = await checkTeamMembership(github, username, team); + const isActiveMember = await checkTeamMembership(github, username, TEAM); if (isActiveMember) { // Make sure Skills Issue is open From bbca6e8a90f2df2fecb1aafcf0eb02e4cbcf7145 Mon Sep 17 00:00:00 2001 From: Will Gillis <40799239+t-will-gillis@users.noreply.github.com> Date: Mon, 28 Jul 2025 14:00:03 -0700 Subject: [PATCH 10/25] Add conditional for GHA to run only if 'hackforla/website' --- .github/workflows/activity-trigger.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/activity-trigger.yml b/.github/workflows/activity-trigger.yml index 476067436c..4497e76839 100644 --- a/.github/workflows/activity-trigger.yml +++ b/.github/workflows/activity-trigger.yml @@ -14,6 +14,7 @@ on: jobs: Gather-Activity-Event-Information: runs-on: ubuntu-latest + if: github.repository == 'hackforla/website' steps: - uses: actions/checkout@v4 From 047e8522c9603f36209190959dc993f4d0d02629 Mon Sep 17 00:00:00 2001 From: t-will-gillis <40799239+t-will-gillis@users.noreply.github.com> Date: Mon, 11 Aug 2025 14:13:15 -0700 Subject: [PATCH 11/25] edits to match existing Skills Issue comments --- .github/workflows/activity-history-post.yml | 21 ------------ .github/workflows/activity-trigger.yml | 6 ++-- .../activity-trigger/activity-trigger.js | 33 ++++++++++++------- .../activity-trigger/post-to-skills-issue.js | 4 +-- .../add-update-label-weekly/add-label.js | 8 +++-- 5 files changed, 32 insertions(+), 40 deletions(-) delete mode 100644 .github/workflows/activity-history-post.yml diff --git a/.github/workflows/activity-history-post.yml b/.github/workflows/activity-history-post.yml deleted file mode 100644 index 3b4aa2c5b6..0000000000 --- a/.github/workflows/activity-history-post.yml +++ /dev/null @@ -1,21 +0,0 @@ -name: Post Activity History to all Users - -on: - schedule: - - cron: 25 4 10 8 * - workflow_dispatch: - -jobs: - Read-CSV: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - - name: Parse CSV - id: parse-csv - uses: actions/github-script@v7 - with: - github-token: ${{ secrets.HACKFORLA_GRAPHQL_TOKEN }} - script: | - const script = require('./github-actions/activity-trigger/first-post-to-skills-issue.js') - return script({g: github, c: context}) diff --git a/.github/workflows/activity-trigger.yml b/.github/workflows/activity-trigger.yml index 4497e76839..255dbda6d6 100644 --- a/.github/workflows/activity-trigger.yml +++ b/.github/workflows/activity-trigger.yml @@ -3,13 +3,15 @@ name: Member Activity Trigger on: workflow_call: issues: - types: [opened, assigned, unassigned, closed] + types: [opened, assigned, unassigned, closed, reopened] issue_comment: types: [created] pull_request: - types: [opened, closed] + types: [opened, closed, reopened] pull_request_review: types: [submitted] + pull_request_review_comment: + types: [created] jobs: Gather-Activity-Event-Information: diff --git a/github-actions/activity-trigger/activity-trigger.js b/github-actions/activity-trigger/activity-trigger.js index 2b6ad4e131..1fcd8871c7 100644 --- a/github-actions/activity-trigger/activity-trigger.js +++ b/github-actions/activity-trigger/activity-trigger.js @@ -25,7 +25,8 @@ async function activityTrigger({g, c}) { let eventActor = context.actor; let activity = []; - const excludedActors = ['HackforLABot', 'elizabethhonest']; + // Exclude all bot actors from being recorded to prevent infinite loops + const excludedActors = ['HackforLABot', 'elizabethhonest', 'github-actions', 'github-advanced-security', 'github-pages', 'dependabot[bot]', 'dependabot-preview[bot]', 'dependabot', 'dependabot-preview']; if (eventName === 'issues') { issueNum = context.payload.issue.number; @@ -45,6 +46,11 @@ async function activityTrigger({g, c}) { eventAction = reason; } } else if (eventName === 'issue_comment') { + // Check if the comment is on an issue or a pull request + let isPullRequest = context.payload.issue?.pull_request; + if (isPullRequest) { + eventName = 'pull_request_comment'; + } issueNum = context.payload.issue.number; eventUrl = context.payload.comment.html_url; timeline = context.payload.comment.updated_at; @@ -85,20 +91,23 @@ async function activityTrigger({g, c}) { // Message templates to post on Skills Issue const actionMap = { - 'issues.opened': 'opened an issue', - 'issues.completed': 'closed an issue as completed', - 'issues.not_planned': 'closed an issue as not planned', - 'issues.duplicate': 'closed an issue as duplicate', - 'issues.assigned': 'been assigned to an issue', - 'issues.unassigned': 'been unassigned from an issue', - 'issue_comment.created': 'commented on an issue or pr', + 'issues.opened': 'opened issue:', + 'issues.completed': 'closed issue as completed', + 'issues.not_planned': 'closed issue as not planned', + 'issues.duplicate': 'closed issue as duplicate', + 'issues.reopened': 'reopened issue', + 'issues.assigned': 'assigned to issue', + 'issues.unassigned': 'unassigned from issue', + 'issue_comment.created': 'commented on issue', + 'pull_request_review.created': 'submitted pull request review', + 'pull_request_comment.created': 'commented on pull request', 'pull_request.opened': 'opened a pull request', - 'pull_request.closed': 'had a pull request closed w/o merging', - 'pull_request.merged': 'had a pull request merged', - 'pull_request_review.submitted': 'submitted a pull request review' + 'pull_request.closed': 'pull request closed w/o merging', + 'pull_request.merged': 'pull request merged', + 'pull_request.reopened': 'reopened pull request' }; const action = actionMap[`${eventName}.${eventAction}`]; - let message = `@ ${eventActor} has ${action}: #[${issueNum}](${eventUrl}) at ${timeline}`; + let message = `@ ${eventActor} ${action}: #[${issueNum}](${eventUrl}) at ${timeline}`; console.log(message); activity = [eventActor, message]; diff --git a/github-actions/activity-trigger/post-to-skills-issue.js b/github-actions/activity-trigger/post-to-skills-issue.js index e39c2e6d8c..ded5ef749d 100644 --- a/github-actions/activity-trigger/post-to-skills-issue.js +++ b/github-actions/activity-trigger/post-to-skills-issue.js @@ -65,8 +65,8 @@ async function postToSkillsIssue({g, c}, activity) { body: updatedBody }); } else { - const body = `${MARKER}\n## Activity Log: ${username}\n${message}`; - await postComment(github, context, skillsIssueNum, body); + const body = `${MARKER}\n## Activity Log: ${username}\n\n##### ⚠ Important note: The bot updates this issue automatically - do not edit\n\n${message}`; + await postComment(skillsIssueNum, body, github, context); } // Check whether eventActor is team member; if so open issue and move to "In progress" diff --git a/github-actions/trigger-schedule/add-update-label-weekly/add-label.js b/github-actions/trigger-schedule/add-update-label-weekly/add-label.js index 6c6faaf6e2..224b23448e 100644 --- a/github-actions/trigger-schedule/add-update-label-weekly/add-label.js +++ b/github-actions/trigger-schedule/add-update-label-weekly/add-label.js @@ -19,6 +19,7 @@ const [ er, epic, dependency, + skillsIssueCompleted, ] = [ "statusUpdated", "statusInactive1", @@ -26,7 +27,8 @@ const [ "draft", "er", "epic", - "dependency" + "dependency", + "skillsIssueCompleted", ].map(retrieveLabelDirectory); const updatedByDays = 3; // If last update update 3 days, the issue is considered updated @@ -100,7 +102,7 @@ async function main({ g, c }) { * @returns {Promise} issueNums - an array of open, assigned, and statused issue numbers */ async function getIssueNumsFromRepo() { - const labelsToExclude = [draft, er, epic, dependency]; + const labelsToExclude = [draft, er, epic, dependency, skillsIssueCompleted]; let issueNums = []; let pageNum = 1; let result = []; @@ -369,7 +371,7 @@ function formatComment(assignees, labelString) { function isCommentByBot(data) { let botLogin = "github-actions[bot]"; let hflaBotLogin = "HackforLABot"; - // If the comment includes the MARKER, return false + // If the comment includes the MARKER, return false so it is not minimized let MARKER = ''; if (data.body.includes(MARKER)) { console.log(`Found "Skills Issue Activity Record" - do not minimize`); From 173646109e07dd299ee9516910c188e90e10dffd Mon Sep 17 00:00:00 2001 From: t-will-gillis <40799239+t-will-gillis@users.noreply.github.com> Date: Tue, 12 Aug 2025 20:42:39 -0700 Subject: [PATCH 12/25] reformat history and add error handling --- .../activity-trigger/activity-trigger.js | 22 ++++++++++++++++--- github-actions/utils/query-issue-info.js | 22 +++++++++++++++++-- 2 files changed, 39 insertions(+), 5 deletions(-) diff --git a/github-actions/activity-trigger/activity-trigger.js b/github-actions/activity-trigger/activity-trigger.js index 1fcd8871c7..e959bef411 100644 --- a/github-actions/activity-trigger/activity-trigger.js +++ b/github-actions/activity-trigger/activity-trigger.js @@ -106,14 +106,16 @@ async function activityTrigger({g, c}) { 'pull_request.merged': 'pull request merged', 'pull_request.reopened': 'reopened pull request' }; - const action = actionMap[`${eventName}.${eventAction}`]; - let message = `@ ${eventActor} ${action}: #[${issueNum}](${eventUrl}) at ${timeline}`; + + let localTime = getDateTime(timeline); + let action = actionMap[`${eventName}.${eventAction}`]; + let message = `- ${eventActor} ${action}: ${eventUrl} at ${localTime}`; console.log(message); activity = [eventActor, message]; return activity; - + /** * Helper function to check if issueNum references a Skills Issue @@ -131,6 +133,20 @@ async function activityTrigger({g, c}) { return isSkillsIssue; } + + + + /** + * Helper function to get the date and time in a readable format + * @param {String} timeline - the date and time string from the event + * @returns {String} dateTime - formatted date and time string + */ + function getDateTime(timeline) { + const date = new Date(timeline); + const options = { timeZone: 'America/Los_Angeles', year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', hour12: true, timeZoneName: 'short' }; + return date.toLocaleString('en-US', options); + } + } module.exports = activityTrigger; diff --git a/github-actions/utils/query-issue-info.js b/github-actions/utils/query-issue-info.js index 8583d0e2fa..affe2c0f3e 100644 --- a/github-actions/utils/query-issue-info.js +++ b/github-actions/utils/query-issue-info.js @@ -44,7 +44,7 @@ async function queryIssueInfo(github, context, issueNum) { // Since there is always one item associated with the issue, // directly get the item's ID from the first index const id = projectData[0].id; - +/* // Iterate through the field values of the first project item // and find the node that contains the 'name' property, then get its 'name' value const statusName = projectData[0].fieldValues.nodes.find((item) => @@ -53,10 +53,28 @@ async function queryIssueInfo(github, context, issueNum) { // Similarly, find node with 'optionId' property, then get is 'optionId' value const statusId = projectData[0].fieldValues.nodes.find((item) => item.hasOwnProperty("optionId")).optionId; +*/ + // Iterate through the field values of the first project item and find the nodes + // for 'name' and 'optionId' properties, then get 'statusName' and 'statusId' + const fieldValues = projectData[0].fieldValues?.nodes || []; + + const statusNameNode = fieldValues.find((item) => item.hasOwnProperty("name")); + const statusIdNode = fieldValues.find((item) => item.hasOwnProperty("optionId")); + + const statusName = statusNameNode?.name || 'Unknown Status'; + const statusId = statusIdNode?.optionId || null; return { id, statusName, statusId }; } catch (error) { - throw new Error(`Error finding Issue #${issueNum} id and status; error = ${error}`); + // If an error occurs, log it and return an object with null values + console.error(`Error querying issue info for Issue #${issueNum}:`, error.message); + return { + id: null, + statusName: 'Error', + statusId: null, + error: error.message, + issueNumber: issueNum + }; } } From e6ffb1253b06fa6a4c04c5e33b4c7525399ffd12 Mon Sep 17 00:00:00 2001 From: t-will-gillis <40799239+t-will-gillis@users.noreply.github.com> Date: Thu, 14 Aug 2025 15:18:19 -0700 Subject: [PATCH 13/25] major updates to get graphQL working --- .../activity-trigger/activity-trigger.js | 10 +-- .../activity-trigger/post-to-skills-issue.js | 24 ++++-- github-actions/utils/get-skills-issue.js | 29 ------- github-actions/utils/query-skills-issue.js | 78 +++++++++++++++++++ 4 files changed, 97 insertions(+), 44 deletions(-) delete mode 100644 github-actions/utils/get-skills-issue.js create mode 100644 github-actions/utils/query-skills-issue.js diff --git a/github-actions/activity-trigger/activity-trigger.js b/github-actions/activity-trigger/activity-trigger.js index e959bef411..14416c317f 100644 --- a/github-actions/activity-trigger/activity-trigger.js +++ b/github-actions/activity-trigger/activity-trigger.js @@ -69,19 +69,11 @@ async function activityTrigger({g, c}) { timeline = context.payload.review.updated_at; } - // Following are for confirmation, can be removed - console.log(`eventName = ${eventName}`); - console.log(`eventAction = ${eventAction}`); - console.log(`eventActor = ${eventActor}`); - console.log(`issueNum = ${issueNum}`); - console.log(`eventUrl = ${eventUrl}`); - console.log(`eventTime = ${timeline}`); - // Return immediately if the issueNum is a Skills Issue- to discourage // infinite loop (recording comment, recording the recording of comment, etc.) const isSkillsIssue = await checkIfSkillsIssue(issueNum); if (isSkillsIssue) { - console.log(`issueNum: ${issueNum} identified as Skills Issue`); + console.log(`- issueNum: ${issueNum} identified as Skills Issue`); return activity; } // Return immediately if the eventActor is a bot- same reason diff --git a/github-actions/activity-trigger/post-to-skills-issue.js b/github-actions/activity-trigger/post-to-skills-issue.js index ded5ef749d..7d4b264360 100644 --- a/github-actions/activity-trigger/post-to-skills-issue.js +++ b/github-actions/activity-trigger/post-to-skills-issue.js @@ -1,5 +1,6 @@ // Import modules -const getSkillsIssue = require('../utils/get-skills-issue'); +const retrieveLabelDirectory = require('../utils/retrieve-label-directory'); +const querySkillsIssue = require('../utils/query-skills-issue'); const postComment = require('../utils/post-issue-comment'); const checkTeamMembership = require('../utils/check-team-membership'); const statusFieldIds = require('../utils/_data/status-field-ids'); @@ -9,6 +10,9 @@ const mutateIssueStatus = require('../utils/mutate-issue-status'); var github; var context; +// `complexity0` refers `Complexity: Prework` label +const SKILLS_LABEL = retrieveLabelDirectory("complexity0"); + /** @@ -30,9 +34,14 @@ async function postToSkillsIssue({g, c}, activity) { const username = activity[0]; const message = activity[1]; const MARKER = ''; + const IN_PROGRESS_ID = statusFieldIds('In_Progress'); - // Get eventActor's Skills Issue - const { skillsIssueNum, skillsIssueNodeId } = await getSkillsIssue(username); + // Get eventActor's Skills Issue number, nodeId, current status + const skillsInfo = await querySkillsIssue(github, context, username, SKILLS_LABEL); + const skillsIssueNum = skillsInfo.issueNum; + const skillsIssueNodeId = skillsInfo.issueId; + const skillsStatusId = skillsInfo.statusId; + // Return immediately if Skills Issue not found if (skillsIssueNum) { console.log(`Found Skills Issue for ${username}: ${skillsIssueNum}`); @@ -54,6 +63,7 @@ async function postToSkillsIssue({g, c}, activity) { const commentFoundId = commentFound ? commentFound.id : null; if (commentFound) { + console.log(`Found comment with MARKER: ${MARKER}`); const commentId = commentFoundId; const originalBody = commentFound.body; const updatedBody = `${originalBody}\n${message}`; @@ -65,6 +75,7 @@ async function postToSkillsIssue({g, c}, activity) { body: updatedBody }); } else { + console.log(`MARKER not found in comments, creating new comment with MARKER...`); const body = `${MARKER}\n## Activity Log: ${username}\n\n##### ⚠ Important note: The bot updates this issue automatically - do not edit\n\n${message}`; await postComment(skillsIssueNum, body, github, context); } @@ -80,9 +91,10 @@ async function postToSkillsIssue({g, c}, activity) { issueNum: skillsIssueNum, state: "open", }); - // Update item's status to "In progress (actively working)" - let statusValue = statusFieldIds('In_Progress'); - await mutateIssueStatus(github, context, skillsIssueNodeId, statusValue); + // Update item's status to "In progress (actively working)" if not already + if (skillsStatusId != IN_PROGRESS_ID) { + await mutateIssueStatus(github, context, skillsIssueNodeId, IN_PROGRESS_ID); + } } } diff --git a/github-actions/utils/get-skills-issue.js b/github-actions/utils/get-skills-issue.js deleted file mode 100644 index b72f0ce48c..0000000000 --- a/github-actions/utils/get-skills-issue.js +++ /dev/null @@ -1,29 +0,0 @@ -/** - * Helper function to retrieve eventActor's Skills Issue - * @param {String} eventActor - Key reference to look up user's Skill Issue - * @return {Object} - eventActor's skillsIssueNum and skillsIssueNodeId - */ -async function retrieveSkillsIssue(eventActor) { - // Note that this returns the first 10 issues assigned to the eventActor- which is presumed - // to include that person's Skills Issue- this might need to be changed if Skill's Issues not found - // https://docs.github.com/en/rest/issues/issues?apiVersion=2022-11-28#list-repository-issues - const issueData = await github.request('GET /repos/{owner}/{repo}/issues', { - owner: context.repo.owner, - repo: context.repo.repo, - assignee: eventActor, - state: 'all', - direction: 'asc', - per_page: 10, - }); - - // Find issue with the `Complexity: Prework` label, then extract skillsIssueNum and skillsIssueNodeId - const skillsIssue = issueData.data.find(issue => issue.labels.some(label => label.name === "Complexity: Prework")); - const skillsIssueNum = skillsIssue ? skillsIssue.number : null; - const skillsIssueNodeId = skillsIssue ? skillsIssue.node_id : null; - - console.log(`Found skills issue ${skillsIssueNum}: ${skillsIssueNodeId}`); - - return {skillsIssueNum, skillsIssueNodeId}; -} - -module.exports = retrieveSkillsIssue; diff --git a/github-actions/utils/query-skills-issue.js b/github-actions/utils/query-skills-issue.js new file mode 100644 index 0000000000..0c1ef8c7e3 --- /dev/null +++ b/github-actions/utils/query-skills-issue.js @@ -0,0 +1,78 @@ +/** + * @description Query for Skills Issue information using assignee and label + * @param {Object} github - GitHub object from function calling queryIssueInfo() + * @params {Object} context - Context of the function calling queryIssueInfo() + * @params {String} assignee - The GitHub username of the assignee + * @params {String} label - The label to filter issues by (e.g., "Complexity: Prework") + * @returns {Object} - An object containing the item ID and its status name + */ +async function querySkillsIssue(github, context, assignee, label) { + const repoOwner = context.repo.owner; + const repoName = context.repo.repo; + + const query = `query($owner: String!, $repo: String!, $assignee: String!, $label: String!) { + repository(owner: $owner, name: $repo) { + issues( + first: 5 + filterBy: {assignee: $assignee, labels: [$label]} + states: [OPEN, CLOSED] + ) { + nodes { + number + projectItems(first: 5) { + nodes { + id + project { + id + title + } + fieldValues(first: 15) { + nodes { + ... on ProjectV2ItemFieldSingleSelectValue { + name + optionId + } + } + } + } + } + } + } + } + }`; + + const variables = { + owner: repoOwner, + repo: repoName, + assignee: assignee, + label: label + }; + + try { + const response = await github.graphql(query, variables); + + // Extract the list of project items associated with the issue + const issueNode = response.repository.issues.nodes[0]; + + const issueNum = issueNode.number; + const issueId = issueNode.projectItems.nodes[0]?.id; + + const fieldValues = response.repository.issues.nodes[0].projectItems.nodes[0].fieldValues.nodes; + const statusField = fieldValues.find(node => node.name && node.optionId); + const statusName = statusField?.name; + const statusId = statusField?.optionId; + + return { issueNum, issueId, statusName, statusId }; + } catch (error) { + // If an error occurs, log it and return an object with null values + console.error(`Error querying skills issue: ${error.message}`); + return { + issueNum: null, + issueId: null, + statusName: "Unknown Status", + statusId: null, + }; + } +} + +module.exports = querySkillsIssue; \ No newline at end of file From e13dcd39fa21bb17cb46a7d449175082584dd9ec Mon Sep 17 00:00:00 2001 From: t-will-gillis <40799239+t-will-gillis@users.noreply.github.com> Date: Thu, 14 Aug 2025 18:56:42 -0700 Subject: [PATCH 14/25] revert to previous --- github-actions/utils/query-issue-info.js | 24 +++--------------------- 1 file changed, 3 insertions(+), 21 deletions(-) diff --git a/github-actions/utils/query-issue-info.js b/github-actions/utils/query-issue-info.js index affe2c0f3e..41777fc6e4 100644 --- a/github-actions/utils/query-issue-info.js +++ b/github-actions/utils/query-issue-info.js @@ -44,7 +44,7 @@ async function queryIssueInfo(github, context, issueNum) { // Since there is always one item associated with the issue, // directly get the item's ID from the first index const id = projectData[0].id; -/* + // Iterate through the field values of the first project item // and find the node that contains the 'name' property, then get its 'name' value const statusName = projectData[0].fieldValues.nodes.find((item) => @@ -53,29 +53,11 @@ async function queryIssueInfo(github, context, issueNum) { // Similarly, find node with 'optionId' property, then get is 'optionId' value const statusId = projectData[0].fieldValues.nodes.find((item) => item.hasOwnProperty("optionId")).optionId; -*/ - // Iterate through the field values of the first project item and find the nodes - // for 'name' and 'optionId' properties, then get 'statusName' and 'statusId' - const fieldValues = projectData[0].fieldValues?.nodes || []; - - const statusNameNode = fieldValues.find((item) => item.hasOwnProperty("name")); - const statusIdNode = fieldValues.find((item) => item.hasOwnProperty("optionId")); - - const statusName = statusNameNode?.name || 'Unknown Status'; - const statusId = statusIdNode?.optionId || null; return { id, statusName, statusId }; } catch (error) { - // If an error occurs, log it and return an object with null values - console.error(`Error querying issue info for Issue #${issueNum}:`, error.message); - return { - id: null, - statusName: 'Error', - statusId: null, - error: error.message, - issueNumber: issueNum - }; + throw new Error(`Error finding Issue #${issueNum} id and status; error = ${error}`); } } -module.exports = queryIssueInfo; +module.exports = queryIssueInfo; \ No newline at end of file From 704cb2f6dd98458f6105aac109e675c5059f3688 Mon Sep 17 00:00:00 2001 From: t-will-gillis <40799239+t-will-gillis@users.noreply.github.com> Date: Sun, 17 Aug 2025 14:02:20 -0700 Subject: [PATCH 15/25] change 'pull request' to 'PR' --- github-actions/activity-trigger/activity-trigger.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/github-actions/activity-trigger/activity-trigger.js b/github-actions/activity-trigger/activity-trigger.js index 14416c317f..df294ac150 100644 --- a/github-actions/activity-trigger/activity-trigger.js +++ b/github-actions/activity-trigger/activity-trigger.js @@ -91,12 +91,12 @@ async function activityTrigger({g, c}) { 'issues.assigned': 'assigned to issue', 'issues.unassigned': 'unassigned from issue', 'issue_comment.created': 'commented on issue', - 'pull_request_review.created': 'submitted pull request review', - 'pull_request_comment.created': 'commented on pull request', - 'pull_request.opened': 'opened a pull request', - 'pull_request.closed': 'pull request closed w/o merging', - 'pull_request.merged': 'pull request merged', - 'pull_request.reopened': 'reopened pull request' + 'pull_request_review.created': 'submitted PR review', + 'pull_request_comment.created': 'commented on PR', + 'pull_request.opened': 'PR opened', + 'pull_request.closed': 'PR closed w/o merging', + 'pull_request.merged': 'PR merged', + 'pull_request.reopened': 'PR reopened' }; let localTime = getDateTime(timeline); From 6c836084e5f16f2e6ff5c57d950628eaaf989934 Mon Sep 17 00:00:00 2001 From: t-will-gillis <40799239+t-will-gillis@users.noreply.github.com> Date: Tue, 19 Aug 2025 12:57:22 -0700 Subject: [PATCH 16/25] major change- 2 actors PR closed --- .github/workflows/activity-trigger.yml | 13 ++-- .../activity-trigger/activity-trigger.js | 73 +++++++++++-------- .../activity-trigger/post-to-skills-issue.js | 25 ++++--- 3 files changed, 62 insertions(+), 49 deletions(-) diff --git a/.github/workflows/activity-trigger.yml b/.github/workflows/activity-trigger.yml index 255dbda6d6..ddb1d35ace 100644 --- a/.github/workflows/activity-trigger.yml +++ b/.github/workflows/activity-trigger.yml @@ -27,8 +27,8 @@ jobs: github-token: ${{ secrets.HACKFORLA_GRAPHQL_TOKEN }} script: | const script = require('./github-actions/activity-trigger/activity-trigger.js') - const activity = script({g: github, c: context}) - return activity + const activities = script({g: github, c: context}) + return activities - if: ${{ steps.gather-event-details.outputs.result != '[]' }} name: Post to Skills Issue @@ -37,7 +37,8 @@ jobs: with: github-token: ${{ secrets.HACKFORLA_GRAPHQL_TOKEN }} script: | - const activity = ${{ steps.gather-event-details.outputs.result }} - const script = require('./github-actions/activity-trigger/post-to-skills-issue.js') - script({g: github, c: context}, activity) - + const activities = JSON.parse(${{ steps.gather-event-details.outputs.result }}); + const script = require('./github-actions/activity-trigger/post-to-skills-issue.js'); + for (const activity of activities) { + await script({g: github, c: context}, activity); + } \ No newline at end of file diff --git a/github-actions/activity-trigger/activity-trigger.js b/github-actions/activity-trigger/activity-trigger.js index df294ac150..3ef9da3c44 100644 --- a/github-actions/activity-trigger/activity-trigger.js +++ b/github-actions/activity-trigger/activity-trigger.js @@ -23,10 +23,11 @@ async function activityTrigger({g, c}) { let eventName = context.eventName; let eventAction = context.payload.action; let eventActor = context.actor; - let activity = []; + let eventPRAuthor = ''; + let activities = []; - // Exclude all bot actors from being recorded to prevent infinite loops - const excludedActors = ['HackforLABot', 'elizabethhonest', 'github-actions', 'github-advanced-security', 'github-pages', 'dependabot[bot]', 'dependabot-preview[bot]', 'dependabot', 'dependabot-preview']; + // Exclude all bot actors from being recorded as a guardrail against infinite loops + const EXCLUDED_ACTORS = ['HackforLABot', 'elizabethhonest', 'github-actions', 'github-advanced-security', 'github-pages', 'dependabot[bot]', 'dependabot-preview[bot]', 'dependabot', 'dependabot-preview']; if (eventName === 'issues') { issueNum = context.payload.issue.number; @@ -58,10 +59,11 @@ async function activityTrigger({g, c}) { issueNum = context.payload.pull_request.number; eventUrl = context.payload.pull_request.html_url; timeline = context.payload.pull_request.updated_at; - // If PR closed, check if merged and change eventActor to the original pr author + // If PR closed, check if 'merged' and save 'eventActor' & 'eventPRAuthor' if (eventAction === 'closed') { - eventAction = context.payload.pull_request.merged ? 'merged' : 'closed'; - eventActor = context.payload.pull_request.user.login; + eventAction = context.payload.pull_request.merged ? 'PRmerged' : 'PRclosed'; + eventActor = context.actor; + eventPRAuthor = context.payload.pull_request.user.login; } } else if (eventName === 'pull_request_review') { issueNum = context.payload.pull_request.number; @@ -74,44 +76,54 @@ async function activityTrigger({g, c}) { const isSkillsIssue = await checkIfSkillsIssue(issueNum); if (isSkillsIssue) { console.log(`- issueNum: ${issueNum} identified as Skills Issue`); - return activity; - } - // Return immediately if the eventActor is a bot- same reason - if (eventActor in excludedActors) { - return activity; + // return activities; <-- confirm before uncommenting } // Message templates to post on Skills Issue const actionMap = { - 'issues.opened': 'opened issue:', - 'issues.completed': 'closed issue as completed', - 'issues.not_planned': 'closed issue as not planned', - 'issues.duplicate': 'closed issue as duplicate', - 'issues.reopened': 'reopened issue', - 'issues.assigned': 'assigned to issue', - 'issues.unassigned': 'unassigned from issue', - 'issue_comment.created': 'commented on issue', - 'pull_request_review.created': 'submitted PR review', - 'pull_request_comment.created': 'commented on PR', - 'pull_request.opened': 'PR opened', - 'pull_request.closed': 'PR closed w/o merging', - 'pull_request.merged': 'PR merged', - 'pull_request.reopened': 'PR reopened' + 'issues.opened': 'opened', + 'issues.completed': 'closed- completed', + 'issues.not_planned': 'closed- not planned', + 'issues.duplicate': 'closed- duplicate', + 'issues.reopened': 'reopened', + 'issues.assigned': 'assigned', + 'issues.unassigned': 'unassigned', + 'issue_comment.created': 'commented', + 'pull_request_review.created': 'submitted review', + 'pull_request_comment.created': 'commented', + 'pull_request.opened': 'opened', + 'pull_request.PRclosed': 'closed', + 'pull_request.PRmerged': 'merged', + 'pull_request.reopened': 'reopened' }; let localTime = getDateTime(timeline); let action = actionMap[`${eventName}.${eventAction}`]; let message = `- ${eventActor} ${action}: ${eventUrl} at ${localTime}`; - console.log(message); - activity = [eventActor, message]; - return activity; + // Check to confirm the eventActor isn't a bot + const isExcluded = (eventActor) => EXCLUDED_ACTORS.includes(eventActor); + if (!isExcluded(eventActor)) { + console.log(`Not a bot. Message to post: ${message}`); + activities.push([eventActor, message]); + } + + // Only if PRclosed or PRmerged, return PRAuthor + if (eventAction === 'PRclosed' || eventAction === 'PRmerged') { + let messagePRAuthor = `- ${eventPRAuthor} PR was ${action}: ${eventUrl} at ${localTime}`; + if (!isExcluded(eventPRAuthor)) { + console.log(`Not a bot. Message to post: ${messagePRAuthor}`); + activities.push([eventPRAuthor, messagePRAuthor]); + } + } + + return JSON.stringify(activities); /** - * Helper function to check if issueNum references a Skills Issue - * @param {Number} issueNum - issue number to check + * Helper function to check if issueNum (that triggered the event) is a Skills Issue + * @param {Number} issueNum - issueNum to check * @returns {Boolean} - true if Skills Issue, false if not */ async function checkIfSkillsIssue(issueNum) { @@ -122,7 +134,6 @@ async function activityTrigger({g, c}) { issue_number: issueNum }); const isSkillsIssue = labelData.data.some(label => label.name === "Complexity: Prework"); - return isSkillsIssue; } diff --git a/github-actions/activity-trigger/post-to-skills-issue.js b/github-actions/activity-trigger/post-to-skills-issue.js index 7d4b264360..1db5f94e60 100644 --- a/github-actions/activity-trigger/post-to-skills-issue.js +++ b/github-actions/activity-trigger/post-to-skills-issue.js @@ -19,7 +19,7 @@ const SKILLS_LABEL = retrieveLabelDirectory("complexity0"); * Function to get eventActor's Skills Issue and post message * @param {Object} github - GitHub object * @param {Object} context - Context object - * @param {Object} activity - eventActor and message + * @param {Object} package - eventActor and message * */ async function postToSkillsIssue({g, c}, activity) { @@ -36,12 +36,12 @@ async function postToSkillsIssue({g, c}, activity) { const MARKER = ''; const IN_PROGRESS_ID = statusFieldIds('In_Progress'); - // Get eventActor's Skills Issue number, nodeId, current status + // Get eventActor's Skills Issue number, nodeId, current statusId (all null if no Skills Issue found) const skillsInfo = await querySkillsIssue(github, context, username, SKILLS_LABEL); const skillsIssueNum = skillsInfo.issueNum; const skillsIssueNodeId = skillsInfo.issueId; const skillsStatusId = skillsInfo.statusId; - + // Return immediately if Skills Issue not found if (skillsIssueNum) { console.log(`Found Skills Issue for ${username}: ${skillsIssueNum}`); @@ -76,26 +76,27 @@ async function postToSkillsIssue({g, c}, activity) { }); } else { console.log(`MARKER not found in comments, creating new comment with MARKER...`); - const body = `${MARKER}\n## Activity Log: ${username}\n\n##### ⚠ Important note: The bot updates this issue automatically - do not edit\n\n${message}`; + const body = `${MARKER}\n## Activity Log: ${username}\n### Repo: https://github.com/hackforla/website\n\n##### ⚠ Important note: The bot updates this comment automatically - do not edit\n\n${message}`; await postComment(skillsIssueNum, body, github, context); } - // Check whether eventActor is team member; if so open issue and move to "In progress" + // If eventActor is team member, open issue and move to "In progress". Else, close issue const isActiveMember = await checkTeamMembership(github, username, TEAM); + let skillsIssueState = "closed"; if (isActiveMember) { - // Make sure Skills Issue is open - await github.request('PATCH /repos/{owner}/{repo}/issues/{issueNum}', { - owner, - repo, - issueNum: skillsIssueNum, - state: "open", - }); + skillsIssueState = "open"; // Update item's status to "In progress (actively working)" if not already if (skillsStatusId != IN_PROGRESS_ID) { await mutateIssueStatus(github, context, skillsIssueNodeId, IN_PROGRESS_ID); } } + await github.request('PATCH /repos/{owner}/{repo}/issues/{issueNum}', { + owner, + repo, + issueNum: skillsIssueNum, + state: skillsIssueState, + }); } module.exports = postToSkillsIssue; From ad7183a2ca7b4946ad294d98f47ac5fc1630f8fb Mon Sep 17 00:00:00 2001 From: t-will-gillis <40799239+t-will-gillis@users.noreply.github.com> Date: Tue, 19 Aug 2025 13:08:13 -0700 Subject: [PATCH 17/25] tweak for pr.closed if actor same for both, return one --- github-actions/activity-trigger/activity-trigger.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/github-actions/activity-trigger/activity-trigger.js b/github-actions/activity-trigger/activity-trigger.js index 3ef9da3c44..141b0edbad 100644 --- a/github-actions/activity-trigger/activity-trigger.js +++ b/github-actions/activity-trigger/activity-trigger.js @@ -108,8 +108,8 @@ async function activityTrigger({g, c}) { activities.push([eventActor, message]); } - // Only if PRclosed or PRmerged, return PRAuthor - if (eventAction === 'PRclosed' || eventAction === 'PRmerged') { + // Only if PRclosed or PRmerged, and PRAuthor != eventActor, return PRAuthor and message + if ((eventAction === 'PRclosed' || eventAction === 'PRmerged') && (eventActor != eventPRAuthor)) { let messagePRAuthor = `- ${eventPRAuthor} PR was ${action}: ${eventUrl} at ${localTime}`; if (!isExcluded(eventPRAuthor)) { console.log(`Not a bot. Message to post: ${messagePRAuthor}`); From b7b0874d52cff2900c4385e49ac67ff06e4c33ee Mon Sep 17 00:00:00 2001 From: Will Gillis <40799239+t-will-gillis@users.noreply.github.com> Date: Wed, 20 Aug 2025 11:34:16 -0700 Subject: [PATCH 18/25] Update activity-trigger.js For issue closed, record both closer and the issue assignee with event --- .../activity-trigger/activity-trigger.js | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/github-actions/activity-trigger/activity-trigger.js b/github-actions/activity-trigger/activity-trigger.js index 141b0edbad..fc5edba508 100644 --- a/github-actions/activity-trigger/activity-trigger.js +++ b/github-actions/activity-trigger/activity-trigger.js @@ -44,7 +44,8 @@ async function activityTrigger({g, c}) { } if (eventAction === 'closed') { let reason = context.payload.issue.state_reason; - eventAction = reason; + eventActor = context.payload.issue.user.login; + eventAction = 'Closed-' + reason; } } else if (eventName === 'issue_comment') { // Check if the comment is on an issue or a pull request @@ -82,9 +83,9 @@ async function activityTrigger({g, c}) { // Message templates to post on Skills Issue const actionMap = { 'issues.opened': 'opened', - 'issues.completed': 'closed- completed', - 'issues.not_planned': 'closed- not planned', - 'issues.duplicate': 'closed- duplicate', + 'issues.Closed-completed': 'closed as completed', + 'issues.Closed-not_planned': 'closed as not planned', + 'issues.Closed-duplicate': 'closed as duplicate', 'issues.reopened': 'reopened', 'issues.assigned': 'assigned', 'issues.unassigned': 'unassigned', @@ -108,6 +109,11 @@ async function activityTrigger({g, c}) { activities.push([eventActor, message]); } + // Only if issue is closed, and eventActor != assignee, return assignee and message + if (eventAction.includes('Closed-') && (eventActor != assignee)) { + message = `- ${assignee} issue ${action}: ${eventUrl} at ${localTime}`; + activities.push([assignee, message]); + } // Only if PRclosed or PRmerged, and PRAuthor != eventActor, return PRAuthor and message if ((eventAction === 'PRclosed' || eventAction === 'PRmerged') && (eventActor != eventPRAuthor)) { let messagePRAuthor = `- ${eventPRAuthor} PR was ${action}: ${eventUrl} at ${localTime}`; From e67917de33adddfb680d777541af9b79fd5a1601 Mon Sep 17 00:00:00 2001 From: Will Gillis <40799239+t-will-gillis@users.noreply.github.com> Date: Sat, 23 Aug 2025 21:53:53 -0700 Subject: [PATCH 19/25] Bump `actions/checkout@v4` --> `@v5` (Dependabot) --- .github/workflows/activity-trigger.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/activity-trigger.yml b/.github/workflows/activity-trigger.yml index ddb1d35ace..33db955230 100644 --- a/.github/workflows/activity-trigger.yml +++ b/.github/workflows/activity-trigger.yml @@ -18,7 +18,7 @@ jobs: runs-on: ubuntu-latest if: github.repository == 'hackforla/website' steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Gather Event Details id: gather-event-details @@ -41,4 +41,4 @@ jobs: const script = require('./github-actions/activity-trigger/post-to-skills-issue.js'); for (const activity of activities) { await script({g: github, c: context}, activity); - } \ No newline at end of file + } From ef5987ab5dcf46570bba540a7e26eb90e2f89c10 Mon Sep 17 00:00:00 2001 From: t-will-gillis <339.colorado.dev@gmail.com> Date: Mon, 25 Aug 2025 15:29:58 -0700 Subject: [PATCH 20/25] add error handling at fieldValues --- github-actions/utils/query-skills-issue.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/github-actions/utils/query-skills-issue.js b/github-actions/utils/query-skills-issue.js index 0c1ef8c7e3..8f56853c3f 100644 --- a/github-actions/utils/query-skills-issue.js +++ b/github-actions/utils/query-skills-issue.js @@ -57,7 +57,7 @@ async function querySkillsIssue(github, context, assignee, label) { const issueNum = issueNode.number; const issueId = issueNode.projectItems.nodes[0]?.id; - const fieldValues = response.repository.issues.nodes[0].projectItems.nodes[0].fieldValues.nodes; + const fieldValues = response.repository.issues.nodes[0].projectItems.nodes[0].fieldValues?.nodes ?? []; const statusField = fieldValues.find(node => node.name && node.optionId); const statusName = statusField?.name; const statusId = statusField?.optionId; From 926d2bf46b56268d2da9394817179494dbec2da2 Mon Sep 17 00:00:00 2001 From: t-will-gillis <339.colorado.dev@gmail.com> Date: Tue, 26 Aug 2025 21:46:54 -0700 Subject: [PATCH 21/25] add in error handling, minor changes --- .github/workflows/activity-trigger.yml | 8 +- .../activity-trigger/activity-trigger.js | 13 +--- .../activity-trigger/post-to-skills-issue.js | 73 ++++++++++--------- github-actions/utils/check-team-membership.js | 4 +- 4 files changed, 48 insertions(+), 50 deletions(-) diff --git a/.github/workflows/activity-trigger.yml b/.github/workflows/activity-trigger.yml index 33db955230..ee1a6a71f2 100644 --- a/.github/workflows/activity-trigger.yml +++ b/.github/workflows/activity-trigger.yml @@ -26,9 +26,9 @@ jobs: with: github-token: ${{ secrets.HACKFORLA_GRAPHQL_TOKEN }} script: | - const script = require('./github-actions/activity-trigger/activity-trigger.js') - const activities = script({g: github, c: context}) - return activities + const script = require('./github-actions/activity-trigger/activity-trigger.js'); + const activities = script({github, context}); + return activities; - if: ${{ steps.gather-event-details.outputs.result != '[]' }} name: Post to Skills Issue @@ -40,5 +40,5 @@ jobs: const activities = JSON.parse(${{ steps.gather-event-details.outputs.result }}); const script = require('./github-actions/activity-trigger/post-to-skills-issue.js'); for (const activity of activities) { - await script({g: github, c: context}, activity); + await script({github, context}, activity); } diff --git a/github-actions/activity-trigger/activity-trigger.js b/github-actions/activity-trigger/activity-trigger.js index fc5edba508..8e71a577c9 100644 --- a/github-actions/activity-trigger/activity-trigger.js +++ b/github-actions/activity-trigger/activity-trigger.js @@ -1,9 +1,3 @@ -// Global variables -var github; -var context; - - - /** * This function parses the triggered event to determine the trigger eventName and eventAction * and from this information decide the eventActor (user who is credited for the event). @@ -11,10 +5,7 @@ var context; * @param {Object} context - Context of the function calling activity-trigger.js * @returns {Object} - An object containing the eventActor and a message */ -async function activityTrigger({g, c}) { - - github = g; - context = c; +async function activityTrigger({github, context}) { let issueNum = ''; let assignee = ''; @@ -110,7 +101,7 @@ async function activityTrigger({g, c}) { } // Only if issue is closed, and eventActor != assignee, return assignee and message - if (eventAction.includes('Closed-') && (eventActor != assignee)) { + if (eventAction.includes('Closed-') && (eventActor !== assignee)) { message = `- ${assignee} issue ${action}: ${eventUrl} at ${localTime}`; activities.push([assignee, message]); } diff --git a/github-actions/activity-trigger/post-to-skills-issue.js b/github-actions/activity-trigger/post-to-skills-issue.js index 1db5f94e60..f0fef96cc8 100644 --- a/github-actions/activity-trigger/post-to-skills-issue.js +++ b/github-actions/activity-trigger/post-to-skills-issue.js @@ -6,10 +6,6 @@ const checkTeamMembership = require('../utils/check-team-membership'); const statusFieldIds = require('../utils/_data/status-field-ids'); const mutateIssueStatus = require('../utils/mutate-issue-status'); -// Global variables -var github; -var context; - // `complexity0` refers `Complexity: Prework` label const SKILLS_LABEL = retrieveLabelDirectory("complexity0"); @@ -19,20 +15,16 @@ const SKILLS_LABEL = retrieveLabelDirectory("complexity0"); * Function to get eventActor's Skills Issue and post message * @param {Object} github - GitHub object * @param {Object} context - Context object - * @param {Object} package - eventActor and message + * @param {Object} activity - username and message * */ -async function postToSkillsIssue({g, c}, activity) { - - github = g; - context = c; +async function postToSkillsIssue({github, context}, activity) { const owner = context.repo.owner; const repo = context.repo.repo; const TEAM = 'website-write'; - const username = activity[0]; - const message = activity[1]; + const [username, message] = activity; const MARKER = ''; const IN_PROGRESS_ID = statusFieldIds('In_Progress'); @@ -44,19 +36,25 @@ async function postToSkillsIssue({g, c}, activity) { // Return immediately if Skills Issue not found if (skillsIssueNum) { - console.log(`Found Skills Issue for ${username}: ${skillsIssueNum}`); + console.log(`Found Skills Issue for ${username}: #${skillsIssueNum}`); } else { console.log(`Did not find Skills Issue for ${username}. Cannot post message.`); return; } // Get all comments from the Skills Issue - // https://docs.github.com/en/rest/issues/comments?apiVersion=2022-11-28#list-issue-comments - const commentData = await github.request('GET /repos/{owner}/{repo}/issues/{issueNum}/comments', { - owner, - repo, - issueNum: skillsIssueNum, - }); + let commentData; + try { + // https://docs.github.com/en/rest/issues/comments?apiVersion=2022-11-28#list-issue-comments + commentData = await github.request('GET /repos/{owner}/{repo}/issues/{issue_number}/comments', { + owner, + repo, + issue_number: skillsIssueNum, + }); + } catch (err) { + console.error(`GET comments failed for issue #${skillsIssueNum}:`, err); + return; + } // Find the comment that includes the MARKER text and append message const commentFound = commentData.data.find(comment => comment.body.includes(MARKER)); @@ -67,13 +65,18 @@ async function postToSkillsIssue({g, c}, activity) { const commentId = commentFoundId; const originalBody = commentFound.body; const updatedBody = `${originalBody}\n${message}`; - // https://docs.github.com/en/rest/issues/comments?apiVersion=2022-11-28#update-an-issue-comment - await github.request('PATCH /repos/{owner}/{repo}/issues/comments/{commentId}', { - owner, - repo, - commentId, - body: updatedBody - }); + try { + // https://docs.github.com/en/rest/issues/comments?apiVersion=2022-11-28#update-an-issue-comment + await github.request('PATCH /repos/{owner}/{repo}/issues/comments/{commentId}', { + owner, + repo, + commentId, + body: updatedBody + }); + } catch (err) { + console.error(`Something went wrong updating comment:`, err); + } + } else { console.log(`MARKER not found in comments, creating new comment with MARKER...`); const body = `${MARKER}\n## Activity Log: ${username}\n### Repo: https://github.com/hackforla/website\n\n##### ⚠ Important note: The bot updates this comment automatically - do not edit\n\n${message}`; @@ -81,22 +84,26 @@ async function postToSkillsIssue({g, c}, activity) { } // If eventActor is team member, open issue and move to "In progress". Else, close issue - const isActiveMember = await checkTeamMembership(github, username, TEAM); + const isActiveMember = await checkTeamMembership(github, context, username, TEAM); let skillsIssueState = "closed"; if (isActiveMember) { skillsIssueState = "open"; // Update item's status to "In progress (actively working)" if not already - if (skillsStatusId != IN_PROGRESS_ID) { + if (skillsIssueNodeId && skillsStatusId !== IN_PROGRESS_ID) { await mutateIssueStatus(github, context, skillsIssueNodeId, IN_PROGRESS_ID); } } - await github.request('PATCH /repos/{owner}/{repo}/issues/{issueNum}', { - owner, - repo, - issueNum: skillsIssueNum, - state: skillsIssueState, - }); + try { + await github.request('PATCH /repos/{owner}/{repo}/issues/{issue_number}', { + owner, + repo, + issue_number: skillsIssueNum, + state: skillsIssueState, + }); + } catch (err) { + console.error(`Failed to update issue #${skillsIssueNum} state:`, err) + } } module.exports = postToSkillsIssue; diff --git a/github-actions/utils/check-team-membership.js b/github-actions/utils/check-team-membership.js index 1d43fbbb41..f3a673763f 100644 --- a/github-actions/utils/check-team-membership.js +++ b/github-actions/utils/check-team-membership.js @@ -11,10 +11,10 @@ Lack of permission will result in a 403 error. docs on printing context information into the log. */ -async function isMemberOfTeam(github, githubUsername, team) { +async function isMemberOfTeam(github, context, githubUsername, team) { try { await github.rest.teams.getMembershipForUserInOrg({ - org: 'hackforla', + org: context.repo.owner, team_slug: team, username: githubUsername }); From b541df6ed3a38278d8a8601207c7bb2bffe8a5bc Mon Sep 17 00:00:00 2001 From: t-will-gillis <339.colorado.dev@gmail.com> Date: Tue, 26 Aug 2025 22:27:20 -0700 Subject: [PATCH 22/25] more error catching, change to eventActor --- .../activity-trigger/post-to-skills-issue.js | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/github-actions/activity-trigger/post-to-skills-issue.js b/github-actions/activity-trigger/post-to-skills-issue.js index f0fef96cc8..ced2abcd33 100644 --- a/github-actions/activity-trigger/post-to-skills-issue.js +++ b/github-actions/activity-trigger/post-to-skills-issue.js @@ -15,7 +15,7 @@ const SKILLS_LABEL = retrieveLabelDirectory("complexity0"); * Function to get eventActor's Skills Issue and post message * @param {Object} github - GitHub object * @param {Object} context - Context object - * @param {Object} activity - username and message + * @param {Object} activity - eventActor and message * */ async function postToSkillsIssue({github, context}, activity) { @@ -24,21 +24,27 @@ async function postToSkillsIssue({github, context}, activity) { const repo = context.repo.repo; const TEAM = 'website-write'; - const [username, message] = activity; + const [eventActor, message] = activity; const MARKER = ''; const IN_PROGRESS_ID = statusFieldIds('In_Progress'); + // If eventActor undefined, exit + if (!eventActor) { + console.log(`eventActor is undefined (likely a bot). Cannot post message.`); + return; + } + // Get eventActor's Skills Issue number, nodeId, current statusId (all null if no Skills Issue found) - const skillsInfo = await querySkillsIssue(github, context, username, SKILLS_LABEL); + const skillsInfo = await querySkillsIssue(github, context, eventActor, SKILLS_LABEL); const skillsIssueNum = skillsInfo.issueNum; const skillsIssueNodeId = skillsInfo.issueId; const skillsStatusId = skillsInfo.statusId; // Return immediately if Skills Issue not found if (skillsIssueNum) { - console.log(`Found Skills Issue for ${username}: #${skillsIssueNum}`); + console.log(`Found Skills Issue for ${eventActor}: #${skillsIssueNum}`); } else { - console.log(`Did not find Skills Issue for ${username}. Cannot post message.`); + console.log(`Did not find Skills Issue for ${eventActor}. Cannot post message.`); return; } @@ -79,12 +85,12 @@ async function postToSkillsIssue({github, context}, activity) { } else { console.log(`MARKER not found in comments, creating new comment with MARKER...`); - const body = `${MARKER}\n## Activity Log: ${username}\n### Repo: https://github.com/hackforla/website\n\n##### ⚠ Important note: The bot updates this comment automatically - do not edit\n\n${message}`; + const body = `${MARKER}\n## Activity Log: ${eventActor}\n### Repo: https://github.com/hackforla/website\n\n##### ⚠ Important note: The bot updates this comment automatically - do not edit\n\n${message}`; await postComment(skillsIssueNum, body, github, context); } // If eventActor is team member, open issue and move to "In progress". Else, close issue - const isActiveMember = await checkTeamMembership(github, context, username, TEAM); + const isActiveMember = await checkTeamMembership(github, context, eventActor, TEAM); let skillsIssueState = "closed"; if (isActiveMember) { From dc14fb4187f153025a88d8120682885c28f52a2f Mon Sep 17 00:00:00 2001 From: Will Gillis <40799239+t-will-gillis@users.noreply.github.com> Date: Sat, 30 Aug 2025 21:17:25 -0700 Subject: [PATCH 23/25] Create activity-history-post.yml add back changed file, then re-delete --- .github/workflows/activity-history-post.yml | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 .github/workflows/activity-history-post.yml diff --git a/.github/workflows/activity-history-post.yml b/.github/workflows/activity-history-post.yml new file mode 100644 index 0000000000..915d7d7359 --- /dev/null +++ b/.github/workflows/activity-history-post.yml @@ -0,0 +1,21 @@ +name: Post Activity History to all Users + +on: + schedule: + - cron: 25 4 10 8 * + workflow_dispatch: + +jobs: + Read-CSV: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + + - name: Parse CSV + id: parse-csv + uses: actions/github-script@v7 + with: + github-token: ${{ secrets.HACKFORLA_GRAPHQL_TOKEN }} + script: | + const script = require('./github-actions/activity-trigger/first-post-to-skills-issue.js') + return script({g: github, c: context}) From 3c3ec75a7d8927a488e55697c0275ee49df70b12 Mon Sep 17 00:00:00 2001 From: Will Gillis <40799239+t-will-gillis@users.noreply.github.com> Date: Sat, 30 Aug 2025 21:47:51 -0700 Subject: [PATCH 24/25] Delete .github/workflows/activity-history-post.yml re-delete (fixing merge conflict) --- .github/workflows/activity-history-post.yml | 21 --------------------- 1 file changed, 21 deletions(-) delete mode 100644 .github/workflows/activity-history-post.yml diff --git a/.github/workflows/activity-history-post.yml b/.github/workflows/activity-history-post.yml deleted file mode 100644 index 915d7d7359..0000000000 --- a/.github/workflows/activity-history-post.yml +++ /dev/null @@ -1,21 +0,0 @@ -name: Post Activity History to all Users - -on: - schedule: - - cron: 25 4 10 8 * - workflow_dispatch: - -jobs: - Read-CSV: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v5 - - - name: Parse CSV - id: parse-csv - uses: actions/github-script@v7 - with: - github-token: ${{ secrets.HACKFORLA_GRAPHQL_TOKEN }} - script: | - const script = require('./github-actions/activity-trigger/first-post-to-skills-issue.js') - return script({g: github, c: context}) From 19d31ce43e71d3935430d6b2c999a6218f08cd0d Mon Sep 17 00:00:00 2001 From: Will Gillis <40799239+t-will-gillis@users.noreply.github.com> Date: Sat, 30 Aug 2025 21:52:58 -0700 Subject: [PATCH 25/25] Update check-team-membership.js fixed === --- github-actions/utils/check-team-membership.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/github-actions/utils/check-team-membership.js b/github-actions/utils/check-team-membership.js index 6c38899532..7977fe3785 100644 --- a/github-actions/utils/check-team-membership.js +++ b/github-actions/utils/check-team-membership.js @@ -16,11 +16,11 @@ async function isMemberOfTeam(github, context, username, team) { console.log(`User '${username}' is member of team '${team}'`); return true; } catch (verificationError) { - if (verificationError.status == 404) { + if (verificationError.status === 404) { console.log(`User '${username}' is not a team member`); return false; } else { throw verificationError; } } -} \ No newline at end of file +}