sourcecode 1.35.24__py3-none-any.whl → 1.35.26__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
sourcecode/__init__.py CHANGED
@@ -1,3 +1,3 @@
1
1
  """sourcecode — Deterministic codebase context maps for AI coding agents."""
2
2
 
3
- __version__ = "1.35.24"
3
+ __version__ = "1.35.26"
@@ -1,14 +1,19 @@
1
1
  """migrate_check.py — Java 8/Spring Boot 2 migration readiness checker.
2
2
 
3
- Scans Java source files for patterns that must be addressed when migrating:
3
+ Scans Java source files, Spring XML config, and build descriptors for patterns
4
+ that must be addressed when migrating:
4
5
  - Spring Boot 2 → 3 (javax → jakarta, Spring Security 6)
5
6
  - Java 8 → 17 / 21 (SecurityManager, Nashorn, Unsafe, reflection, etc.)
7
+ - XML Spring config (applicationContext.xml, web.xml, security XML)
8
+ - Dependency incompatibilities (SpringFox, Hibernate 5, ByteBuddy old)
6
9
 
7
10
  Entry point: run_migrate_check(file_paths, root) → MigrationReport
8
11
  """
9
12
  from __future__ import annotations
10
13
 
14
+ import fnmatch
11
15
  import hashlib
16
+ import os
12
17
  import re
13
18
  from dataclasses import dataclass, field
14
19
  from datetime import datetime, timezone
@@ -17,7 +22,7 @@ from typing import Optional
17
22
 
18
23
 
19
24
  # ---------------------------------------------------------------------------
20
- # Rule catalogue
25
+ # Rule catalogue — Java source rules
21
26
  # ---------------------------------------------------------------------------
22
27
 
23
28
  @dataclass(frozen=True)
@@ -27,11 +32,11 @@ class _Rule:
27
32
  title: str
28
33
  explanation: str
29
34
  fix_hint: str
30
- migration_target: str = "spring_boot_3" # jakarta | spring_security_6 | java_11 | java_15 | java_17 | java_9_plus | java_18_plus
31
- openrewrite_recipe: Optional[str] = None # Official OpenRewrite recipe ID, if one exists
32
- import_pattern: Optional[re.Pattern] = None # matches the import statement
33
- extends_pattern: Optional[re.Pattern] = None # matches an extends clause
34
- code_pattern: Optional[re.Pattern] = None # matches arbitrary code anywhere in the file
35
+ migration_target: str = "spring_boot_3"
36
+ openrewrite_recipe: Optional[str] = None
37
+ import_pattern: Optional[re.Pattern] = None
38
+ extends_pattern: Optional[re.Pattern] = None
39
+ code_pattern: Optional[re.Pattern] = None
35
40
 
36
41
 
37
42
  # ---------------------------------------------------------------------------
@@ -215,7 +220,7 @@ _SPRING_SECURITY_RULES: list[_Rule] = [
215
220
  ]
216
221
 
217
222
  # ---------------------------------------------------------------------------
218
- # Java 11 — APIs removed from the JDK (must add as explicit deps)
223
+ # Java 11 — APIs removed from the JDK
219
224
  # ---------------------------------------------------------------------------
220
225
 
221
226
  _JAVA_11_RULES: list[_Rule] = [
@@ -252,6 +257,28 @@ _JAVA_11_RULES: list[_Rule] = [
252
257
  openrewrite_recipe=None,
253
258
  import_pattern=re.compile(r"^[ \t]*import\s+(javax\.xml\.ws[^;]+);", re.MULTILINE),
254
259
  ),
260
+ _Rule(
261
+ id="MIG-023",
262
+ severity="critical",
263
+ title="CORBA APIs (org.omg.* / javax.rmi.*) — removed from JDK in Java 11",
264
+ explanation=(
265
+ "The CORBA APIs (org.omg.* and javax.rmi.CORBA / javax.rmi.ssl) were deprecated "
266
+ "in Java 9 (JEP 289) and removed from the JDK in Java 11 (JEP 320). Applications "
267
+ "importing these packages will fail to compile or run on Java 11+ unless the "
268
+ "'org.glassfish.corba:glassfish-corba-omgapi' artifact is added explicitly."
269
+ ),
270
+ fix_hint=(
271
+ "Remove CORBA usage where possible — CORBA is effectively dead technology. "
272
+ "If CORBA interop is unavoidable, add 'org.glassfish.corba:glassfish-corba-omgapi' "
273
+ "as an explicit Maven/Gradle dependency."
274
+ ),
275
+ migration_target="java_11",
276
+ openrewrite_recipe=None,
277
+ import_pattern=re.compile(
278
+ r"^[ \t]*import\s+(org\.omg\.[^;]+|javax\.rmi\.CORBA\.[^;]+|javax\.rmi\.ssl\.[^;]+);",
279
+ re.MULTILINE,
280
+ ),
281
+ ),
255
282
  ]
256
283
 
257
284
  # ---------------------------------------------------------------------------
@@ -280,7 +307,7 @@ _JAVA_15_RULES: list[_Rule] = [
280
307
  ]
281
308
 
282
309
  # ---------------------------------------------------------------------------
283
- # Java 17 — SecurityManager removed (JEP 411)
310
+ # Java 17 — SecurityManager removed (JEP 411), Thread deprecated methods
284
311
  # ---------------------------------------------------------------------------
285
312
 
286
313
  _JAVA_17_RULES: list[_Rule] = [
@@ -304,12 +331,37 @@ _JAVA_17_RULES: list[_Rule] = [
304
331
  openrewrite_recipe=None,
305
332
  code_pattern=re.compile(
306
333
  r"System\.(get|set)SecurityManager\s*\(|"
307
- r"\bSecurityManager\s+\w+\s*[=;({]|" # variable declaration, requires code-context char to avoid Javadoc FPs
334
+ r"\bSecurityManager\s+\w+\s*[=;({]|"
308
335
  r"\bnew\s+SecurityManager\s*\(|"
309
336
  r"\bextends\s+SecurityManager\b|"
310
337
  r"\bAccessController\.(doPrivileged|checkPermission|getContext)\s*\(",
311
338
  ),
312
339
  ),
340
+ _Rule(
341
+ id="MIG-024",
342
+ severity="medium",
343
+ title="Thread.stop / Thread.suspend / Thread.resume — deprecated for removal (Java 17+)",
344
+ explanation=(
345
+ "Thread.stop(), Thread.suspend(), and Thread.resume() are deprecated since Java 1.2 "
346
+ "and deprecated-for-removal since Java 17 (JEP 411 scope). Thread.stop() is "
347
+ "inherently unsafe — it throws ThreadDeath which can corrupt object state. "
348
+ "Thread.suspend/resume cause deadlocks when the suspended thread holds a monitor. "
349
+ "Note: detection is best-effort; confirm the variable type is java.lang.Thread."
350
+ ),
351
+ fix_hint=(
352
+ "Use Thread.interrupt() with InterruptedException for cooperative cancellation. "
353
+ "Replace suspend/resume patterns with wait()/notify(), Semaphore, or a higher-level "
354
+ "concurrency abstraction (BlockingQueue, CountDownLatch, etc.)."
355
+ ),
356
+ migration_target="java_17",
357
+ openrewrite_recipe=None,
358
+ code_pattern=re.compile(
359
+ r"\b(?:thread|[a-zA-Z]\w*[Tt]hread)\.(stop|suspend|resume)\s*\("
360
+ r"|\bnew\s+Thread\s*\([^)]{0,120}\)\s*\.(stop|suspend|resume)\s*\("
361
+ r"|\bThread\.currentThread\s*\(\)\s*\.(stop|suspend|resume)\s*\(",
362
+ re.MULTILINE,
363
+ ),
364
+ ),
313
365
  ]
