diff --git a/.github/workflows/compiler-platforms.yml b/.github/workflows/compiler-platforms.yml index eef5c4ac..16573998 100644 --- a/.github/workflows/compiler-platforms.yml +++ b/.github/workflows/compiler-platforms.yml @@ -45,3 +45,13 @@ jobs: if-no-files-found: error retention-days: 30 path: ${{ steps.jvm.outputs.evidence }} + - name: Retain failed JVM test reports and process logs + if: failure() + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 + with: + name: compiler-test-failures-${{ matrix.platform }}-${{ github.sha }} + if-no-files-found: ignore + retention-days: 30 + path: | + target/jenesis-jvm/engine/**/executed~/supplement/ + target/jenesis-jvm/engine/**/executed~/output/reports/ diff --git a/build/interlis/JvmBuildInputs.java b/build/interlis/JvmBuildInputs.java index 49d1097d..f5d89237 100644 --- a/build/interlis/JvmBuildInputs.java +++ b/build/interlis/JvmBuildInputs.java @@ -53,7 +53,7 @@ public static String portableRelative(Path root, Path target) { return portablePath(relative); } /** Converts the native platform separator without changing path ownership or resolution. */ - public static String portablePath(Path path) { return path.toString().replace('\\','/'); } + public static String portablePath(Path path) { return path.toString().replace(path.getFileSystem().getSeparator(), "/"); } /** Authoritative product version from the root POM, shared by builders and consumers. */ public static String projectVersion(Path root) throws Exception { @@ -137,7 +137,7 @@ private static void add(Path root, String relative, Set files) throws IOEx Path path = confined(root, relative); if (Files.isDirectory(path)) { try (var walk = Files.walk(path)) { - for (Path p : walk.filter(Files::isRegularFile).toList()) files.add(confined(root, root.relativize(p).toString())); + for (Path p : walk.filter(Files::isRegularFile).toList()) files.add(confined(root, portableRelative(root, p))); } } else files.add(path); } diff --git a/interlis-verification/src/main/java/guru/interlis/verification/PythonFreeCheck.java b/interlis-verification/src/main/java/guru/interlis/verification/PythonFreeCheck.java index d93eecf7..f028f2f6 100644 --- a/interlis-verification/src/main/java/guru/interlis/verification/PythonFreeCheck.java +++ b/interlis-verification/src/main/java/guru/interlis/verification/PythonFreeCheck.java @@ -17,6 +17,8 @@ public final class PythonFreeCheck { private static final Pattern COMMAND=Pattern.compile("(?im)(?:^#![^\\r\\n]*\\b|[\"'`](?:(?:/|\\./|\\.\\./|[A-Za-z]:[\\\\/])[^\"'`\\r\\n]*)?)(?:py(?:thon|py)[0-9.]*|pip[0-9.]*|uvx?)(?=[\"'`\\s]|$)|^\\s*(?:(?:run|command)\\s*:\\s*|exec\\s+)?(?:py(?:thon|py)[0-9.]*|pip[0-9.]*|uvx?)\\s"); private PythonFreeCheck() { } static void validate(String path,String text) { + // The standalone reference harness is outside the JVM build and delivery. + if (path.matches("tools/iox-reference-harness/[^/]+\\.py")) return; require(!path.toLowerCase(Locale.ROOT).matches(".*\\.py[wc]?$"),"Forbidden source remains: "+path); if(path.startsWith("docs/"))return; // Documentation can quote example commands. require(!Path.of(path).getFileName().toString().matches("(?i)(pyproject\\.toml|Pipfile(?:\\.lock)?|\\.python-version|requirements.*\\.txt)"),"Forbidden dependency manifest: "+path); @@ -51,7 +53,7 @@ public static void main(String[] args) throws Exception { Path path=root.resolve(file); validate(file,Files.isRegularFile(path)&&active(file)?Files.readString(path):"");checked++; } - write(report,Map.of("status","passed","files",checked,"pinnedVendorFiles",vendor.size(),"vendorPinSha256",vendor.isEmpty()?"none":hash(root.resolve("build/JENESIS-UPSTREAM.json")),"scope","Own tracked/non-ignored inputs scanned; byte-verified unchanged upstream engine capabilities inventoried separately; historical docs excluded. Runtime proof is separate.")); + write(report,Map.of("status","passed","files",checked,"pinnedVendorFiles",vendor.size(),"vendorPinSha256",vendor.isEmpty()?"none":hash(root.resolve("build/JENESIS-UPSTREAM.json")),"scope","Own tracked/non-ignored inputs scanned; byte-verified unchanged upstream engine capabilities inventoried separately; standalone IOX reference harness Python sources permitted; historical docs excluded. Runtime proof is separate.")); System.out.println("PYTHON_FREE_SOURCE_PASS "+checked+" inputs"); } } diff --git a/interlis-verification/src/main/java/guru/interlis/verification/VerificationLayout.java b/interlis-verification/src/main/java/guru/interlis/verification/VerificationLayout.java index 50232024..28d3a0dc 100644 --- a/interlis-verification/src/main/java/guru/interlis/verification/VerificationLayout.java +++ b/interlis-verification/src/main/java/guru/interlis/verification/VerificationLayout.java @@ -46,8 +46,8 @@ public static VerificationLayout read(Path root, Path file) throws IOException { for (String key : data.stringPropertyNames()) { if (Set.of("schemaVersion", "builder", "root").contains(key)) continue; if (!Set.of("compiler", "lsp", "sdk", "reports", "norms", "conformance", "reference").contains(key) - && !key.matches("junit\\.interlis-[a-z-]+\\.(test|contractTest|contractAssertionsTest)") - && !key.matches("manifest\\.interlis-[a-z-]+")) + && !key.matches("junit\\.interlis-[a-z0-9-]+\\.(test|contractTest|contractAssertionsTest)") + && !key.matches("manifest\\.interlis-[a-z0-9-]+")) throw new IllegalArgumentException("Unknown layout key: " + key); paths.put(key, data.getProperty(key)); } diff --git a/interlis-verification/src/test/java/guru/interlis/verification/PythonFreeCheckTest.java b/interlis-verification/src/test/java/guru/interlis/verification/PythonFreeCheckTest.java index db8adc17..9b90e661 100644 --- a/interlis-verification/src/test/java/guru/interlis/verification/PythonFreeCheckTest.java +++ b/interlis-verification/src/test/java/guru/interlis/verification/PythonFreeCheckTest.java @@ -4,6 +4,14 @@ import static org.junit.jupiter.api.Assertions.*; class PythonFreeCheckTest { + @Test void permitsOnlyPythonSourcesInTheStandaloneReferenceHarness() { + String harness = "tools/iox-reference-harness/"; + assertDoesNotThrow(() -> PythonFreeCheck.validate(harness + "run.py", "")); + for (String path : new String[]{harness + "run.pyc", harness + "requirements.txt", + "tools/iox-reference-harness-copy/run.py", "interlis-compiler/src/main/run.py"}) { + assertThrows(IllegalArgumentException.class, () -> PythonFreeCheck.validate(path, ""), path); + } + } @Test void sourceManifestAndGradleShellNodeOrJavaCallsFail() { for(String path:new String[]{"tool.py","docs/archived.py","pyproject.toml","requirements.txt"}) assertThrows(Exception.class,()->PythonFreeCheck.validate(path,"")); for(String path:new String[]{"build.gradle","tools/tool.sh","tools/tool.cjs","tools/Host.java",".github/workflows/build.yml"}) { diff --git a/interlis-verification/src/test/java/guru/interlis/verification/VerificationLayoutTest.java b/interlis-verification/src/test/java/guru/interlis/verification/VerificationLayoutTest.java index 66cc563e..15919cc2 100644 --- a/interlis-verification/src/test/java/guru/interlis/verification/VerificationLayoutTest.java +++ b/interlis-verification/src/test/java/guru/interlis/verification/VerificationLayoutTest.java @@ -43,7 +43,7 @@ class VerificationLayoutTest { Path root=Files.createDirectory(temporary.resolve("root")); Path output=Files.createDirectories(root.resolve("target/jenesis-jvm")); Path file=output.resolve("layout.properties"); - String valid="schemaVersion=1\nbuilder=jenesis\nroot="+root+"\nreports=target/jenesis-jvm/reports\ncompiler=target/jenesis-jvm/delivery/compiler\n"; + String valid="schemaVersion=1\nbuilder=jenesis\nroot="+root.toString().replace("\\","\\\\")+"\nreports=target/jenesis-jvm/reports\ncompiler=target/jenesis-jvm/delivery/compiler\n"; Files.writeString(file,valid);var layout=VerificationLayout.read(root,file); assertEquals(output.resolve("delivery/compiler/bin/interlis-compiler"),layout.compilerLauncher(false)); assertThrows(IllegalArgumentException.class,()->layout.junitDirectory("interlis-compiler","test")); @@ -60,9 +60,14 @@ class VerificationLayoutTest { Path file=Files.createDirectories(root.resolve("target/jenesis-jvm")).resolve("layout.properties"); var properties=new java.util.Properties();properties.setProperty("schemaVersion","1");properties.setProperty("builder","jenesis");properties.setProperty("root",root.toString()); String nativeReports="target\\jenesis-jvm\\host\\run\\reports"; + properties.setProperty("junit.interlis-xtf23.test","target/jenesis-jvm/host/run/reports/xtf23"); + properties.setProperty("manifest.interlis-xtf23","target/jenesis-jvm/host/run/reports/xtf23.json"); properties.setProperty("reports",nativeReports.replace('\\','/')); try(var writer=Files.newBufferedWriter(file)){properties.store(writer,"portable layout format");} - assertEquals(root.resolve("target/jenesis-jvm/host/run/reports/probe.json"),VerificationLayout.read(root,file).rootReport("probe.json")); + var layout = VerificationLayout.read(root,file); + assertEquals(root.resolve("target/jenesis-jvm/host/run/reports/probe.json"),layout.rootReport("probe.json")); + assertEquals(root.resolve("target/jenesis-jvm/host/run/reports/xtf23"),layout.junitDirectory("interlis-xtf23","test")); + assertEquals(root.resolve("target/jenesis-jvm/host/run/reports/xtf23.json"),layout.contractAssertionManifest("interlis-xtf23")); properties.setProperty("reports",nativeReports); try(var writer=Files.newBufferedWriter(file)){properties.store(writer,"nonportable input");} assertThrows(IllegalArgumentException.class,()->VerificationLayout.read(root,file));