Skip to content

JS-1905 [EXPERIMENT] Setup SIT-FPS diff validation jobs#7307

Draft
nathsou wants to merge 55 commits into
masterfrom
sit-exporter-experiment
Draft

JS-1905 [EXPERIMENT] Setup SIT-FPS diff validation jobs#7307
nathsou wants to merge 55 commits into
masterfrom
sit-exporter-experiment

Conversation

@nathsou

@nathsou nathsou commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Part of JS-1905


Summary by Gitar

  • SIT Validation Tooling:
    • Added peachee-projects.js to manage project manifest building and shard matrix generation for large-scale analysis.
    • Introduced diffsit reporting logic in output.rs and reader.rs to handle multi-project diffs and summary serialization.
  • CI/CD & Testing:
    • Integrated new workflow logic in sit-pr.yml for automated rule-impacted validation and shard management.
    • Added comprehensive test suites in peachee-workflow.test.ts, publish-workflow.test.ts, and fps-workflow.test.ts to verify SIT/FPS reporting and manifest integrity.
  • Infrastructure & Configuration:
    • Created peachee-public/projects.json as a registry for public projects and included uv.lock for environment reproducibility.
    • Added docs/superpowers/plans/2026-06-17-sonarjs-sit-diff-validation.md outlining the implementation roadmap for the S5759 canary-based diff validation.

This will update automatically on new commits.

@sonarqubecloud

sonarqubecloud Bot commented Jun 17, 2026

Copy link
Copy Markdown

Agentic Analysis: Early Results

Agentic Analysis and Context Augmentation are available on your project. Here are some issues that could have been prevented. Follow the links to learn how to put them into action.

76 issue(s) found across 19 file(s):

