name: Process failed tests on: workflow_call: inputs: docker: required: true type: string job: required: true type: string slack_report_channel: required: true type: string ci_event: required: true type: string report_repo_id: required: true type: string commit_sha: required: false type: string pr_number: required: false type: string outputs: report: description: "Content of the report of new failures" value: ${{ jobs.process_new_failures_with_commit_info.outputs.report }} env: HF_HOME: /mnt/cache TRANSFORMERS_IS_CI: yes OMP_NUM_THREADS: 8 MKL_NUM_THREADS: 8 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`. HF_TOKEN: ${{ secrets.HF_HUB_READ_TOKEN }} TF_FORCE_GPU_ALLOW_GROWTH: true CUDA_VISIBLE_DEVICES: 0,1 jobs: check_new_failures: name: "Find commits for new failing tests" strategy: matrix: run_idx: [1] runs-on: group: aws-g5-4xlarge-cache outputs: process: ${{ steps.check_file.outputs.process }} container: image: ${{ inputs.docker }} options: --gpus all --shm-size "16gb" --ipc host -v /mnt/cache/.cache/huggingface:/mnt/cache/ steps: - uses: actions/download-artifact@v4 with: name: ci_results_${{ inputs.job }} path: /transformers/ci_results_${{ inputs.job }} - name: Check file id: check_file working-directory: /transformers env: job: ${{ inputs.job }} run: | if [ -f "ci_results_${job}/new_failures.json" ]; then echo "\`ci_results_${job}/new_failures.json\` exists, continue ..." echo "process=true" >> $GITHUB_ENV echo "process=true" >> $GITHUB_OUTPUT else echo "\`ci_results_${job}/new_failures.json\` doesn't exist, abort." echo "process=false" >> $GITHUB_ENV echo "process=false" >> $GITHUB_OUTPUT fi - uses: actions/download-artifact@v4 if: ${{ env.process == 'true' }} with: pattern: setup_values* path: setup_values merge-multiple: true - name: Prepare some setup values if: ${{ env.process == 'true' }} run: | if [ -f setup_values/prev_workflow_run_id.txt ]; then echo "PREV_WORKFLOW_RUN_ID=$(cat setup_values/prev_workflow_run_id.txt)" >> $GITHUB_ENV else echo "PREV_WORKFLOW_RUN_ID=" >> $GITHUB_ENV fi - name: Update clone working-directory: /transformers if: ${{ env.process == 'true' }} env: commit_sha: ${{ inputs.commit_sha || github.sha }} run: | git fetch origin "$commit_sha" && git checkout "$commit_sha" - name: Get `START_SHA` working-directory: /transformers/utils if: ${{ env.process == 'true' }} env: commit_sha: ${{ inputs.commit_sha || github.sha }} run: | echo "START_SHA=$commit_sha" >> $GITHUB_ENV # This is used if the CI is triggered from a pull request `self-comment-ci.yml` (after security check is verified) - name: Extract the base commit on `main` (of the merge commit created by Github) if it is a PR id: pr_info if: ${{ env.process == 'true' && inputs.pr_number != '' }} uses: actions/github-script@v6 with: script: | const { data: pr } = await github.rest.pulls.get({ owner: context.repo.owner, repo: context.repo.repo, pull_number: ${{ inputs.pr_number }} }); const { data: merge_commit } = await github.rest.repos.getCommit({ owner: pr.base.repo.owner.login, repo: pr.base.repo.name, ref: '${{ inputs.commit_sha }}', }); core.setOutput('merge_commit_base_sha', merge_commit.parents[0].sha); # Usually, `END_SHA` should be the commit of the last previous workflow run of the **SAME** (scheduled) workflow. # (This is why we don't need to specify `workflow_id` which would be fetched automatically in the python script.) - name: Get `END_SHA` from previous CI runs of the same workflow working-directory: /transformers/utils if: ${{ env.process == 'true' && inputs.pr_number == '' }} env: ACCESS_TOKEN: ${{ secrets.ACCESS_REPO_INFO_TOKEN }} run: | echo "END_SHA=$(TOKEN="$ACCESS_TOKEN" python3 -c 'import os; from get_previous_daily_ci import get_last_daily_ci_run_commit; commit=get_last_daily_ci_run_commit(token=os.environ["TOKEN"], workflow_run_id=os.environ["PREV_WORKFLOW_RUN_ID"]); print(commit)')" >> $GITHUB_ENV # However, for workflow runs triggered by `issue_comment` (for pull requests), we want to check against the # parent commit (on `main`) of the `merge_commit` (dynamically created by GitHub). In this case, the goal is to # see if a reported failing test is actually ONLY failing on the `merge_commit`. - name: Set `END_SHA` if: ${{ env.process == 'true' && inputs.pr_number != '' }} env: merge_commit_base_sha: ${{ steps.pr_info.outputs.merge_commit_base_sha }} run: | echo "END_SHA=$merge_commit_base_sha" >> $GITHUB_ENV - name: Reinstall transformers in edit mode (remove the one installed during docker image build) working-directory: /transformers if: ${{ env.process == 'true' }} run: python3 -m pip uninstall -y transformers && python3 -m pip install -e . - name: NVIDIA-SMI if: ${{ env.process == 'true' }} run: | nvidia-smi - name: Environment working-directory: /transformers if: ${{ env.process == 'true' }} run: | python3 utils/print_env.py - name: Install pytest-flakefinder if: ${{ env.process == 'true' }} run: python3 -m pip install pytest-flakefinder - name: Show installed libraries and their versions working-directory: /transformers if: ${{ env.process == 'true' }} run: pip freeze - name: Check failed tests working-directory: /transformers if: ${{ env.process == 'true' }} env: job: ${{ inputs.job }} run_idx: ${{ matrix.run_idx }} run: python3 utils/check_bad_commit.py --start_commit "$START_SHA" --end_commit "$END_SHA" --file "ci_results_${job}/new_failures.json" --output_file "new_failures_with_bad_commit_${job}_${run_idx}.json" - name: Show results working-directory: /transformers if: ${{ env.process == 'true' }} env: job: ${{ inputs.job }} run_idx: ${{ matrix.run_idx }} run: | ls -l "new_failures_with_bad_commit_${job}_${run_idx}.json" cat "new_failures_with_bad_commit_${job}_${run_idx}.json" - name: Upload artifacts uses: actions/upload-artifact@v4 with: name: new_failures_with_bad_commit_${{ inputs.job }}_${{ matrix.run_idx }} path: /transformers/new_failures_with_bad_commit_${{ inputs.job }}_${{ matrix.run_idx }}.json process_new_failures_with_commit_info: name: "process bad commit reports" needs: check_new_failures if: needs.check_new_failures.outputs.process == 'true' runs-on: group: aws-g5-4xlarge-cache outputs: report: ${{ steps.set_output.outputs.report }} container: image: ${{ inputs.docker }} options: --gpus all --shm-size "16gb" --ipc host -v /mnt/cache/.cache/huggingface:/mnt/cache/ steps: - uses: actions/download-artifact@v4 with: name: ci_results_${{ inputs.job }} path: /transformers/ci_results_${{ inputs.job }} - uses: actions/download-artifact@v4 with: pattern: new_failures_with_bad_commit_${{ inputs.job }}* path: /transformers/new_failures_with_bad_commit_${{ inputs.job }} merge-multiple: true - name: Check files working-directory: /transformers env: job: ${{ inputs.job }} run: | ls -la /transformers ls -la "/transformers/new_failures_with_bad_commit_${job}" # Currently, we only run with a single runner by using `run_idx: [1]`. We might try to run with multiple runners # to further reduce the false positive caused by flaky tests, which requires further processing to merge reports. - name: Merge files shell: bash working-directory: /transformers env: job: ${{ inputs.job }} run: | cp "/transformers/new_failures_with_bad_commit_${job}/new_failures_with_bad_commit_${job}_1.json" new_failures_with_bad_commit.json - name: Update clone working-directory: /transformers env: commit_sha: ${{ inputs.commit_sha || github.sha }} run: | git fetch origin "$commit_sha" && git checkout "$commit_sha" - name: Process report shell: bash working-directory: /transformers env: ACCESS_REPO_INFO_TOKEN: ${{ secrets.ACCESS_REPO_INFO_TOKEN }} TRANSFORMERS_CI_RESULTS_UPLOAD_TOKEN: ${{ secrets.TRANSFORMERS_CI_RESULTS_UPLOAD_TOKEN }} JOB_NAME: ${{ inputs.job }} REPORT_REPO_ID: ${{ inputs.report_repo_id }} run: | { echo 'REPORT_TEXT<> "$GITHUB_ENV" # The output is useful if a caller needs more processing, for example, we have a chain # self-comment-ci.yml -> self-scheduled.yml -> this one (check_failed_tests.yml), # and `self-comment-ci.yml` needs further processing before sending a GitHub comment to the pull request page. - name: Show results & Set outputs id: set_output working-directory: /transformers run: | ls -l new_failures_with_bad_commit.json cat new_failures_with_bad_commit.json { echo 'report<> "$GITHUB_OUTPUT" - name: Upload artifacts uses: actions/upload-artifact@v4 with: name: new_failures_with_bad_commit_${{ inputs.job }} path: /transformers/new_failures_with_bad_commit.json - name: Prepare Slack report title working-directory: /transformers env: ci_event: ${{ inputs.ci_event }} job: ${{ inputs.job }} run: | pip install slack_sdk echo "title=$(python3 -c 'import sys; import os; sys.path.append("utils"); from utils.notification_service import job_to_test_map; ci_event = os.environ["ci_event"]; job = os.environ["job"]; test_name = job_to_test_map[job]; title = f"New failed tests of {ci_event}" + ":" + f" {test_name}"; print(title)')" >> $GITHUB_ENV - name: Send processed report if: ${{ !endsWith(env.REPORT_TEXT, '{}') }} uses: slackapi/slack-github-action@6c661ce58804a1a20f6dc5fbee7f0381b469e001 with: # Slack channel id, channel name, or user id to post message. # See also: https://api.slack.com/methods/chat.postMessage#channels channel-id: '#${{ inputs.slack_report_channel }}' # For posting a rich message using Block Kit payload: | { "blocks": [ { "type": "header", "text": { "type": "plain_text", "text": "${{ env.title }}" } }, { "type": "section", "text": { "type": "mrkdwn", "text": "${{ env.REPORT_TEXT }}" } } ] } env: SLACK_BOT_TOKEN: ${{ secrets.SLACK_CIFEEDBACK_BOT_TOKEN }}