Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 70 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,73 @@ jobs:
fail-fast: false
with:
os: ${{ matrix.os }}
notify-failure:
needs: [linux-unit-tests, windows-unit-tests, nuts]
if: >-
failure() &&
github.actor != 'svc-cli-bot' &&
github.actor != 'dependabot[bot]'
runs-on: ubuntu-latest
steps:
- name: Get failed jobs
id: failed
run: |
FAILED=""
if [ "$LINUX" = "failure" ]; then FAILED="linux-unit-tests"; fi
if [ "$WINDOWS" = "failure" ]; then FAILED="${FAILED:+$FAILED, }windows-unit-tests"; fi
if [ "$NUTS" = "failure" ]; then FAILED="${FAILED:+$FAILED, }nuts"; fi
echo "jobs=$FAILED" >> "$GITHUB_OUTPUT"
env:
LINUX: ${{ needs.linux-unit-tests.result }}
WINDOWS: ${{ needs.windows-unit-tests.result }}
NUTS: ${{ needs.nuts.result }}
- name: Get PR info
id: pr
run: |
PR_JSON=$(gh api "repos/${REPO}/pulls?head=${OWNER}:${BRANCH}&state=open" --jq '.[0] | {number, title}')
echo "number=$(echo "$PR_JSON" | jq -r '.number')" >> "$GITHUB_OUTPUT"
echo "title=$(echo "$PR_JSON" | jq -r '.title')" >> "$GITHUB_OUTPUT"
env:
GH_TOKEN: ${{ github.token }}
REPO: ${{ github.repository }}
OWNER: ${{ github.repository_owner }}
BRANCH: ${{ github.ref_name }}
- name: Get Slack user ID
id: slack-user
run: |
SLACK_ID=$(echo "$USER_MAP" | jq -r --arg user "$GITHUB_ACTOR" '.[$user] // empty')
if [ -n "$SLACK_ID" ]; then
echo "mention=<@${SLACK_ID}>" >> "$GITHUB_OUTPUT"
else
echo "mention=${GITHUB_ACTOR}" >> "$GITHUB_OUTPUT"
fi
env:
USER_MAP: ${{ secrets.SLACK_USER_MAP }}
GITHUB_ACTOR: ${{ github.actor }}
- name: Notify Slack
if: steps.pr.outputs.number != 'null'
uses: slackapi/slack-github-action@v1.26.0
env:
SLACK_WEBHOOK_URL: ${{ secrets.CLI_ALERTS_SLACK_WEBHOOK }}
SLACK_WEBHOOK_TYPE: INCOMING_WEBHOOK
with:
payload: |
{
"text": "Workflow \"tests/${{ steps.failed.outputs.jobs }}\" failed in ${{ github.event.repository.name }}",
"blocks": [
{
"type": "header",
"text": {
"type": "plain_text",
"text": ":x: Workflow \"tests/${{ steps.failed.outputs.jobs }}\" failed in ${{ github.event.repository.name }} :x:"
}
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "*PR:* <${{ github.event.repository.html_url }}/pull/${{ steps.pr.outputs.number }}|#${{ steps.pr.outputs.number }} ${{ steps.pr.outputs.title }}>\n*Author:* ${{ steps.slack-user.outputs.mention }}\n*Job url:* ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"
}
}
]
}
97 changes: 51 additions & 46 deletions src/commands/doctor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
import { EOL } from 'node:os';
import { resolve as pathResolve, join } from 'node:path';
import { spawn } from 'node:child_process';
import { Flags, loglevel, SfCommand } from '@salesforce/sf-plugins-core';
import { Flags, loglevel, SfCommand, StandardColors } from '@salesforce/sf-plugins-core';
import { Lifecycle, Messages, SfError } from '@salesforce/core';
import open from 'open';
import got from 'got';
Expand Down Expand Up @@ -73,7 +73,14 @@ export default class Doctor extends SfCommand<SfDoctorDiagnosis> {
this.outputDir = pathResolve(flags['output-dir'] ?? process.cwd());

lifecycle.on<DiagnosticStatus>('Doctor:diagnostic', async (data) => {
this.log(`${data.status} - ${data.testName}`);
const colorMap = {
pass: StandardColors.success,
fail: StandardColors.error,
warn: StandardColors.warning,
unknown: StandardColors.warning,
} as const;
const colorFn = colorMap[data.status];
this.log(`${colorFn(data.status)} - ${data.testName}`);
return Promise.resolve(this.doctor.addDiagnosticStatus(data));
});

Expand Down Expand Up @@ -122,7 +129,14 @@ export default class Doctor extends SfCommand<SfDoctorDiagnosis> {

this.log();
this.styledHeader('Suggestions');
diagnosis.suggestions.forEach((s) => this.log(` * ${s}`));
const pinnedCount = 2;
const pinned = diagnosis.suggestions.slice(0, pinnedCount);
const actionable = diagnosis.suggestions.slice(pinnedCount);
if (actionable.length) {
actionable.forEach((s) => this.log(` ${StandardColors.warning('⚠')} ${s}`));
this.log();
}
pinned.forEach((s) => this.log(` * ${s}`));

if (flags['create-issue']) {
const raw = 'https://raw.githubusercontent.com/forcedotcom/cli/main/.github/ISSUE_TEMPLATE/bug_report.md';
Expand Down Expand Up @@ -162,51 +176,42 @@ export default class Doctor extends SfCommand<SfDoctorDiagnosis> {
}

private generateIssueMarkdown(body: string, diagnosis: SfDoctorDiagnosis): string {
const info = `
\`\`\`
CLI:
${diagnosis.cliConfig.userAgent}
const diagnosticIcon = (status: string): string => {
if (status === 'pass') return ':white_check_mark:';
if (status === 'warn') return ':warning:';
return ':x:';
};

Plugin Version:
${diagnosis.versionDetail.pluginVersions.join(EOL)}
\`\`\`
${
diagnosis.sfdxEnvVars.length
? `
\`\`\`
SFDX ENV. VARS.
${diagnosis.sfdxEnvVars.join(EOL)}
\`\`\`
`
: ''
}
${
diagnosis.sfEnvVars.length
? `
\`\`\`
SF ENV. VARS.
${diagnosis.sfEnvVars.join(EOL)}
\`\`\`
`
: ''
}
\`\`\`
Windows: ${diagnosis.cliConfig.windows}
Shell: ${diagnosis.cliConfig.shell}
Channel: ${diagnosis.cliConfig.channel}
\`\`\`
---
### Diagnostics
${this.doctor
.getDiagnosis()
.diagnosticResults.map(
(res) => `${res.status === 'pass' ? ':white_check_mark:' : ':x:'} ${res.status} - ${res.testName}`
)
.join(EOL)}
`;
const systemInfo = [
'```',
'CLI:',
diagnosis.cliConfig.userAgent,
'',
'Plugin Version:',
...diagnosis.versionDetail.pluginVersions,
'```',
...(diagnosis.sfdxEnvVars.length ? ['', '```', 'SFDX ENV. VARS.', ...diagnosis.sfdxEnvVars, '```'] : []),
...(diagnosis.sfEnvVars.length ? ['', '```', 'SF ENV. VARS.', ...diagnosis.sfEnvVars, '```'] : []),
'',
'```',
`Windows: ${diagnosis.cliConfig.windows}`,
`Shell: ${diagnosis.cliConfig.shell}`,
`Channel: ${diagnosis.cliConfig.channel}`,
'```',
'---',
'### Diagnostics',
...this.doctor
.getDiagnosis()
.diagnosticResults.map((res) => `${diagnosticIcon(res.status)} ${res.status} - ${res.testName}`),
].join(EOL);

// Remove the frontmatter and Note block, but preserve Summary, Steps To Reproduce,
// and all other sections. Replace the System Information placeholder with actual data.
return body
.replace(/---(?:.*\n)*>\s.*\n/gm, '')
.replace(/<!-- Which shell(?:.*\n)*.*/gm, info)
.replace(/---\nname:[\s\S]*?---\n/, '')
.replace(/> \*\*Note\*\*[\s\S]*?> {3}- If you require immediate assistance.*\n/m, '')
.replace(/> \[!TIP\]\n>.*\n/m, '')
.replace(/<!-- Which shell[\s\S]*PASTE_VERSION_OUTPUT_HERE\n```/m, systemInfo)
.trim();
}

Expand Down
6 changes: 6 additions & 0 deletions test/commands/doctor.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,12 @@ import { prompts } from '../../src/shared/prompts.js';
Messages.importMessagesDirectoryFromMetaUrl(import.meta.url);
const messages = Messages.loadMessages('@salesforce/plugin-info', 'doctor');

describe('TEMPORARY - test slack notification', () => {
it('should fail on purpose', () => {
expect(true).to.equal(false);
});
});

let oclifConfig: Config;

const getVersionDetailStub = (overrides?: Partial<Interfaces.VersionDetails>): Interfaces.VersionDetails => {
Expand Down
Loading