Skip to content

Add CI stress metrics artifacts and optional Pushgateway export#58

Merged
Mehdi-Bl merged 2 commits into
mainfrom
chore/ci-stress-metrics-pushgateway
Feb 11, 2026
Merged

Add CI stress metrics artifacts and optional Pushgateway export#58
Mehdi-Bl merged 2 commits into
mainfrom
chore/ci-stress-metrics-pushgateway

Conversation

@Mehdi-Bl

@Mehdi-Bl Mehdi-Bl commented Feb 11, 2026

Copy link
Copy Markdown
Contributor

Summary

  • add scripts/publish-stress-metrics.js to consolidate stress benchmark JSON into:
    • dist/benchmarks/summary.json
    • dist/benchmarks/stress-metrics.prom
  • support optional Pushgateway publish via PUSHGATEWAY_URL (+ PUSHGATEWAY_JOB, PUSHGATEWAY_INSTANCE, PUSHGATEWAY_STRICT)
  • wire QA workflow to summarize stress metrics on Linux and upload benchmark artifacts
  • pass Sonar CPD exclusions explicitly in SonarCloud workflow args for consistency
  • document stress metrics command/output in tests/catalog.md

Validation

  • npm run lint
  • npm test -- --runInBand
  • npm run stress:metrics

Notes

  • Pushgateway publish is opt-in and non-strict by default, so CI will not fail when endpoint is absent/unreachable.

Summary by CodeRabbit

  • New Features

    • Added stress benchmark metrics collection and aggregation capability
    • Enabled optional metrics publishing to Pushgateway for distributed monitoring
  • CI/CD

    • Enhanced CI pipeline to capture and artifact stress benchmark results
    • Configured code duplication analysis exclusions for test files
  • Documentation

    • Updated command reference with stress metrics summary command

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

@sourcery-ai

sourcery-ai Bot commented Feb 11, 2026

Copy link
Copy Markdown

Reviewer's Guide

Adds a new stress metrics collection/publish script, wires it into the Linux QA workflow to generate artifacts and optionally push Prometheus metrics to a Pushgateway, documents how to run and consume the outputs, and tightens SonarCloud configuration with explicit CPD exclusions.

Sequence diagram for CI stress metrics publish to Pushgateway

sequenceDiagram
  participant GitHubRunner
  participant Npm as npm_cli
  participant Script as publish_stress_metrics_js
  participant FS as FileSystem
  participant PushGW as Pushgateway

  GitHubRunner->>Npm: npm run stress:metrics
  Npm->>Script: node scripts/publish-stress-metrics.js

  Script->>FS: read benchmark JSON files from dist/benchmarks
  FS-->>Script: JSON contents and file stats

  Script->>Script: normalize records and build summary
  Script->>FS: write summary.json
  Script->>Script: build Prometheus metrics payload
  Script->>FS: write stress-metrics.prom

  Script->>Script: check PUSHGATEWAY_URL env
  alt PUSHGATEWAY_URL not set
    Script-->>GitHubRunner: log skipping Pushgateway publish
  else PUSHGATEWAY_URL set
    Script->>PushGW: HTTP PUT metrics with job and instance labels
    alt HTTP 2xx
      PushGW-->>Script: success status
      Script-->>GitHubRunner: log successful push
    else non-2xx or network error
      Script->>Script: check PUSHGATEWAY_STRICT
      alt strict mode
        Script-->>GitHubRunner: log failure and exit 1
      else non-strict mode
        Script-->>GitHubRunner: warn but exit 0
      end
    end
  end
Loading

File-Level Changes

Change Details Files
Introduce stress metrics publisher script that consolidates JSON benchmarks, emits Prometheus metrics, and optionally pushes to a Pushgateway.
  • Create Node script that scans dist/benchmarks for JSON files (excluding the summary), normalizes them into scenario records, and writes a consolidated summary.json.
  • Generate Prometheus text-format metrics (latency percentiles, sample counts, file counts, iterations) from normalized records into stress-metrics.prom.
  • Implement optional Pushgateway export via HTTP(S) PUT using environment-driven job/instance identifiers and a strict/non-strict mode flag.
scripts/publish-stress-metrics.js
Expose a stress:metrics npm script and integrate it into the Linux QA matrix to run after tests and upload artifacts.
  • Add npm run stress:metrics command that executes the stress metrics publisher script.
  • In the QA matrix workflow, run stress:metrics only on Linux with Pushgateway-related envs plumbed from GitHub secrets/vars.
  • Upload generated stress benchmark JSON and Prometheus files as a Linux-only CI artifact with a 14-day retention and fail if missing.
package.json
.github/workflows/qa-matrix.yml
Document the new stress metrics command, outputs, CI artifact, and Pushgateway configuration.
  • Add stress:metrics to the tests catalog as a core command for stress metrics summary and optional Pushgateway publish.
  • Describe the locations and names of raw/summary JSON, Prometheus metrics file, CI artifact, and required/optional Pushgateway env vars.