314
366
 
315
367
  # ---------------------------------------------------------------------------
@@ -382,6 +434,32 @@ _JAVA_9_RULES: list[_Rule] = [
382
434
  openrewrite_recipe=None,
383
435
  code_pattern=re.compile(r"\.setAccessible\s*\(\s*true\s*\)"),
384
436
  ),
437
+ _Rule(
438
+ id="MIG-025",
439
+ severity="medium",
440
+ title="ReflectionFactory / MethodHandles.privateLookupIn — deep-reflection JPMS risk",
441
+ explanation=(
442
+ "sun.reflect.ReflectionFactory bypasses module encapsulation and is not part of "
443
+ "the public API. MethodHandles.privateLookupIn() grants private lookup access that "
444
+ "requires --add-opens on Java 9+. Both patterns are common in serialization "
445
+ "frameworks and mocking libraries and may break under strict JPMS modules."
446
+ ),
447
+ fix_hint=(
448
+ "Replace sun.reflect.ReflectionFactory with MethodHandles.lookup() or VarHandle. "
449
+ "For MethodHandles.privateLookupIn, ensure the calling module has been opened "
450
+ "via 'opens <package> to <module>' in module-info.java."
451
+ ),
452
+ migration_target="java_9_plus",
453
+ openrewrite_recipe=None,
454
+ import_pattern=re.compile(
455
+ r"^[ \t]*import\s+(sun\.reflect\.ReflectionFactory[^;]*);",
456
+ re.MULTILINE,
457
+ ),
458
+ code_pattern=re.compile(
459
+ r"\bReflectionFactory\s*\.\s*getReflectionFactory\s*\("
460
+ r"|\bMethodHandles\s*\.\s*privateLookupIn\s*\(",
461
+ ),
462
+ ),
385
463
  ]
386
464
 
387
465
  # ---------------------------------------------------------------------------
@@ -404,7 +482,7 @@ _JAVA_18_RULES: list[_Rule] = [
404
482
  "java.lang.ref.Cleaner for resource cleanup."
405
483
  ),
406
484
  migration_target="java_18_plus",
407
- openrewrite_recipe=None,
485
+ openrewrite_recipe="org.openrewrite.java.migrate.RemoveFinalizeMethod",
408
486
  code_pattern=re.compile(
409
487
  r"\b(?:protected|public)\s+void\s+finalize\s*\(\s*\)",
410
488
  ),
@@ -442,7 +520,7 @@ _LEGACY_API_RULES: list[_Rule] = [
442
520
  ]
443
521
 
444
522
  # ---------------------------------------------------------------------------
445
- # All rules list
523
+ # All Java source rules
446
524
  # ---------------------------------------------------------------------------
447
525
 
