diff --git a/.cirrus.yml b/.cirrus.yml
index e94ab7dc233..55c16823a08 100644
--- a/.cirrus.yml
+++ b/.cirrus.yml
@@ -129,6 +129,7 @@ test_analyze_task:
- ./check-license-compliance.sh
cleanup_before_cache_script: cleanup_maven_repository
+# Migrated to GHA.
qa_os_win_task:
ec2_instance:
image: base-windows-jdk21-v*
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index fee6d900c88..90b715cc369 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -271,3 +271,42 @@ jobs:
-Dmaven.test.redirectTestOutputToFile=false
-Dparallel=methods
-DuseUnlimitedThreads=true
+
+ qa-os-win:
+ name: Build and Unit Test on Windows
+ # No dependency on build step, because we do not need the build number.
+ runs-on: github-windows-latest-m
+ permissions:
+ id-token: write # Required for Vault OIDC authentication
+ contents: write # Required for repository access and tagging
+ steps:
+ - name: Config Git
+ run: git config --global core.autocrlf input
+ - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
+ - uses: jdx/mise-action@5ac50f778e26fac95da98d50503682459e86d566 # v3.2.0
+ with:
+ version: 2025.7.12
+ - name: Run Maven
+ run: mvn clean verify --batch-mode
+
+ promote:
+ needs:
+ - build
+ - ruling-qa
+ - plugin-qa
+ - sanity
+ - test-analyze
+ - autoscan
+ - qa-os-win
+ if: ${{ needs.build.outputs.deployed }}
+ runs-on: github-ubuntu-latest-s # Public repo uses custom GitHub-hosted runners
+ name: Promote
+ permissions:
+ id-token: write
+ contents: write
+ env:
+ BUILD_NUMBER: ${{ needs.build.outputs.build-number }}
+ steps:
+ - uses: SonarSource/ci-github-actions/promote@v1
+ with:
+ promote-pull-request: true
diff --git a/java-checks-test-sources/pom.xml b/java-checks-test-sources/pom.xml
index f586103b9e9..a425fa42ec3 100644
--- a/java-checks-test-sources/pom.xml
+++ b/java-checks-test-sources/pom.xml
@@ -43,8 +43,7 @@
/
:${line.separator}
test
-
- $${M2_REPO}
+ M2_REPO
UTF-8
${project.build.directory}/test-classpath.txt
diff --git a/java-checks-test-sources/test-classpath-reader/src/main/java/org/sonar/java/test/classpath/TestClasspathUtils.java b/java-checks-test-sources/test-classpath-reader/src/main/java/org/sonar/java/test/classpath/TestClasspathUtils.java
index 6319142c3a0..94dab5afc1a 100644
--- a/java-checks-test-sources/test-classpath-reader/src/main/java/org/sonar/java/test/classpath/TestClasspathUtils.java
+++ b/java-checks-test-sources/test-classpath-reader/src/main/java/org/sonar/java/test/classpath/TestClasspathUtils.java
@@ -158,11 +158,12 @@ public static List loadFromFile(String classpathTextFilePath) {
String mavenRepository = findMavenLocalRepository(System::getenv, System::getProperty);
try {
String content = Files.readString(toPath(classpathTextFilePath), UTF_8);
- Arrays.stream(content.split(":"))
+ // Split on ":", but not when it follows Windows drive letter (e.g. "C:\").
+ Arrays.stream(content.split("(? !line.isBlank())
.map(TestClasspathUtils::fixSeparator)
- .map(line -> line.replace("${M2_REPO}", mavenRepository))
+ .map(line -> line.replace("M2_REPO", mavenRepository))
.map(Paths::get)
.forEach(dependencyPath -> {
if (!Files.exists(dependencyPath)) {
diff --git a/java-checks-test-sources/test-classpath-reader/src/test/resources/classpath-example.txt b/java-checks-test-sources/test-classpath-reader/src/test/resources/classpath-example.txt
index 2ada24e3497..610310ad0d9 100644
--- a/java-checks-test-sources/test-classpath-reader/src/test/resources/classpath-example.txt
+++ b/java-checks-test-sources/test-classpath-reader/src/test/resources/classpath-example.txt
@@ -1,2 +1,2 @@
-${M2_REPO}/com/google/code/findbugs/jsr305/3.0.2/jsr305-3.0.2.jar:
+M2_REPO/com/google/code/findbugs/jsr305/3.0.2/jsr305-3.0.2.jar:
diff --git a/java-checks-test-sources/test-classpath-reader/src/test/resources/invalid-classpath-example.txt b/java-checks-test-sources/test-classpath-reader/src/test/resources/invalid-classpath-example.txt
index 462d6ed2b84..a33cb733005 100644
--- a/java-checks-test-sources/test-classpath-reader/src/test/resources/invalid-classpath-example.txt
+++ b/java-checks-test-sources/test-classpath-reader/src/test/resources/invalid-classpath-example.txt
@@ -1,2 +1,2 @@
-${M2_REPO}/com/google/code/findbugs/jsr305/3.0.2/jsr305-3.0.2.jar:
-${M2_REPO}/org/bad/luck/missing-artifact/666/missing-artifact-666.jar:
+M2_REPO/com/google/code/findbugs/jsr305/3.0.2/jsr305-3.0.2.jar:
+M2_REPO/org/bad/luck/missing-artifact/666/missing-artifact-666.jar: