Skip to content
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

feat: Triage bot v2 #33555

Draft
wants to merge 7 commits into
base: master
Choose a base branch
from
Draft
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
34 changes: 25 additions & 9 deletions .github/ISSUE_TEMPLATE/01-react-components-bug-report.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,15 @@ body:
- Breadcrumb
- Button
- Card
- CardFooter
- CardHeader
- CardPreview
- Carousel
- CarouselNav
- Checkbox
- Combobox
- CompoundButton
- Counter Badge
- DataGrid
- Dialog
- Divider
Expand All @@ -38,14 +45,19 @@ body:
- Image
- InfoLabel
- Input
- InteractionTag
- Label
- Link
- List
- Menu
- MenuButton
- MenuList
- MessageBar
- Overflow
- Persona
- Popover
- Portal
- PresenceBadge
- ProgressBar
- RadioGroup
- Rating
Expand All @@ -56,32 +68,36 @@ body:
- Slider
- SpinButton
- Spinner
- SplitButton
- SwatchPicker
- Switch
- Table
- TabList
- Table
- Tag
- TagGroup
- TagPicker
- TeachingPopover
- Text
- Textarea
- Toast
- ToggleButton
- Toolbar
- Tooltip
- Tree
- ColorPicker (Preview)
- Nav (Preview)
- Virtualizer (Preview)
- VirtualizerScrollView (Preview)
- VirtualizerScrollViewDynamic (Preview)
- Calendar (Compat)
- DatePicker (Compat)
- TimePicker (Compat)
- Carousel (Preview)
- List (Preview)
- Nav (Preview)
- Virtualizer (Preview)
- Motion
- Icons
- Migration Shims V0
- Migration Shims V8
- Motion
- Theme/Tokens
- Utilities (utilities we provide besides Components, e.g. apis from react-utilities)
- Migration Shims v0
- Migration Shims v8
- Utilities
- Other...
validations:
required: true
Expand Down
24 changes: 24 additions & 0 deletions .github/triage-bot.config-v2.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
Copy link
Collaborator

@fabricteam fabricteam Jan 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🕵🏾‍♀️ visual regressions to review in the fluentuiv8 Visual Regression Report

react-charting-AreaChart 1 screenshots
Image Name Diff(in Pixels) Image Type
react-charting-AreaChart.Custom Accessibility.chromium.png 11 Changed
react-charting-LineChart 1 screenshots
Image Name Diff(in Pixels) Image Type
react-charting-LineChart.Gaps.chromium.png 1 Changed

Copy link
Collaborator

@fabricteam fabricteam Jan 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🕵🏾‍♀️ visual regressions to review in the fluentuiv9 Visual Regression Report

Avatar Converged 1 screenshots
Image Name Diff(in Pixels) Image Type
Avatar Converged.Badge Mask RTL.chromium.png 5 Changed
Drawer 2 screenshots
Image Name Diff(in Pixels) Image Type
Drawer.Full Overlay RTL.chromium.png 1167 Changed
Drawer.Full Overlay High Contrast.chromium.png 2247 Changed