448
526
  _ALL_RULES: list[_Rule] = (
@@ -459,19 +537,407 @@ _ALL_RULES: list[_Rule] = (
459
537
  SEVERITY_ORDER: dict[str, int] = {"critical": 0, "high": 1, "medium": 2, "low": 3}
460
538
 
461
539
 
540
+ # ---------------------------------------------------------------------------
541
+ # XML config rules (applied to Spring XML config files)
542
+ # ---------------------------------------------------------------------------
543
+
544
+ @dataclass(frozen=True)
545
+ class _XmlRule:
546
+ id: str
547
+ severity: str
548
+ title: str
549
+ explanation: str
550
+ fix_hint: str
551
+ migration_target: str
552
+ openrewrite_recipe: Optional[str] = None
553
+ pattern: Optional[re.Pattern] = None
554
+
555
+
556
+ _XML_RULES: list[_XmlRule] = [
557
+ _XmlRule(
558
+ id="MIG-030",
559
+ severity="high",
560
+ title="javax.* class reference in Spring XML config — namespace not migrated",
561
+ explanation=(
562
+ "Spring XML bean definitions using class='javax.*' reference the old Java EE "
563
+ "namespace. When the application migrates to Spring Boot 3 / Jakarta EE 9+, these "
564
+ "bean class names must be updated to use the jakarta.* namespace equivalents. "
565
+ "Typical occurrences: persistence providers, validators, transaction managers."
566
+ ),
567
+ fix_hint=(
568
+ "Update class='javax.*' attributes in XML bean definitions to the corresponding "
569
+ "jakarta.* class names. Run OpenRewrite or grep for 'javax.' in all XML config files."
570
+ ),
571
+ migration_target="jakarta",
572
+ openrewrite_recipe=None,
573
+ pattern=re.compile(
574
+ r'(?:class|type|value)\s*=\s*["\'][^"\']*\bjavax\.[a-zA-Z]',
575
+ re.MULTILINE,
576
+ ),
577
+ ),
578
+ _XmlRule(
579
+ id="MIG-031",
580
+ severity="high",
581
+ title="Spring Security XML — old-style <http auto-config> or versioned schema ≤5",
582
+ explanation=(
583
+ "XML-based Spring Security configuration using <http auto-config='true'> or "
584
+ "pointing to a spring-security-[3-5].x.xsd schema requires significant migration "
585
+ "for Spring Security 6 (Spring Boot 3). The auto-config shortcut and many XML "
586
+ "namespace attributes were changed or removed in Spring Security 6."
587
+ ),
588
+ fix_hint=(
589
+ "Migrate XML security config to Java-based @Configuration with SecurityFilterChain "
590
+ "@Bean. See the Spring Security 6 XML migration guide. "
591
+ "Update schema references to spring-security.xsd (no version) or use Spring Security 6 schemas."
592
+ ),
593
+ migration_target="spring_security_6",
594
+ openrewrite_recipe="org.openrewrite.java.spring.security6.WebSecurityConfigurerAdapterToSecurityFilterChain",
595
+ pattern=re.compile(
596
+ r"<(?:\w+:)?http\s[^>]*auto-config\s*=\s*[\"']true[\"']"
597
+ r"|spring-security-[2345]\.\d+\.xsd",
598
+ re.IGNORECASE | re.MULTILINE,
599
+ ),
600
+ ),
601
+ _XmlRule(
602
+ id="MIG-032",
603
+ severity="high",
604
+ title="web.xml with Servlet ≤4 namespace — javax.servlet, must migrate to jakarta",
605
+ explanation=(
606
+ "A web.xml using the Java EE namespace (java.sun.com/xml/ns/javaee or "
607
+ "xmlns.jcp.org/xml/ns/javaee) declares a Servlet 2.x/3.x/4.x deployment descriptor. "
608
+ "These namespaces map to javax.servlet. Spring Boot 3 requires Jakarta Servlet 5.0+ "
609
+ "(namespace: jakarta.ee/xml/ns/jakartaee). The deployment descriptor must be updated."
610
+ ),
611
+ fix_hint=(
612
+ "Update web.xml namespace from 'http://xmlns.jcp.org/xml/ns/javaee' to "
613
+ "'https://jakarta.ee/xml/ns/jakartaee' and set version='5.0' or '6.0'. "
614
+ "Update all filter-class and servlet-class entries from javax.* to jakarta.* equivalents."
615
+ ),
616
+ migration_target="jakarta",
617
+ openrewrite_recipe=None,
618
+ pattern=re.compile(
619
+ r'xmlns\s*=\s*["\']https?://(?:java\.sun\.com|xmlns\.jcp\.org)/xml/ns/javaee["\']',
620
+ re.IGNORECASE | re.MULTILINE,
621
+ ),
622
+ ),
623
+ ]
624
+
625
+ # XML files to scan: name-based heuristic (avoids scanning unrelated XML like Maven reports)
626
+ _XML_FILE_GLOBS: tuple[str, ...] = (
627
+ "web.xml",
628
+ "applicationContext.xml",
629
+ "applicationContext-*.xml",
630
+ "*applicationContext*.xml",
631
+ "*-context.xml",
632
+ "*Context.xml",
633
+ "*-config.xml",
634
+ "*Config.xml",
635
+ "*security*.xml",
636
+ "*Security*.xml",
637
+ "*servlet*.xml",
638
+ "*Servlet*.xml",
639
+ "beans.xml",
640
+ "*-beans.xml",
641
+ "*spring*.xml",
642
+ "*Spring*.xml",
643
+ "*dispatcher*.xml",
644
+ "*Dispatcher*.xml",
645
+ )
646
+
647
+ _SKIP_DIRS: frozenset[str] = frozenset([
648
+ "target", "build", ".git", ".gradle", ".mvn",
649
+ "node_modules", "__pycache__", ".idea", ".vscode",
650
+ "out", "dist", "bin", "generated-sources",
651
+ ])
652
+
653
+
654
+ def _is_spring_xml_candidate(fname: str) -> bool:
655
+ return any(fnmatch.fnmatch(fname, g) for g in _XML_FILE_GLOBS)
656
+
657
+
658
+ def _find_xml_config_files(root: Path) -> list[tuple[Path, str]]:
659
+ """Compatibility shim — calls the combined scanner."""
660
+ xml_files, _ = _find_non_java_files(root)
661
+ return xml_files
662
+
663
+
664
+ def _find_build_files(root: Path) -> list[tuple[Path, str]]:
665
+ """Compatibility shim — calls the combined scanner."""
666
+ _, build_files = _find_non_java_files(root)
667
+ return build_files
668
+
669
+
670
+ def _find_non_java_files(
671
+ root: Path,
672
+ ) -> tuple[list[tuple[Path, str]], list[tuple[Path, str]]]:
673
+ """Single os.walk returning (xml_config_files, build_files), excluding build dirs."""
674
+ xml_files: list[tuple[Path, str]] = []
675
+ build_files: list[tuple[Path, str]] = []
676
+ for dirpath, dirnames, filenames in os.walk(root):
677
+ dirnames[:] = [d for d in dirnames if d not in _SKIP_DIRS]
678
+ dp = Path(dirpath)
679
+ try:
680
+ rel_dir = dp.relative_to(root)
681
+ except ValueError:
682
+ continue
683
+ rel_prefix = str(rel_dir) if str(rel_dir) != "." else ""
684
+ for fname in filenames:
685
+ rel = f"{rel_prefix}/{fname}" if rel_prefix else fname
686
+ abs_path = dp / fname
687
+ if fname.endswith(".xml"):
688
+ if fname == "pom.xml":
689
+ build_files.append((abs_path, rel))
690
+ elif _is_spring_xml_candidate(fname):
691
+ xml_files.append((abs_path, rel))
692
+ elif fname in ("build.gradle", "build.gradle.kts"):
693
+ build_files.append((abs_path, rel))
694
+ return xml_files, build_files
695
+
696
+
697
+ def _scan_xml_file(text: str, rel_path: str) -> list["MigrationFinding"]:
698
+ """Apply XML rules to raw XML text. Returns one finding per matched rule."""
699
+ findings: list[MigrationFinding] = []
700
+ for rule in _XML_RULES:
701
+ if rule.pattern is None:
702
+ continue
703
+ matches = list(rule.pattern.finditer(text))
704
+ if not matches:
705
+ continue
706
+ first_line = text[: matches[0].start()].count("\n") + 1
707
+ snippets = [m.group(0)[:120].strip() for m in matches[:5]]
708
+ findings.append(
709
+ MigrationFinding(
710
+ id=MigrationFinding.make_id(rule.id, rel_path),
711
+ rule_id=rule.id,
712
+ severity=rule.severity,
713
+ title=rule.title,
714
+ source_file=rel_path,
715
+ first_line=first_line,
716
+ imports_found=snippets,
717
+ explanation=rule.explanation,
718
+ fix_hint=rule.fix_hint,
719
+ migration_target=rule.migration_target,
720
+ openrewrite_recipe=rule.openrewrite_recipe,
721
+ )
722
+ )
723
+ return findings
724
+
725
+
726
+ # ---------------------------------------------------------------------------
727
+ # Dependency rules (applied to pom.xml / build.gradle / build.gradle.kts)
728
+ # ---------------------------------------------------------------------------
729
+
730
+ @dataclass(frozen=True)
731
+ class _DepRule:
732
+ id: str
733
+ severity: str
734
+ title: str
735
+ explanation: str
736
+ fix_hint: str
737
+ migration_target: str
738
+ openrewrite_recipe: Optional[str] = None
739
+ # Patterns applied to raw build file text.
740
+ # Each is tried independently; first match wins.
741
+ maven_pattern: Optional[re.Pattern] = None
742
+ gradle_pattern: Optional[re.Pattern] = None
743
+ # Optional fast pre-check: skip expensive regex if this string is absent.
744
+ quick_filter: Optional[str] = None
745
+
746
+
747
+ _DEP_RULES: list[_DepRule] = [
748
+ _DepRule(
749
+ id="MIG-040",
750
+ severity="high",
751
+ title="SpringFox (io.springfox) — incompatible with Spring Boot 3 / Spring Framework 6",
752
+ explanation=(
753
+ "SpringFox relies on Spring MVC internal request mapping infrastructure that was "
754
+ "removed in Spring Framework 6. Applications declaring io.springfox:springfox-* "
755
+ "dependencies will fail to start after migration to Spring Boot 3, even if the "
756
+ "Java source code compiles cleanly."
757
+ ),
758
+ fix_hint=(
759
+ "Replace springfox-swagger2 + springfox-swagger-ui with "
760
+ "springdoc-openapi-starter-webmvc-ui (OpenAPI 3). "
761
+ "Also remove @EnableSwagger2 and any SpringFox Docket configuration beans."
762
+ ),
763
+ migration_target="spring_boot_3",
764
+ openrewrite_recipe=None,
765
+ maven_pattern=re.compile(r"\bio\.springfox\b", re.IGNORECASE),
766
+ gradle_pattern=re.compile(r"\bio\.springfox\b", re.IGNORECASE),
767
+ ),
768
+ _DepRule(
769
+ id="MIG-041",
770
+ severity="high",
771
+ title="Hibernate 5.x explicitly pinned — Spring Boot 3 requires Hibernate 6",
772
+ explanation=(
773
+ "Spring Boot 3 ships with Hibernate 6.x as the JPA provider, which implements "
774
+ "Jakarta Persistence 3.0. An explicit <version>5.*</version> for hibernate-core "
775
+ "overrides the Spring Boot BOM and will cause runtime incompatibilities: Hibernate 5 "
776
+ "implements javax.persistence (not jakarta.persistence)."
777
+ ),
778
+ fix_hint=(
779
+ "Remove the explicit Hibernate version override and let the Spring Boot 3 BOM "
780
+ "manage it (Hibernate 6.x). Review breaking API changes between Hibernate 5 and 6 "
781
+ "in the Hibernate 6 migration guide."
782
+ ),
783
+ migration_target="jakarta",
784
+ openrewrite_recipe=None,
785
+ maven_pattern=re.compile(
786
+ r"<dependency>(?:(?!</dependency>).)*?hibernate-core(?![-\w])(?:(?!</dependency>).)*?"
787
+ r"<version>\s*5\.",
788
+ re.DOTALL | re.IGNORECASE,
789
+ ),
790
+ gradle_pattern=re.compile(
791
+ r"""['"](org\.hibernate(?:\.orm)?):hibernate-core:5\.""",
792
+ re.IGNORECASE,
793
+ ),
794
+ quick_filter="hibernate-core",
795
+ ),
796
+ _DepRule(
797
+ id="MIG-042",
798
+ severity="medium",
799
+ title="ByteBuddy < 1.12.x — may not support Java 17+ strong encapsulation",
800
+ explanation=(
801
+ "ByteBuddy versions before 1.12 lack stable support for Java 17+ strong JPMS "
802
+ "encapsulation. Spring AOP, Mockito, and Hibernate proxies all depend on ByteBuddy "
803
+ "internally. If an application pins byte-buddy at 1.0–1.11.x, proxy creation "
804
+ "may fail with InaccessibleObjectException on Java 17+."
805
+ ),
806
+ fix_hint=(
807
+ "Remove explicit ByteBuddy version overrides and let Spring Boot 3 BOM manage it "
808
+ "(ships with 1.14.x+). If you must pin it, use >= 1.12.18."
809
+ ),
810
+ migration_target="java_17",
811
+ openrewrite_recipe=None,
812
+ maven_pattern=re.compile(
813
+ r"<dependency>(?:(?!</dependency>).)*?byte-buddy(?:(?!</dependency>).)*?"
814
+ r"<version>\s*1\.(?:[0-9]|1[01])\.",
815
+ re.DOTALL | re.IGNORECASE,
816
+ ),
817
+ gradle_pattern=re.compile(
818
+ r"""['"](net\.bytebuddy):byte-buddy:1\.(?:[0-9]|1[01])\.""",
819
+ re.IGNORECASE,
820
+ ),
821
+ quick_filter="byte-buddy",
822
+ ),
823
+ _DepRule(
824
+ id="MIG-043",
825
+ severity="high",
826
+ title="EhCache 2.x — incompatible with Spring Boot 3 / JCache JSR-107 migration",
827
+ explanation=(
828
+ "EhCache 2.x (net.sf.ehcache) uses the old JSR-107 cache API and is not compatible "
829
+ "with the Spring Boot 3 cache abstraction. Spring Boot 3 requires EhCache 3.x "
830
+ "(org.ehcache) which implements JCache 1.1 and uses a different configuration format."
831
+ ),
832
+ fix_hint=(
833
+ "Migrate from net.sf.ehcache:ehcache to org.ehcache:ehcache:3.x. "
834
+ "Update ehcache.xml configuration to the EhCache 3 XML format. "
835
+ "Add the 'org.ehcache:ehcache::jakarta' classifier for Jakarta EE compatibility."
836
+ ),
837
+ migration_target="spring_boot_3",
838
+ openrewrite_recipe=None,
839
+ maven_pattern=re.compile(
840
+ r"<groupId>\s*net\.sf\.ehcache\s*</groupId>",
841
+ re.IGNORECASE,
842
+ ),
843
+ gradle_pattern=re.compile(
844
+ r"""['"](net\.sf\.ehcache):[^'"]+""",
845
+ re.IGNORECASE,
846
+ ),
847
+ ),
848
+ ]
849
+
850
+ _BUILD_FILE_NAMES: tuple[str, ...] = ("pom.xml", "build.gradle", "build.gradle.kts")
851
+
852
+
853
+ def _find_build_files(root: Path) -> list[tuple[Path, str]]:
854
+ """Return (abs_path, rel_path) for pom.xml / build.gradle files, excluding build dirs."""
855
+ results: list[tuple[Path, str]] = []
856
+ for dirpath, dirnames, filenames in os.walk(root):
857
+ dirnames[:] = [d for d in dirnames if d not in _SKIP_DIRS]
858
+ dp = Path(dirpath)
859
+ try:
860
+ rel_dir = dp.relative_to(root)
861
+ except ValueError:
862
+ continue
863
+ for fname in filenames:
864
+ if fname in _BUILD_FILE_NAMES:
865
+ abs_path = dp / fname
866
+ rel = str(rel_dir / fname) if str(rel_dir) != "." else fname
867
+ results.append((abs_path, rel))
868
+ return results
869
+
870
+
871
+ def _resolve_maven_properties(text: str) -> str:
872
+ """Substitute ${prop} references with values from the <properties> block.
873
+
874
+ Handles single-level property references that appear in the same pom.xml.
875
+ Multi-level references (${a} where a=${b}) are resolved up to 3 passes.
876
+ """
877
+ props: dict[str, str] = {}
878
+ for m in re.finditer(r'<([A-Za-z][\w.\-]*)>\s*([^<${}]+?)\s*</\1>', text):
879
+ props[m.group(1)] = m.group(2).strip()
880
+ if not props:
881
+ return text
882
+
883
+ resolved = text
884
+ for _ in range(3):
885
+ def _sub(m: re.Match) -> str: # noqa: E306
886
+ return props.get(m.group(1), m.group(0))
887
+ resolved_new = re.sub(r'\$\{([\w.\-]+)\}', _sub, resolved)
888
+ if resolved_new == resolved:
889
+ break
890
+ resolved = resolved_new
891
+ return resolved
892
+
893
+
894
+ def _scan_dep_file(text: str, rel_path: str) -> list["MigrationFinding"]:
895
+ """Apply dependency rules to a build file. Returns one finding per matched rule."""
896
+ is_gradle = rel_path.endswith((".gradle", ".gradle.kts"))
897
+ if not is_gradle and rel_path.endswith(".xml"):
898
+ text = _resolve_maven_properties(text)
899
+ findings: list[MigrationFinding] = []
900
+ for rule in _DEP_RULES:
901
+ if rule.quick_filter is not None and rule.quick_filter not in text:
902
+ continue
903
+ pattern = rule.gradle_pattern if is_gradle else rule.maven_pattern
904
+ if pattern is None:
905
+ continue
906
+ m = pattern.search(text)
907
+ if m is None:
908
+ continue
909
+ first_line = text[: m.start()].count("\n") + 1
910
+ findings.append(
911
+ MigrationFinding(
912
+ id=MigrationFinding.make_id(rule.id, rel_path),
913
+ rule_id=rule.id,
914
+ severity=rule.severity,
915
+ title=rule.title,
916
+ source_file=rel_path,
917
+ first_line=first_line,
918
+ imports_found=[m.group(0)[:120].strip()],
919
+ explanation=rule.explanation,
920
+ fix_hint=rule.fix_hint,
921
+ migration_target=rule.migration_target,
922
+ openrewrite_recipe=rule.openrewrite_recipe,
923
+ )
924
+ )
925
+ return findings
926
+
927
+
462
928
  # ---------------------------------------------------------------------------
463
929
  # Finding
464
930
  # ---------------------------------------------------------------------------
465
931
 
466
932
  @dataclass
467
933
  class MigrationFinding:
468
- id: str # deterministic: "{rule_id}-{file_hash[:12]}"
469
- rule_id: str # "MIG-001" .. "MIG-022"
470
- severity: str # "critical" | "high" | "medium" | "low"
934
+ id: str
935
+ rule_id: str
936
+ severity: str
471
937
  title: str
472
- source_file: str # relative path
473
- first_line: int # 1-based line number of first match
474
- imports_found: list[str] = field(default_factory=list) # matched import stmts or code snippets
938
+ source_file: str
939
+ first_line: int
940
+ imports_found: list[str] = field(default_factory=list)
475
941
  explanation: str = ""
476
942
  fix_hint: str = ""
477
943
  migration_target: str = ""
@@ -493,11 +959,14 @@ class MigrationFinding:
493
959
  "explanation": self.explanation,
494
960
  "fix_hint": self.fix_hint,
495
961
  "migration_target": self.migration_target,
962
+ "auto_fix_available": bool(self.openrewrite_recipe),
496
963
  }
497
964
  if self.imports_found:
498
965
  d["imports_found"] = self.imports_found
499
966
  if self.openrewrite_recipe:
500
967
  d["openrewrite_recipe"] = self.openrewrite_recipe
968
+ else:
969
+ d["manual_migration"] = True
501
970
  return d
502
971
 
503
972
 
@@ -507,14 +976,13 @@ class MigrationFinding:
507
976
 
508
977
  @dataclass
509
978
  class MigrationReport:
510
- schema_version: str = "1.1"
979
+ schema_version: str = "1.2"
511
980
  generated_at: str = ""
512
981
  repo_id: str = ""
513
982
  git_head: str = ""
514
983
 
515
- # Core metrics
516
- readiness_score: int = 100 # 0–100; 100 = ready to migrate
517
- blocking_count: int = 0 # critical + high finding count
984
+ readiness_score: int = 100
985
+ blocking_count: int = 0
518
986
  estimated_effort_days: float = 0.0
519
987
  spring_boot_2_detected: bool = False
520
988
 
@@ -540,8 +1008,6 @@ class MigrationReport:
540
1008
 
541
1009
  self.blocking_count = by_severity["critical"] + by_severity["high"]
542
1010
 
543
- # Score: deduct per affected-file/severity combination (not per finding, to avoid
544
- # double-counting a file that imports 10 javax.persistence classes).
545
1011
  critical_files: set[str] = set()
546
1012
  high_files: set[str] = set()
547
1013
  medium_files: set[str] = set()
@@ -564,7 +1030,6 @@ class MigrationReport:
564
1030
  )
