ghaperf
ghaperf analyzes the performance of GitHub Actions workflows using GitHub API and raw job logs. Unlike other tools, it can detect bottlenecks inside composite actions by parsing job logs and extracting step-level timing data.
Why ghaperf?
Existing tools rely on the Workflow Jobs API, which doesn't include step-level data from composite actions. ghaperf solves this by:
- Retrieving and parsing raw job logs via the API
- Extracting timing data from all log groups, including steps within composite actions
Quick Start
- Install ghaperf
- (Optional) Set your GitHub Access token to avoid GitHub API rate limit and to access private repositories
- Run ghaperf
export GITHUB_TOKEN=xxx
# Analyze a workflow across multiple runs
ghaperf --repo aquaproj/aqua-registry --workflow test.yaml --count 10 --threshold 2s
Example Report Output
This report was generated by ghaperf.
| ghaperf version | v0.0.4-0 |
| Created At | 2025-11-01T12:22:40+09:00 |
| Threshold | 2s |
| The Number of Workflow Runs | 10 |
| Workflow Name | test.yaml |
Job: test / test / test (windows-latest)
| Average Job Duration | 35s (5m45s/10) |
| Slowest Jobs | 44s, 43s, 40s |
Slow steps
- 13s (2m6s/10): Run aquaproj/aqua-installer@ea518c135a02fc11ff8024364510c181a5c6b342
- 8s (1m15s/9): Run $(if($env:AQUA_ROOT_DIR) {echo $env:AQUA_ROOT_DIR} else {echo "$HOME/AppData/Local/aquaproj-aqua/bin"}) | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
- 5s (43s/9): Run if [ "${SKIP_INSTALL_AQUA:-}" = true ] && command -v aqua >/dev/null; then
- 8s (1m22s/10): Run actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8
- 3s (15s/6): Setting up auth
- 11s (11s/1): Fetching the repository
- 4s (42s/10): Set up job
- 4s (38s/10): Run aquaproj/registry-action/test@68f10339de561d67f9acea40b91dc36aa5011ea8
- 2s (19s/8): Run aqua exec -- ci-info run | sed -E "s/^export //" >> "$GITHUB_ENV"
- 4s (4s/1): Run aqua i --test
Job: test / test / test (windows-latest, arm64)
| Average Job Duration | 33s (5m33s/10) |
| Slowest Jobs | 48s, 36s, 36s |
Slow steps
- 15s (2m29s/10): Run aquaproj/aqua-installer@ea518c135a02fc11ff8024364510c181a5c6b342
- 11s (1m42s/9): Run $(if($env:AQUA_ROOT_DIR) {echo $env:AQUA_ROOT_DIR} else {echo "$HOME/AppData/Local/aquaproj-aqua/bin"}) | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
- 4s (39s/9): Run if [ "${SKIP_INSTALL_AQUA:-}" = true ] && command -v aqua >/dev/null; then
- 8s (1m16s/10): Run actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8
- 3s (19s/7): Setting up auth
- 2s (2s/1): Fetching the repository
- 3s (31s/10): Run aquaproj/registry-action/test@68f10339de561d67f9acea40b91dc36aa5011ea8
- 3s (29s/10): Set up job
- 2s (20s/10): Post Run actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8
- 3s (19s/7): Run aqua i --test
- 5s (5s/1): Run actions/download-artifact@eaceaf801fd36c7dee90939fad912460b18a1ffe
Job: test / test / test (macos-13)
| Average Job Duration | 22s (3m35s/10) |
| Slowest Jobs | 31s, 25s, 24s |
Slow steps
- 6s (1m1s/10): Set up job
- 5s (46s/9): Run aquaproj/registry-action/test@68f10339de561d67f9acea40b91dc36aa5011ea8
- 4s (22s/5): Run aqua i --test
- 2s (16s/7): Run aqua exec -- ci-info run | sed -E "s/^export //" >> "$GITHUB_ENV"
- 4s (34s/9): Run aquaproj/aqua-installer@ea518c135a02fc11ff8024364510c181a5c6b342
- 4s (33s/9): Run actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8
Job: test / test / test (macos-14)
| Average Job Duration | 19s (3m10s/10) |
| Slowest Jobs | 22s, 21s, 21s |
Slow steps
- 6s (58s/10): Set up job
- 4s (36s/10): Run aquaproj/registry-action/test@68f10339de561d67f9acea40b91dc36aa5011ea8
- 3s (12s/4): Run aqua i --test
- 2s (2s/1): Run aqua exec -- ci-info run | sed -E "s/^export //" >> "$GITHUB_ENV"
- 3s (30s/10): Run aquaproj/aqua-installer@ea518c135a02fc11ff8024364510c181a5c6b342
- 2s (23s/10): Run actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8
Job: test / lintnet / lintnet
| Average Job Duration | 15s (2m33s/10) |
| Slowest Jobs | 18s, 17s, 17s |
Slow steps
- 6s (1m0s/10): Run lintnet lint
- 3s (29s/10): Run aquaproj/aqua-installer@ea518c135a02fc11ff8024364510c181a5c6b342
- 2s (20s/10): Post Run actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8
Job: test / test / test (ubuntu-24.04)
| Average Job Duration | 14s (2m22s/10) |
| Slowest Jobs | 18s, 16s, 15s |
Slow steps
- 4s (36s/10): Set up job
- 3s (32s/10): Run aquaproj/registry-action/test@68f10339de561d67f9acea40b91dc36aa5011ea8
- 3s (7s/2): Run aqua i --test
- 2s (2s/1): Run aqua exec -- ci-info run | sed -E "s/^export //" >> "$GITHUB_ENV"
- 2s (21s/10): Run aquaproj/aqua-installer@ea518c135a02fc11ff8024364510c181a5c6b342
Job: test / test / test (ubuntu-24.04-arm)
| Average Job Duration | 14s (2m18s/10) |
| Slowest Jobs | 19s, 18s, 15s |
Slow steps
- 3s (32s/10): Set up job
- 3s (28s/10): Run aquaproj/registry-action/test@68f10339de561d67f9acea40b91dc36aa5011ea8
- 2s (23s/10): Run aquaproj/aqua-installer@ea518c135a02fc11ff8024364510c181a5c6b342
Job: test / ci-info / ci-info
| Average Job Duration | 10s (1m37s/10) |
| Slowest Jobs | 14s, 11s, 11s |
Slow steps
- 2s (21s/10): Run suzuki-shunsuke/ci-info-action/store@ceeb10dd50cd632db31e7eccf92cbbb6856f3191
- 2s (20s/10): Run aquaproj/aqua-installer@ea518c135a02fc11ff8024364510c181a5c6b342
Job: test / check-files / check-files
| Average Job Duration | 5s (51s/10) |
| Slowest Jobs | 7s, 6s, 6s |
The job has no slow steps
Job: test / path-filter
| Average Job Duration | 4s (41s/10) |
| Slowest Jobs | 5s, 5s, 5s |
The job has no slow steps
Job: status-check
| Average Job Duration | 4s (4s/1) |
| Slowest Jobs | 4s, 0s, 0s |
The job has no slow steps
Key Features
- Deep visibility into composite actions - Detect bottlenecks inside composite actions that other tools miss
- Multiple analysis modes - Analyze workflows, workflow runs, or individual jobs
- Markdown reports - Generate shareable performance reports
- No infrastructure needed - Just a CLI tool, no backend or metrics storage required
- Intelligent caching - Cache GitHub API responses for completed runs to speed up analysis
- Flexible filtering - Filter and normalize job names using configuration files
Installation
See INSTALL.md for detailed installation instructions.
Usage
For complete usage documentation, see USAGE.md.
Prerequisites
A GitHub access token is required to avoid API rate limits and access private repositories. See GitHub Access Token for details.
export GITHUB_TOKEN=xxx
Analysis Modes
1. Analyze multiple workflow runs (recommended for performance insights)
ghaperf \
--repo suzuki-shunsuke/ghaperf \
--workflow test.yaml \
--count 10 \
--threshold 2s
[!NOTE]
Higher --count values provide better insights but take longer to process.
2. Analyze a single workflow run
ghaperf \
--repo "suzuki-shunsuke/tfaction" \
--run-id "<workflow run id>"
3. Analyze a specific job
ghaperf \
--repo suzuki-shunsuke/tfaction \
--job-id "<workflow job id>"
4. Analyze a local log file
ghaperf --log-file path/to/job.log
See the example log file for format reference.
Configuration
Environment Variables
| Variable |
Description |
Default |
GHAPERF_LOG_LEVEL |
Log level: debug, info, warn, error |
info |
GHAPERF_GITHUB_TOKEN |
GitHub access token |
- |
GITHUB_TOKEN |
GitHub access token (alternative) |
- |
GHAPERF_GHTKN |
Enable ghtkn integration |
false |
GHAPERF_THRESHOLD |
Default threshold for slow steps/log groups |
30s |
GitHub Access Token
A GitHub access token is required to fetch workflow runs, jobs, and logs via the GitHub API.
An access token isn't required for public repositories, but it's recommended to avoid API rate limit.
Required Permissions:
- Public repositories: No specific permissions needed
- Private repositories:
Actions: Read permission
Setup:
# Option 1: Use GITHUB_TOKEN
export GITHUB_TOKEN=ghp_xxxxxxxxxxxxx
# Option 2: Use GHAPERF_GITHUB_TOKEN
export GHAPERF_GITHUB_TOKEN=ghp_xxxxxxxxxxxxx
# Option 3: Use ghtkn integration
export GHAPERF_GHTKN=true
Threshold
ghaperf reports steps and log groups that exceed the specified threshold:
Threshold Configuration:
- Default:
30s
- Set via
--threshold flag or GHAPERF_THRESHOLD environment variable
- Format: Go duration (e.g.,
1s, 2m30s)
Configuration File
Use configuration files to filter jobs by job name and normalize job names.
All settings are optional.
ghperf --config <configuration file path> ...
e.g.
# Only analyze jobs matching these glob patterns
job_names:
- "test / test / test *"
# Exclude jobs matching these glob patterns
# excluded_job_names:
# - "test / test / test *"
# Normalize matrix job names for aggregation
job_name_mappings:
"test / test / test *": "test / test / test"
Available Fields:
job_names: List of glob patterns - only matching jobs are analyzed
excluded_job_names: List of glob patterns - matching jobs are excluded
job_name_mappings: Map of glob patterns to normalized names for matrix jobs
JSON Schema and Validation:
The configuration schema is available at json-schema/ghaperf.json.
Validate your configuration with ajv-cli:
ajv --spec=draft2020 -s json-schema/ghaperf.json -d ghaperf.yaml
IDE Support:
Enable auto-completion in your editor by adding this to your config file:
Latest version:
# yaml-language-server: $schema=https://raw.githubusercontent.com/suzuki-shunsuke/ghaperf/main/json-schema/ghaperf.json
Or pin to a specific version:
# yaml-language-server: $schema=https://raw.githubusercontent.com/suzuki-shunsuke/ghaperf/v0.0.3/json-schema/ghaperf.json
Advanced Topics
Caching
ghaperf automatically caches API responses for completed workflow runs and jobs to improve performance on subsequent analyses.
Cache location: ${XDG_CACHE_HOME:-${HOME}/.cache}/ghaperf/
This speeds up repeated analyses and reduces API calls.
Important Notes
-
Log availability timing: Job logs must be fully processed by GitHub. If a job just completed, the API may not have logs ready yet. Wait a few moments and retry.
-
Log format changes: GitHub's log format is not officially documented. ghaperf parses logs based on observed patterns, which may break if GitHub changes the format unexpectedly.
-
Log retention: GitHub retains workflow logs for 90 days by default. Analysis of older runs may fail if logs have been deleted.
While these tools are excellent for analyzing GitHub Actions performance, they don't provide step-level visibility into composite actions like ghaperf does.
We don't aim to replace these tools with ghaperf.
For this reason, it is not our intention to re-implement features already present in other tools, such as CIAnalyzer, in ghaperf.
Rather, ghaperf preserves features that other tools are missing.
Tools like CIAnalyzer are more suitable if you want to know how performance changes over the medium to long term.
On the other hand, ghaperf is more suitable if you want to investigate current performance bottlenecks in more detail.