mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Automate the rotation of issue trage squad (#50526)
Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/50526 This change wants to automate the rotation of the issue triaging squad on Discord. This is taking us a few minutes every week to rotate the oncall. This change can save us some time. The configuration file format is supposed to be like this: ``` { "userMap": { "discord-username1": "discord-id1", "discord-username2": "discord-id2", "discord-username3": "discord-id3", "discord-username4": "discord-id4" }, "schedule": { "date1": ["discord-username1", "discord-username2"], "date2": ["discord-username3", "discord-username4"], } } ``` ## Changelog [Internal] - Automate the issue triage oncall rotation Reviewed By: cortinico Differential Revision: D72569435 fbshipit-source-id: 435c13350cf503e99302775674e78a20e328e68d
This commit is contained in:
committed by
Facebook GitHub Bot
parent
c3c7f9300f
commit
890b8f7ea3
@@ -0,0 +1,83 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @format
|
||||
*/
|
||||
|
||||
const {extractIssueOncalls} = require('../extractIssueOncalls');
|
||||
|
||||
const userMap = {
|
||||
'@g': '1785',
|
||||
'@c': '1781',
|
||||
'@s': '1272',
|
||||
'@d': '1332',
|
||||
'@m': '9555',
|
||||
'@p': '6097',
|
||||
'@f': '7565',
|
||||
};
|
||||
|
||||
const schedule = {
|
||||
'2025-04-01': ['@m', '@f'],
|
||||
'2025-04-08': ['@g', '@d'],
|
||||
};
|
||||
|
||||
describe('extractIssueOncalls', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
jest.useFakeTimers('modern');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.useRealTimers();
|
||||
});
|
||||
it('extracts m and f on 6 of April', () => {
|
||||
jest.setSystemTime(new Date(2025, 3, 6));
|
||||
const oncalls = extractIssueOncalls(schedule, userMap);
|
||||
expect(oncalls).toEqual([userMap['@m'], userMap['@f']]);
|
||||
});
|
||||
|
||||
it('extracts m and f on 7 of April', () => {
|
||||
jest.setSystemTime(new Date(2025, 3, 7));
|
||||
const oncalls = extractIssueOncalls(schedule, userMap);
|
||||
expect(oncalls).toEqual([userMap['@m'], userMap['@f']]);
|
||||
});
|
||||
|
||||
it('extracts g and d on 8 of April', () => {
|
||||
jest.setSystemTime(new Date(2025, 3, 8));
|
||||
const oncalls = extractIssueOncalls(schedule, userMap);
|
||||
expect(oncalls).toEqual([userMap['@g'], userMap['@d']]);
|
||||
});
|
||||
|
||||
it('extracts g and d on 9 of April', () => {
|
||||
jest.setSystemTime(new Date(2025, 3, 9));
|
||||
const oncalls = extractIssueOncalls(schedule, userMap);
|
||||
expect(oncalls).toEqual([userMap['@g'], userMap['@d']]);
|
||||
});
|
||||
|
||||
it('extracts g and d on 10 of April', () => {
|
||||
jest.setSystemTime(new Date(2025, 3, 10));
|
||||
const oncalls = extractIssueOncalls(schedule, userMap);
|
||||
expect(oncalls).toEqual([userMap['@g'], userMap['@d']]);
|
||||
});
|
||||
|
||||
it('extracts g and d on 11 of April', () => {
|
||||
jest.setSystemTime(new Date(2025, 3, 11));
|
||||
const oncalls = extractIssueOncalls(schedule, userMap);
|
||||
expect(oncalls).toEqual([userMap['@g'], userMap['@d']]);
|
||||
});
|
||||
|
||||
it('extracts g and d on 12 of April', () => {
|
||||
jest.setSystemTime(new Date(2025, 3, 12));
|
||||
const oncalls = extractIssueOncalls(schedule, userMap);
|
||||
expect(oncalls).toEqual([userMap['@g'], userMap['@d']]);
|
||||
});
|
||||
|
||||
it('extracts g and d on 13 of April', () => {
|
||||
jest.setSystemTime(new Date(2025, 3, 13));
|
||||
const oncalls = extractIssueOncalls(schedule, userMap);
|
||||
expect(oncalls).toEqual([userMap['@g'], userMap['@d']]);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,61 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @format
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
|
||||
const MSEC_IN_DAY = 1000 * 60 * 60 * 24;
|
||||
|
||||
function formatUsers(users) {
|
||||
console.log(`${users[0]} ${users[1]}`.trim());
|
||||
}
|
||||
|
||||
function extractUsersFromScheduleAndDate(schedule, userMap, date) {
|
||||
const year = date.getFullYear();
|
||||
const month = date.getMonth() + 1; // 0 is January, 1 is February
|
||||
const day = date.getDate();
|
||||
const dateStr = `${year}-${month < 10 ? `0${month}` : month}-${day < 10 ? `0${day}` : day}`;
|
||||
const user1 = userMap[schedule[dateStr][0]];
|
||||
const user2 = userMap[schedule[dateStr][1]];
|
||||
return [user1, user2];
|
||||
}
|
||||
|
||||
function main() {
|
||||
const configuration = process.argv[2];
|
||||
const {userMap, schedule} = JSON.parse(configuration);
|
||||
extractIssueOncalls(schedule, userMap);
|
||||
}
|
||||
|
||||
function extractIssueOncalls(schedule, userMap) {
|
||||
const now = new Date();
|
||||
const dayOfTheWeek = now.getDay(); // 0 is Sunday, 1 is Monday, etc.
|
||||
let users;
|
||||
if (dayOfTheWeek === 2) {
|
||||
// exact match in the schedule
|
||||
users = extractUsersFromScheduleAndDate(schedule, userMap, now);
|
||||
} else if (dayOfTheWeek < 2) {
|
||||
// sunday
|
||||
// go to the tuesday of the last week
|
||||
const lastWeekTuesday = new Date(now - (5 + dayOfTheWeek) * MSEC_IN_DAY);
|
||||
users = extractUsersFromScheduleAndDate(schedule, userMap, lastWeekTuesday);
|
||||
} else if (dayOfTheWeek > 1) {
|
||||
// go to the previous tuesday
|
||||
const thisWeekTuesday = new Date(now - (dayOfTheWeek - 2) * MSEC_IN_DAY);
|
||||
users = extractUsersFromScheduleAndDate(schedule, userMap, thisWeekTuesday);
|
||||
}
|
||||
formatUsers(users);
|
||||
return users;
|
||||
}
|
||||
|
||||
if (require.main === module) {
|
||||
void main();
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
extractIssueOncalls,
|
||||
};
|
||||
@@ -5,14 +5,33 @@ on:
|
||||
- cron: "0 0,6,12,18 * * *"
|
||||
workflow_dispatch:
|
||||
|
||||
# Reminder for when we have to update the schedule (before Jan 2026):
|
||||
# the secrets.ONCALL_SCHEDULE secret must be on a single line and must have all the `"` escaped as `\"`.
|
||||
# Only a meta engineer can update it through the OSS internal portal.
|
||||
|
||||
jobs:
|
||||
monitor-issues:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20'
|
||||
- name: Install dependencies
|
||||
uses: ./.github/actions/yarn-install
|
||||
- name: Extract next oncall
|
||||
run: |
|
||||
ONCALLS=$(node ./.github/workflow-scripts/extractIssueOncalls.js "${{ secrets.ONCALL_SCHEDULE }}")
|
||||
ONCALL1=$(echo $ONCALLS | cut -d ' ' -f 1)
|
||||
ONCALL2=$(echo $ONCALLS | cut -d ' ' -f 2)
|
||||
echo "oncall1=$ONCALL1" >> $GITHUB_ENV
|
||||
echo "oncall2=$ONCALL2" >> $GITHUB_ENV
|
||||
- name: Print oncalls
|
||||
run: |
|
||||
echo "oncall1: ${{ env.oncall1 }}"
|
||||
echo "oncall2: ${{ env.oncall2 }}"
|
||||
- name: Monitor New Issues
|
||||
uses: react-native-community/repo-monitor@v1.0.1
|
||||
with:
|
||||
@@ -23,5 +42,5 @@ jobs:
|
||||
repo_owner: "facebook"
|
||||
repo_name: "react-native"
|
||||
discord_webhook_url: "${{ secrets.DISCORD_WEBHOOK_URL }}"
|
||||
discord_id_type: "role"
|
||||
discord_ids: "1339243367841927228"
|
||||
discord_id_type: "user"
|
||||
discord_ids: "${{ env.oncall1 }},${{ env.oncall2 }}"
|
||||
|
||||
Reference in New Issue
Block a user