Skip to content

Benchmark

Benchmark #506

Workflow file for this run

name: Benchmark
on:
issue_comment:
types: [created]
workflow_dispatch:
inputs:
compare-base:
description: 'Branch name to compare against'
type: string
default: b12.1.1
required: true
notify:
description: 'Notify Slack channel'
type: boolean
default: true
required: true
schedule:
- cron: '0 0 * * *' # Run daily at midnight UTC
- cron: '0 1 * * *' # Run daily at 01:00 UTC
env:
NX_NO_CLOUD: true
NX_CLOUD_ACCESS_TOKEN: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }}
NX_BRANCH: ${{ github.ref }}
BRANCH_NAME: ${{ github.head_ref || github.ref_name }}
jobs:
# Check permissions for issue_comment events
check-permissions:
runs-on: ubuntu-latest
outputs:
allowed: ${{ steps.check.outputs.allowed }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Check user permissions
id: check
uses: ./external/ag-shared/github/actions/check-issue-comment-permissions
with:
command: '/benchmarks'
benchmark:
runs-on: macos-benchmark
permissions:
pull-requests: write
needs: check-permissions
# Only run if permissions check passed (success) or was skipped (for non-issue_comment events)
if: |
needs.check-permissions.result == 'success' && (
github.event_name == 'workflow_dispatch' ||
github.event_name == 'schedule' ||
(github.event_name == 'issue_comment' && needs.check-permissions.outputs.allowed == 'true')
)
env:
AG_SKIP_NATIVE_DEP_VERSION_CHECK: 1
JOB_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
AG_BENCHMARK_SOFT_FAIL: 'true'
steps:
- name: Notify start
id: notify-start
uses: actions/github-script@v7
continue-on-error: true
if: github.event_name == 'issue_comment'
with:
result-encoding: string
script: |
const { data: comment } = await github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: `⏱️ ${process.env.JOB_URL}`
})
return comment.id;
- id: 'get-branch'
run: |
if [[ "${{ github.event_name }}" == "issue_comment" ]] ; then
# For PR comments, get the branch from the PR
echo "branch=$(gh pr view $PR_NO --repo $REPO --json headRefName --jq '.headRefName')" >> $GITHUB_OUTPUT
echo "base=$(gh pr view $PR_NO --repo $REPO --json baseRefName --jq '.baseRefName')" >> $GITHUB_OUTPUT
elif [[ "${{ github.event_name }}" == "schedule" && "${{ github.event.schedule }}" == "0 1 * * *" ]] ; then
# For secondary scheduled runs, compare against b11.3.1
echo "branch=latest" >> $GITHUB_OUTPUT
echo "base=b11.3.1" >> $GITHUB_OUTPUT
elif [[ "${{ github.event_name }}" == "schedule" ]] ; then
# For scheduled runs, get the most recent branch matching 'b' followed by semver (e.g. b1.2.3)
release=$(git ls-remote --heads origin 'b[0-9]*\.[0-9]*\.[0-9]*' | sort -t '/' -k 2 -V | tail -n 1 | sed 's/.*refs\/heads\///')
echo "branch=latest" >> $GITHUB_OUTPUT
echo "base=$release" >> $GITHUB_OUTPUT
else
# For manual workflow dispatch, use the current branch
echo "branch=${{ github.ref_name }}" >> $GITHUB_OUTPUT
echo "base=${{ inputs.compare-base }}" >> $GITHUB_OUTPUT
fi
env:
REPO: ${{ github.repository }}
PR_NO: ${{ github.event.issue.number }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Checkout
uses: actions/checkout@v4
with:
ref: ${{ steps.get-branch.outputs.branch }}
fetch-depth: 0
- name: Fetch Refs
run: |
set -x
if [[ "${{ steps.get-branch.outputs.base }}" =~ "^@" ]] ; then
base=$(git rev-parse --abbrev-ref ${{ steps.get-branch.outputs.base }})
else
base=origin/${{ steps.get-branch.outputs.base }}
fi
git fetch --unshallow origin $base || echo "Not a shallow ref?"
# Preemptively fail if merge-base is not found.
mergeBase=$(git merge-base HEAD $base || echo "failed")
if [[ ${mergeBase} == "failed" ]] ; then
# Debugging
git log -n 1 --decorate HEAD
git log -n 20 --decorate $base
# Allow for a few minutes for debugging
sleep 600
fi
- name: Notify Progress
uses: actions/github-script@v7
continue-on-error: true
if: github.event_name == 'issue_comment'
with:
script: |
github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: ${{ steps.notify-start.outputs.result }},
body: `⏱️ ${process.env.JOB_URL} (running setup...)`
})
- name: Setup
id: setup
uses: ./.github/actions/setup-nx
with:
cache_mode: rw
base_ref: ${{ steps.get-branch.outputs.base }}
fetch_base: skip
- name: Notify Progress
uses: actions/github-script@v7
continue-on-error: true
if: github.event_name == 'issue_comment'
with:
script: |
github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: ${{ steps.notify-start.outputs.result }},
body: `⏱️ ${process.env.JOB_URL} (running benchmarks...)`
})
- name: nx benchmark
id: benchmark-table
if: github.event_name == 'issue_comment' && (steps.setup.outcome == 'success' || steps.setup.outcome == 'skipped')
run: |
./tools/benchmark/compare-latest.sh origin/${{ steps.get-branch.outputs.base }}
echo "report<<EOF" >> $GITHUB_OUTPUT
cat ./reports/benchmark.log >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
- name: nx benchmark
id: benchmark-json
if: github.event_name != 'issue_comment' && (steps.setup.outcome == 'success' || steps.setup.outcome == 'skipped')
run: |
./tools/benchmark/compare-latest.sh -j origin/${{ steps.get-branch.outputs.base }}
- name: Notify Success
uses: actions/github-script@v7
continue-on-error: true
if: success() && github.event_name == 'issue_comment'
env:
CONTENT: ${{ steps.benchmark-table.outputs.report }}
with:
script: |
const body = `✅ ${process.env.JOB_URL}
\`\`\`
${process.env.CONTENT}
\`\`\``;
github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: ${{ steps.notify-start.outputs.result }},
body,
})
- name: Notify Failure
uses: actions/github-script@v7
continue-on-error: true
if: failure() && github.event_name == 'issue_comment'
with:
script: |
const body = `❌ ${process.env.JOB_URL}`
github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: ${{ steps.notify-start.outputs.result }},
body,
})
- name: Slack Notification
if: success() && (github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && inputs.notify == true))
continue-on-error: true
env:
SLACK_CHANNEL: '#charts-performance'
SLACK_ICON: 'https://avatars.slack-edge.com/2020-11-25/1527503386626_319578f21381f9641cd8_192.png'
SLACK_USERNAME: 'ag-charts CI'
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
SLACK_COLOR: ${{ steps.benchmark-json.outputs.status != 'failure' && 'success' || 'failure' }}
run: |
node ./tools/benchmark/format-slack-message.js > ./reports/benchmark-slack.json
curl -X POST -H 'Content-type: application/json' ${{ secrets.SLACK_WEBHOOK }} --data @./reports/benchmark-slack.json
- name: Slack Notification
if: failure() && (github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && inputs.notify == true))
continue-on-error: true
env:
SLACK_CHANNEL: '#charts-performance'
SLACK_ICON: 'https://avatars.slack-edge.com/2020-11-25/1527503386626_319578f21381f9641cd8_192.png'
SLACK_USERNAME: 'ag-charts CI'
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
SLACK_COLOR: ${{ steps.benchmark-json.outputs.status != 'failure' && 'success' || 'failure' }}
run: |
cat <<EOF > ./reports/benchmark-slack.json
{
"channel": "${SLACK_CHANNEL}",
"username": "${SLACK_USERNAME}",
"icon_url": "${SLACK_ICON}",
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "❌ Benchmark failed: [Run #${{ github.run_id }}](${JOB_URL})"
}
}
]
}
EOF
curl -X POST -H 'Content-type: application/json' ${{ secrets.SLACK_WEBHOOK }} --data @./reports/benchmark-slack.json
- name: Perist benchmark results
if: always()
uses: actions/upload-artifact@v4
with:
name: benchmark-results
path: |
packages/ag-charts-website/src/content/docs/benchmarks/_examples/summary/*