tests/catalog.md
Align SonarCloud analysis configuration by explicitly passing CPD exclusions via workflow arguments.
  • Extend SonarCloud workflow scanner args with sonar.cpd.exclusions for tests and src tests directories.
.github/workflows/sonarcloud.yml

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@github-actions

github-actions Bot commented Feb 11, 2026

Copy link
Copy Markdown

Dependency Review

✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.

Scanned Files

None

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

Review Summary by Qodo

Add stress metrics reporting and optional Pushgateway integration

✨ Enhancement 🧪 Tests

Grey Divider

Walkthroughs

Description
• Add stress metrics reporting script with Prometheus format export
• Integrate optional Pushgateway publishing for CI metrics tracking
• Wire stress metrics task into QA workflow on Linux runners
• Upload benchmark artifacts and document stress testing outputs
• Add explicit SonarCloud CPD exclusions for consistency
Diagram
flowchart LR
  A["Benchmark JSON Files"] -->|normalizeBenchmarkRecord| B["Normalized Records"]
  B -->|buildPrometheusPayload| C["Prometheus Metrics"]
  C -->|Write Files| D["summary.json<br/>stress-metrics.prom"]
  C -->|Optional Push| E["Pushgateway"]
  D -->|Upload| F["CI Artifacts"]
Loading

Grey Divider

File Changes

1. scripts/publish-stress-metrics.js ✨ Enhancement +280/-0

Stress metrics reporting and Pushgateway integration script

• New script to consolidate stress benchmark JSON files into summary and Prometheus format
• Normalizes benchmark records with latency percentiles, sample counts, and file metrics
• Implements optional Pushgateway publishing with configurable job/instance names
• Supports non-strict mode for graceful failure when Pushgateway is unavailable

scripts/publish-stress-metrics.js


2. .github/workflows/qa-matrix.yml ✨ Enhancement +20/-0

Wire stress metrics into QA workflow with artifact upload

• Add stress metrics summarization step on Linux runners after unit tests
• Configure Pushgateway environment variables from secrets and GitHub context
• Upload stress benchmark artifacts with JSON and Prometheus format files
• Set 14-day retention for benchmark artifacts with error handling

.github/workflows/qa-matrix.yml


3. .github/workflows/sonarcloud.yml ⚙️ Configuration changes +1/-0

Add SonarCloud CPD exclusion configuration

• Add explicit SonarCloud CPD exclusions for tests and __tests__ directories
• Improves consistency by passing exclusions as workflow arguments

.github/workflows/sonarcloud.yml


View more (2)
4. package.json ⚙️ Configuration changes +2/-1

Add stress metrics npm script

• Add stress:metrics npm script that runs the publish-stress-metrics.js script

package.json


5. tests/catalog.md 📝 Documentation +9/-0

Document stress metrics outputs and configuration

• Document stress metrics command and its Pushgateway integration options
• Add section describing benchmark output files and CI artifact locations
• List environment variables for configuring Pushgateway behavior

tests/catalog.md


Grey Divider

Qodo Logo

@gemini-code-assist

Copy link
Copy Markdown

Summary of Changes

Hello @Mehdi-Bl, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the project's continuous integration capabilities by introducing automated collection and reporting of stress test metrics. It provides a structured way to process benchmark results, generate Prometheus-compatible metrics, and optionally push them to a Pushgateway for external monitoring. This integration aims to provide better visibility into application performance and stability over time within the CI pipeline.

Highlights

  • Stress Metrics Generation: A new Node.js script (scripts/publish-stress-metrics.js) has been added to process stress benchmark JSON files, consolidate them into a summary, and generate Prometheus-compatible metrics.
  • Optional Pushgateway Export: The generated stress metrics can now be optionally published to a Prometheus Pushgateway endpoint, configurable via environment variables (PUSHGATEWAY_URL, PUSHGATEWAY_JOB, PUSHGATEWAY_INSTANCE, PUSHGATEWAY_STRICT).
  • CI Integration: The QA workflow will be updated to utilize this new script, summarizing stress metrics on Linux and uploading benchmark artifacts, enhancing continuous integration with performance insights.
  • Documentation Update: The tests/catalog.md file has been updated to document the new stress:metrics command and the various output artifacts generated by the stress benchmarks.
  • Build Script Addition: A new npm script, stress:metrics, has been added to package.json to easily execute the stress metrics publishing script.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Changelog
  • package.json
    • Added a new npm script stress:metrics to execute the stress metrics publishing script.
  • scripts/publish-stress-metrics.js
    • Created a new Node.js script responsible for reading stress benchmark JSON files from dist/benchmarks.
    • Implemented logic to normalize benchmark records, extract key performance indicators like p50, p95, p99 latencies, sample counts, file counts, and iterations.
    • Developed functionality to build a consolidated JSON summary of all benchmarks.
    • Added the capability to generate metrics in Prometheus text format, including HELP and TYPE declarations for clarity.
    • Integrated an optional feature to push the generated Prometheus metrics to a configurable Pushgateway endpoint using HTTP/HTTPS requests.
    • Included error handling and non-strict mode for Pushgateway publishing to prevent CI failures when the endpoint is unavailable.
  • tests/catalog.md
    • Updated the 'Core Commands' section to include the new npm run stress:metrics command.
    • Added a new section detailing the outputs of stress benchmarks, including raw JSON, consolidated summary, Prometheus text format, CI artifact name, and Pushgateway configuration options.
