Copilot commented on code in PR #8165:
URL: https://github.com/apache/incubator-seata/pull/8165#discussion_r3663557444


##########
.github/workflows/native.yml:
##########
@@ -0,0 +1,187 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+name: Native Build
+
+on:
+  push:
+    branches: [ test*, "*.*.*" ]
+  pull_request:
+    branches: [ 2.x, develop, master ]
+    paths:
+      - '.github/workflows/native.yml'
+      - 
'namingserver/src/main/resources/META-INF/native-image/org.apache.seata/seata-namingserver/**'
+  workflow_call:
+    inputs:
+      repository:
+        description: 'Repository to check out'
+        type: string
+        required: false
+        default: ''
+      ref:
+        description: 'Branch or tag to check out'
+        type: string
+        required: false
+        default: ''
+
+concurrency:
+  group: ${{ github.workflow }}-${{ github.event.pull_request.number || 
github.ref }}
+  cancel-in-progress: ${{ github.event_name == 'pull_request' }}
+
+jobs:
+  build:
+    name: build
+    runs-on: ${{ matrix.os }}
+    strategy:
+      fail-fast: false
+      matrix:
+        os: [ ubuntu-24.04, ubuntu-24.04-arm, macos-26, windows-latest ]

Review Comment:
   The workflow uses POSIX shell (`sh`) and backgrounding (`&`, `$!`) while 
also running on `windows-latest`, and it uses runner labels that may not exist 
(`ubuntu-24.04-arm`, `macos-26`). This is likely to fail on Windows and may 
fail due to invalid runner labels. Consider splitting OS-specific logic (e.g., 
separate jobs or conditional steps for Windows vs Unix), using a 
Windows-compatible shell/commands, and switching to runner labels that are 
known to exist.



