Skip to content

Commit

Permalink
feat: validate new issues GHA (#2190)
Browse files Browse the repository at this point in the history
* 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
iowillhoit and jshackell-sfdc committed Jun 12, 2023
1 parent dc70c31 commit c220c8d
Show file tree
Hide file tree
Showing 10 changed files with 372 additions and 25 deletions.
48 changes: 23 additions & 25 deletions .github/ISSUE_TEMPLATE/bug_report.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,41 +6,39 @@ labels: 'investigating'
assignees: ''

---

<!--
NOTICE: GitHub is not a mechanism for receiving support under any agreement or SLA. If you require immediate assistance, please use official support channels.
-->
> **Note**
> Before you submit your issue, make sure that:
> - You're using the latest version of Salesforce CLI.
> - You've searched both open and closed issues for related posts.
> - You've used the `doctor` command to diagnose common issues.
> - You understand that GitHub Issues don't adhere to any agreement or SLA.
> - If you require immediate assistance, use official channels such as Salesforce Customer Support.
### Summary
<!-- Short summary of what's going on or to provide context -->

_Short summary of what is going on or to provide context_.

### Steps To Reproduce:
### Steps To Reproduce

**Repository to reproduce:** [dreamhouse-lwc](https://github.com/dreamhouseapp/dreamhouse-lwc)
> **IMPORTANT**
> Provide a repository that's configured to reproduce the issue. If you are unable to provide a repo, please explain why not. The more info we have from the start, the faster we can resolve your issue.
> We may close your issue if you don't include proper instructions.
>
> - Generate a project with `sf project generate` or fork [dreamhouse-lwc](https://github.com/trailheadapps/dreamhouse-lwc).
> - Provide detailed step-by-step instructions on how to reproduce the issue.
***NOTE:** If your issue is not reproducable by dreamhouse-lwc, i.e. requires specific metadata or files, we **require** a link to a simple Salesforce project repository with a script to setup a scratch org that reproduces your problem.*

1. This is step 1.
1. This is step 2. All steps should start with '1.'

### Expected result

_Describe what should have happened_.
<!-- Describe what should have happened -->

### Actual result

_Describe what actually happened_.
<!-- Describe what actually happened -->

### System Information
<!-- Which shell or terminal are you using? (bash, zsh, powershell 7, cmd.exe, etc) -->
<!-- Paste the **full** output of the `version --verbose --json` command for the CLI you're using (`sf` or `sfdx`) below -->

- Which shell/terminal are you using? (e.g. bash, zsh, powershell 5, powershell 7, cmd.exe, etc.)

- If you are using `sfdx`
- Run `sfdx version --verbose --json`
- If you are using `sf`
- Run `sf version --verbose --json`
- Paste the output here
```json
PASTE_VERSION_OUTPUT_HERE
```
### Additional information

_Feel free to attach a screenshot_.
<!-- Feel free to attach a screenshot -->
17 changes: 17 additions & 0 deletions .github/actions/validate-issue/action.yml
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"
168 changes: 168 additions & 0 deletions .github/actions/validate-issue/index.js
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();
9 changes: 9 additions & 0 deletions .github/actions/validate-issue/messages/old-cli.md
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 .github/actions/validate-issue/messages/provide-version.md
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!
70 changes: 70 additions & 0 deletions .github/actions/validate-issue/sample-context.json
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"
}
}
37 changes: 37 additions & 0 deletions .github/workflows/issue-updated.yml
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}}
Loading

0 comments on commit c220c8d

Please sign in to comment.