Ignored Files
  • Ignored by pattern: .github/workflows/** (2)
    • .github/workflows/qa-matrix.yml
    • .github/workflows/sonarcloud.yml
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@coderabbitai

coderabbitai Bot commented Feb 11, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

This change introduces a stress benchmark metrics workflow that aggregates benchmark results, publishes them in Prometheus format, and optionally pushes to a Pushgateway. It adds CI steps, an npm script, a new metrics aggregation script, and documentation updates to enable stress test monitoring.

Changes

Cohort / File(s) Summary
CI Workflow Enhancements
.github/workflows/qa-matrix.yml, .github/workflows/sonarcloud.yml
Adds Linux-only CI steps for stress benchmark metrics summarization and artifact uploads to GitHub Actions; adds CPD exclusion patterns to SonarCloud scan to ignore test directories.
Package Configuration
package.json
Adds stress:metrics npm script; fixes trailing comma in build:linux script entry for valid JSON structure.
Metrics Publishing
scripts/publish-stress-metrics.js
New Node.js script that reads benchmark JSON files from dist/benchmarks, normalizes records with latency percentiles (p50/p95/p99) and metrics, generates summary JSON and Prometheus-format output file, and optionally publishes to Pushgateway with configurable job/instance labels and strict-mode error handling.
Documentation
tests/catalog.md
Updates command catalog with new stress metrics summary command and expands documentation of stress benchmark outputs and publishing targets.

Sequence Diagram

sequenceDiagram
    participant CI as GitHub Actions<br/>(qa-matrix)
    participant FS as File System<br/>(dist/benchmarks)
    participant Script as publish-stress-metrics.js
    participant PG as Pushgateway<br/>(optional)

    CI->>FS: Stress tests complete,<br/>write JSON files
    CI->>Script: npm run stress:metrics
    Script->>FS: Read benchmark JSON files
    Script->>Script: Normalize records<br/>(p50, p95, p99, etc.)
    Script->>Script: Build summary payload<br/>& Prometheus format
    Script->>FS: Write summary.json<br/>& stress-metrics.prom
    alt PUSHGATEWAY_URL configured
        Script->>PG: POST metrics payload<br/>(with job/instance labels)
        PG-->>Script: Success or error response
        alt Strict mode
            Script->>Script: Throw on error
        else Warning mode
            Script->>Script: Log warning, continue
        end
    else No Pushgateway
        Script->>Script: Skip push
    end
    Script->>CI: Complete, log output paths
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Poem

🐰 Metrics now flow like carrots in spring,
Benchmarks aggregate, stress tests take wing,
Prometheus records each latency tale,
Pushgateway listens—performance won't fail! 🏃‍♂️📊

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title accurately summarizes the main changes: adding CI stress metrics artifact uploads and optional Pushgateway export functionality. It is clear, concise, and directly related to the changeset.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch chore/ci-stress-metrics-pushgateway

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 1 issue, and left some high level feedback:

  • The pushToPushgateway HTTP(S) request currently has no timeout, so a slow or unresponsive Pushgateway could stall the CI job; consider adding a reasonable request timeout or abort mechanism, ideally configurable via env.
  • In readBenchmarkFiles/normalizeBenchmarkRecord, a single malformed or partially written JSON file will cause the whole script to fail; you may want to catch and log per-file parse errors and skip those files so metrics generation is more robust.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The `pushToPushgateway` HTTP(S) request currently has no timeout, so a slow or unresponsive Pushgateway could stall the CI job; consider adding a reasonable request timeout or abort mechanism, ideally configurable via env.
- In `readBenchmarkFiles`/`normalizeBenchmarkRecord`, a single malformed or partially written JSON file will cause the whole script to fail; you may want to catch and log per-file parse errors and skip those files so metrics generation is more robust.

## Individual Comments

### Comment 1
<location> `scripts/publish-stress-metrics.js:182-191` </location>
<code_context>
+function pushToPushgateway(endpointUrl, payload) {
</code_context>

<issue_to_address>
**suggestion (bug_risk):** The Pushgateway HTTP request has no timeout, which can hang the process if the endpoint is slow or unresponsive.

Relying on default socket behavior means a slow or misbehaving Pushgateway can leave the CI job hanging until an external timeout intervenes. Please add an explicit request timeout (for example, via `request.setTimeout(...)` that rejects on timeout) so failures are bounded and predictable in the CI context.

Suggested implementation:

```javascript
function pushToPushgateway(endpointUrl, payload) {
  return new Promise((resolve, reject) => {
    const client = endpointUrl.protocol === 'https:' ? https : http;
    const REQUEST_TIMEOUT_MS = 10_000;

    const request = client.request(
      endpointUrl,
      {
        method: 'PUT',
        timeout: REQUEST_TIMEOUT_MS,
        headers: {
          'Content-Length': Buffer.byteLength(payload),
          'Content-Type': 'text/plain; version=0.0.4; charset=utf-8',

```

To fully implement the explicit timeout behavior so that the promise rejects instead of just emitting a `timeout` event, you should also:

1. Add a `request.on('timeout', ...)` handler after the `client.request(...)` call that:
   - Calls `request.destroy(new Error('Request to Pushgateway timed out'))` (or similar).
   - Ensures the promise is rejected exactly once (e.g. via a small `let settled = false;` guard shared with the existing `error`/`response` handlers if present).
2. Ensure any existing `request.on('error', ...)` handler will surface the timeout error by rejecting the promise.

Place the `request.on('timeout', ...)` alongside the existing `request.on('error', ...)` / response handlers within `pushToPushgateway`.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines +182 to +191
function pushToPushgateway(endpointUrl, payload) {
return new Promise((resolve, reject) => {
const client = endpointUrl.protocol === 'https:' ? https : http;

const request = client.request(
endpointUrl,
{
method: 'PUT',
headers: {
'Content-Length': Buffer.byteLength(payload),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (bug_risk): The Pushgateway HTTP request has no timeout, which can hang the process if the endpoint is slow or unresponsive.

Relying on default socket behavior means a slow or misbehaving Pushgateway can leave the CI job hanging until an external timeout intervenes. Please add an explicit request timeout (for example, via request.setTimeout(...) that rejects on timeout) so failures are bounded and predictable in the CI context.

Suggested implementation:

function pushToPushgateway(endpointUrl, payload) {
  return new Promise((resolve, reject) => {
    const client = endpointUrl.protocol === 'https:' ? https : http;
    const REQUEST_TIMEOUT_MS = 10_000;

    const request = client.request(
      endpointUrl,
      {
        method: 'PUT',
        timeout: REQUEST_TIMEOUT_MS,
        headers: {
          'Content-Length': Buffer.byteLength(payload),
          'Content-Type': 'text/plain; version=0.0.4; charset=utf-8',

To fully implement the explicit timeout behavior so that the promise rejects instead of just emitting a timeout event, you should also:

  1. Add a request.on('timeout', ...) handler after the client.request(...) call that:
    • Calls request.destroy(new Error('Request to Pushgateway timed out')) (or similar).
    • Ensures the promise is rejected exactly once (e.g. via a small let settled = false; guard shared with the existing error/response handlers if present).
  2. Ensure any existing request.on('error', ...) handler will surface the timeout error by rejecting the promise.

Place the request.on('timeout', ...) alongside the existing request.on('error', ...) / response handlers within pushToPushgateway.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a new script for processing stress test benchmark results, generating a summary and Prometheus metrics, and optionally publishing them to a Pushgateway. The implementation is robust and well-structured. I've provided a couple of suggestions to refactor the new script to reduce some code duplication, which would improve its long-term maintainability. The accompanying changes to package.json and documentation are correct and complete.

Comment on lines +84 to +89
const runs = Array.isArray(parsed.runs)
? parsed.runs.map((value) => toFiniteNumber(value)).filter((value) => value !== null)
: [];
const lagSamplesMs = Array.isArray(parsed.lagSamplesMs)
? parsed.lagSamplesMs.map((value) => toFiniteNumber(value)).filter((value) => value !== null)
: [];

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The logic for parsing and filtering numeric arrays from parsed.runs and parsed.lagSamplesMs is duplicated. This can be extracted into a helper function to improve readability and reduce code duplication. For example, you could define a helper and use it like this:

const getNumericArray = (data) =>
  Array.isArray(data)
    ? data.map(toFiniteNumber).filter((v) => v !== null)
    : [];

const runs = getNumericArray(parsed.runs);
const lagSamplesMs = getNumericArray(parsed.lagSamplesMs);

Comment on lines +136 to +170
lines.push(`# HELP ${METRIC_PREFIX}_sample_count Number of sampled points for each scenario.`);
lines.push(`# TYPE ${METRIC_PREFIX}_sample_count gauge`);

for (const record of records) {
const line = toMetricLine(`${METRIC_PREFIX}_sample_count`, record.sampleCount, {
scenario: record.scenario,
});
if (line) {
lines.push(line);
}
}

lines.push(`# HELP ${METRIC_PREFIX}_file_count Number of files exercised by the stress scenario.`);
lines.push(`# TYPE ${METRIC_PREFIX}_file_count gauge`);

for (const record of records) {
const line = toMetricLine(`${METRIC_PREFIX}_file_count`, record.fileCount, {
scenario: record.scenario,
});
if (line) {
lines.push(line);
}
}

lines.push(`# HELP ${METRIC_PREFIX}_iterations Number of iterations executed by the scenario.`);
lines.push(`# TYPE ${METRIC_PREFIX}_iterations gauge`);

for (const record of records) {
const line = toMetricLine(`${METRIC_PREFIX}_iterations`, record.iterations, {
scenario: record.scenario,
});
if (line) {
lines.push(line);
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

There's significant code duplication in how gauge metrics for sample_count, file_count, and iterations are generated. This can be refactored by iterating over a configuration array to build these metrics, which will make the code more concise and easier to maintain.

  const gaugeMetrics = [
    { name: 'sample_count', help: 'Number of sampled points for each scenario.', key: 'sampleCount' },
    { name: 'file_count', help: 'Number of files exercised by the stress scenario.', key: 'fileCount' },
    { name: 'iterations', help: 'Number of iterations executed by the scenario.', key: 'iterations' },
  ];

  for (const { name, help, key } of gaugeMetrics) {
    lines.push(`# HELP ${METRIC_PREFIX}_${name} ${help}`);
    lines.push(`# TYPE ${METRIC_PREFIX}_${name} gauge`);

    for (const record of records) {
      const line = toMetricLine(`${METRIC_PREFIX}_${name}`, record[key], {
        scenario: record.scenario,
      });
      if (line) {
        lines.push(line);
      }
    }
  }

@sonarqubecloud

Copy link
Copy Markdown

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (2) 📘 Rule violations (2) 📎 Requirement gaps (0)

Grey Divider


Action required

1. pushEndpoint logged with secret 📘 Rule violation ⛨ Security
Description
The script logs pushEndpoint.toString() which is derived from PUSHGATEWAY_URL (a secret in CI)
and can leak credentials if the URL contains embedded auth or sensitive query params. This violates
secure logging requirements by potentially exposing secrets in CI logs.
Code

scripts/publish-stress-metrics.js[R247-266]

+  const pushgatewayUrl = (process.env.PUSHGATEWAY_URL || '').trim();
+  if (!pushgatewayUrl) {
+    console.log('PUSHGATEWAY_URL not set; skipping pushgateway publish.');
+    return;
+  }
+
+  const defaultInstance =
+    [process.env.GITHUB_RUN_ID, process.env.GITHUB_RUN_ATTEMPT, process.env.RUNNER_OS]
+      .filter((segment) => typeof segment === 'string' && segment.length > 0)
+      .join('-') || os.hostname();
+
+  const jobName = (process.env.PUSHGATEWAY_JOB || 'ai_code_fusion_stress').trim();
+  const instanceName = (process.env.PUSHGATEWAY_INSTANCE || defaultInstance).trim();
+  const strictMode = process.env.PUSHGATEWAY_STRICT === 'true';
+
+  const pushEndpoint = buildPushgatewayUrl(pushgatewayUrl, jobName, instanceName);
+  try {
+    await pushToPushgateway(pushEndpoint, prometheusPayload);
+    console.log(`Stress metrics pushed to Pushgateway: ${pushEndpoint.toString()}`);
+  } catch (error) {
Evidence
PR Compliance ID 5 forbids secrets in logs. The code reads PUSHGATEWAY_URL from environment and
then logs the full Pushgateway URL via pushEndpoint.toString(), which may include sensitive
components from the secret.

Rule 5: Generic: Secure Logging Practices
scripts/publish-stress-metrics.js[247-266]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`scripts/publish-stress-metrics.js` logs `pushEndpoint.toString()` which is derived from `PUSHGATEWAY_URL` provided via CI secrets; this can leak credentials or sensitive query parameters into CI logs.

## Issue Context
`PUSHGATEWAY_URL` is provided from GitHub Actions secrets and may contain embedded credentials (e.g., basic auth) depending on environment. Logging the full URL violates secure logging requirements.

## Fix Focus Areas
- scripts/publish-stress-metrics.js[247-266]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

2. Pushgateway request no timeout 📘 Rule violation ⛯ Reliability
Description
The Pushgateway HTTP(S) request has no explicit timeout and could hang indefinitely on network
issues, reducing graceful degradation in CI. This is a missing edge-case handling for external
dependencies.
Code

scripts/publish-stress-metrics.js[R182-220]

+function pushToPushgateway(endpointUrl, payload) {
+  return new Promise((resolve, reject) => {
+    const client = endpointUrl.protocol === 'https:' ? https : http;
+
+    const request = client.request(
+      endpointUrl,
+      {
+        method: 'PUT',
+        headers: {
+          'Content-Length': Buffer.byteLength(payload),
+          'Content-Type': 'text/plain; version=0.0.4; charset=utf-8',
+        },
+      },
+      (response) => {
+        const responseChunks = [];
+
+        response.on('data', (chunk) => responseChunks.push(chunk));
+        response.on('end', () => {
+          const responseBody = Buffer.concat(responseChunks).toString('utf8').trim();
+          if (response.statusCode && response.statusCode >= 200 && response.statusCode < 300) {
+            resolve();
+            return;
+          }
+
+          reject(
+            new Error(
+              `Pushgateway returned ${response.statusCode || 'unknown status'}${
+                responseBody ? `: ${responseBody}` : ''
+              }`
+            )
+          );
+        });
+      }
+    );
+
+    request.on('error', (error) => reject(error));
+    request.write(payload);
+    request.end();
+  });
Evidence
PR Compliance ID 3 requires robust handling of failure points for external dependencies. The
client.request(...) call does not set any timeout or abort logic, so network stalls can cause the
script to block until the runner terminates it.

Rule 3: Generic: Robust Error Handling and Edge Case Management
scripts/publish-stress-metrics.js[182-220]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The Pushgateway PUT request does not set a timeout, so it may hang indefinitely on stalled connections.

## Issue Context
This code runs in CI via `npm run stress:metrics`; a hung request can block the job longer than necessary and reduces robustness when the Pushgateway endpoint is slow or unreachable.

## Fix Focus Areas
- scripts/publish-stress-metrics.js[182-220]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Artifact upload overly strict 🐞 Bug ⛯ Reliability
Description
The workflow uploads stress benchmark artifacts with always() but fails the step if no files are
found. If tests fail before generating dist/benchmarks/* outputs, the upload step adds an
additional (and potentially confusing) failure.
Code

.github/workflows/qa-matrix.yml[R99-108]

+      - name: Upload stress benchmark artifacts
+        if: runner.os == 'Linux' && always()
+        uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f
+        with:
+          name: stress-benchmarks-linux
+          path: |
+            dist/benchmarks/*.json
+            dist/benchmarks/*.prom
+          if-no-files-found: error
+          retention-days: 14
Evidence
The artifact step is configured to run regardless of prior failures and to error when missing files.
Benchmark JSONs are only written by the stress tests when they reach writeBenchmarkArtifact(), so
early test failures can leave no artifacts to upload.

.github/workflows/qa-matrix.yml[99-108]
tests/stress/main-process/ipc-latency.stress.test.ts[129-136]
tests/stress/main-process/ipc-latency.stress.test.ts[184-193]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
The stress benchmark artifact upload runs under `always()` but uses `if-no-files-found: error`, which can introduce an additional failure when tests fail before producing `dist/benchmarks/*`.

### Issue Context
Benchmarks are emitted by stress tests; if tests abort early, outputs may be absent.

### Fix Focus Areas
- .github/workflows/qa-matrix.yml[99-108]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


4. Fails on empty benchmarks 🐞 Bug ⛯ Reliability
Description
The metrics publisher exits with an error when dist/benchmarks is missing or has no JSON files,
and the workflow runs it unconditionally on Linux. Because npm test uses --passWithNoTests, CI
can pass the test step but still fail here if no stress outputs are produced/discovered.
Code

scripts/publish-stress-metrics.js[R54-69]

+function readBenchmarkFiles() {
+  if (!fs.existsSync(BENCHMARK_DIR)) {
+    throw new Error(`Benchmark directory not found: ${BENCHMARK_DIR}`);
+  }
+
+  const filePaths = fs
+    .readdirSync(BENCHMARK_DIR, { withFileTypes: true })
+    .filter((entry) => entry.isFile())
+    .map((entry) => entry.name)
+    .filter((fileName) => fileName.endsWith('.json'))
+    .filter((fileName) => fileName !== path.basename(SUMMARY_FILE))
+    .map((fileName) => path.join(BENCHMARK_DIR, fileName));
+
+  if (filePaths.length === 0) {
+    throw new Error(`No stress benchmark JSON files found in ${BENCHMARK_DIR}`);
+  }
Evidence
readBenchmarkFiles() throws if the benchmark dir/files are absent; the workflow calls the script
on Linux without checking for outputs. Separately, the test command is configured to pass even if no
tests are found, so it’s possible to reach the metrics step with no benchmark files to summarize.

scripts/publish-stress-metrics.js[54-69]
.github/workflows/qa-matrix.yml[43-46]
package.json[24-26]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`scripts/publish-stress-metrics.js` hard-fails when no benchmark directory or JSON files exist. The CI workflow invokes it without verifying that benchmarks were produced.

### Issue Context
`npm test` is configured with `--passWithNoTests`, so it’s possible for the test step to succeed even when no stress outputs exist.

### Fix Focus Areas
- scripts/publish-stress-metrics.js[54-69]
- .github/workflows/qa-matrix.yml[43-46]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

ⓘ The new review experience is currently in Beta. Learn more

Grey Divider

Qodo Logo

Comment on lines +247 to +266
const pushgatewayUrl = (process.env.PUSHGATEWAY_URL || '').trim();
if (!pushgatewayUrl) {
console.log('PUSHGATEWAY_URL not set; skipping pushgateway publish.');
return;
}

const defaultInstance =
[process.env.GITHUB_RUN_ID, process.env.GITHUB_RUN_ATTEMPT, process.env.RUNNER_OS]
.filter((segment) => typeof segment === 'string' && segment.length > 0)
.join('-') || os.hostname();

const jobName = (process.env.PUSHGATEWAY_JOB || 'ai_code_fusion_stress').trim();
const instanceName = (process.env.PUSHGATEWAY_INSTANCE || defaultInstance).trim();
const strictMode = process.env.PUSHGATEWAY_STRICT === 'true';

const pushEndpoint = buildPushgatewayUrl(pushgatewayUrl, jobName, instanceName);
try {
await pushToPushgateway(pushEndpoint, prometheusPayload);
console.log(`Stress metrics pushed to Pushgateway: ${pushEndpoint.toString()}`);
} catch (error) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

1. pushendpoint logged with secret 📘 Rule violation ⛨ Security

The script logs pushEndpoint.toString() which is derived from PUSHGATEWAY_URL (a secret in CI)
and can leak credentials if the URL contains embedded auth or sensitive query params. This violates
secure logging requirements by potentially exposing secrets in CI logs.
Agent Prompt
## Issue description
`scripts/publish-stress-metrics.js` logs `pushEndpoint.toString()` which is derived from `PUSHGATEWAY_URL` provided via CI secrets; this can leak credentials or sensitive query parameters into CI logs.

## Issue Context
`PUSHGATEWAY_URL` is provided from GitHub Actions secrets and may contain embedded credentials (e.g., basic auth) depending on environment. Logging the full URL violates secure logging requirements.

## Fix Focus Areas
- scripts/publish-stress-metrics.js[247-266]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Fix all issues with AI agents
In @.github/workflows/qa-matrix.yml:
- Around line 99-108: The upload step "Upload stress benchmark artifacts"
currently runs unconditionally (if: runner.os == 'Linux' && always()) and uses
if-no-files-found: error which causes a hard fail when the stress step is
skipped or fails; change the step so it either (A) sets if-no-files-found: warn
instead of error, or (B) gate the step on the stress step succeeding by
replacing always() with the stress step outcome (e.g., check that the job step
that runs stress/metrics completed successfully) so the artifact upload only
runs when artifacts are expected (update the step condition and/or
if-no-files-found accordingly).

In `@scripts/publish-stress-metrics.js`:
- Line 274: Current console.log prints pushEndpoint.toString() which can leak
embedded credentials; update the log to print a sanitized URL instead. Locate
the console.log using the pushEndpoint variable and replace
pushEndpoint.toString() with either pushEndpoint.origin + pushEndpoint.pathname
or a redacted form that omits pushEndpoint.username and pushEndpoint.password
(e.g., build a string using pushEndpoint.protocol, host without credentials, and
pathname) so credentials are not logged. Ensure any tests or downstream messages
still receive the safe string.
- Around line 191-230: The pushToPushgateway function creates an HTTP/HTTPS
request without any socket/response timeout, so the CI can hang if the
Pushgateway accepts connections but never responds; update pushToPushgateway to
attach a timeout (e.g., using request.setTimeout or request.on('socket', s =>
s.setTimeout(...))) and on timeout abort the request and reject the promise with
a descriptive timeout Error; ensure you wire the timeout handler to call
request.destroy() or request.abort() and remove/cleanup listeners so the promise
always resolves or rejects (reference function pushToPushgateway and the local
variable request).

Comment on lines +99 to +108
- name: Upload stress benchmark artifacts
if: runner.os == 'Linux' && always()
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f
with:
name: stress-benchmarks-linux
path: |
dist/benchmarks/*.json
dist/benchmarks/*.prom
if-no-files-found: error
retention-days: 14

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Upload will hard-fail when the stress step is skipped or fails.

If npm test (Line 41) or stress:metrics (Line 43) fails, the upload step still runs due to always(), but if-no-files-found: error will produce a second, misleading failure. Consider using warn instead, or gating the upload on the stress step's outcome.

Suggested fix
       - name: Upload stress benchmark artifacts
-        if: runner.os == 'Linux' && always()
+        if: runner.os == 'Linux' && success()
         uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f
         with:
           name: stress-benchmarks-linux
           path: |
             dist/benchmarks/*.json
             dist/benchmarks/*.prom
-          if-no-files-found: error
+          if-no-files-found: warn
           retention-days: 14
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- name: Upload stress benchmark artifacts
if: runner.os == 'Linux' && always()
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f
with:
name: stress-benchmarks-linux
path: |
dist/benchmarks/*.json
dist/benchmarks/*.prom
if-no-files-found: error
retention-days: 14
- name: Upload stress benchmark artifacts
if: runner.os == 'Linux' && success()
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f
with:
name: stress-benchmarks-linux
path: |
dist/benchmarks/*.json
dist/benchmarks/*.prom
if-no-files-found: warn
retention-days: 14
🤖 Prompt for AI Agents
In @.github/workflows/qa-matrix.yml around lines 99 - 108, The upload step
"Upload stress benchmark artifacts" currently runs unconditionally (if:
runner.os == 'Linux' && always()) and uses if-no-files-found: error which causes
a hard fail when the stress step is skipped or fails; change the step so it
either (A) sets if-no-files-found: warn instead of error, or (B) gate the step
on the stress step succeeding by replacing always() with the stress step outcome
(e.g., check that the job step that runs stress/metrics completed successfully)
so the artifact upload only runs when artifacts are expected (update the step
condition and/or if-no-files-found accordingly).

Comment on lines +191 to +230
function pushToPushgateway(endpointUrl, payload) {
return new Promise((resolve, reject) => {
const client = endpointUrl.protocol === 'https:' ? https : http;

const request = client.request(
endpointUrl,
{
method: 'PUT',
headers: {
'Content-Length': Buffer.byteLength(payload),
'Content-Type': 'text/plain; version=0.0.4; charset=utf-8',
},
},
(response) => {
const responseChunks = [];

response.on('data', (chunk) => responseChunks.push(chunk));
response.on('end', () => {
const responseBody = Buffer.concat(responseChunks).toString('utf8').trim();
if (response.statusCode && response.statusCode >= 200 && response.statusCode < 300) {
resolve();
return;
}

reject(
new Error(
`Pushgateway returned ${response.statusCode || 'unknown status'}${
responseBody ? `: ${responseBody}` : ''
}`
)
);
});
}
);

request.on('error', (error) => reject(error));
request.write(payload);
request.end();
});
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Missing request timeout — CI job can hang indefinitely if the Pushgateway is unresponsive.

The HTTP request has no socket or response timeout. If the Pushgateway accepts the connection but never responds, the CI runner will block until the GitHub Actions job-level timeout (typically 6 hours).

Suggested fix — add a socket timeout
     const request = client.request(
       endpointUrl,
       {
         method: 'PUT',
+        timeout: 15_000,
         headers: {
           'Content-Length': Buffer.byteLength(payload),
           'Content-Type': 'text/plain; version=0.0.4; charset=utf-8',
         },
       },
       (response) => {
+    request.on('timeout', () => {
+      request.destroy(new Error('Pushgateway request timed out'));
+    });
     request.on('error', (error) => reject(error));
     request.write(payload);
     request.end();
🤖 Prompt for AI Agents
In `@scripts/publish-stress-metrics.js` around lines 191 - 230, The
pushToPushgateway function creates an HTTP/HTTPS request without any
socket/response timeout, so the CI can hang if the Pushgateway accepts
connections but never responds; update pushToPushgateway to attach a timeout
(e.g., using request.setTimeout or request.on('socket', s => s.setTimeout(...)))
and on timeout abort the request and reject the promise with a descriptive
timeout Error; ensure you wire the timeout handler to call request.destroy() or
request.abort() and remove/cleanup listeners so the promise always resolves or
rejects (reference function pushToPushgateway and the local variable request).

const pushEndpoint = buildPushgatewayUrl(pushgatewayUrl, jobName, instanceName);
try {
await pushToPushgateway(pushEndpoint, prometheusPayload);
console.log(`Stress metrics pushed to Pushgateway: ${pushEndpoint.toString()}`);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Minor: logging the full Pushgateway URL may leak credentials embedded in the URL.

If someone sets PUSHGATEWAY_URL with basic-auth credentials (e.g. https://user:pass@gateway/), pushEndpoint.toString() will include them. Consider using pushEndpoint.origin + pushEndpoint.pathname or redacting username/password.

🤖 Prompt for AI Agents
In `@scripts/publish-stress-metrics.js` at line 274, Current console.log prints
pushEndpoint.toString() which can leak embedded credentials; update the log to
print a sanitized URL instead. Locate the console.log using the pushEndpoint
variable and replace pushEndpoint.toString() with either pushEndpoint.origin +
pushEndpoint.pathname or a redacted form that omits pushEndpoint.username and
pushEndpoint.password (e.g., build a string using pushEndpoint.protocol, host
without credentials, and pathname) so credentials are not logged. Ensure any
tests or downstream messages still receive the safe string.

@Mehdi-Bl Mehdi-Bl merged commit 71c6e5a into main Feb 11, 2026
17 checks passed
@Mehdi-Bl Mehdi-Bl deleted the chore/ci-stress-metrics-pushgateway branch February 11, 2026 00:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant