Some checks failed
Self-hosted runner (nightly-past-ci-caller) / Get number (push) Has been cancelled
Self-hosted runner (nightly-past-ci-caller) / TensorFlow 2.11 (push) Has been cancelled
Self-hosted runner (nightly-past-ci-caller) / TensorFlow 2.10 (push) Has been cancelled
Self-hosted runner (nightly-past-ci-caller) / TensorFlow 2.9 (push) Has been cancelled
Self-hosted runner (nightly-past-ci-caller) / TensorFlow 2.8 (push) Has been cancelled
Self-hosted runner (nightly-past-ci-caller) / TensorFlow 2.7 (push) Has been cancelled
Self-hosted runner (nightly-past-ci-caller) / TensorFlow 2.6 (push) Has been cancelled
Self-hosted runner (nightly-past-ci-caller) / TensorFlow 2.5 (push) Has been cancelled
Self-hosted runner (benchmark) / Benchmark (aws-g5-4xlarge-cache) (push) Has been cancelled
Build documentation / build (push) Has been cancelled
Build documentation / build_other_lang (push) Has been cancelled
CodeQL Security Analysis / CodeQL Analysis (push) Has been cancelled
New model PR merged notification / Notify new model (push) Has been cancelled
PR CI / pr-ci (push) Has been cancelled
Slow tests on important models (on Push - A10) / Get all modified files (push) Has been cancelled
Secret Leaks / trufflehog (push) Has been cancelled
Update Transformers metadata / build_and_package (push) Has been cancelled
Slow tests on important models (on Push - A10) / Model CI (push) Has been cancelled
Check Tiny Models / Check tiny models (push) Has been cancelled
Self-hosted runner (Intel Gaudi3 scheduled CI caller) / Model CI (push) Has been cancelled
Self-hosted runner (Intel Gaudi3 scheduled CI caller) / Pipeline CI (push) Has been cancelled
Self-hosted runner (Intel Gaudi3 scheduled CI caller) / Example CI (push) Has been cancelled
Self-hosted runner (Intel Gaudi3 scheduled CI caller) / DeepSpeed CI (push) Has been cancelled
Self-hosted runner (Intel Gaudi3 scheduled CI caller) / Trainer/FSDP CI (push) Has been cancelled
Nvidia CI - Flash Attn / Setup (push) Has been cancelled
Nvidia CI - Flash Attn / Model CI (push) Has been cancelled
Nvidia CI / Setup (push) Has been cancelled
Nvidia CI / Model CI (push) Has been cancelled
Nvidia CI / Torch pipeline CI (push) Has been cancelled
Nvidia CI / Example CI (push) Has been cancelled
Nvidia CI / Trainer/FSDP CI (push) Has been cancelled
Nvidia CI / DeepSpeed CI (push) Has been cancelled
Nvidia CI / Quantization CI (push) Has been cancelled
Nvidia CI / Kernels CI (push) Has been cancelled
Doctests / Setup (push) Has been cancelled
Doctests / Call doctest jobs (push) Has been cancelled
Doctests / Send results to webhook (push) Has been cancelled
Extras Smoke Test / Get supported Python versions (push) Has been cancelled
Extras Smoke Test / Test extras on Python ${{ matrix.python-version }} (push) Has been cancelled
Extras Smoke Test / Check Slack token availability (push) Has been cancelled
Extras Smoke Test / Notify failures to Slack (push) Has been cancelled
Self-hosted runner (AMD scheduled CI caller) / Trigger Scheduled AMD CI (push) Has been cancelled
Stale Bot / Close Stale Issues (push) Has been cancelled
189 lines
7.6 KiB
YAML
189 lines
7.6 KiB
YAML
name: PR slow CI - Suggestion
|
||
on:
|
||
pull_request_target:
|
||
types: [opened, synchronize, reopened]
|
||
|
||
permissions:
|
||
contents: read
|
||
|
||
jobs:
|
||
get-pr-number:
|
||
name: Get PR number
|
||
uses: ./.github/workflows/get-pr-number.yml
|
||
|
||
get-pr-info:
|
||
name: Get PR commit SHA
|
||
needs: get-pr-number
|
||
if: ${{ needs.get-pr-number.outputs.PR_NUMBER != ''}}
|
||
uses: ./.github/workflows/get-pr-info.yml
|
||
with:
|
||
pr_number: ${{ needs.get-pr-number.outputs.PR_NUMBER }}
|
||
|
||
get-jobs:
|
||
name: Get test files to run
|
||
runs-on: ubuntu-22.04
|
||
needs: [get-pr-number, get-pr-info]
|
||
outputs:
|
||
jobs: ${{ steps.get_jobs.outputs.jobs_to_run }}
|
||
steps:
|
||
# This checkout to the main branch
|
||
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
|
||
with:
|
||
fetch-depth: "0"
|
||
persist-credentials: false
|
||
|
||
# Refetch the PR file list from the API instead of receiving it as an
|
||
# input from `get-pr-info.yml`. The previous approaches either expanded
|
||
# attacker-controlled content into a shell/JS source via `${{ ... }}`
|
||
# (template injection, fixed in #45956) or passed the full JSON through
|
||
# an env var (`E2BIG` / "Argument list too long" once the patch payload
|
||
# exceeds the per-env-var kernel limit, ~128KB). Fetching inside the
|
||
# action keeps the JSON in Node heap memory and writes it straight to
|
||
# disk, so it never crosses an execve argv/envp boundary.
|
||
- name: Write pr_files file
|
||
uses: actions/github-script@d7906e4ad0b1822421a7e6a35d5ca353c962f410 # v6.4.1
|
||
env:
|
||
PR_NUMBER: ${{ needs.get-pr-number.outputs.PR_NUMBER }}
|
||
with:
|
||
script: |
|
||
const fs = require('node:fs');
|
||
const files = await github.paginate(github.rest.pulls.listFiles, {
|
||
owner: context.repo.owner,
|
||
repo: context.repo.repo,
|
||
pull_number: parseInt(process.env.PR_NUMBER, 10),
|
||
});
|
||
fs.writeFileSync('pr_files.txt', JSON.stringify(files));
|
||
|
||
- name: Get repository content
|
||
id: repo_content
|
||
uses: actions/github-script@d7906e4ad0b1822421a7e6a35d5ca353c962f410 # v6.4.1
|
||
env:
|
||
PR_HEAD_REPO_OWNER: ${{ needs.get-pr-info.outputs.PR_HEAD_REPO_OWNER }}
|
||
PR_HEAD_REPO_NAME: ${{ needs.get-pr-info.outputs.PR_HEAD_REPO_NAME }}
|
||
PR_HEAD_SHA: ${{ needs.get-pr-info.outputs.PR_HEAD_SHA }}
|
||
with:
|
||
script: |
|
||
const fs = require('node:fs');
|
||
const { PR_HEAD_REPO_OWNER, PR_HEAD_REPO_NAME, PR_HEAD_SHA } = process.env;
|
||
|
||
const { data: tests_dir } = await github.rest.repos.getContent({
|
||
owner: PR_HEAD_REPO_OWNER,
|
||
repo: PR_HEAD_REPO_NAME,
|
||
path: 'tests',
|
||
ref: PR_HEAD_SHA,
|
||
});
|
||
|
||
const { data: tests_models_dir } = await github.rest.repos.getContent({
|
||
owner: PR_HEAD_REPO_OWNER,
|
||
repo: PR_HEAD_REPO_NAME,
|
||
path: 'tests/models',
|
||
ref: PR_HEAD_SHA,
|
||
});
|
||
|
||
const { data: tests_quantization_dir } = await github.rest.repos.getContent({
|
||
owner: PR_HEAD_REPO_OWNER,
|
||
repo: PR_HEAD_REPO_NAME,
|
||
path: 'tests/quantization',
|
||
ref: PR_HEAD_SHA,
|
||
});
|
||
|
||
// Write to files instead of outputs
|
||
fs.writeFileSync('tests_dir.txt', JSON.stringify(tests_dir, null, 2));
|
||
fs.writeFileSync('tests_models_dir.txt', JSON.stringify(tests_models_dir, null, 2));
|
||
fs.writeFileSync('tests_quantization_dir.txt', JSON.stringify(tests_quantization_dir, null, 2));
|
||
|
||
- name: Run script to get jobs to run
|
||
id: get_jobs
|
||
run: |
|
||
python utils/get_pr_run_slow_jobs.py | tee output.txt
|
||
echo "jobs_to_run: $(tail -n 1 output.txt)"
|
||
echo "jobs_to_run=$(tail -n 1 output.txt)" >> $GITHUB_OUTPUT
|
||
|
||
send_comment:
|
||
# Will delete the previous comment and send a new one if:
|
||
# - either the content is changed
|
||
# - or the previous comment is 30 minutes or more old
|
||
name: Send a comment to suggest jobs to run
|
||
if: ${{ needs.get-jobs.outputs.jobs != '' }}
|
||
needs: [get-pr-number, get-jobs]
|
||
permissions:
|
||
pull-requests: write
|
||
runs-on: ubuntu-22.04
|
||
steps:
|
||
- name: Check and update comment if needed
|
||
uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7.1.0
|
||
env:
|
||
BODY: "\n\nrun-slow: ${{ needs.get-jobs.outputs.jobs }}"
|
||
PR_NUMBER: ${{ needs.get-pr-number.outputs.PR_NUMBER }}
|
||
with:
|
||
script: |
|
||
const prNumber = parseInt(process.env.PR_NUMBER, 10);
|
||
const commentPrefix = "**[For maintainers]** Suggested jobs to run (before merge)";
|
||
const thirtyMinutesAgo = new Date(Date.now() - 30 * 60 * 1000); // 30 minutes ago
|
||
const newBody = `${commentPrefix}${process.env.BODY}`;
|
||
|
||
// Get all comments on the PR
|
||
const { data: comments } = await github.rest.issues.listComments({
|
||
owner: context.repo.owner,
|
||
repo: context.repo.repo,
|
||
issue_number: prNumber
|
||
});
|
||
|
||
// Find existing comments that start with our prefix
|
||
const existingComments = comments.filter(comment =>
|
||
comment.user.login === 'github-actions[bot]' &&
|
||
comment.body.startsWith(commentPrefix)
|
||
);
|
||
|
||
let shouldCreateNewComment = true;
|
||
let commentsToDelete = [];
|
||
|
||
if (existingComments.length > 0) {
|
||
// Get the most recent comment
|
||
const mostRecentComment = existingComments
|
||
.sort((a, b) => new Date(b.created_at) - new Date(a.created_at))[0];
|
||
|
||
const commentDate = new Date(mostRecentComment.created_at);
|
||
const isOld = commentDate < thirtyMinutesAgo;
|
||
const isDifferentContent = mostRecentComment.body !== newBody;
|
||
|
||
console.log(`Most recent comment created: ${mostRecentComment.created_at}`);
|
||
console.log(`Is older than 30 minutes: ${isOld}`);
|
||
console.log(`Has different content: ${isDifferentContent}`);
|
||
|
||
if (isOld || isDifferentContent) {
|
||
// Delete all existing comments and create new one
|
||
commentsToDelete = existingComments;
|
||
console.log(`Will delete ${commentsToDelete.length} existing comment(s) and create new one`);
|
||
} else {
|
||
// Content is same and comment is recent, skip
|
||
shouldCreateNewComment = false;
|
||
console.log('Comment is recent and content unchanged, skipping update');
|
||
}
|
||
} else {
|
||
console.log('No existing comments found, will create new one');
|
||
}
|
||
|
||
// Delete old comments if needed
|
||
for (const comment of commentsToDelete) {
|
||
console.log(`Deleting comment #${comment.id} (created: ${comment.created_at})`);
|
||
await github.rest.issues.deleteComment({
|
||
owner: context.repo.owner,
|
||
repo: context.repo.repo,
|
||
comment_id: comment.id
|
||
});
|
||
}
|
||
|
||
// Create new comment if needed
|
||
if (shouldCreateNewComment) {
|
||
await github.rest.issues.createComment({
|
||
owner: context.repo.owner,
|
||
repo: context.repo.repo,
|
||
issue_number: prNumber,
|
||
body: newBody
|
||
});
|
||
console.log('✅ New comment created');
|
||
} else {
|
||
console.log('ℹ️ No comment update needed');
|
||
}
|