Rule File Line Message
java:S1192 its/sit-exporter/src/test/java/org/sonar/javascript/it/sit/SitExporterTest.java 41 Define a constant instead of duplicating this literal "src/main.js" 5 times.
java:S1192 its/sit-exporter/src/test/java/org/sonar/javascript/it/sit/SitExporterTest.java 42 Define a constant instead of duplicating this literal "javascript:S1116" 4 times.
java:S1192 its/sit-exporter/src/test/java/org/sonar/javascript/it/sit/SitExporterTest.java 57 Define a constant instead of duplicating this literal "component_path" 3 times.
java:S1192 its/sit-exporter/src/test/java/org/sonar/javascript/it/sit/SitExporterTest.java 97 Define a constant instead of duplicating this literal "sources" 3 times.
java:S1192 its/sit-exporter/src/test/java/org/sonar/javascript/it/sit/SitExporterTest.java 119 Define a constant instead of duplicating this literal "packages/ruling/projects.json" 3 times.
java:S1192 its/sit-exporter/src/test/java/org/sonar/javascript/it/sit/SitExporterTest.java 127 Define a constant instead of duplicating this literal "folder" 3 times.
java:S1192 its/sit-exporter/src/test/java/org/sonar/javascript/it/sit/SitExporterTest.java 250 Define a constant instead of duplicating this literal "custom-jsts" 3 times.
css:S7924 tools/diffsit/src/resources/report.css 173 Text does not meet the minimal contrast requirement with its background.
css:S7924 tools/diffsit/src/resources/report.css 177 Text does not meet the minimal contrast requirement with its background.
css:S7924 tools/diffsit/src/resources/report.css 283 Text does not meet the minimal contrast requirement with its background.
javascript:S3504 tools/diffsit/src/resources/report.js 18 Unexpected var, use let or const instead.
javascript:S3504 tools/diffsit/src/resources/report.js 19 Unexpected var, use let or const instead.
javascript:S3504 tools/diffsit/src/resources/report.js 20 Unexpected var, use let or const instead.
javascript:S3504 tools/diffsit/src/resources/report.js 24 Unexpected var, use let or const instead.
javascript:S3504 tools/diffsit/src/resources/report.js 32 Unexpected var, use let or const instead.
javascript:S3504 tools/diffsit/src/resources/report.js 40 Unexpected var, use let or const instead.
javascript:S3504 tools/diffsit/src/resources/report.js 41 Unexpected var, use let or const instead.
javascript:S3504 tools/diffsit/src/resources/report.js 42 Unexpected var, use let or const instead.
javascript:S3504 tools/diffsit/src/resources/report.js 43 Unexpected var, use let or const instead.
javascript:S3504 tools/diffsit/src/resources/report.js 46 Unexpected var, use let or const instead.
javascript:S3504 tools/diffsit/src/resources/report.js 47 Unexpected var, use let or const instead.
javascript:S3504 tools/diffsit/src/resources/report.js 58 Unexpected var, use let or const instead.
javascript:S3504 tools/diffsit/src/resources/report.js 63 Unexpected var, use let or const instead.
javascript:S7780 tools/sit/build-publish-payload.js 47 String.raw should be used to avoid escaping \.
javascript:S7778 tools/sit/build-publish-payload.js 111 Do not call Array#push() multiple times.
javascript:S7778 tools/sit/build-publish-payload.js 112 Do not call Array#push() multiple times.
javascript:S109 tools/sit/build-publish-payload.js 221 No magic number: 5.
javascript:S3776 tools/sit/build-publish-payload.js 232 Refactor this function to reduce its Cognitive Complexity from 18 to the 15 allowed.
javascript:S4624 tools/sit/build-publish-payload.js 252 Refactor this code to not use nested template literals.
javascript:S7786 tools/sit/build-publish-payload.js 353 new Error() is too unspecific for a type check. Use new TypeError() instead.
javascript:S2871 tools/sit/build-publish-payload.js 363 Provide a compare function to avoid sorting elements alphabetically.
javascript:S2871 tools/sit/build-publish-payload.js 367 Provide a compare function to avoid sorting elements alphabetically.
javascript:S109 tools/sit/build-publish-payload.js 401 No magic number: 3_600.
javascript:S109 tools/sit/build-publish-payload.js 402 No magic number: 3_600.
javascript:S109 tools/sit/build-publish-payload.js 429 No magic number: 3.
javascript:S7785 tools/sit/build-publish-payload.js 472 Prefer top-level await over using a promise chain.
typescript:S134 tools/sit/detect-changed-rules.ts 62 Refactor this code to not nest more than 3 if/for/while/switch/try statements.
typescript:S4036 tools/sit/detect-changed-rules.ts 119 Make sure the "PATH" variable only contains fixed, unwriteable directories.
typescript:S7785 tools/sit/detect-changed-rules.ts 149 Prefer top-level await over using a promise chain.
typescript:S7748 tools/sit/fps-workflow.test.ts 121 Don't use a zero fraction in the number.
typescript:S109 tools/sit/fps-workflow.test.ts 162 No magic number: 12.
javascript:S7785 tools/sit/generate-peachee-manifest.js 51 Prefer top-level await over using a promise chain.
javascript:S7785 tools/sit/generate-peachee-public-snapshot.js 60 Prefer top-level await over using a promise chain.
javascript:S7785 tools/sit/normalize-sit-bundles.js 110 Prefer top-level await over using a promise chain.
javascript:S2871 tools/sit/peachee-projects.js 56 Provide a compare function to avoid sorting elements alphabetically.
javascript:S2871 tools/sit/peachee-projects.js 70 Provide a compare function to avoid sorting elements alphabetically.
javascript:S7781 tools/sit/peachee-projects.js 266 Prefer String#replaceAll() over String#replace().
javascript:S7758 tools/sit/peachee-projects.js 266 Prefer String.fromCodePoint() over String.fromCharCode().
javascript:S7781 tools/sit/peachee-projects.js 267 Prefer String#replaceAll() over String#replace().
javascript:S7781 tools/sit/peachee-projects.js 268 Prefer String#replaceAll() over String#replace().

Showing 50 of 76 issues.

Analyzed by SonarQube Agentic Analysis in 9.2 s

@hashicorp-vault-sonar-prod hashicorp-vault-sonar-prod Bot changed the title Setup SIT diff validation jobs JS-1905 Setup SIT diff validation jobs Jun 17, 2026
@hashicorp-vault-sonar-prod

hashicorp-vault-sonar-prod Bot commented Jun 17, 2026

Copy link
Copy Markdown

JS-1905

Comment thread tools/sit/detect-changed-rules.ts Outdated
Comment thread .github/workflows/sit-pr.yml Outdated
Comment thread .github/workflows/sit-pr.yml Outdated
Comment thread .github/workflows/sit-export.yml
@github-actions

