Skip to content

CI/CD

CI/CD #39

Workflow file for this run

name: Java CI with Maven, Nacos, and Python
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
jobs:
unit-tests:
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- uses: actions/checkout@v4
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'
cache: maven
- name: Run Maven Tests
run: |
set -euo pipefail
mvn -B -ntp clean test
- name: Upload Surefire Reports
if: always()
uses: actions/upload-artifact@v4
with:
name: surefire-reports
path: |
**/target/surefire-reports/*.txt
**/target/surefire-reports/*.xml
go-client-check:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version-file: go_client/go.mod
cache-dependency-path: go_client/go.sum
- name: Verify Go Client Build
working-directory: go_client
run: |
set -euo pipefail
go mod download
go test ./...
go build -o /tmp/go-rpc-client main.go
integration-test:
runs-on: ubuntu-latest
timeout-minutes: 30
needs: [unit-tests, go-client-check]
steps:
- uses: actions/checkout@v4
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'
cache: maven
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.x'
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version-file: go_client/go.mod
cache-dependency-path: go_client/go.sum
- name: Install Python Dependencies
run: |
set -euo pipefail
python -m pip install --upgrade pip
pip install grpcio grpcio-tools protobuf
- name: Build Jars For Integration Test
run: |
set -euo pipefail
mvn -B -ntp -DskipTests clean package
- name: Pre-pull Nacos Image
run: |
set -euo pipefail
for i in 1 2 3; do
echo "Pulling nacos image (attempt ${i}/3)..."
if docker pull nacos/nacos-server:v2.3.1-slim; then
exit 0
fi
sleep 5
done
echo "Failed to pull nacos image after 3 attempts."
exit 1
- name: Start Nacos Container
run: |
set -euo pipefail
docker rm -f nacos-server >/dev/null 2>&1 || true
STARTED=0
for i in 1 2 3; do
echo "Starting nacos container (attempt ${i}/3)..."
if docker run -d \
--name nacos-server \
-e MODE=standalone \
-e AUTH_ENABLE=false \
-e NACOS_AUTH_ENABLE=false \
-p 8848:8848 \
-p 9848:9848 \
nacos/nacos-server:v2.3.1-slim; then
STARTED=1
break
fi
docker rm -f nacos-server >/dev/null 2>&1 || true
sleep 5
done
if [[ "${STARTED}" -ne 1 ]]; then
echo "Failed to start nacos container after 3 attempts."
docker ps -a
exit 1
fi
docker ps --filter "name=nacos-server"
- name: Wait For Nacos Ready
run: |
set -euo pipefail
NACOS_ID="$(docker ps -aqf 'name=^nacos-server$' || true)"
if [[ -z "${NACOS_ID}" ]]; then
echo "Nacos container not found."
docker ps -a
exit 1
fi
echo "Nacos service container id: ${NACOS_ID}"
# Up to 240s total wait (120 * 2s)
for i in {1..120}; do
STATUS=$(docker inspect -f '{{.State.Status}}' "${NACOS_ID}")
HEALTH=$(docker inspect -f '{{if .State.Health}}{{.State.Health.Status}}{{else}}none{{end}}' "${NACOS_ID}")
echo "[${i}/120] nacos status=${STATUS}, health=${HEALTH}"
if [[ "${STATUS}" != "running" ]]; then
echo "Nacos container is not running. Dumping logs..."
docker logs "${NACOS_ID}" | tail -n 300 | tee nacos.log || true
exit 1
fi
if curl -sf "http://localhost:8848/nacos/v1/console/health/readiness" >/dev/null 2>&1 \
&& bash -c 'echo > /dev/tcp/localhost/9848' 2>/dev/null; then
echo "Nacos readiness API and ports 8848/9848 are reachable."
sleep 10
exit 0
fi
sleep 2
done
echo "Timed out waiting for Nacos readiness and ports."
docker logs "${NACOS_ID}" | tail -n 300 | tee nacos.log || true
exit 1
- name: Dump Nacos Service Logs (On Failure)
if: failure()
run: |
set +e
touch nacos.log
echo "Nacos service container id: $(docker ps -aqf 'name=^nacos-server$')"
docker ps -a
docker logs nacos-server > nacos.log 2>&1
tail -n 300 nacos.log
- name: Start RPC Provider (Java Server)
run: |
set -euo pipefail
# Build classpath including project jars and dependencies.
CP_FILE="$(mktemp)"
mvn -q dependency:build-classpath -Dmdep.outputFile="${CP_FILE}" -pl rpc-provider -am
CLASSPATH="rpc-provider/target/rpc-provider-1.0-SNAPSHOT.jar:rpc-transport-netty/target/rpc-transport-netty-1.0-SNAPSHOT.jar:rpc-core/target/rpc-core-1.0-SNAPSHOT.jar:rpc-common/target/rpc-common-1.0-SNAPSHOT.jar:rpc-api/target/rpc-api-1.0-SNAPSHOT.jar:$(cat "${CP_FILE}")"
rm -f "${CP_FILE}"
echo "Starting Java Server..."
echo "Provider protocol override: rpc.protocol=grpc"
# Run in background with nohup
nohup java -Dlogback.configurationFile=rpc-core/src/main/resources/logback.xml -Drpc.protocol=grpc -cp "$CLASSPATH" com.xiaoyu.rpc.provider.ProviderApp > server.log 2>&1 &
# Wait for server port 8080 to be available.
echo "Waiting for Java Server to start on port 8080..."
if timeout 60s bash -c 'until echo > /dev/tcp/localhost/8080; do sleep 1; done'; then
echo "Java Server is running!"
else
echo "Java Server failed to start within 60s. Recent logs:"
tail -n 200 server.log || true
exit 1
fi
# Optional: Print initial logs.
head -n 20 server.log
- name: Wait For Service Registration In Nacos
run: |
set -euo pipefail
echo "Waiting for HelloService registration..."
if timeout 60s bash -c 'until grep -q "Service registered: com.xiaoyu.rpc.api.HelloService" server.log; do sleep 1; done'; then
echo "HelloService registration confirmed."
else
echo "Service registration wait timed out. Recent server logs:"
tail -n 120 server.log || true
exit 1
fi
- name: Run Multi-Client Chains In Parallel
run: |
set -euo pipefail
mkdir -p .ci-status
CP_FILE="$(mktemp)"
mvn -q dependency:build-classpath -Dmdep.outputFile="${CP_FILE}" -pl rpc-consumer -am
CLASSPATH="rpc-consumer/target/rpc-consumer-1.0-SNAPSHOT.jar:rpc-transport-netty/target/rpc-transport-netty-1.0-SNAPSHOT.jar:rpc-core/target/rpc-core-1.0-SNAPSHOT.jar:rpc-common/target/rpc-common-1.0-SNAPSHOT.jar:rpc-api/target/rpc-api-1.0-SNAPSHOT.jar:$(cat "${CP_FILE}")"
rm -f "${CP_FILE}"
run_java() {
echo "Running Java Client (grpc)..."
if timeout 120s java -Dlogback.configurationFile=rpc-core/src/main/resources/logback.xml -Drpc.protocol=grpc -Drpc.serializer=protobuf -cp "$CLASSPATH" com.xiaoyu.rpc.consumer.ConsumerApp | tee java-client.log \
&& grep -q "Result1:" java-client.log \
&& grep -q "Result2:" java-client.log; then
echo "PASS" > .ci-status/java.status
else
echo "Java client chain failed or timed out."
echo "FAIL" > .ci-status/java.status
fi
}
run_python() {
echo "Running Python Client..."
if timeout 120s bash -c 'cd python_client && python client.py' | tee python-client.log \
&& grep -q "Message: Success" python-client.log; then
echo "PASS" > .ci-status/python.status
else
echo "Python client chain failed or timed out."
echo "FAIL" > .ci-status/python.status
fi
}
run_go() {
echo "Running Go Client..."
if timeout 120s bash -c 'cd go_client && go run main.go' | tee go-client.log \
&& grep -q "Message: Success" go-client.log; then
echo "PASS" > .ci-status/go.status
else
echo "Go client chain failed or timed out."
echo "FAIL" > .ci-status/go.status
fi
}
run_java &
PID_JAVA=$!
run_python &
PID_PY=$!
run_go &
PID_GO=$!
wait "${PID_JAVA}" || true
wait "${PID_PY}" || true
wait "${PID_GO}" || true
- name: Aggregate Multi-Client Results
run: |
set -euo pipefail
JAVA_RESULT=$(cat .ci-status/java.status 2>/dev/null || echo "FAIL")
PY_RESULT=$(cat .ci-status/python.status 2>/dev/null || echo "FAIL")
GO_RESULT=$(cat .ci-status/go.status 2>/dev/null || echo "FAIL")
echo "Multi-client chain summary:"
echo " Java : ${JAVA_RESULT}"
echo " Python : ${PY_RESULT}"
echo " Go : ${GO_RESULT}"
if [[ "${JAVA_RESULT}" != "PASS" || "${PY_RESULT}" != "PASS" || "${GO_RESULT}" != "PASS" ]]; then
echo "At least one client chain failed."
exit 1
fi
echo "All three client chains passed."
- name: Upload CI Logs
if: always()
uses: actions/upload-artifact@v4
with:
name: ci-logs
path: |
nacos.log
server.log
java-client.log
python-client.log
go-client.log
.ci-status/*.status