Skip to content

CI Duration Monitor #20

CI Duration Monitor

CI Duration Monitor #20

# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
#
---
name: "CI Duration Monitor"
on: # yamllint disable-line rule:truthy
schedule:
# A few hours after the daily scheduled CI canaries, so the latest main run is in the window.
- cron: '0 9 * * *'
workflow_dispatch:
permissions:
contents: read
actions: read
env:
GITHUB_REPOSITORY: ${{ github.repository }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}
jobs:
monitor-ci-durations:
name: "Monitor CI durations on main"
runs-on: ubuntu-latest
steps:
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: "Analyze CI job durations"
id: analyze
run: python3 scripts/ci/analyze_ci_job_durations.py
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# main coverage comes from the scheduled canary runs of the AMD workflow.
WORKFLOW_NAME: "ci-amd.yml"
BRANCH: "main"
# Only successful (green) canary runs feed the baseline — a failed or cancelled
# canary stops partway, so its truncated wall-clock and per-job durations would
# skew the trend downwards and mask real regressions. Set explicitly so the
# green-only guarantee is visible at the call site and can't be silently changed.
ONLY_SUCCESSFUL: "true"
MAX_RUNS: "25"
# Compare the median of the last few nightly runs (not a single run) against the
# baseline so one unlucky run — slow PyPI, runner queue pressure, cold cache — does
# not trip the alert. With LATEST_RUNS=1 both sides were asymmetric (raw point vs
# median) and the alert fired most nights on whichever jobs happened to be slow.
LATEST_RUNS: "3"
# Network-bound jobs (constraint resolution, provider installs) legitimately swing
# tens of minutes run-to-run; require a larger sustained jump before flagging them.
JOB_MIN_ABS_INCREASE_MINUTES: "6"
OUTPUT_FILE: "slack-message.json"
- name: "Post duration alert to Slack"
if: steps.analyze.outputs.has-regression == 'true'
uses: slackapi/slack-github-action@dcb1066f776dd043e64d0e8ba94ca15cc7e1875d # v4.0.0
with:
method: chat.postMessage
token: ${{ env.SLACK_BOT_TOKEN }}
payload-file-path: "slack-message.json"
- name: "Upload duration analysis"
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: "ci-duration-analysis"
path: slack-message.json
retention-days: 14
if: always() && steps.analyze.outputs.has-regression == 'true'