##########
script/native/MergeNativeImageConfig.java:
##########
@@ -0,0 +1,781 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.*;
+
+/**
+ * Merge reachability-metadata.json (generated by GraalVM native-image agent)
+ * into the corresponding native-image config files.
+ *
+ * <p>This is a zero-dependency standalone tool — run anywhere a JDK 21+ is
+ * available via source-code mode ({@code java MergeNativeImageConfig.java}).
+ *
+ * <p><b>Requires JDK 21+</b> — uses switch pattern-matching syntax
+ * ({@code case Boolean b -> ...}) introduced in JDK 21.
+ *
+ * <p>Usage:
+ * <pre>
+ *     java script/native/MergeNativeImageConfig.java --target-dir PATH 
[--source PATH]
+ * </pre>
+ */
+@SuppressWarnings({"unchecked", "rawtypes"})
+public class MergeNativeImageConfig {
+
+    // 
=========================================================================
+    // Configuration
+    // 
=========================================================================
+
+    /**
+     * Describes how a top-level source key maps to a target config file.
+     *
+     * @param filename target filename (e.g. "reflect-config.json")
+     * @param srcKey   identifier field in source entries (e.g. "type")
+     * @param tgtKey   identifier field in target entries (e.g. "name")
+     * @param format   "list" for JSON-array targets, "includes" for
+     *                 {@code {"resources":{"includes":[...]}}} targets
+     */
+    record MergeTarget(String filename, String srcKey, String tgtKey, String 
format) {}
+
+    static final Map<String, MergeTarget> KEY_TO_FILE = Map.of(
+            "reflection",
+            new MergeTarget("reflect-config.json", "type", "name", "list"),
+            "resources",
+            new MergeTarget("resource-config.json", "glob", "pattern", 
"includes"));
+
+    /**
+     * Default source file, relative to the current working directory.
+     */
+    static final Path DEFAULT_SOURCE = Path.of("target", 
"native-image-config", "reachability-metadata.json");
+
+    // 
=========================================================================
+    // JSON Parser  (instance methods — kept unchanged from original)
+    // 
=========================================================================
+
+    private String json;
+    private int index;
+
+    /**
+     * Parse a JSON file and return the result as a {@code Map<String, 
Object>}.
+     */
+    public static Map<String, Object> parseFile(Path filePath) throws 
IOException {
+        String content = Files.readString(filePath);
+        return new MergeNativeImageConfig().parseObject(content);
+    }
+
+    /**
+     * Parse a JSON file that may be an array or any other JSON value.
+     */
+    public static Object parseAny(Path filePath) throws IOException {
+        String content = Files.readString(filePath);
+        return new MergeNativeImageConfig().parseAny(content);
+    }
+
+    private Map<String, Object> parseObject(String json) {
+        this.json = json.trim();
+        this.index = 0;
+        Object result = parseValue();
+        if (result instanceof Map) {
+            return (Map<String, Object>) result;
+        }
+        throw new IllegalStateException("Root must be a JSON object");
+    }
+
+    private Object parseAny(String json) {
+        this.json = json.trim();
+        this.index = 0;
+        return parseValue();
+    }
+
+    // ---------- parser core ----------
+
+    private Object parseValue() {
+        skipWhitespace();
+        if (index >= json.length()) throw new RuntimeException("Unexpected 
end");
+        char c = json.charAt(index);
+        return switch (c) {
+            case '{' -> parseObject();
+            case '[' -> parseArray();
+            case '"' -> parseString();
+            case 't', 'f', 'n' -> parseLiteral();
+            default -> {
+                if (c == '-' || Character.isDigit(c)) {
+                    yield parseNumber();
+                }
+                throw new RuntimeException("Unexpected character: " + c);
+            }
+        };
+    }
+
+    private Map<String, Object> parseObject() {
+        Map<String, Object> map = new LinkedHashMap<>();
+        index++; // skip '{'
+        skipWhitespace();
+        if (json.charAt(index) == '}') {
+            index++;
+            return map;
+        }
+        while (true) {
+            skipWhitespace();
+            if (json.charAt(index) != '"') {
+                throw new RuntimeException("Expected string key");
+            }
+            String key = parseString();
+            skipWhitespace();
+            if (json.charAt(index) != ':') {
+                throw new RuntimeException("Expected ':'");
+            }
+            index++; // skip ':'
+            Object value = parseValue();
+            map.put(key, value);
+            skipWhitespace();
+            char next = json.charAt(index);
+            if (next == '}') {
+                index++;
+                break;
+            } else if (next == ',') {
+                index++;
+                // continue
+            } else {
+                throw new RuntimeException("Expected ',' or '}'");
+            }
+        }
+        return map;
+    }
+
+    private List<Object> parseArray() {
+        List<Object> list = new ArrayList<>();
+        index++; // skip '['
+        skipWhitespace();
+        if (json.charAt(index) == ']') {
+            index++;
+            return list;
+        }
+        while (true) {
+            list.add(parseValue());
+            skipWhitespace();
+            char next = json.charAt(index);
+            if (next == ']') {
+                index++;
+                break;
+            } else if (next == ',') {
+                index++;
+            } else {
+                throw new RuntimeException("Expected ',' or ']'");
+            }
+        }
+        return list;
+    }
+
+    private String parseString() {
+        index++; // skip opening quote
+        StringBuilder sb = new StringBuilder();
+        while (index < json.length()) {
+            char c = json.charAt(index);
+            if (c == '"') {
+                index++; // skip closing quote
+                return sb.toString();
+            }
+            if (c == '\\') {
+                // escape handling (simplified, supports common escapes)
+                index++;
+                char esc = json.charAt(index);
+                switch (esc) {
+                    case '"':
+                        sb.append('"');
+                        break;
+                    case '\\':
+                        sb.append('\\');
+                        break;
+                    case '/':
+                        sb.append('/');
+                        break;
+                    case 'b':
+                        sb.append('\b');
+                        break;
+                    case 'f':
+                        sb.append('\f');
+                        break;
+                    case 'n':
+                        sb.append('\n');
+                        break;
+                    case 'r':
+                        sb.append('\r');
+                        break;
+                    case 't':
+                        sb.append('\t');
+                        break;
+                    case 'u': // simple skip unicode (not handled)
+                        sb.append('?');
+                        index += 4;
+                        break;
+                    default:
+                        sb.append(esc);
+                }
+                index++;
+            } else {
+                sb.append(c);
+                index++;
+            }
+        }
+        throw new RuntimeException("Unterminated string");
+    }
+
+    private Number parseNumber() {
+        int start = index;
+        // allow sign, digits, decimal point, scientific notation (basic but 
usable)
+        while (index < json.length()) {
+            char c = json.charAt(index);
+            if (c == '-' || c == '+' || c == '.' || c == 'e' || c == 'E' || 
Character.isDigit(c)) {
+                index++;
+            } else {
+                break;
+            }
+        }
+        String numStr = json.substring(start, index);
+        try {
+            if (numStr.contains(".") || numStr.contains("e") || 
numStr.contains("E")) {
+                return Double.parseDouble(numStr);
+            } else {
+                long val = Long.parseLong(numStr);
+                if (val <= Integer.MAX_VALUE && val >= Integer.MIN_VALUE) {
+                    return (int) val;
+                }
+                return val;
+            }
+        } catch (NumberFormatException e) {
+            throw new RuntimeException("Invalid number: " + numStr);
+        }
+    }
+
+    private Object parseLiteral() {
+        if (json.startsWith("true", index)) {
+            index += 4;
+            return true;
+        }
+        if (json.startsWith("false", index)) {
+            index += 5;
+            return false;
+        }
+        if (json.startsWith("null", index)) {
+            index += 4;
+            return null;
+        }
+        throw new RuntimeException("Unexpected literal");
+    }
+
+    private void skipWhitespace() {
+        while (index < json.length() && 
Character.isWhitespace(json.charAt(index))) {
+            index++;
+        }
+    }
+
+    // 
=========================================================================
+    // JSON Serializer
+    // 
=========================================================================
+
+    /**
+     * Serialize a parsed JSON structure to a formatted JSON string,
+     * including a trailing newline.
+     */
+    public static String toJson(Object value, int indent) {
+        StringBuilder sb = new StringBuilder();
+        writeValue(sb, value, indent, 0);
+        sb.append('\n');
+        return sb.toString();
+    }
+
+    private static void writeValue(StringBuilder sb, Object value, int indent, 
int level) {
+        switch (value) {
+            case null -> sb.append("null");
+            case Boolean b -> sb.append(value);
+            case Number number -> sb.append(numberToString(number));
+            case String s -> writeString(sb, s);
+            case Map map -> writeObject(sb, (Map<String, Object>) value, 
indent, level);
+            case List list -> writeArray(sb, (List<Object>) value, indent, 
level);
+            default ->
+                // Fallback: treat as string
+                writeString(sb, value.toString());
+        }
+    }
+
+    private static void writeObject(StringBuilder sb, Map<String, Object> map, 
int indent, int level) {
+        if (map.isEmpty()) {
+            sb.append("{}");
+            return;
+        }
+        sb.append('{');
+        int childLevel = level + 1;
+        boolean first = true;
+        for (Map.Entry<String, Object> entry : map.entrySet()) {
+            if (!first) {
+                sb.append(',');
+            }
+            first = false;
+            sb.append('\n');
+            indentSpaces(sb, childLevel * indent);
+            writeString(sb, entry.getKey());
+            sb.append(": ");
+            writeValue(sb, entry.getValue(), indent, childLevel);
+        }
+        sb.append('\n');
+        indentSpaces(sb, level * indent);
+        sb.append('}');
+    }
+
+    private static void writeArray(StringBuilder sb, List<Object> list, int 
indent, int level) {
+        if (list.isEmpty()) {
+            sb.append("[]");
+            return;
+        }
+        sb.append('[');
+        int childLevel = level + 1;
+        boolean first = true;
+        for (Object item : list) {
+            if (!first) {
+                sb.append(',');
+            }
+            first = false;
+            sb.append('\n');
+            indentSpaces(sb, childLevel * indent);
+            writeValue(sb, item, indent, childLevel);
+        }
+        sb.append('\n');
+        indentSpaces(sb, level * indent);
+        sb.append(']');
+    }
+
+    /**
+     * Write a JSON-escaped string (with surrounding quotes).
+     */
+    private static void writeString(StringBuilder sb, String str) {
+        sb.append('"');
+        for (int i = 0; i < str.length(); i++) {
+            char c = str.charAt(i);
+            switch (c) {
+                case '"':
+                    sb.append("\\\"");
+                    break;
+                case '\\':
+                    sb.append("\\\\");
+                    break;
+                case '\b':
+                    sb.append("\\b");
+                    break;
+                case '\f':
+                    sb.append("\\f");
+                    break;
+                case '\n':
+                    sb.append("\\n");
+                    break;
+                case '\r':
+                    sb.append("\\r");
+                    break;
+                case '\t':
+                    sb.append("\\t");
+                    break;
+                default:
+                    if (c < 0x20) {
+                        sb.append(String.format("\\u%04x", (int) c));
+                    } else {
+                        sb.append(c);
+                    }
+            }
+        }
+        sb.append('"');
+    }
+
+    /**
+     * Convert a Number to its JSON representation.
+     */
+    private static String numberToString(Number n) {
+        if (n instanceof Double) {
+            double d = n.doubleValue();
+            if (Double.isNaN(d) || Double.isInfinite(d)) {
+                return "null"; // JSON does not support NaN/Infinity
+            }
+        }
+        return n.toString();
+    }
+
+    /**
+     * Append {@code count} space characters.
+     */
+    private static void indentSpaces(StringBuilder sb, int count) {
+        sb.repeat(" ", Math.max(0, count));
+    }

Review Comment:
   `StringBuilder` does not have a `repeat(String, int)` (or similar) API in 
standard Java. This will not compile. Use a supported approach such as 
appending a repeated string (e.g., `sb.append(\" \".repeat(count))`) or a 
simple loop to append spaces.



##########
.github/workflows/native.yml:
##########
@@ -0,0 +1,187 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+name: Native Build
+
+on:
+  push:
+    branches: [ test*, "*.*.*" ]
+  pull_request:
+    branches: [ 2.x, develop, master ]
+    paths:
+      - '.github/workflows/native.yml'
+      - 
'namingserver/src/main/resources/META-INF/native-image/org.apache.seata/seata-namingserver/**'
+  workflow_call:
+    inputs:
+      repository:
+        description: 'Repository to check out'
+        type: string
+        required: false
+        default: ''
+      ref:
+        description: 'Branch or tag to check out'
+        type: string
+        required: false
+        default: ''
+
+concurrency:
+  group: ${{ github.workflow }}-${{ github.event.pull_request.number || 
github.ref }}
+  cancel-in-progress: ${{ github.event_name == 'pull_request' }}
+
+jobs:
+  build:
+    name: build
+    runs-on: ${{ matrix.os }}
+    strategy:
+      fail-fast: false
+      matrix:
+        os: [ ubuntu-24.04, ubuntu-24.04-arm, macos-26, windows-latest ]
+        module: [ namingserver ]
+    env:
+      USERNAME: seata
+      PASSWORD: seata
+    steps:
+      - name: "Checkout"
+        uses: actions/[email protected]
+        with:
+          repository: ${{ inputs.repository || github.repository }}
+          ref: ${{ inputs.ref || github.ref }}
+      - name: "Set up Java JDK"
+        uses: actions/[email protected]
+        with:
+          distribution: 'graalvm'
+          java-version: 25
+      - name: "Print maven version"
+        run: ./mvnw -version
+      - name: "Restore local maven repository cache"
+        uses: actions/cache/[email protected]
+        id: cache-maven-repository
+        with:
+          path: ~/.m2/repository
+          key: ${{ runner.os }}-${{ runner.arch }}-maven-${{ 
hashFiles('**/pom.xml') }}
+          restore-keys: |
+            ${{ runner.os }}-${{ runner.arch }}-maven-
+      - name: "Install dependencies for ${{ matrix.module }} in ${{ matrix.os 
}}"
+        run: ./mvnw -T 4C clean -B -pl ${{ matrix.module }},server install 
-DskipTests -am
+      - name: "Compile native image for ${{ matrix.module }} in ${{ matrix.os 
}}"
+        run: ./mvnw -T 2C clean -B -pl ${{ matrix.module }} package 
-DskipTests -Pnative spring-boot:process-aot native:compile
+      - name: "Upload binaries for ${{ matrix.module }} in ${{ matrix.os }}"
+        uses: actions/[email protected]
+        with:
+          name: ${{ matrix.module }}-native-${{ runner.os }}-${{ runner.arch }}
+          path: |
+            ${{ matrix.module }}/target/seata-*-*
+            !${{ matrix.module }}/target/*.jar
+          if-no-files-found: error
+      - name: "Verify native binary for ${{ matrix.module }} in ${{ matrix.os 
}}"
+        shell: sh

Review Comment:
   The workflow uses POSIX shell (`sh`) and backgrounding (`&`, `$!`) while 
also running on `windows-latest`, and it uses runner labels that may not exist 
(`ubuntu-24.04-arm`, `macos-26`). This is likely to fail on Windows and may 
fail due to invalid runner labels. Consider splitting OS-specific logic (e.g., 
separate jobs or conditional steps for Windows vs Unix), using a 
Windows-compatible shell/commands, and switching to runner labels that are 
known to exist.



##########
test-suite/test-native-namingserver/pom.xml:
##########
@@ -0,0 +1,79 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"; 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
+       xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
https://maven.apache.org/xsd/maven-4.0.0.xsd";>
+       <modelVersion>4.0.0</modelVersion>
+       <parent>
+               <groupId>org.apache.seata</groupId>
+               <artifactId>seata-parent</artifactId>
+               <version>${revision}</version>
+               <relativePath>../../pom.xml</relativePath>
+       </parent>
+       <artifactId>seata-test-native-namingserver</artifactId>
+       <name/>
+       <description/>
+       <url/>
+       <licenses>
+               <license/>
+       </licenses>
+       <developers>
+               <developer/>
+       </developers>
+       <scm>
+               <connection/>
+               <developerConnection/>
+               <tag/>
+               <url/>
+       </scm>
+       <properties>
+               <java.version>17</java.version>
+               
<spring-boot-for-server.version>4.0.6</spring-boot-for-server.version>
+       </properties>
+       <dependencies>
+               <dependency>
+                       <groupId>org.apache.seata</groupId>
+                       <artifactId>seata-common</artifactId>
+                       <version>${revision}</version>
+               </dependency>
+
+               <dependency>
+                       <groupId>org.springframework.boot</groupId>
+                       <artifactId>spring-boot-starter-webmvc</artifactId>
+               </dependency>
+
+               <dependency>
+                       <groupId>org.springframework.boot</groupId>
+                       <artifactId>spring-boot-starter-webmvc-test</artifactId>
+                       <scope>test</scope>
+               </dependency>

Review Comment:
   This module pins `spring-boot-for-server.version` to `4.0.6` and depends on 
`spring-boot-starter-webmvc-test`. As written, this is likely to break 
dependency resolution/builds if those coordinates don’t exist in the project’s 
supported Spring Boot line. Prefer inheriting the Spring Boot version from the 
parent BOM/properties already used by the repo, and use standard test 
dependencies (commonly `spring-boot-starter-test` plus any needed extras) that 
are actually published for that Spring Boot version.



##########
test-suite/test-native-namingserver/pom.xml:
##########
@@ -0,0 +1,79 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"; 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
+       xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
https://maven.apache.org/xsd/maven-4.0.0.xsd";>
+       <modelVersion>4.0.0</modelVersion>
+       <parent>
+               <groupId>org.apache.seata</groupId>
+               <artifactId>seata-parent</artifactId>
+               <version>${revision}</version>
+               <relativePath>../../pom.xml</relativePath>
+       </parent>
+       <artifactId>seata-test-native-namingserver</artifactId>
+       <name/>
+       <description/>
+       <url/>
+       <licenses>
+               <license/>
+       </licenses>
+       <developers>
+               <developer/>
+       </developers>
+       <scm>
+               <connection/>
+               <developerConnection/>
+               <tag/>
+               <url/>
+       </scm>
+       <properties>
+               <java.version>17</java.version>
+               
<spring-boot-for-server.version>4.0.6</spring-boot-for-server.version>
+       </properties>

Review Comment:
   This module pins `spring-boot-for-server.version` to `4.0.6` and depends on 
`spring-boot-starter-webmvc-test`. As written, this is likely to break 
dependency resolution/builds if those coordinates don’t exist in the project’s 
supported Spring Boot line. Prefer inheriting the Spring Boot version from the 
parent BOM/properties already used by the repo, and use standard test 
dependencies (commonly `spring-boot-starter-test` plus any needed extras) that 
are actually published for that Spring Boot version.



##########
.github/workflows/native.yml:
##########
@@ -0,0 +1,187 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+name: Native Build
+
+on:
+  push:
+    branches: [ test*, "*.*.*" ]
+  pull_request:
+    branches: [ 2.x, develop, master ]
+    paths:
+      - '.github/workflows/native.yml'
+      - 
'namingserver/src/main/resources/META-INF/native-image/org.apache.seata/seata-namingserver/**'
+  workflow_call:
+    inputs:
+      repository:
+        description: 'Repository to check out'
+        type: string
+        required: false
+        default: ''
+      ref:
+        description: 'Branch or tag to check out'
+        type: string
+        required: false
+        default: ''
+
+concurrency:
+  group: ${{ github.workflow }}-${{ github.event.pull_request.number || 
github.ref }}
+  cancel-in-progress: ${{ github.event_name == 'pull_request' }}
+
+jobs:
+  build:
+    name: build
+    runs-on: ${{ matrix.os }}
+    strategy:
+      fail-fast: false
+      matrix:
+        os: [ ubuntu-24.04, ubuntu-24.04-arm, macos-26, windows-latest ]
+        module: [ namingserver ]
+    env:
+      USERNAME: seata
+      PASSWORD: seata
+    steps:
+      - name: "Checkout"
+        uses: actions/[email protected]
+        with:
+          repository: ${{ inputs.repository || github.repository }}
+          ref: ${{ inputs.ref || github.ref }}
+      - name: "Set up Java JDK"
+        uses: actions/[email protected]
+        with:
+          distribution: 'graalvm'
+          java-version: 25
+      - name: "Print maven version"
+        run: ./mvnw -version
+      - name: "Restore local maven repository cache"
+        uses: actions/cache/[email protected]
+        id: cache-maven-repository
+        with:
+          path: ~/.m2/repository
+          key: ${{ runner.os }}-${{ runner.arch }}-maven-${{ 
hashFiles('**/pom.xml') }}
+          restore-keys: |
+            ${{ runner.os }}-${{ runner.arch }}-maven-
+      - name: "Install dependencies for ${{ matrix.module }} in ${{ matrix.os 
}}"
+        run: ./mvnw -T 4C clean -B -pl ${{ matrix.module }},server install 
-DskipTests -am
+      - name: "Compile native image for ${{ matrix.module }} in ${{ matrix.os 
}}"
+        run: ./mvnw -T 2C clean -B -pl ${{ matrix.module }} package 
-DskipTests -Pnative spring-boot:process-aot native:compile
+      - name: "Upload binaries for ${{ matrix.module }} in ${{ matrix.os }}"
+        uses: actions/[email protected]
+        with:
+          name: ${{ matrix.module }}-native-${{ runner.os }}-${{ runner.arch }}
+          path: |
+            ${{ matrix.module }}/target/seata-*-*
+            !${{ matrix.module }}/target/*.jar
+          if-no-files-found: error
+      - name: "Verify native binary for ${{ matrix.module }} in ${{ matrix.os 
}}"
+        shell: sh
+        env:
+          CONSOLE_USER_USERNAME: ${{ env.USERNAME }}
+          CONSOLE_USER_PASSWORD: ${{ env.PASSWORD }}
+        run: |
+          rm -rf ./${{ matrix.module }}/target/*.jar
+          ./${{ matrix.module }}/target/seata-${{ matrix.module }}-* &
+          NATIVE_PID=$!
+          echo "$NATIVE_PID" > ./seata-native-${{ matrix.module }}.pid
+          echo "Native binary started with PID=$NATIVE_PID"
+          sleep 10
+          if kill -0 $NATIVE_PID 2>/dev/null; then
+            echo "Native binary is running normally (PID=$NATIVE_PID)"
+          else
+            echo "ERROR: Native binary failed to start or crashed 
(PID=$NATIVE_PID)"
+            exit 1
+          fi
+      - name: "Check port 8081 for ${{ matrix.module }} in ${{ matrix.os }}"
+        shell: sh
+        if: matrix.module == 'namingserver'
+        env:
+          MAX_RETRIES: 6
+          SLEEP_SECONDS: 5
+        run: |
+          NATIVE_PID=$(cat ./seata-native-${{ matrix.module }}.pid)
+          echo "NATIVE_PID=$NATIVE_PID"
+          i=1
+          while [ $i -le $MAX_RETRIES ]; do
+            if curl -sSf http://localhost:8081/naming/v1/health >/dev/null 
2>&1; then
+              echo "Port 8081 is responding"
+              exit 0
+            fi
+            echo "Waiting for port 8081... ($i/$MAX_RETRIES)"
+            sleep $SLEEP_SECONDS
+            i=$((i + 1))
+          done
+          echo "ERROR: Port 8081 is not responding after $((MAX_RETRIES * 
SLEEP_SECONDS)) seconds"
+          exit 1
+      - name: "Start Seata server for ${{ matrix.module }} in ${{ matrix.os }}"
+        if: matrix.module == 'namingserver'
+        env:
+          SEATA_REGISTRY_TYPE: seata
+          SEATA_REGISTRY_SEATA_SERVER_ADDR: 127.0.0.1:8081
+          SEATA_REGISTRY_SEATA_USERNAME: ${{ env.USERNAME }}
+          SEATA_REGISTRY_SEATA_PASSWORD: ${{ env.PASSWORD }}
+          MAX_RETRIES: 12
+          SLEEP_SECONDS: 5
+        shell: sh
+        run: |
+          ./mvnw -T 2C clean -B -pl server spring-boot:run -Prelease-seata-jar 
&
+          SEATA_SERVER_PID=$!
+          echo "$SEATA_SERVER_PID" > ./seata-server.pid
+          echo "Seata server started with PID=$SEATA_SERVER_PID"
+          i=1
+          while [ $i -le $MAX_RETRIES ]; do
+            if curl -sSf http://localhost:8091/health >/dev/null 2>&1; then
+              echo "Port 8091 is responding"
+              exit 0
+            fi
+            echo "Waiting for port 8091... ($i/$MAX_RETRIES)"
+            sleep $SLEEP_SECONDS
+            i=$((i + 1))
+          done
+          echo "ERROR: Port 8091 is not responding after $((MAX_RETRIES * 
SLEEP_SECONDS)) seconds"
+          exit 1
+      - name: "Run native tests for ${{ matrix.module }} in ${{ matrix.os }}"
+        run: ./mvnw -T 2C clean -B test -Ptest-native-${{ matrix.module }} -pl 
test-suite/test-native-${{ matrix.module }}
+      - name: "Stop background processes"
+        if: always()
+        shell: sh
+        run: |
+          for pid_file in ./seata-native-*.pid ./seata-server.pid; do

Review Comment:
   The workflow uses POSIX shell (`sh`) and backgrounding (`&`, `$!`) while 
also running on `windows-latest`, and it uses runner labels that may not exist 
(`ubuntu-24.04-arm`, `macos-26`). This is likely to fail on Windows and may 
fail due to invalid runner labels. Consider splitting OS-specific logic (e.g., 
separate jobs or conditional steps for Windows vs Unix), using a 
Windows-compatible shell/commands, and switching to runner labels that are 
known to exist.



##########
.github/workflows/native.yml:
##########
@@ -0,0 +1,187 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+name: Native Build
+
+on:
+  push:
+    branches: [ test*, "*.*.*" ]
+  pull_request:
+    branches: [ 2.x, develop, master ]
+    paths:
+      - '.github/workflows/native.yml'
+      - 
'namingserver/src/main/resources/META-INF/native-image/org.apache.seata/seata-namingserver/**'
+  workflow_call:
+    inputs:
+      repository:
+        description: 'Repository to check out'
+        type: string
+        required: false
+        default: ''
+      ref:
+        description: 'Branch or tag to check out'
+        type: string
+        required: false
+        default: ''
+
+concurrency:
+  group: ${{ github.workflow }}-${{ github.event.pull_request.number || 
github.ref }}
+  cancel-in-progress: ${{ github.event_name == 'pull_request' }}
+
+jobs:
+  build:
+    name: build
+    runs-on: ${{ matrix.os }}
+    strategy:
+      fail-fast: false
+      matrix:
+        os: [ ubuntu-24.04, ubuntu-24.04-arm, macos-26, windows-latest ]
+        module: [ namingserver ]
+    env:
+      USERNAME: seata
+      PASSWORD: seata
+    steps:
+      - name: "Checkout"
+        uses: actions/[email protected]
+        with:
+          repository: ${{ inputs.repository || github.repository }}
+          ref: ${{ inputs.ref || github.ref }}
+      - name: "Set up Java JDK"
+        uses: actions/[email protected]
+        with:
+          distribution: 'graalvm'
+          java-version: 25
+      - name: "Print maven version"
+        run: ./mvnw -version
+      - name: "Restore local maven repository cache"
+        uses: actions/cache/[email protected]
+        id: cache-maven-repository
+        with:
+          path: ~/.m2/repository
+          key: ${{ runner.os }}-${{ runner.arch }}-maven-${{ 
hashFiles('**/pom.xml') }}
+          restore-keys: |
+            ${{ runner.os }}-${{ runner.arch }}-maven-
+      - name: "Install dependencies for ${{ matrix.module }} in ${{ matrix.os 
}}"
+        run: ./mvnw -T 4C clean -B -pl ${{ matrix.module }},server install 
-DskipTests -am
+      - name: "Compile native image for ${{ matrix.module }} in ${{ matrix.os 
}}"
+        run: ./mvnw -T 2C clean -B -pl ${{ matrix.module }} package 
-DskipTests -Pnative spring-boot:process-aot native:compile
+      - name: "Upload binaries for ${{ matrix.module }} in ${{ matrix.os }}"
+        uses: actions/[email protected]
+        with:
+          name: ${{ matrix.module }}-native-${{ runner.os }}-${{ runner.arch }}
+          path: |
+            ${{ matrix.module }}/target/seata-*-*
+            !${{ matrix.module }}/target/*.jar
+          if-no-files-found: error
+      - name: "Verify native binary for ${{ matrix.module }} in ${{ matrix.os 
}}"
+        shell: sh
+        env:
+          CONSOLE_USER_USERNAME: ${{ env.USERNAME }}
+          CONSOLE_USER_PASSWORD: ${{ env.PASSWORD }}
+        run: |
+          rm -rf ./${{ matrix.module }}/target/*.jar
+          ./${{ matrix.module }}/target/seata-${{ matrix.module }}-* &
+          NATIVE_PID=$!

Review Comment:
   The workflow uses POSIX shell (`sh`) and backgrounding (`&`, `$!`) while 
also running on `windows-latest`, and it uses runner labels that may not exist 
(`ubuntu-24.04-arm`, `macos-26`). This is likely to fail on Windows and may 
fail due to invalid runner labels. Consider splitting OS-specific logic (e.g., 
separate jobs or conditional steps for Windows vs Unix), using a 
Windows-compatible shell/commands, and switching to runner labels that are 
known to exist.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to