Skip to content
12 changes: 12 additions & 0 deletions torchci/clickhouse_queries/autorevert_commits/params.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"params": {
"repo": "String",
"shas": "Array(String)"
},
"tests": [
{
"repo": "pytorch/pytorch",
"shas": ["abc123", "def456"]
}
]
}
11 changes: 11 additions & 0 deletions torchci/clickhouse_queries/autorevert_commits/query.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
SELECT
commit_sha,
groupArray(workflows) as all_workflows,
groupArray(source_signal_keys) as all_source_signal_keys
FROM misc.autorevert_events_v2
WHERE repo = {repo: String}
AND action = 'revert'
AND dry_run = 0
AND failed = 0
AND commit_sha IN {shas: Array(String)}
GROUP BY commit_sha
12 changes: 12 additions & 0 deletions torchci/clickhouse_queries/autorevert_details/params.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"params": {
"repo": "String",
"sha": "String"
},
"tests": [
{
"repo": "pytorch/pytorch",
"sha": "321e60abc123"
}
]
}
13 changes: 13 additions & 0 deletions torchci/clickhouse_queries/autorevert_details/query.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
SELECT
commit_sha,
workflows,
source_signal_keys,
ts
FROM misc.autorevert_events_v2
WHERE
repo = {repo: String}
AND commit_sha = {sha: String}
AND action = 'revert'
AND dry_run = 0
AND failed = 0
ORDER BY ts DESC
52 changes: 52 additions & 0 deletions torchci/components/commit/AutorevertBanner.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
.autorevertBanner {
background-color: var(--sev-banner-bg);
border: 2px solid var(--autoreverted-signal-border);
border-radius: 8px;
padding: 16px;
margin: 16px 0;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}

.bannerHeader {
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 12px;
font-size: 1.1em;
}

.warningIcon {
font-size: 1.2em;
}

.bannerContent {
color: var(--text-color);
}

.bannerContent p {
margin: 8px 0;
}

.workflowList {
margin: 12px 0;
padding-left: 24px;
}

.workflowList li {
margin: 6px 0;
list-style-type: disc;
}

.workflowList a {
color: var(--link-color);
text-decoration: none;
}

.workflowList a:hover {
text-decoration: underline;
}

.investigateMessage {
font-style: italic;
margin-top: 12px;
}
133 changes: 133 additions & 0 deletions torchci/components/commit/AutorevertBanner.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
import useSWR from "swr";
import styles from "./AutorevertBanner.module.css";

interface AutorevertDetails {
commit_sha: string;
workflows: string[];
source_signal_keys: string[];
job_ids: number[];
job_base_names: string[];
wf_run_ids: number[];
created_at: string;
}

interface SignalInfo {
workflow_name: string;
signals: Array<{
key: string;
job_url?: string;
hud_url?: string;
}>;
}

export function AutorevertBanner({
repoOwner,
repoName,
sha,
}: {
repoOwner: string;
repoName: string;
sha: string;
}) {
const { data: autorevertData, error } = useSWR<AutorevertDetails>(
`/api/autorevert/${repoOwner}/${repoName}/${sha}`,
async (url) => {
try {
const response = await fetch(url);

if (response.status === 404) {
// No autorevert data for this commit
return null;
}

if (!response.ok) {
const errorText = await response.text();
throw new Error(`Failed to fetch: ${response.status} - ${errorText}`);
}

const data = await response.json();
return data;
} catch (e) {
// Silently fail - no autorevert data
return null;
}
},
{
refreshInterval: 0, // Don't refresh autorevert data
}
);

// Don't show banner if no data or error
if (!autorevertData) {
return null;
}

// Handle case where arrays might be undefined or have different structure
const workflows = autorevertData.workflows || [];
Copy link
Contributor

Choose a reason for hiding this comment

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

is this really necessary? shouldn't backend have a predictable response structure?

const sourceSignalKeys = autorevertData.source_signal_keys || [];
const jobIds = autorevertData.job_ids || [];
Copy link
Contributor

Choose a reason for hiding this comment

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

those are unused!

const wfRunIds = autorevertData.wf_run_ids || [];
const jobBaseNames = autorevertData.job_base_names || [];

// If no workflows data, don't show the banner
if (!workflows.length) {
return null;
}

// Group signals by workflow
const signalsByWorkflow = new Map<string, SignalInfo>();

workflows.forEach((workflow, idx) => {
if (!signalsByWorkflow.has(workflow)) {
signalsByWorkflow.set(workflow, {
workflow_name: workflow,
signals: [],
});
}

const signalKey = sourceSignalKeys[idx] || "";

const signal = {
key: signalKey,
// Since we don't have job IDs in the table, we can't create direct job links
job_url: undefined,
// Try to create a HUD URL using the signal key as a filter
hud_url: signalKey
? `/hud/${repoOwner}/${repoName}/main?nameFilter=${encodeURIComponent(
signalKey
)}`
: undefined,
};

signalsByWorkflow.get(workflow)!.signals.push(signal);
});

return (
<div className={styles.autorevertBanner}>
<div className={styles.bannerHeader}>
<span className={styles.warningIcon}>⚠️</span>
<strong>This commit was automatically reverted</strong>
</div>
<div className={styles.bannerContent}>
<p>This PR is attributed to have caused regression in:</p>
<ul className={styles.workflowList}>
{Array.from(signalsByWorkflow.values()).map((workflowInfo) => (
<li key={workflowInfo.workflow_name}>
<strong>{workflowInfo.workflow_name}:</strong>{" "}
{workflowInfo.signals.map((signal, idx) => (
<span key={idx}>
{idx > 0 && ", "}
<span>{signal.key}</span>
</span>
))}
</li>
))}
</ul>
<p className={styles.investigateMessage}>
You can add the label <code>autorevert: disable</code> to disable
autorevert for a specific PR.
</p>
</div>
</div>
);
}
9 changes: 9 additions & 0 deletions torchci/components/hud.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,15 @@
background-color: var(--forced-merge-failure-bg);
}

.autoreverted {
background-color: var(--autoreverted-bg);
}

.autorevertSignal {
background-color: var(--autoreverted-signal-bg);
border: 2px solid var(--autoreverted-signal-border);
}

.selectedRow {
background-color: var(--selected-row-bg);
font-weight: bold;
Expand Down
Loading