name: "CodSpeed Performance Analysis" description: "Continuous benchmarking and performance checks" branding: color: orange icon: activity author: "Arthur Pastel" inputs: run: description: "The command to run the benchmarks" required: false mode: description: | The mode(s) to run the benchmarks in. Accepts a single value or a comma-separated list (e.g., "simulation,walltime"). The following modes are available: - `simulation`: Run the benchmarks with CPU simulation measurements. - `walltime`: Run the benchmarks with walltime measurement. - `memory`: Run the benchmarks with allocation measurements. - `instrumentation`: (Deprecated) Legacy name for `simulation`. Please use `simulation` instead. We strongly recommend starting with the `simulation` mode. Using the `walltime` mode on traditional VMs/Hosted Runners might lead to inconsistent data. For the best results, we recommend using CodSpeed Hosted Macro Runners, which are fine-tuned for performance measurement consistency. Check out the [Walltime Instrument Documentation](https://docs.codspeed.io/instruments/walltime/) for more details. required: true token: description: | CodSpeed upload token. Only required for private repositories. required: false working-directory: description: | The directory where the `run` command will be executed. Warning: if you use defaults.working-directory, you must still set this parameter. required: false upload-url: description: "The upload endpoint (for on-premise deployments)" required: false runner-version: description: "The version of the runner to use. Use 'latest' to automatically fetch the latest release version from GitHub, or specify a version like '3.5.0' or 'v3.5.0'." required: false instruments: description: | Comma separated list of instruments to enable. The following instruments are available: - `mongodb`: MongoDB instrumentation, requires the MongoDB instrument to be enabled for the organization in CodSpeed required: false mongo-uri-env-name: description: | The name of the environment variable containing the MongoDB URI. Requires the `mongodb` instrument to be activated in `instruments`. If the instrumentation is enabled and this value is not set, the user will need to dynamically provide the MongoDB URI to the CodSpeed runner. required: false cache-instruments: description: | Enable caching of instrument installations (like valgrind or perf) to speed up subsequent workflow runs. Set to 'false' to disable caching. required: false default: "true" instruments-cache-dir: description: | The directory to use for caching installations of instruments (like valgrind or perf). Defaults to `$HOME/.cache/codspeed-action`. required: false default: "~/.cache/codspeed-action" allow-empty: description: | Allow the action to complete successfully even if no benchmarks were found or run. Set to 'true' to enable this behavior. required: false default: "false" go-runner-version: description: "The version of the go-runner to use (e.g., 1.0.0, 1.0.0-beta.1). If not specified, the latest version will be installed" required: false config: description: "Path to a CodSpeed configuration file (codspeed.yml). If not specified, the runner will look for a codspeed.yml file in the repository root." required: false skip-hash-check-warning: description: "Suppress the warning emitted when hash verification is not available (e.g. when using 'latest', branch, or revision versions)." required: false default: "false" runs: using: "composite" steps: - name: Determine runner and kernel version id: versions shell: bash run: | RUNNER_VERSION="${{ inputs.runner-version }}" if [ -z "$RUNNER_VERSION" ]; then RUNNER_VERSION=$(cat $GITHUB_ACTION_PATH/.codspeed-runner-version) fi # Strip 'v' prefix if present to normalize version format RUNNER_VERSION="${RUNNER_VERSION#v}" # Detect version type (priority: latest > version number > branch/rev prefixes) if [ "$RUNNER_VERSION" = "latest" ]; then VERSION_TYPE="latest" elif echo "$RUNNER_VERSION" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+-'; then VERSION_TYPE="prerelease" elif echo "$RUNNER_VERSION" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+'; then VERSION_TYPE="release" elif echo "$RUNNER_VERSION" | grep -q '^branch:'; then VERSION_TYPE="branch" RUNNER_VERSION=$(echo "$RUNNER_VERSION" | sed 's/^branch://') elif echo "$RUNNER_VERSION" | grep -q '^rev:'; then VERSION_TYPE="rev" RUNNER_VERSION=$(echo "$RUNNER_VERSION" | sed 's/^rev://') else # Default to release version VERSION_TYPE="release" fi echo "runner-version=$RUNNER_VERSION" >> $GITHUB_OUTPUT echo "version-type=$VERSION_TYPE" >> $GITHUB_OUTPUT # Get kernel version for cache key KERNEL_VERSION=$(uname -r) echo "kernel-version=$KERNEL_VERSION" >> $GITHUB_OUTPUT # Normalize mode for cache key (commas are not allowed in cache keys) MODE_CACHE_KEY=$(echo "${{ inputs.mode }}" | tr ',' '-') echo "mode-cache-key=$MODE_CACHE_KEY" >> $GITHUB_OUTPUT - name: Cache CodSpeed instruments if: inputs.cache-instruments == 'true' uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 with: path: ${{ inputs.instruments-cache-dir }} key: codspeed-instruments-${{ steps.versions.outputs.mode-cache-key }}-${{ runner.os }}-${{ runner.arch }}-${{ steps.versions.outputs.kernel-version }}-${{ steps.versions.outputs.runner-version }} restore-keys: | codspeed-instruments-${{ steps.versions.outputs.mode-cache-key }}-${{ runner.os }}-${{ runner.arch }}-${{ steps.versions.outputs.kernel-version }}- - name: Lookup installer hash id: installer-hash uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: RUNNER_VERSION: ${{ steps.versions.outputs.runner-version }} with: script: | const hashes = require(`${process.env.GITHUB_ACTION_PATH}/.codspeed-runner-installer-hashes.json`) const version = process.env.RUNNER_VERSION core.setOutput('hash', hashes[version] || '') - name: Install CodSpeed runner shell: bash run: | RUNNER_VERSION="${{ steps.versions.outputs.runner-version }}" VERSION_TYPE="${{ steps.versions.outputs.version-type }}" SKIP_HASH_CHECK_WARNING="${{ inputs.skip-hash-check-warning }}" if [ "$VERSION_TYPE" = "latest" ]; then if [ "$SKIP_HASH_CHECK_WARNING" != "true" ]; then echo "::warning::Hash verification is not available when using 'latest' version. Consider pinning a specific version for supply chain security." fi curl -fsSL https://codspeed.io/install.sh | bash -s -- --quiet elif [ "$VERSION_TYPE" = "prerelease" ]; then # Prereleases are not pinned, so install from the versioned installer without hash verification. if [ "$SKIP_HASH_CHECK_WARNING" != "true" ]; then echo "::warning::Hash verification is not available for prerelease version $RUNNER_VERSION." fi curl -fsSL "https://codspeed.io/v$RUNNER_VERSION/install.sh" | bash -s -- --quiet elif [ "$VERSION_TYPE" = "branch" ]; then # Install from specific branch using cargo if [ "$SKIP_HASH_CHECK_WARNING" != "true" ]; then echo "::warning::Hash verification is not available when installing from a branch." fi source $HOME/.cargo/env cargo install --locked --git https://github.com/CodSpeedHQ/codspeed --branch "$RUNNER_VERSION" codspeed-runner elif [ "$VERSION_TYPE" = "rev" ]; then # Install from specific commit/rev using cargo if [ "$SKIP_HASH_CHECK_WARNING" != "true" ]; then echo "::warning::Hash verification is not available when installing from a revision." fi source $HOME/.cargo/env cargo install --locked --git https://github.com/CodSpeedHQ/codspeed --rev "$RUNNER_VERSION" codspeed-runner else # Release version - download to temp file and verify hash INSTALLER_URL="https://codspeed.io/v$RUNNER_VERSION/install.sh" INSTALLER_TMP=$(mktemp) trap "rm -f $INSTALLER_TMP" EXIT CURL_ERR=$(mktemp) if ! HTTP_CODE=$(curl -sSL -o "$INSTALLER_TMP" -w "%{http_code}" "$INSTALLER_URL" 2>"$CURL_ERR"); then echo "::error title=Failed to install CodSpeed CLI::Installation of CodSpeed CLI with version $RUNNER_VERSION failed.%0AReason: $(cat "$CURL_ERR")" exit 1 fi if [ "$HTTP_CODE" -ge 400 ]; then error_body=$(cat "$INSTALLER_TMP") echo "::error title=Failed to install CodSpeed CLI::Installation of CodSpeed CLI with version $RUNNER_VERSION failed.%0AReason: HTTP $HTTP_CODE - ${error_body:-no response body}" exit 1 fi EXPECTED_HASH="${{ steps.installer-hash.outputs.hash }}" if [ -z "$EXPECTED_HASH" ]; then echo "::error::No pinned hash found for installer version $RUNNER_VERSION. Update .codspeed-runner-installer-hashes.json." exit 1 fi ACTUAL_HASH=$(sha256sum "$INSTALLER_TMP" | awk '{print $1}') if [ "$ACTUAL_HASH" != "$EXPECTED_HASH" ]; then echo "::error::Installer hash mismatch for version $RUNNER_VERSION. Expected: $EXPECTED_HASH, Got: $ACTUAL_HASH" exit 1 fi echo "Installer hash verified for version $RUNNER_VERSION" bash "$INSTALLER_TMP" --quiet fi - name: Run the benchmarks shell: bash env: GH_MATRIX: "${{ toJson(matrix) }}" GH_STRATEGY: "${{ toJson(strategy) }}" # This is needed to properly handle quotes and multiline commands in the 'run' input properly, rather than doing `RUN_INPUT_RUN=${{ inputs.run }}` in the script CODSPEED_INPUT_RUN: ${{ inputs.run }} run: | # Validate required inputs # (custom message for smoother v4 migration) if [ -z "${{ inputs.mode }}" ]; then echo "::error title=Missing required input 'mode'::The 'mode' input is required as of CodSpeed Action v4. Please explicitly set 'mode' to 'simulation' or 'walltime'. Before, this variable was automatically set to instrumentation on every runner except for CodSpeed macro runners where it was set to walltime by default. See https://codspeed.io/docs/instruments for details." exit 1 fi # Build the runner arguments array RUNNER_ARGS=() if [ -n "${{ inputs.token }}" ]; then RUNNER_ARGS+=(--token "${{ inputs.token }}") fi if [ -n "${{ inputs.working-directory }}" ]; then RUNNER_ARGS+=(--working-directory="${{ inputs.working-directory }}") fi if [ -n "${{ inputs.upload-url }}" ]; then RUNNER_ARGS+=(--upload-url="${{ inputs.upload-url }}") fi if [ -n "${{ inputs.mode }}" ]; then RUNNER_ARGS+=(--mode="${{ inputs.mode }}") fi if [ -n "${{ inputs.instruments }}" ]; then RUNNER_ARGS+=(--instruments="${{ inputs.instruments }}") fi if [ -n "${{ inputs.mongo-uri-env-name }}" ]; then RUNNER_ARGS+=(--mongo-uri-env-name="${{ inputs.mongo-uri-env-name }}") fi if [ "${{ inputs.cache-instruments }}" = "true" ] && [ -n "${{ inputs.instruments-cache-dir }}" ]; then RUNNER_ARGS+=(--setup-cache-dir="${{ inputs.instruments-cache-dir }}") fi if [ "${{ inputs.allow-empty }}" = "true" ]; then RUNNER_ARGS+=(--allow-empty) fi if [ -n "${{ inputs.go-runner-version }}" ]; then RUNNER_ARGS+=(--go-runner-version="${{ inputs.go-runner-version }}") fi if [ -n "${{ inputs.config }}" ]; then RUNNER_ARGS+=(--config="${{ inputs.config }}") fi if [ -n "$CODSPEED_INPUT_RUN" ]; then RUNNER_ARGS+=(-- "$CODSPEED_INPUT_RUN") fi # Run the benchmarks codspeed run "${RUNNER_ARGS[@]}"