"$schema": "../scripts/triage-bot/triage-bot.schema-v2.json",
"params": [
{
"frameworkType": "Fluent UI react-components (v9)",
"headingToParse": "Component",
"mapping": {
"packages/react-components/react-accordion": ["Accordion"],
"packages/react-components/react-avatar": ["Avatar", "AvatarGroup"],
"packages/react-components/react-utilities": ["Utilities"],
"packages/react-components/react-provider": ["FluentProvider"],
"packages/react-components/react-migration-v0-v9": ["Migration Shims v0"],
"packages/react-components/react-migration-v8-v9": ["Migration Shims v8"]
}
},
{
"frameworkType": "Fluent UI react (v8)",
"headingToParse": "Package",
"mapping": {
"packages/azure-themes": ["azure-themes"]
}
}
]
}
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@
"scrub": "node ./scripts/executors/src/scrub.js"
},
"devDependencies": {
"@actions/core": "1.9.1",
"@actions/github": "5.0.3",
"@actions/core": "1.11.1",
"@actions/github": "6.0.0",
"@babel/core": "7.24.6",
"@babel/generator": "7.24.6",
"@babel/parser": "7.24.6",
Expand Down Expand Up @@ -137,6 +137,7 @@
"@types/eslint": "8.56.10",
"@types/express": "4.17.21",
"@types/fs-extra": "8.0.1",
"@types/github-script": "github:actions/github-script#v7.0.1",
"@types/glob": "7.1.1",
"@types/graphviz": "0.0.34",
"@types/gulp": "4.0.9",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export { FirstRunExperience } from './CarouselFirstRunExperience.stories';
export { Eventing } from './CarouselEventing.stories';

export default {
title: 'Components/Carousel',
title: 'Components/Carousel/Carousel',
component: Carousel,
subcomponents: {
CarouselAutoplayButton,
Expand Down
66 changes: 66 additions & 0 deletions scripts/triage-bot/src/codeowners-parser.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/**
*
* @param {Array<import('./types').FileOwnershipMatcher>} rules
*/
function serialize(rules) {
return rules
.map(rule => {
return [rule.path, rule.owners].join(' ');
})
.join('\n');
}

/**
*
* @param {string} content
*/
function parse(content) {
/** @type {Array<import('./types').FileOwnershipMatcher>} */
const rules = [];

if (content.length === 0) {
return rules;
}

const rawLines = content.trim().split('\n');

for (const rawLine of rawLines) {
const line = rawLine.trim();

if (!line || line.startsWith('#')) {
continue;
}

rules.push(createRule(line));
}

return rules;
}

/**
*
* @param {string} rule
* @returns {import('./types').FileOwnershipMatcher}
*/
function createRule(rule) {
const rawParts = rule.split(/\s+/);
/** @type {string[]} */
const parts = [];
for (const part of rawParts) {
if (part.startsWith('#')) {
break;
}
parts.push(part);
}

// The first part is expected to be the path
const path = parts[0];

// Rest of parts is expected to be the owners
const owners = parts.length > 1 ? parts.slice(1, parts.length) : [];

return { path, owners };
}

exports.serialize = serialize;
exports.parse = parse;
124 changes: 124 additions & 0 deletions scripts/triage-bot/src/codeowners-parser.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
import { parse, serialize } from './codeowners-parser'; // Replace with the actual module path

describe('#serialize', () => {
it('should return an empty string when rules are empty', () => {
const result = serialize([]);
expect(result).toEqual('');
});

it(`should return valid codeowners file based on rules`, () => {
const result = serialize([
{ path: 'src/*.js', owners: ['@owner1', '@[email protected]'] },
{ path: 'docs/*.md', owners: ['@owner3'] },
]);
expect(result).toMatchInlineSnapshot(`
"src/*.js @owner1,@[email protected]
docs/*.md @owner3"
`);
});
});

describe('#parse', () => {
it('should return an empty array when content is empty', () => {
const result = parse('');
expect(result).toEqual([]);
});

it('should return an empty array when content only contains comments', () => {
const content = `
# This is a comment
# Another comment line
`;
const result = parse(content);
expect(result).toEqual([]);
});

it('should parse a single rule correctly', () => {
const content = `
src/*.js @owner1 @owner2 @team/one [email protected]
`;
const result = parse(content);
expect(result).toEqual([
{
path: 'src/*.js',
owners: ['@owner1', '@owner2', '@team/one', '[email protected]'],
},
]);
});

it('should ignore inline comments after rules', () => {
const content = `
src/*.js @owner1 @owner2 # inline comment
`;
const result = parse(content);
expect(result).toEqual([
{
path: 'src/*.js',
owners: ['@owner1', '@owner2'],
},
]);
});

it('should parse multiple rules correctly', () => {
const content = `
src/*.js @owner1 @owner2
docs/*.md @owner3
# A comment
assets/*.png
`;
const result = parse(content);
expect(result).toEqual([
{
path: 'src/*.js',
owners: ['@owner1', '@owner2'],
},
{
path: 'docs/*.md',
owners: ['@owner3'],
},
{
path: 'assets/*.png',
owners: [],
},
]);
});

it('should trim whitespace from lines and handle empty lines', () => {
const content = `
src/*.js @owner1 @owner2

docs/*.md @owner3

# Comment
`;
const result = parse(content);
expect(result).toEqual([
{
path: 'src/*.js',
owners: ['@owner1', '@owner2'],
},
{
path: 'docs/*.md',
owners: ['@owner3'],
},
]);
});

it('should handle rules with no owners', () => {
const content = `
src/*.js
docs/*.md
`;
const result = parse(content);
expect(result).toEqual([
{
path: 'src/*.js',
owners: [],
},
{
path: 'docs/*.md',
owners: [],
},
]);
});
});
33 changes: 33 additions & 0 deletions scripts/triage-bot/src/triage-bot-v2.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/* eslint-disable @typescript-eslint/naming-convention */

/** @typedef {import('github-script').AsyncFunctionArguments} AsyncFunctionArguments */

/**
*
* @param {AsyncFunctionArguments} options
*/
async function main(options) {
const { context, github } = options;

const issueNumber = context.payload?.issue?.number;

if (!issueNumber) {
throw new Error('no issue number provided!');
}

const issue = await github.rest.issues.get({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issueNumber,
});

const issueMD = issue.data.body;

// 1. todo parse markdown
// 2. extract Component string `## Component > content`
// 3. assign Labels based on Component
// 4. assign code-owners based on Labels
console.log(issueMD);
}

module.exports = main;
20 changes: 20 additions & 0 deletions scripts/triage-bot/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,23 @@ export interface Schema {
assignees: string[];
}>;
}

export interface SchemaV2 {
params: Array<{
/** Label added by issue template */
frameworkType: string;
/** Which heading is the source of truth for parsing selected Option */
headingToParse: string;
/**
* Mapping used against parsed CODEOWNERS file
*/
mapping: {
[projectRoot: string]: /* Options within issue Dropdown */ string[];
};
}>;
}

export interface FileOwnershipMatcher {
path: string;
owners: string[];
}
Loading
Loading