565
1031
  self.readiness_score = max(0, 100 - deduction)
566
1032
 
567
- # Effort: sum per distinct affected file weighted by severity
568
1033
  self.estimated_effort_days = round(
569
1034
  len(critical_files) * 0.5
570
1035
  + len(high_files) * 0.25
@@ -631,7 +1096,7 @@ class MigrationReport:
631
1096
 
632
1097
 
633
1098
  # ---------------------------------------------------------------------------
634
- # Scanner
1099
+ # Java source scanner
635
1100
  # ---------------------------------------------------------------------------
636
1101
 
637
1102
  def _scan_file(
@@ -642,8 +1107,6 @@ def _scan_file(
642
1107
  findings: list[MigrationFinding] = []
643
1108
 
644
1109
  for rule in rules:
645
- # An import_pattern and code_pattern can coexist on the same rule (OR semantics).
646
- # A finding is created if EITHER matches; we report the earliest match position.
647
1110
  matched_imports: list[str] = []
648
1111
  import_first_line: Optional[int] = None
649
1112
  code_first_line: Optional[int] = None
@@ -661,14 +1124,12 @@ def _scan_file(
661
1124
  code_first_line = source[: m.start()].count("\n") + 1
662
1125
  code_snippets = [m.group(0).strip()]
663
1126
 
664
- # extends_pattern is a legacy form of code_pattern
665
1127
  extends_first_line: Optional[int] = None
666
1128
  if rule.extends_pattern is not None:
667
1129
  m = rule.extends_pattern.search(source)
668
1130
  if m is not None:
669
1131
  extends_first_line = source[: m.start()].count("\n") + 1
670
1132
 
671
- # Determine overall match
672
1133
  candidate_lines = [
673
1134
  ln for ln in (import_first_line, code_first_line, extends_first_line)
674
1135
  if ln is not None
@@ -708,13 +1169,17 @@ def run_migrate_check(
708
1169
  *,
709
1170
  min_severity: str = "low",
710
1171
  ) -> MigrationReport:
711
- """Scan Java files for migration blockers (Spring Boot 2→3, Java 8→17/21).
1172
+ """Scan a Java repository for migration blockers.
1173
+
1174
+ Scans:
1175
+ - Java source files (.java) against all 24 rules (MIG-001..MIG-025)
1176
+ - Spring XML config files (applicationContext.xml, web.xml, security XML, etc.)
1177
+ - Build descriptors (pom.xml, build.gradle) for incompatible dependencies
712
1178
 
713
1179
  Args:
714
1180
  file_paths: Relative Java file paths (from find_java_files).
715
1181
  root: Absolute repo root.
716
- min_severity: Filter threshold findings below this severity are excluded
717
- from the report. Choices: critical | high | medium | low.
1182
+ min_severity: Filter threshold. Choices: critical | high | medium | low.
718
1183
 
719
1184
  Returns:
720
1185
  MigrationReport with findings, readiness_score, effort estimate, and
@@ -725,6 +1190,7 @@ def run_migrate_check(
725
1190
  limitations: list[str] = []
726
1191
  read_errors = 0
727
1192
 
1193
+ # ── Java source scan ────────────────────────────────────────────────────
728
1194
  for rel_path in file_paths:
729
1195
  abs_path = root / rel_path
730
1196
  try:
@@ -734,16 +1200,58 @@ def run_migrate_check(
734
1200
  continue
735
1201
 
736
1202
  file_findings = _scan_file(source, rel_path, _ALL_RULES)
737
- # Apply min_severity filter
738
1203
  filtered = [f for f in file_findings if SEVERITY_ORDER.get(f.severity, 3) <= min_order]
739
1204
  all_findings.extend(filtered)
740
1205
 
741
1206
  if read_errors:
742
1207
  limitations.append(f"{read_errors} file(s) could not be read and were skipped.")
743
1208
 
1209
+ # ── XML + dependency scan (single tree walk) ─────────────────────────────
1210
+ xml_files, build_files = _find_non_java_files(root)
1211
+ xml_read_errors = 0
1212
+ for abs_path, rel_path in xml_files:
1213
+ try:
1214
+ text = abs_path.read_text(encoding="utf-8", errors="replace")
1215
+ except OSError:
1216
+ xml_read_errors += 1
1217
+ continue
1218
+ xml_findings = _scan_xml_file(text, rel_path)
1219
+ filtered = [f for f in xml_findings if SEVERITY_ORDER.get(f.severity, 3) <= min_order]
1220
+ all_findings.extend(filtered)
1221
+
1222
+ if xml_read_errors:
1223
+ limitations.append(f"{xml_read_errors} XML file(s) could not be read and were skipped.")
1224
+
1225
+ dep_read_errors = 0
1226
+ raw_dep_findings: list[MigrationFinding] = []
1227
+ for abs_path, rel_path in build_files:
1228
+ try:
1229
+ text = abs_path.read_text(encoding="utf-8", errors="replace")
1230
+ except OSError:
1231
+ dep_read_errors += 1
1232
+ continue
1233
+ dep_findings = _scan_dep_file(text, rel_path)
1234
+ filtered = [f for f in dep_findings if SEVERITY_ORDER.get(f.severity, 3) <= min_order]
1235
+ raw_dep_findings.extend(filtered)
1236
+
1237
+ # Deduplicate dep findings by rule_id: same dependency in parent + child poms
1238
+ # is one logical finding. Keep the first occurrence (root pom sorts first).
1239
+ _seen_dep_rules: dict[str, int] = {} # rule_id → count
1240
+ for f in raw_dep_findings:
1241
+ _seen_dep_rules[f.rule_id] = _seen_dep_rules.get(f.rule_id, 0) + 1
1242
+ _dedup_dep: list[MigrationFinding] = []
1243
+ _emitted: set[str] = set()
1244
+ for f in raw_dep_findings:
1245
+ if f.rule_id not in _emitted:
1246
+ _dedup_dep.append(f)
1247
+ _emitted.add(f.rule_id)
1248
+ all_findings.extend(_dedup_dep)
1249
+
1250
+ if dep_read_errors:
1251
+ limitations.append(f"{dep_read_errors} build file(s) could not be read and were skipped.")
1252
+
744
1253
  limitations.extend(_STATIC_LIMITATIONS)
745
1254
 
746
- # Detect Spring Boot 2 pom.xml heuristic (best-effort, non-fatal)
747
1255
  spring_boot_2 = _detect_spring_boot_2(root)
748
1256
 
749
1257
  report = MigrationReport(
@@ -752,12 +1260,15 @@ def run_migrate_check(
752
1260
  limitations=limitations,
753
1261
  metadata={
754
1262
  "java_files_scanned": len(file_paths),
1263
+ "xml_files_scanned": len(xml_files),
1264
+ "build_files_scanned": len(build_files),
755
1265
  "min_severity": min_severity,
756
1266
  "rules_applied": [r.id for r in _ALL_RULES],
1267
+ "xml_rules_applied": [r.id for r in _XML_RULES],
1268
+ "dep_rules_applied": [r.id for r in _DEP_RULES],
757
1269
  },
758
1270
  )
759
1271
 
760
- # Populate git_head — non-fatal
761
1272
  try:
762
1273
  import subprocess as _sub
763
1274
  _r = _sub.run(
@@ -772,19 +1283,18 @@ def run_migrate_check(
772
1283
  return report.finalize()
773
1284
 
774
1285
 
775
- # Items that static analysis cannot determine, always emitted as limitations
1286
+ # Remaining static limitations things that truly require runtime analysis
776
1287
  _STATIC_LIMITATIONS: list[str] = [
777
- "Thread.stop/suspend/resume deprecation: cannot reliably detect without type resolution "
778
- "(requires knowing that a variable is typed as java.lang.Thread).",
779
- "CORBA removal (Java 11): org.omg.* usage not scanned; add manually if project uses CORBA.",
780
- "Module compatibility (JPMS): --add-opens requirements cannot be determined without "
1288
+ "Thread.stop/suspend/resume detection is best-effort: variable type cannot be confirmed "
1289
+ "without compilation. Verify that flagged variables are typed as java.lang.Thread.",
1290
+ "JPMS --add-opens requirements: exact set of required flags cannot be determined without "
781
1291
  "running the application against the target JDK.",
782
- "Transitive dependency compatibility: library versions (Hibernate, Jackson, etc.) must be "
783
- "verified separately against Spring Boot 3 BOM.",
784
- "XML-based Spring config (applicationContext.xml, web.xml): not scanned bean class names "
785
- "and servlet filter chains in XML may reference javax.* classes.",
786
- "Runtime proxy behaviour (CGLIB/ByteBuddy subclass proxies): compatibility with Java 17+ "
787
- "strong encapsulation depends on framework version, not detectable via import scanning.",
1292
+ "Transitive dependency compatibility: library versions resolved transitively (not declared "
1293
+ "directly) require 'mvn dependency:tree' or Gradle dependency insight for full analysis.",
1294
+ "Runtime proxy behaviour (CGLIB subclass proxies): compatibility with Java 17+ strong "
1295
+ "encapsulation depends on framework version at runtime, not import-level analysis.",
1296
+ "XML bean definitions referencing class names via property placeholders (${bean.class}) "
1297
+ "cannot be resolved statically.",
788
1298
  ]
789
1299
 
790
1300
 
@@ -105,7 +105,12 @@ class EvidenceBundle:
105
105
  _PKG_RE = re.compile(r'^package\s+([\w.]+)\s*;', re.MULTILINE)
106
106
  _IMPORT_RE = re.compile(r'^import\s+(?:static\s+)?([\w.]+(?:\.\*)?)\s*;', re.MULTILINE)
107
107
  _ANN_RE = re.compile(r'^(@[\w.]+)')
108
- _ANN_WITH_ARGS_RE = re.compile(r'^(@[\w.]+)\s*(?:\(([^)]*)\))?')
108
+ _ANN_WITH_ARGS_RE = re.compile(
109
+ r'^(@[\w.]+)\s*'
110
+ r'(?:\('
111
+ r'((?:[^()"\']*|"[^"]*"|\'[^\']*\'|\((?:[^()"\']*|"[^"]*"|\'[^\']*\')*\))*)'
112
+ r'\))?'
113
+ )
109
114
 
110
115
  _CLASS_DECL_RE = re.compile(
111
116
  r'(?:^|(?<=\s))'
@@ -3303,6 +3308,52 @@ def extract_java_endpoints(root: Path) -> "dict[str, Any]":
3303
3308
  else:
3304
3309
  security_model = "unknown"
3305
3310
 
3311
+ # Detect XML-based Spring Security config. When present, per-endpoint
3312
+ # none_detected is expected and does NOT mean the endpoint is unsecured —
3313
+ # security is declared in XML (HttpSecurity rules, filter chains, web.xml
3314
+ # security constraints). Update security_model and re-tag affected endpoints
3315
+ # so the output cannot be misread as "unprotected".
3316
+ _XML_SECURITY_RE = re.compile(
3317
+ r'(?:xmlns(?::[a-z]+)?="http://www\.springframework\.org/schema/security"'
3318
+ r'|<security:http\b'
3319
+ r'|<http\s[^>]*use-expressions'
3320
+ r'|spring-security-[2345]'
3321
+ r'|xmlns:security="http://www\.springframework\.org/schema/security")',
3322
+ re.IGNORECASE,
3323
+ )
3324
+ _xml_security_detected = False
3325
+ _XML_GLOBS = (
3326
+ "*security*.xml", "*Security*.xml",
3327
+ "*applicationContext*.xml", "*-context.xml", "*Context.xml",
3328
+ "*spring*.xml", "*Spring*.xml",
3329
+ )
3330
+ for _glob in _XML_GLOBS:
3331
+ for _xf in root.rglob(_glob):
3332
+ if "target/" in str(_xf).replace("\\", "/"):
3333
+ continue
3334
+ try:
3335
+ _xt = _xf.read_text(encoding="utf-8", errors="replace")
3336
+ except OSError:
3337
+ continue
3338
+ if _XML_SECURITY_RE.search(_xt):
3339
+ _xml_security_detected = True
3340
+ break
3341
+ if _xml_security_detected:
3342
+ break
3343
+
3344
+ if _xml_security_detected and security_model == "unknown":
3345
+ security_model = "xml_or_filter_chain"
3346
+ # Re-tag per-endpoint none_detected → xml_or_filter_chain so the output
3347
+ # cannot be misread as "endpoint is unprotected".
3348
+ for ep in endpoints:
3349
+ if ep.get("security", {}).get("policy") == "none_detected":
3350
+ ep["security"] = {"policy": "xml_or_filter_chain"}
3351
+ # Recompute no_security_signal (now counts only truly unknown endpoints)
3352
+ no_security_signal = sum(
3353
+ 1 for e in endpoints
3354
+ if e.get("security", {}).get("policy") == "none_detected"
3355
+ )
3356
+
3306
3357
  return {
3307
3358
  "endpoints": endpoints,
3308
3359
  "total": len(endpoints),
@@ -197,14 +197,17 @@ def _compute_event_risk(
197
197
  consumer_count: int,
198
198
  before_commit_count: int,
199
199
  cross_module: bool,
200
+ sync_in_tx_count: int = 0,
200
201
  ) -> str:
201
202
  """Deterministic risk scoring per spec.
202
203
 
203
204
  high: fanout > 5 OR cross-module propagation OR BEFORE_COMMIT consumers
205
+ OR sync @EventListener inside @Transactional publisher
204
206
  medium: 2–5 consumers
205
207
  low: ≤1 consumer
206
208
  """
207
- if consumer_count > _RISK_FANOUT_HIGH or cross_module or before_commit_count > 0:
209
+ if (consumer_count > _RISK_FANOUT_HIGH or cross_module
210
+ or before_commit_count > 0 or sync_in_tx_count > 0):
208
211
  return "high"
209
212
  if consumer_count >= _RISK_FANOUT_MEDIUM:
210
213
  return "medium"
@@ -327,9 +330,23 @@ class EventTopologyOrchestrator:
327
330
  # ── 7. TX context ──────────────────────────────────────────────────
328
331
  after_commit = [c.fqn for c in consumers if c.transactional_phase == "AFTER_COMMIT"]
329
332
  before_commit_risks = [c.fqn for c in consumers if c.transactional_phase == "BEFORE_COMMIT"]
333
+
334
+ # Detect sync @EventListener inside @Transactional publisher.
335
+ # Plain @EventListener fires synchronously; if the publisher method is
336
+ # @Transactional the listener runs inside that TX — listener exception
337
+ # rolls back the outer TX, and DB state may be partially committed.
338
+ tx_publishers = [
339
+ p for p in publishers
340
+ if "@Transactional" in ((fqn_index.get(p) or {}).get("annotations") or [])
341
+ ]
342
+ sync_in_tx_risks = [
343
+ c.fqn for c in consumers
344
+ if c.type == "spring_event" and tx_publishers
345
+ ]
330
346
  tx_context = {
331
347
  "after_commit_consumers": after_commit,
332
348
  "before_commit_risks": before_commit_risks,
349
+ "sync_in_tx_risks": sync_in_tx_risks,
333
350
  }
334
351
 
335
352
  # ── 8. Cross-module detection ──────────────────────────────────────
@@ -352,6 +369,7 @@ class EventTopologyOrchestrator:
352
369
  consumer_count=len(consumers),
353
370
  before_commit_count=len(before_commit_risks),
354
371
  cross_module=cross_module,
372
+ sync_in_tx_count=len(sync_in_tx_risks),
355
373
  )
356
374
 
357
375
  # ── 10. Confidence ─────────────────────────────────────────────────
@@ -385,6 +403,7 @@ class EventTopologyOrchestrator:
385
403
  "kafka_listeners_in_repo": kafka_count,
386
404
  "rabbit_listeners_in_repo": rabbit_count,
387
405
  "before_commit_risk_count": len(before_commit_risks),
406
+ "sync_in_tx_risk_count": len(sync_in_tx_risks),
388
407
  "level2_events": list(level2_events.keys()),
389
408
  "cross_module": cross_module,
390
409
  "model_build_time_ms": model.build_time_ms,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sourcecode
3
- Version: 1.35.24
3
+ Version: 1.35.26
4
4
  Summary: Persistent structural context and ultra-fast repeated analysis for AI coding agents
5
5
  License-File: LICENSE
6
6
  Keywords: agents,ai,codebase,context,developer-tools,llm
@@ -40,7 +40,7 @@ Description-Content-Type: text/markdown
40
40
 
41
41
  **Persistent structural context and ultra-fast repeated analysis for AI coding agents.**
42
42
 
43
- ![Version](https://img.shields.io/badge/version-1.35.24-blue)
43
+ ![Version](https://img.shields.io/badge/version-1.35.26-blue)
44
44
  ![Python](https://img.shields.io/badge/python-3.10%2B-green)
45
45
 
46
46
  ---
@@ -114,7 +114,7 @@ pipx install sourcecode
114
114
 
115
115
  ```bash
116
116
  sourcecode version
117
- # sourcecode 1.35.24
117
+ # sourcecode 1.35.26
118
118
  ```
119
119
 
120
120
  ---
@@ -1,4 +1,4 @@
1
- sourcecode/__init__.py,sha256=2yK-pjDWylHCv6BnJnDbJ6apRllmWf4VbNJHpb2m3Ec,104
1
+ sourcecode/__init__.py,sha256=HYA42OYABbKZ43Trr1VCdpUkXMtz0AqpwPt0ENMTTjo,104
2
2
  sourcecode/adaptive_scanner.py,sha256=XffluXKzJUXrMtjEiAOnSNPZnztdIcts17T9ouHeID0,10521
3
3
  sourcecode/architecture_analyzer.py,sha256=qh749a7ykPtGmQI1MR9y6j8TtL_jBdVYFx9YRsLqOMw,44121
4
4
  sourcecode/architecture_summary.py,sha256=z34_6v7cSwy98cof2UVciGho7SCrZ93tiqMmq5WNzRQ,20405
@@ -29,7 +29,7 @@ sourcecode/graph_analyzer.py,sha256=DHR8fY69oU_Pi4SYaWboX6EoEFrctQKB9dsjpqwGMzw,
29
29
  sourcecode/license.py,sha256=3JCV2OeTVttKrOGBguU5uZC0c02Stig-KLB0mP2lNiY,22742
30
30
  sourcecode/mcp_nudge.py,sha256=5ELU_ixzh6uA83NXLOZT8h00OhL53okfQdji3jyKOjg,2917
31
31
  sourcecode/metrics_analyzer.py,sha256=m0ENgtqKeBL17kUIK3fmGkgo7UfXBNHxCMj0H_Y5K7c,22750
32
- sourcecode/migrate_check.py,sha256=aJMpsfiS9D8FKTyfiLAh0BylAAk178ZsWpadJffVpaY,33794
32
+ sourcecode/migrate_check.py,sha256=GuYK36DDFkwf07jbAgcoc-Ovq8ttLQNMsRqhsUilMzY,54514
33
33
  sourcecode/output_budget.py,sha256=Js9yUlfQtPhqBl9R6wn_9UHVjjJc3GtLcqyfjf5t50Q,9869
34
34
  sourcecode/path_filters.py,sha256=ROFRQ8eSLBEMiixK9f45-RO7um4VEEcjoD5AA4I427I,3739
35
35
  sourcecode/pr_comment_renderer.py,sha256=smHslxiG14lrytCkq5nFrFu-qTHgA-t-LFYfdrfjz2o,14423
@@ -40,14 +40,14 @@ sourcecode/ranking_engine.py,sha256=ZAucq_YX2KkWUuAZf4P0lhtQ_38vEFnUhuGtSZd1S0E,
40
40
  sourcecode/redactor.py,sha256=SB4hwIvg8h-hvcqKcDWaZvA-aSyn-at-BIRwa0tUv5E,3227
41
41
  sourcecode/relevance_scorer.py,sha256=0AgEt4KrV73nioMqBgjhGjtY7L2C7L7cSyKtj3IKcrw,9408
42
42
  sourcecode/repo_classifier.py,sha256=FG1vaWKdWXsWdl-S8hjVMiTqcwgaRXkDyvK4rPcOGtQ,22681
43
- sourcecode/repository_ir.py,sha256=vTdWuFj0iRUavs4mOWl87JETR7Je9bKVeDycKBqOFp8,169640
43
+ sourcecode/repository_ir.py,sha256=hl7Vc7o5LD0xWZ6Er7-x2IDrLurJZIfBKGPD-3cfraU,171754
44
44
  sourcecode/ris.py,sha256=RcqLVwC-doFcKKViYDkCjZLBqf_wzLES7-F6vHEeWzE,20419
45
45
  sourcecode/runtime_classifier.py,sha256=uTAD6BDCiBLUZEDRfqk718kM4RTT_vAbfkcOI2_Xx58,18432
46
46
  sourcecode/scanner.py,sha256=WdOQ78mMzjR1NjmKTlbxdgwinnCTfAhxCVLBEFQiFHU,8899
47
47
  sourcecode/schema.py,sha256=aHNXDf8LGyUC8ZDE_VS9kiskC2-Oswhi_WnpdGy6HDw,24897
48
48
  sourcecode/semantic_analyzer.py,sha256=TDuC3wzZR2DPm1mgrAg1YSLk2QzJoueS3TZAmyGGpCU,89417
49
49
  sourcecode/serializer.py,sha256=7SBJIbpC_Lg0RGWq8jjNbF5TiuZwoP_fi0qhHnzQM8M,124386
50
- sourcecode/spring_event_topology.py,sha256=LvGv5RXtU_O-fVB_OO9eDD2UmZM72Jn2oUHgOo50Qm0,17157
50
+ sourcecode/spring_event_topology.py,sha256=5_ON_21Le5zbG-1GRc5GLIi5HJfy_QjcXLVPC5WeUGQ,18055
51
51
  sourcecode/spring_findings.py,sha256=8V91iHOg9hFgg6tLLl4FSsgrF-dBqOcO2s-K5sD_goA,5417
52
52
  sourcecode/spring_impact.py,sha256=Ohm2k3W4Wts8Kx8Z7DIM-J-cwGtTJBWKFBsX-WkupBQ,32943
53
53
  sourcecode/spring_model.py,sha256=IzMcM5ftw1_EHG3FGUDT7qdAMpo3eqbAE1LRuasfr_4,14739
@@ -94,8 +94,8 @@ sourcecode/telemetry/consent.py,sha256=wLMvGNJeSSyZoNkQXpoUioY6mMv4Qdvuw7S9jAEWn
94
94
  sourcecode/telemetry/events.py,sha256=oEvvulfsv5GIDWG2174gSS6tNB95w38AIYiYeifGKlE,2294
95
95
  sourcecode/telemetry/filters.py,sha256=Asa71oRl7q3Wt_FMwuufIZJFzSYdgRNKS8LHCIyFeYE,4805
96
96
  sourcecode/telemetry/transport.py,sha256=QSslxIwij8YkRWcVvxykODDrkiN_GAAEu3dUP7KIWeE,1651
97
- sourcecode-1.35.24.dist-info/METADATA,sha256=gCMQSuUMZ5yET1UzAxaflOMZK-yra4U_lSogJ12PC7o,21297
98
- sourcecode-1.35.24.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
99
- sourcecode-1.35.24.dist-info/entry_points.txt,sha256=ex3F9rmbXeyDIoFQHtkEqTsKSaJow8F0LrVu8XfIktQ,57
100
- sourcecode-1.35.24.dist-info/licenses/LICENSE,sha256=7DdHrU9Z_3e7dSvq4ISijZNjnuHo5NIHNiHDouMQ9JU,10491
101
- sourcecode-1.35.24.dist-info/RECORD,,
97
+ sourcecode-1.35.26.dist-info/METADATA,sha256=M8Y5dAsVOUGs7ij_YJC26hy4SJtJ_4fFGZZ6NpuabKQ,21297
98
+ sourcecode-1.35.26.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
99
+ sourcecode-1.35.26.dist-info/entry_points.txt,sha256=ex3F9rmbXeyDIoFQHtkEqTsKSaJow8F0LrVu8XfIktQ,57
100
+ sourcecode-1.35.26.dist-info/licenses/LICENSE,sha256=7DdHrU9Z_3e7dSvq4ISijZNjnuHo5NIHNiHDouMQ9JU,10491
101
+ sourcecode-1.35.26.dist-info/RECORD,,