-
-
Notifications
You must be signed in to change notification settings - Fork 845
Create new GHA to add comments to Skills Issue in re: event activities #8248
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
t-will-gillis
merged 28 commits into
hackforla:gh-pages
from
t-will-gillis:gha-add-comments-skills-4820
Aug 31, 2025
Merged
Changes from 5 commits
Commits
Show all changes
28 commits
Select commit
Hold shift + click to select a range
b2d9079
Create member-activity-trigger.yml
t-will-gillis d108995
Rename member-activity-trigger.yml to activity-trigger.yml
t-will-gillis 9f912c6
Create activity-trigger.js
t-will-gillis 9557aef
Create post-to-skills-issue.js
t-will-gillis 297b97c
Create get-skills-issue.js
t-will-gillis d198314
change 'retrieve' --> 'get'
t-will-gillis f99e604
revise comments
t-will-gillis 01154d1
Declare variables
t-will-gillis 190f7bb
declared variables; team --> TEAM; add semicolon; remove variable dec…
t-will-gillis bbca6e8
Add conditional for GHA to run only if 'hackforla/website'
t-will-gillis 424dcd3
Merge branch 'hackforla:gh-pages' into gha-add-comments-skills-4820
t-will-gillis 047e852
edits to match existing Skills Issue comments
t-will-gillis 1736461
reformat history and add error handling
t-will-gillis e6ffb12
major updates to get graphQL working
t-will-gillis e13dcd3
revert to previous
t-will-gillis 2e72548
Merge branch 'hackforla:gh-pages' into gha-add-comments-skills-4820
t-will-gillis 704cb2f
change 'pull request' to 'PR'
t-will-gillis 6c83608
major change- 2 actors PR closed
t-will-gillis ad7183a
tweak for pr.closed if actor same for both, return one
t-will-gillis b7b0874
Update activity-trigger.js
t-will-gillis e67917d
Bump `actions/checkout@v4` --> `@v5` (Dependabot)
t-will-gillis ef5987a
add error handling at fieldValues
t-will-gillis 926d2bf
add in error handling, minor changes
t-will-gillis b541df6
more error catching, change to eventActor
t-will-gillis dc14fb4
Create activity-history-post.yml
t-will-gillis 0c608a9
Merge branch 'gh-pages' into gha-add-comments-skills-4820
t-will-gillis 3c3ec75
Delete .github/workflows/activity-history-post.yml
t-will-gillis 19d31ce
Update check-team-membership.js
t-will-gillis File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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) | ||
|
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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; | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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; | ||
github-advanced-security[bot] marked this conversation as resolved.
Fixed
Show fixed
Hide fixed
|
||
| context = c; | ||
github-advanced-security[bot] marked this conversation as resolved.
Fixed
Show fixed
Hide fixed
|
||
|
|
||
| const owner = context.repo.owner; | ||
| const repo = context.repo.repo; | ||
| const TEAM = 'website-write'; | ||
t-will-gillis marked this conversation as resolved.
Fixed
Show fixed
Hide fixed
|
||
|
|
||
| const username = activity[0]; | ||
| const message = activity[1]; | ||
| const MARKER = '<!-- Skills Issue Activity Record -->'; | ||
|
|
||
| // 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; | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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; |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.