-
Notifications
You must be signed in to change notification settings - Fork 78
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: validate new issues GHA (#2190)
* feat: validate new issues GHA * edit bug_report.md * edit old-cli.md * edit provide-version.md * Update old-cli.md * Update bug_report.md * chore: add/remove labels, supports comments * add date check --------- Co-authored-by: Juliet Shackell <63259011+jshackell-sfdc@users.noreply.github.com>
- Loading branch information
1 parent
dc70c31
commit c220c8d
Showing
10 changed files
with
372 additions
and
25 deletions.
There are no files selected for viewing
This file contains 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
This file contains 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,17 @@ | ||
# | ||
# Copyright (c) 2023, salesforce.com, inc. | ||
# All rights reserved. | ||
# Licensed under the BSD 3-Clause license. | ||
# For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause | ||
# | ||
|
||
name: "Salesforce CLI Validate Issues" | ||
description: "Validate information provided in an new Github issue." | ||
author: "Eric Willhoit" | ||
inputs: | ||
repo-token: | ||
required: true | ||
description: "Token taken from secrets env var" | ||
runs: | ||
using: "node16" | ||
main: "index.js" |
This file contains 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,168 @@ | ||
const fs = require("fs"); | ||
const path = require("path"); | ||
const { getInput, setOutput, setFailed } = require("@actions/core"); | ||
const { context, getOctokit } = require("@actions/github"); | ||
const execSync = require("child_process").execSync; | ||
const semver = require("semver"); | ||
|
||
async function run() { | ||
try { | ||
// Set this env var to true to test locally | ||
// Example: GHA_VALIDATE_ISSUE_LOCAL=true node .github/actions/validate-issue/index.js | ||
const local = process.env.GHA_VALIDATE_ISSUE_LOCAL; | ||
const issue = local ? JSON.parse(getFile("./sample-context.json")) : context.payload.issue; | ||
|
||
if (!issue) { | ||
setFailed("github.context.payload.issue does not exist"); | ||
return; | ||
} | ||
|
||
// Temporary check to prevent this action from running on old issues | ||
// This will prevent noise on tickets already being investigated | ||
// This can be removed once the action has been running for a while | ||
const creationDate = new Date(issue.created_at); | ||
const cutoffDate = new Date("2023-06-11T00:00:00Z"); | ||
if (creationDate < cutoffDate) { | ||
console.log("Issue was created before 6/11/2023, skipping"); | ||
return; | ||
} | ||
|
||
const token = local ? process.env.GH_TOKEN : getInput("repo-token"); | ||
|
||
// Create a GitHub client | ||
const octokit = getOctokit(token); | ||
|
||
// Get owner and repo from context | ||
if (local) process.env.GITHUB_REPOSITORY = "iowillhoit/gha-sandbox"; | ||
const owner = context.repo.owner; | ||
const repo = context.repo.repo; | ||
const issue_number = issue.number; | ||
|
||
console.log("Issue URL:", issue.html_url); | ||
|
||
const { body } = issue; | ||
const { login: author } = issue.user; | ||
const { data: comments } = await getAllComments(); | ||
|
||
// For version checks, we only care about comments from the author | ||
const authorComments = comments.filter((comment) => comment.user.login === author); | ||
// Build an array of the issue body and all of the comment bodies | ||
const bodies = [body, ...authorComments.map((comment) => comment.body)]; | ||
|
||
const sfVersionRegex = /@salesforce\/cli\/([0-9]+.[0-9]+.[0-9]+(-[a-zA-Z0-9]+.[0-9]+)?)/g; | ||
const sfdxVersionRegex = /sfdx-cli\/([0-9]+.[0-9]+.[0-9]+(-[a-zA-Z0-9]+.[0-9]+)?)/g; | ||
const pluginVersionsRegex = /pluginVersions|Plugin Version:/; | ||
|
||
// Search all bodies and get an array of all versions found (first capture group) | ||
const sfVersions = bodies.map((body) => [...body.matchAll(sfVersionRegex)].map((match) => match[1])).flat(); | ||
const sfdxVersions = bodies.map((body) => [...body.matchAll(sfdxVersionRegex)].map((match) => match[1])).flat(); | ||
// If we match pluginVersionRegex anywhere, we assume the user has provided the full --verbose output | ||
const pluginVersionsIncluded = bodies.some((body) => body.match(pluginVersionsRegex)); | ||
|
||
console.log("sfVersions", sfVersions); | ||
console.log("sfdxVersions", sfdxVersions); | ||
console.log("pluginVersionsIncluded", pluginVersionsIncluded); | ||
|
||
if ((sfVersions.length > 0 || sfdxVersions.length > 0) && pluginVersionsIncluded) { | ||
// FUTURE TODO: | ||
// - Check for bundled plugins that are user installed (user) or linked (link) | ||
// - Could do a check to see if the users has a prerelease version installed | ||
let valid = true; | ||
|
||
if (sfVersions.length > 0) { | ||
const sfLatest = getLatestVersion("@salesforce/cli"); | ||
const oneSatisfies = sfVersions.some((version) => semver.gte(version, sfLatest)); | ||
|
||
if (!oneSatisfies) { | ||
const oldSf = getFile("./messages/old-cli.md", { THE_AUTHOR: author, USER_CLI: "sf", USER_VERSION: sfVersions.join("`, `"), LATEST_VERSION: sfLatest }); | ||
postComment(oldSf); | ||
valid = false; | ||
} | ||
} | ||
if (sfdxVersions.length > 0) { | ||
// TODO: Eventually suggest using sf@v2, a new md template could be created | ||
const sfdxLatest = getLatestVersion("sfdx-cli"); | ||
const oneSatisfies = sfdxVersions.some((version) => semver.gte(version, sfdxLatest)); | ||
|
||
if (!oneSatisfies) { | ||
const oldSfdx = getFile("./messages/old-cli.md", { THE_AUTHOR: author, USER_CLI: "sfdx", USER_VERSION: sfdxVersions.join("`, `"), LATEST_VERSION: sfdxLatest }); | ||
postComment(oldSfdx); | ||
valid = false; | ||
} | ||
} | ||
|
||
if (valid) { | ||
console.log("All information provided is valid!"); | ||
removeLabel("more information needed"); | ||
addLabel("investigating"); | ||
// This label will prevent the action from running again after version info has been confirmed | ||
// Otherwise, this action will continue to trigger after every weekly release as `latest` is bumped | ||
addLabel("validated"); | ||
} else { | ||
console.log("Information provided is NOT valid"); | ||
addLabel("more information needed"); | ||
removeLabel("investigating"); | ||
} | ||
} else { | ||
console.log("Full version information was not provided"); | ||
const message = getFile("./messages/provide-version.md", { THE_AUTHOR: issue.user.login }); | ||
postComment(message); | ||
addLabel("more information needed"); | ||
removeLabel("investigating"); | ||
} | ||
|
||
// --------- | ||
// FUNCTIONS | ||
// --------- | ||
async function getAllComments() { | ||
return await octokit.rest.issues.listComments({ owner, repo, issue_number }); | ||
} | ||
|
||
async function postComment(body) { | ||
// Check that this comment has not been previously commented | ||
if (comments.length) { | ||
if (comments.some((comment) => comment.body === body)) { | ||
console.log("Already commented"); | ||
return; | ||
} | ||
} | ||
|
||
return await octokit.rest.issues.createComment({ owner, repo, issue_number, body }); | ||
} | ||
|
||
async function addLabel(label) { | ||
await octokit.rest.issues.addLabels({ owner, repo, issue_number, labels: [label] }); | ||
} | ||
|
||
async function removeLabel(label) { | ||
try { | ||
await octokit.rest.issues.removeLabel({ owner, repo, issue_number, name: label }); | ||
} catch (error) { | ||
if (error.status === 404) { | ||
console.log(`Cannot remove label '${label}' since it was not applied`); | ||
return; | ||
} | ||
throw error; | ||
} | ||
} | ||
|
||
function getLatestVersion(plugin) { | ||
const distTags = execSync(`npm view ${plugin} dist-tags --json`).toString(); | ||
return JSON.parse(distTags).latest; | ||
} | ||
|
||
function getFile(filename, replacements) { | ||
let contents = fs.readFileSync(path.join(__dirname, filename), "utf8"); | ||
|
||
Object.entries(replacements || {}).map(([key, value]) => { | ||
contents = contents.replaceAll(key, value); | ||
}); | ||
|
||
return contents; | ||
} | ||
} catch (error) { | ||
setFailed(error.message); | ||
} | ||
} | ||
|
||
run(); |
This file contains 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,9 @@ | ||
Hello @THE_AUTHOR :wave: None of the versions of `USER_CLI` you shared match the latest release. | ||
|
||
Shared: `USER_VERSION` | ||
Latest: `LATEST_VERSION` | ||
|
||
Update to the latest version of Salesforce CLI ([docs](https://developer.salesforce.com/docs/atlas.en-us.sfdx_setup.meta/sfdx_setup/sfdx_setup_update_cli.htm)) and confirm that you're still seeing your issue. | ||
You can also try the `rc` and `nightly` releases! ([docs](https://developer.salesforce.com/docs/atlas.en-us.sfdx_setup.meta/sfdx_setup/sfdx_setup_install_cli_rc.htm)) | ||
|
||
After updating, share the full output of `USER_CLI version --verbose --json` |
13 changes: 13 additions & 0 deletions
13
.github/actions/validate-issue/messages/provide-version.md
This file contains 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,13 @@ | ||
Hello @THE_AUTHOR :wave: It looks like you didn't include the full Salesforce CLI version information in your issue. | ||
Please provide the output of `version --verbose --json` for the CLI you're using (`sf` or `sfdx`). | ||
|
||
A few more things to check: | ||
|
||
- Make sure you've provided detailed steps to reproduce your issue. | ||
- A repository that clearly demonstrates the bug is ideal. | ||
- Make sure you've installed the latest version of Salesforce CLI. ([docs](https://developer.salesforce.com/docs/atlas.en-us.sfdx_setup.meta/sfdx_setup/sfdx_setup_update_cli.htm)) | ||
- Better yet, try the `rc` or `nightly` versions. ([docs](https://developer.salesforce.com/docs/atlas.en-us.sfdx_setup.meta/sfdx_setup/sfdx_setup_install_cli_rc.htm)) | ||
- Try running the `doctor` command to diagnose common issues. | ||
- Search GitHub for existing related issues. | ||
|
||
Thank you! |
This file contains 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,70 @@ | ||
{ | ||
"active_lock_reason": null, | ||
"assignee": null, | ||
"assignees": [], | ||
"author_association": "OWNER", | ||
"body": "@salesforce/cli/1.60.5 sfdx-cli/7.204.6 darwin-arm64 node-v18.15.0 pluginVersions", | ||
"closed_at": null, | ||
"comments": 0, | ||
"comments_url": "https://api.github.com/repos/iowillhoit/gha-sandbox/issues/16/comments", | ||
"created_at": "2023-06-06T21:03:32Z", | ||
"events_url": "https://api.github.com/repos/iowillhoit/gha-sandbox/issues/16/events", | ||
"html_url": "https://github.com/iowillhoit/gha-sandbox/issues/16", | ||
"id": 1744609461, | ||
"labels": [ | ||
{ | ||
"color": "FBCA04", | ||
"default": false, | ||
"description": "", | ||
"id": 5589614072, | ||
"name": "investigating", | ||
"node_id": "LA_kwDOI7bi188AAAABTSq9-A", | ||
"url": "https://api.github.com/repos/iowillhoit/gha-sandbox/labels/investigating" | ||
} | ||
], | ||
"labels_url": "https://api.github.com/repos/iowillhoit/gha-sandbox/issues/16/labels{/name}", | ||
"locked": false, | ||
"milestone": null, | ||
"node_id": "I_kwDOI7bi185n_KC1", | ||
"number": 16, | ||
"performed_via_github_app": null, | ||
"reactions": { | ||
"+1": 0, | ||
"-1": 0, | ||
"confused": 0, | ||
"eyes": 0, | ||
"heart": 0, | ||
"hooray": 0, | ||
"laugh": 0, | ||
"rocket": 0, | ||
"total_count": 0, | ||
"url": "https://api.github.com/repos/iowillhoit/gha-sandbox/issues/16/reactions" | ||
}, | ||
"repository_url": "https://api.github.com/repos/iowillhoit/gha-sandbox", | ||
"state": "open", | ||
"state_reason": null, | ||
"timeline_url": "https://api.github.com/repos/iowillhoit/gha-sandbox/issues/16/timeline", | ||
"title": "Test", | ||
"updated_at": "2023-06-06T21:49:50Z", | ||
"url": "https://api.github.com/repos/iowillhoit/gha-sandbox/issues/16", | ||
"user": { | ||
"avatar_url": "https://avatars.githubusercontent.com/u/1715111?v=4", | ||
"events_url": "https://api.github.com/users/iowillhoit/events{/privacy}", | ||
"followers_url": "https://api.github.com/users/iowillhoit/followers", | ||
"following_url": "https://api.github.com/users/iowillhoit/following{/other_user}", | ||
"gists_url": "https://api.github.com/users/iowillhoit/gists{/gist_id}", | ||
"gravatar_id": "", | ||
"html_url": "https://github.com/iowillhoit", | ||
"id": 1715111, | ||
"login": "iowillhoit", | ||
"node_id": "MDQ6VXNlcjE3MTUxMTE=", | ||
"organizations_url": "https://api.github.com/users/iowillhoit/orgs", | ||
"received_events_url": "https://api.github.com/users/iowillhoit/received_events", | ||
"repos_url": "https://api.github.com/users/iowillhoit/repos", | ||
"site_admin": false, | ||
"starred_url": "https://api.github.com/users/iowillhoit/starred{/owner}{/repo}", | ||
"subscriptions_url": "https://api.github.com/users/iowillhoit/subscriptions", | ||
"type": "User", | ||
"url": "https://api.github.com/users/iowillhoit" | ||
} | ||
} |
This file contains 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,37 @@ | ||
# | ||
# Copyright (c) 2021, salesforce.com, inc. | ||
# All rights reserved. | ||
# Licensed under the BSD 3-Clause license. | ||
# For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause | ||
# | ||
|
||
name: "validate-updated-issue" | ||
on: | ||
issues: | ||
types: [edited] | ||
issue_comment: | ||
types: [created, edited] | ||
|
||
jobs: | ||
validate-issue: | ||
# Has label: 'more information needed' | ||
# Does NOT have labels: | ||
# - 'validated' | ||
# - 'investigating' | ||
# - 'feature' | ||
# - 'owned by another team' | ||
# - 'bug' | ||
if: contains(github.event.issue.labels.*.name, 'more information needed') && !contains(github.event.issue.labels.*.name, 'validated') && !contains(github.event.issue.labels.*.name, 'investigating') && !contains(github.event.issue.labels.*.name, 'feature') && !contains(github.event.issue.labels.*.name, 'owned by another team') && !contains(github.event.issue.labels.*.name, 'bug') | ||
runs-on: ubuntu-latest | ||
steps: | ||
- uses: actions/checkout@v3.0.0 | ||
- uses: actions/setup-node@v3 | ||
with: | ||
node-version: lts/* | ||
- name: Install dependencies | ||
run: yarn install | ||
- name: Validate issue | ||
id: validate-issue | ||
uses: ./.github/actions/validate-issue | ||
with: | ||
repo-token: ${{ secrets.GITHUB_TOKEN}} |
Oops, something went wrong.