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]