github-actions Bot commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Ruling Report

Code no longer flagged (519 issues)

S5852

TypeScript/Gulpfile.ts:412

   410 |         .pipe(insert.transform((contents, file) => {
   411 |             file.path = standaloneDefinitionsFile;
>  412 |             return contents.replace(/^(\s*)(export )?const enum (\S+) {(\s*)$/gm, "$1$2enum $3 {$4");
   413 |         }));
   414 |     return merge2([

TypeScript/scripts/authors.ts:22

    20 | 
    21 | function getKnownAuthors(): Author[] {
>   22 |     const segmentRegExp = /\s?([^<]+)\s+<([^>]+)>/g;
    23 |     const preferedNameRegeExp = /\s?#\s?([^#]+)$/;
    24 |     const knownAuthors: Author[] = [];

TypeScript/scripts/authors.ts:115

   113 |         const cmd = "git shortlog -se " + specs.join(" ");
   114 |         console.log(cmd);
>  115 |         const outputRegExp = /\d+\s+([^<]+)<([^>]+)>/;
   116 |         const tty = process.platform === 'win32' ? 'CON' : '/dev/tty';
   117 |         const authors: { name: string, email: string, knownAuthor?: Author }[] = [];

TypeScript/src/compiler/commandLineParser.ts:1399

  1397 | 
  1398 |     function trimString(s: string) {
> 1399 |         return typeof s.trim === "function" ? s.trim() : s.replace(/^[\s]+|[\s]+$/g, "");
  1400 |     }
  1401 | 

TypeScript/src/compiler/commandLineParser.ts:1458

  1456 |      *  \/          # matches a directory separator.
  1457 |      */
> 1458 |     const watchRecursivePattern = /\/[^/]*?[*?][^/]*\//;
  1459 | 
  1460 |     /**

TypeScript/src/compiler/program.ts:298

   296 |                     const lineEnd = i < lastLineInFile ? getPositionOfLineAndCharacter(file, i + 1, 0) : file.text.length;
   297 |                     let lineContent = file.text.slice(lineStart, lineEnd);
>  298 |                     lineContent = lineContent.replace(/\s+$/g, "");  // trim from end
   299 |                     lineContent = lineContent.replace("\t", " ");    // convert tabs to single spaces
   300 | 

TypeScript/src/compiler/utilities.ts:640

   638 |     }
   639 | 
>  640 |     export let fullTripleSlashReferencePathRegEx = /^(\/\/\/\s*<reference\s+path\s*=\s*)('|")(.+?)\2.*?\/>/;
   641 |     export let fullTripleSlashReferenceTypeReferenceDirectiveRegEx = /^(\/\/\/\s*<reference\s+types\s*=\s*)('|")(.+?)\2.*?\/>/;
   642 |     export let fullTripleSlashAMDReferencePathRegEx = /^(\/\/\/\s*<amd-dependency\s+path\s*=\s*)('|")(.+?)\2.*?\/>/;

TypeScript/src/compiler/utilities.ts:641

   639 | 
   640 |     export let fullTripleSlashReferencePathRegEx = /^(\/\/\/\s*<reference\s+path\s*=\s*)('|")(.+?)\2.*?\/>/;
>  641 |     export let fullTripleSlashReferenceTypeReferenceDirectiveRegEx = /^(\/\/\/\s*<reference\s+types\s*=\s*)('|")(.+?)\2.*?\/>/;
   642 |     export let fullTripleSlashAMDReferencePathRegEx = /^(\/\/\/\s*<amd-dependency\s+path\s*=\s*)('|")(.+?)\2.*?\/>/;
   643 | 

TypeScript/src/compiler/utilities.ts:642

   640 |     export let fullTripleSlashReferencePathRegEx = /^(\/\/\/\s*<reference\s+path\s*=\s*)('|")(.+?)\2.*?\/>/;
   641 |     export let fullTripleSlashReferenceTypeReferenceDirectiveRegEx = /^(\/\/\/\s*<reference\s+types\s*=\s*)('|")(.+?)\2.*?\/>/;
>  642 |     export let fullTripleSlashAMDReferencePathRegEx = /^(\/\/\/\s*<amd-dependency\s+path\s*=\s*)('|")(.+?)\2.*?\/>/;
   643 | 
   644 |     export function isPartOfTypeNode(node: Node): boolean {

TypeScript/src/compiler/utilities.ts:3002

  3000 |     function writeTrimmedCurrentLine(text: string, commentEnd: number, writer: EmitTextWriter, newLine: string, pos: number, nextLineStart: number) {
  3001 |         const end = Math.min(commentEnd, nextLineStart - 1);
> 3002 |         const currentLineText = text.substring(pos, end).replace(/^\s+|\s+$/g, "");
  3003 |         if (currentLineText) {
  3004 |             // trimmed forward and ending spaces text

...and 509 more

New issues flagged (1266 issues)

S8786

Ghost/core/client/app/utils/date-formatting.js:20

    18 | // Add missing timestamps
    19 | function verifyTimeStamp(dateString) {
>   20 |     if (dateString && !dateString.slice(-5).match(/\d+:\d\d/)) {
    21 |         dateString += ' 12:00';
    22 |     }

Ghost/core/client/app/utils/word-count.js:7

     5 |     let nonANumLetters = new XRegExp("[^\\s\\d\\p{L}]", 'g'); // all non-alphanumeric letters regexp
     6 | 
>    7 |     s = s.replace(/<(.|\n)*?>/g, ' '); // strip tags
     8 |     s = s.replace(nonANumLetters, ''); // ignore non-alphanumeric letters
     9 |     s = s.replace(/(^\s*)|(\s*$)/gi, ''); // exclude starting and ending white-space

Ghost/core/client/app/utils/word-count.js:9

     7 |     s = s.replace(/<(.|\n)*?>/g, ' '); // strip tags
     8 |     s = s.replace(nonANumLetters, ''); // ignore non-alphanumeric letters
>    9 |     s = s.replace(/(^\s*)|(\s*$)/gi, ''); // exclude starting and ending white-space
    10 |     s = s.replace(/\n /gi, ' '); // convert newlines to spaces
    11 |     s = s.replace(/\n+/gi, ' ');

Ghost/core/server/api/mail.js:20

    18 |     mail;
    19 | 
>   20 | _.templateSettings.interpolate = /{{([\s\S]+?)}}/g;
    21 | 
    22 | /**

Ghost/core/server/data/meta/excerpt.js:8

     6 |     excerpt = excerpt.replace(/<div class="footnotes"><ol>.*?<\/ol><\/div>/, '');
     7 |     // Strip other html
>    8 |     excerpt = excerpt.replace(/<\/?[^>]+>/gi, '');
     9 |     excerpt = excerpt.replace(/(\r\n|\n|\r)+/gm, ' ');
    10 |     /*jslint regexp:false */

Ghost/core/server/data/meta/next_url.js:2

     1 | var config = require('../../config'),
>    2 |     trimmedUrlpattern = /.+(?=\/page\/\d*\/)/,
     3 |     tagOrAuthorPattern = /\/(tag)|(author)\//;
     4 | 

Ghost/core/server/data/meta/previous_url.js:2

     1 | var config = require('../../config'),
>    2 |     trimmedUrlpattern = /.+(?=\/page\/\d*\/)/;
     3 | 
     4 | function getPreviousUrl(data, absolute) {

Ghost/core/server/errors/index.js:257

   255 | 
   256 |             // TODO: split out line numbers
>  257 |             var stackRegex = /\s*at\s*(\w+)?\s*\(([^\)]+)\)\s*/i;
   258 | 
   259 |             return (

Ghost/core/server/helpers/get.js:51

    49 |  */
    50 | function resolvePaths(data, value) {
>   51 |     var regex = /\{\{(.*?)\}\}/g;
    52 | 
    53 |     value = value.replace(regex, function (match, path) {

Ghost/core/server/helpers/navigation.js:45

    43 |     // strips trailing slashes and compares urls
    44 |     function _isCurrentUrl(href, currentUrl) {
>   45 |         var strippedHref = href.replace(/\/+$/, ''),
    46 |             strippedCurrentUrl = currentUrl.replace(/\/+$/, '');
    47 |         return strippedHref === strippedCurrentUrl;

...and 1256 more

📋 View full report

Code no longer flagged (519)

S5852

S3504

Comment thread packages/analysis/src/jsts/rules/S2123/unit.test.ts Outdated
@nathsou nathsou self-assigned this Jun 17, 2026
@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@sonarqube-next

Copy link
Copy Markdown

@github-actions

This comment has been minimized.

@nathsou nathsou changed the title JS-1905 Setup SIT diff validation jobs JS-1905 Setup SIT-FPS diff validation jobs Jun 19, 2026
@nathsou nathsou changed the title JS-1905 Setup SIT-FPS diff validation jobs JS-1905 [EXPERIMENT] Setup SIT-FPS diff validation jobs Jun 23, 2026
@nathsou

nathsou commented Jun 23, 2026

Copy link
Copy Markdown
Contributor Author

Note: As noted by Michal, be should explore using the JS integration tests (à la ruling) and then generate the expected diff format for FPS to speed up the analysis even more.
Also look at https://github.com/SonarSource/SonarJS/tree/master/its/plugin/fast-tests

nathsou and others added 16 commits June 30, 2026 10:19
- Drop S2925 port (rule sources, fixtures, rule-data, java check)
- Drop regenerated-and-gitignored files erroneously committed
  (AllChecks.java, metas.ts, rules.ts, plugin-rules.ts, rspec.sha)
- Drop SIT validation design/plan docs
- Drop sharding scripts and vendored peachee-public snapshot
- Drop helper tests and SitExporterTest (out of scope for setup PR)
- Drop orphan tools/diffsit/build.gradle.kts (SonarJS is Maven-only)
- Restore stale eslint ruling entry (unrelated to SIT)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- git mv tools/diffsit dev-tools/diffsit to align with sonar-skunk
- Copy sit_pr_fps and sit_pr_diffsit Python helpers verbatim from
  sonar-skunk, then apply minimal parametrization so they work for
  SonarJS without forking divergence:
  - normalize_sit_bundles.py: add optional --language flag for flat
    input layouts
  - build_publish_payload.py: rename --dre-plugin-version to
    --plugin-version, add --rule-key-format and
    --sit-artifact-name-template (defaults match sonar-skunk)
  - prepare_diffsit_inputs.py: add --rule-key-format and --flat-bundles
    (defaults match sonar-skunk)
- detect_changed_rules.py is JS-aware (reads compatibleLanguages from
  rule metadata to fan out to javascript/typescript/css repositories)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Replace tools/sit/*.js Node helpers with the Python ports under
  dev-tools/scripts/{sit_pr_fps,sit_pr_diffsit}/.
- Rewrite .github/workflows/sit-pr.yml as sit-pr-fps.yml mirroring
  sonar-skunk's pipeline: prepare_rules -> target_sit_export &
  baseline_sit_export -> diffsit -> fps -> publish. No sharding, no
  language matrix.
- Slim .github/workflows/sit-export.yml to a single job that builds
  the SIT exporter and runs it against the curated 20-project
  manifest at dev-tools/peach-analysis/javascript/projects.json. The
  in-workflow peachee clone loop is gone; the Java SitExporter clones
  projects from projects.json itself.
- Update build.yml: rename caller job sit_pr -> sit-pr-fps, invoke
  dev-tools/scripts/sit_pr_fps/detect_changed_rules.py to emit the
  rules_json step output, and stop base64-encoding the payload.
- Drop tools/sit/ entirely.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The SitExporter is a CLI tool with no unit tests, so its
src/test directory does not exist. Override the Sonar scanner
default to avoid 'directory does not exist' failures.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Adds token/secret/apikey/auth/credential to the default password
keyword list. This will fire on many code-base sites that previously
were ignored, exercising both the SIT (new hits) and FPS (likely
false positives in test fixtures and error messages) sides of the
pipeline.

Revert before merging.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Three independent CI failures:

1) SitExporter expected a JSON array of {name,testDir,exclusions}
   pointing to the its/sources/projects git submodule, not the
   peachee-public dict-with-clone-URLs format. Rewrite projects.json
   to match SonarJS' ruling schema and keep 20 curated projects.

2) baseline_sit_export was checking out base_sha to build everything
   including the SIT exporter itself, but base_sha (pre-PR) does not
   have its/sit-exporter/. Restructure sit-export.yml so the exporter
   is always built from the workflow ref; for baseline runs, check
   out the baseline ref into baseline-src/ and build just the plugin
   there. The exporter then runs against that baseline plugin jar.

3) Publish job's 'python = "3.13"' resolved to 3.13.14, which mise
   tried to install as a freethreaded build and failed. Pin to
   '3.13.7' across all sit-pr-fps jobs.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The Python detect_changed_rules.py helper emits 'javascript'/'typescript'
(matching SQ repository keys), but SitExporter only accepted the short
'js'/'ts' filename-key form. Accept both.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace 'eslint' (absent from jsts-test-sources submodule) with 'ace'
in the curated projects.json. Also make SitExporter skip projects with
missing source directories instead of failing the whole export, which
matches the resilient behaviour the sonar-skunk pipeline expects.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
dev-tools/scripts depends on the private SonarSource/mega-false-positive-slayer-3000
repo via uv git source. The diffsit job needs to log in with the rspec
GitHub token and run 'gh auth setup-git' before running uv, so that
the underlying 'git clone' can authenticate.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The Vault role for SonarSource/SonarJS is not granted access to the
RAD squad's anthropic-rad-squad path. Use the JS squad's own Portkey
secret (development/team/analysis-js-squad/kv/data/sonarjs-portkey)
that was already used in the previous Node-based pipeline. The
mega-fp-slayer LM provider prefers PORTKEY_ANTHROPIC_API_KEY when set.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Mark the squad-owned Vault path as the single per-analyser line that
must be adjusted when copying this workflow to another analyser repo.
Long-term: align all analysers on a single shared Vault path (one-time
policy grant per repo) so the workflow can be truly identical.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The vault-action wrapper parses each non-empty line of the 'secrets:'
block as 'path key | env_var' and rejects lines that start with '#'.
Move the per-analyser guidance comment to a YAML comment above the
step instead.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
'gh auth login' refuses to run when GH_TOKEN is set in the environment
(it would use that value instead, and the subsequent gh auth setup-git
then no-ops). Rename the env var holding the rspec token so 'gh auth
login --with-token' actually reads from stdin and stores credentials.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The -rspec token is scoped only to the rspec repo, so 'uv sync' could
not clone the private SonarSource/mega-false-positive-slayer-3000 git
dependency. Switch the 'gh auth' steps in both DiffSIT and FPS to the
broader -submodules token, which already has access to private
SonarSource repos (and is what sonar-skunk uses). FPS still receives
the -rspec token via GITHUB_TOKEN/GH_TOKEN for rule-spec fetching.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@sonarqube-next

sonarqube-next Bot commented Jul 1, 2026

Copy link
Copy Markdown

Quality Gate failed Quality Gate failed

Failed conditions
2 New issues

See analysis details on SonarQube

Catch issues before they fail your Quality Gate with our IDE extension SonarQube for IDE SonarQube for IDE

@github-actions

github-actions Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

SIT/FPS PR Automation

FPS per rule

javascript:S2068success — 0 issues — FP rate 0.0%

No cluster data (FPS failed or report missing).

typescript:S2068success — 0 issues — FP rate 0.0%

No cluster data (FPS failed or report missing).

@gitar-bot

gitar-bot Bot commented Jul 1, 2026

Copy link
Copy Markdown
CI failed: The SIT-FPS validation job failed due to a Rust version mismatch where the `zip` dependency requires a newer rustc version (1.88+) than is currently configured in the CI environment (1.85.0).

Overview

One build failure was identified in the SIT / DiffSIT job. The failure is caused by an incompatible dependency version requiring a newer Rust toolchain than the one currently provided by the runner environment.

Failures

Rustc Version Mismatch (confidence: high)

  • Type: build
  • Affected jobs: 84480847486
  • Related to change: yes
  • Root cause: The zip crate dependency expects rustc 1.88+, but the CI environment is provisioned with rustc 1.85.0 via mise.
  • Suggested fix: Either upgrade the Rust toolchain version in your mise.toml (or equivalent workflow configuration) to 1.88 or downgrade the zip crate version in Cargo.toml to a release compatible with rustc 1.85.0.

Summary

  • Change-related failures: 1 (Build failure in the new DiffSIT pipeline due to dependency constraints).
  • Infrastructure/flaky failures: 0
  • Recommended action: Update the project's toolchain definition to meet the minimum requirements of the new dependencies added in this PR.
Code Review ✅ Approved 10 resolved / 10 findings

Implements automated SIT/FPS diff validation jobs with project manifest management and shard-based analysis. All previously reported security and functional issues, including secret handling and hang-prone loops, have been resolved.

✅ 10 resolved
Security: Secret token interpolated directly into shell echo

📄 .github/workflows/sit-pr.yml:263-266
The Register the personal access token step pipes the vault secret directly into the run script via echo "${{ fromJSON(steps.secrets.outputs.vault).github_submodules_token }}" | gh auth login --with-token. Interpolating a secret straight into the rendered shell command (rather than passing it through an environment variable) is discouraged: it embeds the secret value into the generated step script and is more fragile than the env-var approach used everywhere else in this file. GitHub log masking still applies, so impact is low, but the safer idiom is to expose the token via env: and read it from the variable.

Edge Case: Non-numeric DiffSIT summary fields can render as NaN

📄 tools/sit/summarize-diffsit-reports.js:79 📄 tools/sit/summarize-diffsit-reports.js:94-100 📄 tools/sit/build-publish-payload.js:38-43
extractSummary builds each count via Number(rawSummary[key] ?? 0). If a report field is present but non-numeric (e.g. a string that isn't a valid number), Number() yields NaN, which then flows into aggregateResults and ultimately into formatCount, where String(NaN) renders the literal NaN in the PR comment table. Since the report is produced by an external tool, a malformed/unexpected field would surface as an opaque NaN rather than a clear error or 0. Consider coercing with a fallback (e.g. Number.isFinite(n) ? n : 0) so the summary stays well-formed.

Quality: Empty SIT timing placeholder still renders an all-n/a table

📄 tools/sit/build-publish-payload.js:118-120 📄 tools/sit/build-publish-payload.js:186-200
When the diffsit job does not succeed, the publish job uses the placeholder {"baseline":{},"target":{}} for the timing summary. In buildCommentLines the guard if (context.sitSummary?.target || context.sitSummary?.baseline) is satisfied because empty objects are truthy, so renderSitTimingSummary runs and emits a 'SIT timing' table whose rows show n/a for every column (project_count/total/average via formatCount/formatDuration of undefined). This produces a cosmetically noisy section rather than omitting it. Consider gating on a populated field, e.g. details?.project_count != null per row, or checking summary.target?.project_count || summary.baseline?.project_count before rendering the section.

Edge Case: Artifact pagination loop can hang instead of failing over

📄 .github/workflows/sit-pr.yml:563-577
The new artifact-metadata pagination loop runs inside an if ( ... ); then condition. Because the subshell is the condition of an if, set -e is suppressed for everything inside it, so individual command failures no longer abort the subshell. If gh api fails on a page (rate limit, transient network/auth error), the redirect truncates $tmp_json to empty, jq -s then fails, and count becomes an empty string. The test [ "$count" -lt 100 ] then errors (integer expression expected) instead of evaluating true, so the loop neither breaks nor reaches the graceful else fallback — it just increments page and retries gh api forever (until the job-level timeout kills it). The previous single-command version (if gh api ... > file; then ... else ...) failed fast into the empty-list fallback. Recommend capturing the gh api exit code explicitly and breaking out (or exiting the subshell non-zero) on failure, and validating that count is numeric before comparing.

Edge Case: Explicitly requested project is silently dropped if also excluded

📄 tools/sit/peachee-projects.js:50-64
In selectEnabledPeacheeProjects, when a project appears in both rawProjectFilter and rawExcludedProjects, it passes the missingProjects validation (it's enabled) but is then silently removed by requestedProjects.has(project) && !excludedProjects.has(project) (tools/sit/peachee-projects.js:76-78). A user who explicitly requests a project via --project-filter gets no error or warning that their request was overridden by the exclusion list, which can be confusing to debug.

In the current workflow the exclude list is a hardcoded operator-controlled constant and project_filter comes from inputs, so the impact is low. Consider either documenting this precedence or emitting a warning (or error) when an explicitly requested project is also excluded, so the override is observable.

...and 5 more resolved from earlier reviews

Tip

Comment Gitar fix CI or enable auto-apply: gitar auto-apply:on

Options

Auto-apply is off → Gitar will not commit updates to this branch.
Display: compact → Showing less information.

Comment with these commands to change:

Auto-apply Compact
gitar auto-apply:on         
gitar display:verbose         

Was this helpful? React with 👍 / 👎 | Gitar

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