Skip to content

Commit 80f4c0c

Browse files
authored
CI when PR merged to main (#40451)
* up * up * up * up * up * update --------- Co-authored-by: ydshieh <ydshieh@users.noreply.github.com>
1 parent ff8b88a commit 80f4c0c

File tree

7 files changed

+190
-155
lines changed

7 files changed

+190
-155
lines changed

.github/workflows/push-important-models.yml

Lines changed: 136 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,6 @@ on:
44
push:
55
branches: [ main ]
66

7-
env:
8-
OUTPUT_SLACK_CHANNEL_ID: "C06L2SGMEEA"
9-
HF_HUB_READ_TOKEN: ${{ secrets.HF_HUB_READ_TOKEN }}
10-
HF_HOME: /mnt/cache
11-
TRANSFORMERS_IS_CI: yes
12-
OMP_NUM_THREADS: 8
13-
MKL_NUM_THREADS: 8
14-
RUN_SLOW: yes # For gated repositories, we still need to agree to share information on the Hub repo. page in order to get access. # This token is created under the bot `hf-transformers-bot`.
15-
SIGOPT_API_TOKEN: ${{ secrets.SIGOPT_API_TOKEN }}
16-
TF_FORCE_GPU_ALLOW_GROWTH: true
17-
187
jobs:
198
get_modified_models:
209
name: "Get all modified files"
@@ -25,111 +14,143 @@ jobs:
2514
- name: Check out code
2615
uses: actions/checkout@v4
2716

28-
- name: Get changed files
29-
id: changed-files
30-
uses: tj-actions/changed-files@1c8e6069583811afb28f97afeaf8e7da80c6be5c
17+
- name: Get changed files using `actions/github-script`
18+
id: get-changed-files
19+
uses: actions/github-script@v7
3120
with:
32-
files: src/transformers/models/**
33-
34-
- name: Run step if only the files listed above change
35-
if: steps.changed-files.outputs.any_changed == 'true'
36-
id: set-matrix
21+
script: |
22+
let files = [];
23+
24+
// Only handle push events
25+
if (context.eventName === 'push') {
26+
const afterSha = context.payload.after;
27+
const branchName = context.payload.ref.replace('refs/heads/', '');
28+
29+
let baseSha;
30+
31+
if (branchName === 'main') {
32+
console.log('Push to main branch, comparing to parent commit');
33+
// Get the parent commit of the pushed commit
34+
const { data: commit } = await github.rest.repos.getCommit({
35+
owner: context.repo.owner,
36+
repo: context.repo.repo,
37+
ref: afterSha
38+
});
39+
baseSha = commit.parents[0]?.sha;
40+
if (!baseSha) {
41+
throw new Error('No parent commit found for the pushed commit');
42+
}
43+
} else {
44+
console.log(`Push to branch ${branchName}, comparing to main`);
45+
baseSha = 'main';
46+
}
47+
48+
const { data: comparison } = await github.rest.repos.compareCommits({
49+
owner: context.repo.owner,
50+
repo: context.repo.repo,
51+
base: baseSha,
52+
head: afterSha
53+
});
54+
55+
// Include added, modified, and renamed files
56+
files = comparison.files
57+
.filter(file => file.status === 'added' || file.status === 'modified' || file.status === 'renamed')
58+
.map(file => file.filename);
59+
}
60+
61+
// Include all files under src/transformers/ (not just models subdirectory)
62+
const filteredFiles = files.filter(file =>
63+
file.startsWith('src/transformers/')
64+
);
65+
66+
core.setOutput('changed_files', filteredFiles.join(' '));
67+
core.setOutput('any_changed', filteredFiles.length > 0 ? 'true' : 'false');
68+
69+
- name: Parse changed files with Python
70+
if: steps.get-changed-files.outputs.any_changed == 'true'
3771
env:
38-
ALL_CHANGED_FILES: ${{ steps.changed-files.outputs.all_changed_files }}
72+
CHANGED_FILES: ${{ steps.get-changed-files.outputs.changed_files }}
73+
id: set-matrix
3974
run: |
40-
model_arrays=()
41-
for file in $ALL_CHANGED_FILES; do
42-
model_path="${file#*models/}"
43-
model_path="models/${model_path%%/*}"
44-
if grep -qFx "$model_path" utils/important_models.txt; then
45-
# Append the file to the matrix string
46-
model_arrays+=("$model_path")
47-
fi
48-
done
49-
matrix_string=$(printf '"%s", ' "${model_arrays[@]}" | sed 's/, $//')
50-
echo "matrix=[$matrix_string]" >> $GITHUB_OUTPUT
51-
test_modified_files:
75+
python3 - << 'EOF'
76+
import os
77+
import sys
78+
import json
79+
80+
# Add the utils directory to Python path
81+
sys.path.insert(0, 'utils')
82+
83+
# Import the important models list
84+
from important_files import IMPORTANT_MODELS
85+
86+
print(f"Important models: {IMPORTANT_MODELS}")
87+
88+
# Get the changed files from the previous step
89+
changed_files_str = os.environ.get('CHANGED_FILES', '')
90+
changed_files = changed_files_str.split() if changed_files_str else []
91+
92+
# Filter to only Python files
93+
python_files = [f for f in changed_files if f.endswith('.py')]
94+
print(f"Python files changed: {python_files}")
95+
96+
result_models = set()
97+
98+
# Specific files that trigger all models
99+
transformers_utils_files = [
100+
'modeling_utils.py',
101+
'modeling_rope_utils.py',
102+
'modeling_flash_attention_utils.py',
103+
'modeling_attn_mask_utils.py',
104+
'cache_utils.py',
105+
'masking_utils.py',
106+
'pytorch_utils.py'
107+
]
108+
109+
# Single loop through all Python files
110+
for file in python_files:
111+
# Check for files under src/transformers/models/
112+
if file.startswith('src/transformers/models/'):
113+
remaining_path = file[len('src/transformers/models/'):]
114+
if '/' in remaining_path:
115+
model_dir = remaining_path.split('/')[0]
116+
if model_dir in IMPORTANT_MODELS:
117+
result_models.add(model_dir)
118+
print(f"Added model directory: {model_dir}")
119+
120+
# Check for specific files under src/transformers/ or src/transformers/generation/ files
121+
elif file.startswith('src/transformers/generation/') or \
122+
(file.startswith('src/transformers/') and os.path.basename(file) in transformers_utils_files):
123+
print(f"Found core file: {file} - including all important models")
124+
result_models.update(IMPORTANT_MODELS)
125+
break # No need to continue once we include all models
126+
127+
# Convert to sorted list and create matrix
128+
result_list = sorted(list(result_models))
129+
print(f"Final model list: {result_list}")
130+
131+
if result_list:
132+
matrix_json = json.dumps(result_list)
133+
print(f"matrix={matrix_json}")
134+
135+
# Write to GITHUB_OUTPUT
136+
with open(os.environ['GITHUB_OUTPUT'], 'a') as f:
137+
f.write(f"matrix={matrix_json}\n")
138+
else:
139+
print("matrix=[]")
140+
with open(os.environ['GITHUB_OUTPUT'], 'a') as f:
141+
f.write("matrix=[]\n")
142+
EOF
143+
144+
model-ci:
145+
name: Model CI
146+
uses: ./.github/workflows/self-scheduled.yml
52147
needs: get_modified_models
53-
name: Slow & FA2 tests
54-
runs-on:
55-
group: aws-g5-4xlarge-cache
56-
container:
57-
image: huggingface/transformers-all-latest-gpu
58-
options: --gpus all --privileged --ipc host -v /mnt/cache/.cache/huggingface:/mnt/cache/
59-
if: ${{ needs.get_modified_models.outputs.matrix != '[]' && needs.get_modified_models.outputs.matrix != '' && fromJson(needs.get_modified_models.outputs.matrix)[0] != null }}
60-
strategy:
61-
fail-fast: false
62-
matrix:
63-
model-name: ${{ fromJson(needs.get_modified_models.outputs.matrix) }}
64-
65-
steps:
66-
- name: Check out code
67-
uses: actions/checkout@v4
68-
69-
- name: Install locally transformers & other libs
70-
run: |
71-
apt install sudo
72-
sudo -H pip install --upgrade pip
73-
sudo -H pip uninstall -y transformers
74-
sudo -H pip install -U -e ".[testing]"
75-
MAX_JOBS=4 pip install flash-attn --no-build-isolation
76-
pip install bitsandbytes
77-
78-
- name: NVIDIA-SMI
79-
run: |
80-
nvidia-smi
81-
82-
- name: Show installed libraries and their versions
83-
run: pip freeze
84-
85-
- name: Run FA2 tests
86-
id: run_fa2_tests
87-
run:
88-
pytest -rsfE -m "flash_attn_test" --make-reports=${{ matrix.model-name }}_fa2_tests/ tests/${{ matrix.model-name }}/test_modeling_*
89-
90-
- name: "Test suite reports artifacts: ${{ matrix.model-name }}_fa2_tests"
91-
if: ${{ always() }}
92-
uses: actions/upload-artifact@v4
93-
with:
94-
name: ${{ matrix.model-name }}_fa2_tests
95-
path: /transformers/reports/${{ matrix.model-name }}_fa2_tests
96-
97-
- name: Post to Slack
98-
if: always()
99-
uses: huggingface/hf-workflows/.github/actions/post-slack@main
100-
with:
101-
slack_channel: ${{ env.OUTPUT_SLACK_CHANNEL_ID }}
102-
title: 🤗 Results of the FA2 tests - ${{ matrix.model-name }}
103-
status: ${{ steps.run_fa2_tests.conclusion}}
104-
slack_token: ${{ secrets.CI_SLACK_BOT_TOKEN }}
105-
106-
- name: Run integration tests
107-
id: run_integration_tests
108-
if: always()
109-
run:
110-
pytest -rsfE -k "IntegrationTest" --make-reports=tests_integration_${{ matrix.model-name }} tests/${{ matrix.model-name }}/test_modeling_*
111-
112-
- name: "Test suite reports artifacts: tests_integration_${{ matrix.model-name }}"
113-
if: ${{ always() }}
114-
uses: actions/upload-artifact@v4
115-
with:
116-
name: tests_integration_${{ matrix.model-name }}
117-
path: /transformers/reports/tests_integration_${{ matrix.model-name }}
118-
119-
- name: Post to Slack
120-
if: always()
121-
uses: huggingface/hf-workflows/.github/actions/post-slack@main
122-
with:
123-
slack_channel: ${{ env.OUTPUT_SLACK_CHANNEL_ID }}
124-
title: 🤗 Results of the Integration tests - ${{ matrix.model-name }}
125-
status: ${{ steps.run_integration_tests.conclusion}}
126-
slack_token: ${{ secrets.CI_SLACK_BOT_TOKEN }}
127-
128-
- name: Tailscale # In order to be able to SSH when a test fails
129-
if: ${{ runner.debug == '1'}}
130-
uses: huggingface/tailscale-action@v1
131-
with:
132-
authkey: ${{ secrets.TAILSCALE_SSH_AUTHKEY }}
133-
slackChannel: ${{ secrets.SLACK_CIFEEDBACK_CHANNEL }}
134-
slackToken: ${{ secrets.SLACK_CIFEEDBACK_BOT_TOKEN }}
135-
waitForSSH: true
148+
with:
149+
job: run_models_gpu
150+
slack_report_channel: "#transformers-ci-push"
151+
docker: huggingface/transformers-all-latest-gpu
152+
ci_event: push
153+
report_repo_id: hf-internal-testing/transformers_ci_push
154+
commit_sha: ${{ github.sha }}
155+
models: ${{ needs.get_modified_models.outputs.matrix }}
156+
secrets: inherit

.github/workflows/self-scheduled.yml

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,10 @@ on:
3131
commit_sha:
3232
required: false
3333
type: string
34-
34+
models:
35+
default: ""
36+
required: false
37+
type: string
3538

3639
env:
3740
HF_HOME: /mnt/cache
@@ -68,7 +71,7 @@ jobs:
6871
- name: Update clone
6972
working-directory: /transformers
7073
run: |
71-
git fetch && git checkout ${{ github.sha }}
74+
git fetch && git checkout ${{ inputs.commit_sha || github.sha }}
7275
7376
- name: Cleanup
7477
working-directory: /transformers
@@ -87,7 +90,7 @@ jobs:
8790
working-directory: /transformers/tests
8891
run: |
8992
if [ "${{ inputs.job }}" = "run_models_gpu" ]; then
90-
echo "folder_slices=$(python3 ../utils/split_model_tests.py --num_splits ${{ env.NUM_SLICES }})" >> $GITHUB_OUTPUT
93+
echo "folder_slices=$(python3 ../utils/split_model_tests.py --models '${{ inputs.models }}' --num_splits ${{ env.NUM_SLICES }})" >> $GITHUB_OUTPUT
9194
echo "slice_ids=$(python3 -c 'd = list(range(${{ env.NUM_SLICES }})); print(d)')" >> $GITHUB_OUTPUT
9295
echo "runner_map=$(python3 ../utils/get_runner_map.py)" >> $GITHUB_OUTPUT
9396
elif [ "${{ inputs.job }}" = "run_trainer_and_fsdp_gpu" ]; then

.github/workflows/slack-report.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,8 @@ jobs:
7575
SLACK_REPORT_CHANNEL: ${{ inputs.slack_report_channel }}
7676
ACCESS_REPO_INFO_TOKEN: ${{ secrets.ACCESS_REPO_INFO_TOKEN }}
7777
CI_EVENT: ${{ inputs.ci_event }}
78+
# This `CI_TITLE` would be empty for `schedule` or `workflow_run` events.
79+
CI_TITLE: ${{ github.event.head_commit.message }}
7880
CI_SHA: ${{ inputs.commit_sha || github.sha }}
7981
CI_TEST_JOB: ${{ inputs.job }}
8082
SETUP_STATUS: ${{ inputs.setup_status }}

utils/important_files.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# List here the models to always test.
2+
IMPORTANT_MODELS = [
3+
"auto",
4+
"bert",
5+
"gpt2",
6+
"t5",
7+
"modernbert",
8+
"vit,clip",
9+
"detr",
10+
"table_transformer",
11+
"got_ocr2",
12+
"whisper",
13+
"wav2vec2",
14+
"qwen2_audio",
15+
"speech_t5",
16+
"csm",
17+
"llama",
18+
"gemma3",
19+
"qwen2",
20+
"mistral3",
21+
"qwen2_5_vl",
22+
"llava",
23+
"smolvlm",
24+
"internvl",
25+
"gemma3n",
26+
"gpt_oss",
27+
"qwen2_5_omni",
28+
]

utils/notification_service.py

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1072,18 +1072,14 @@ def pop_default(l: list[Any], i: int, default: Any) -> Any:
10721072
pr_number_re = re.compile(r"\(#(\d+)\)$")
10731073

10741074
# Add Commit/PR title with a link for push CI
1075-
# (check the title in 2 env. variables - depending on the CI is triggered via `push` or `workflow_run` event)
1076-
ci_title_push = os.environ.get("CI_TITLE_PUSH")
1077-
ci_title_workflow_run = os.environ.get("CI_TITLE_WORKFLOW_RUN")
1078-
ci_title = ci_title_push if ci_title_push else ci_title_workflow_run
1079-
1075+
ci_title = os.environ.get("CI_TITLE", "")
10801076
ci_sha = os.environ.get("CI_SHA")
10811077

10821078
ci_url = None
10831079
if ci_sha:
10841080
ci_url = f"https://github.com/{repository_full_name}/commit/{ci_sha}"
10851081

1086-
if ci_title is not None:
1082+
if ci_title:
10871083
if ci_url is None:
10881084
raise ValueError(
10891085
"When a title is found (`ci_title`), it means a `push` event or a `workflow_run` even (triggered by "
@@ -1112,9 +1108,9 @@ def pop_default(l: list[Any], i: int, default: Any) -> Any:
11121108
merged_by = ci_details["merged_by"]["login"]
11131109

11141110
if merged_by is None:
1115-
ci_title = f"<{ci_url}|{ci_title}>\nAuthor: {ci_author}"
1111+
ci_title = f"<{ci_url}|{ci_title}>\nAuthor: GH_{ci_author}"
11161112
else:
1117-
ci_title = f"<{ci_url}|{ci_title}>\nAuthor: {ci_author} | Merged by: {merged_by}"
1113+
ci_title = f"<{ci_url}|{ci_title}>\nAuthor: GH_{ci_author} | Merged by: GH_{merged_by}"
11181114

11191115
elif ci_sha:
11201116
ci_title = f"<{ci_url}|commit: {ci_sha}>"

utils/split_model_tests.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,18 @@
3333
"""
3434

3535
import argparse
36+
import ast
3637
import os
3738

3839

3940
if __name__ == "__main__":
4041
parser = argparse.ArgumentParser()
42+
parser.add_argument(
43+
"--models",
44+
type=str,
45+
default="",
46+
help="the list of pre-computed model names.",
47+
)
4148
parser.add_argument(
4249
"--num_splits",
4350
type=int,
@@ -53,6 +60,10 @@
5360
d1.remove("models")
5461
d = d2 + d1
5562

63+
if args.models != "":
64+
model_tests = ast.literal_eval(args.models)
65+
d = sorted(filter(os.path.isdir, [f"models/{x}" for x in model_tests]))
66+
5667
num_jobs = len(d)
5768
num_jobs_per_splits = num_jobs // args.num_splits
5869

0 commit comments

Comments
 (0)