This is an automated email from the ASF dual-hosted git repository.

pengjunzhi pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-hugegraph-doc.git


The following commit(s) were added to refs/heads/master by this push:
     new 5fd9c864 refactor: update release workflow and design new validate 
steps (#427)
5fd9c864 is described below

commit 5fd9c8649b5b7485d2d2c667c60340fee905d584
Author: imbajin <[email protected]>
AuthorDate: Sat Nov 15 10:12:45 2025 +0800

    refactor: update release workflow and design new validate steps (#427)
    
    * chore: update release workflow and .gitignore entries
    
    Bumped default release version to 1.7.0 and updated default GPG user in the 
release workflow. Added installation step for subversion on Ubuntu. Appended 
WARP.md to .gitignore.
    
    * refactor: unify release validation script and add local path support
    
    Deleted validate-release-in-local.sh and enhanced validate-release.sh to 
support both SVN and local directory validation.
    
    Added color-coded output, improved argument handling, and included Java 
version checks for better usability and error reporting.
    
    * refactor: revamp release validation script with enhanced checks V2
    
    Major rewrite of validate-release.sh for Apache HugeGraph, adding modular 
structure, improved logging, error/warning collection, colorized output, and 
comprehensive validation steps for source and binary packages.
    
     New features include dependency checks, GPG key management, license 
compliance, file size and binary checks, version consistency, and automated 
server/toolchain testing.
    
    Usage instructions and help output are expanded for clarity.
    
    * refactor: enhance release validation for multi-arch and license checks V3
    
    Updated CI workflow to support additional OS and architectures (arm64, 
macOS 14). Improved documentation and script usage instructions.
    
    The license header check now covers more file types and excludes 
generated/vendor files. Maven build commands in docs and scripts now use 
'-DskipTests' and '-Dcheckstyle.skip=true' for consistency. Added a detailed 
README for the release validation script.
    
    * refactor: improve validation script error context and reporting V4
    
    Adds contextual error and warning reporting with step and package 
information, enhances license category and header checks, improves version 
consistency logic, and refines summary output with execution time and clearer 
formatting.
    
    These changes make validation results more actionable and easier to 
interpret, especially for multi-package and multi-step validations.
    
    * fix: add JSON to CATEGORY_X license validation
    
    The JSON license was added to the CATEGORY_X regex in the binary package 
validation step to ensure packages with this license are properly flagged 
during release validation.
    
    * Update .github/workflows/validate-release.yml
    
    * Update .github/workflows/validate-release.yml
    
    ---------
    
    Co-authored-by: Peng Junzhi <[email protected]>
---
 .github/workflows/validate-release.yml             |   13 +-
 .gitignore                                         |    1 +
 .../contribution-guidelines/validate-release.md    |    4 +-
 .../contribution-guidelines/validate-release.md    |    4 +-
 dist/README.md                                     |  353 ++++
 dist/validate-release-in-local.sh                  |  356 ----
 dist/validate-release.sh                           | 1712 ++++++++++++++++----
 7 files changed, 1744 insertions(+), 699 deletions(-)

diff --git a/.github/workflows/validate-release.yml 
b/.github/workflows/validate-release.yml
index e25a4404..41460220 100644
--- a/.github/workflows/validate-release.yml
+++ b/.github/workflows/validate-release.yml
@@ -6,11 +6,11 @@ on:
       release_version:
         required: true
         description: svn release version
-        default: '1.5.0'
+        default: '1.7.0'
       gpg_user:
         required: true
         description: current release manager (gpg username)
-        default: 'vgalaxies'
+        default: 'pengjunzhi'
 
   push:
     branches:
@@ -67,6 +67,9 @@ jobs:
           if [[ ${{ matrix.os }} =~ "macos" ]]; then
             brew install svn
           fi
+          if [[ ${{ matrix.os }} =~ "ubuntu" ]]; then
+            sudo apt-get install -y subversion
+          fi
           rm -rf dist/${{ inputs.release_version }}
           svn co ${URL_PREFIX}/${{ inputs.release_version }} dist/${{ 
inputs.release_version }}
 
@@ -241,7 +244,7 @@ jobs:
       - name: 7. Validate Binary Packages
         run: |
           cd dist/${{ inputs.release_version }} || exit
-          CATEGORY_X="\bGPL|\bLGPL|Sleepycat 
License|BSD-4-Clause|\bBCL\b|JSR-275|Amazon Software 
License|\bRSAL\b|\bQPL\b|\bSSPL|\bCPOL|\bNPL1|Creative Commons Non-Commercial"
+          CATEGORY_X="\bGPL|\bLGPL|Sleepycat 
License|BSD-4-Clause|\bBCL\b|JSR-275|Amazon Software 
License|\bRSAL\b|\bQPL\b|\bSSPL|\bCPOL|\bNPL1|Creative Commons 
Non-Commercial|JSON\.org"
           for i in *.tar.gz; do
             if [[ "$i" == *-src.tar.gz ]]; then
               # skip source packages
@@ -347,5 +350,5 @@ jobs:
       matrix:
         # disable java8 because of server
         java_version: ['11']
-        # TODO: support windows-latest or other OS in future
-        os: [ubuntu-latest, macos-latest]
+        # Support multiple OS and architectures (x64 and arm64)
+        os: [ubuntu-latest, ubuntu-24.04-arm, macos-latest, macos-14]
diff --git a/.gitignore b/.gitignore
index 39893e59..86f2983b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -28,3 +28,4 @@ GEMINI.md
 .vscode/settings.json
 .aider*
 .gemini/
+WARP.md
diff --git a/content/cn/docs/contribution-guidelines/validate-release.md 
b/content/cn/docs/contribution-guidelines/validate-release.md
index 86f6a22e..7794a4ba 100644
--- a/content/cn/docs/contribution-guidelines/validate-release.md
+++ b/content/cn/docs/contribution-guidelines/validate-release.md
@@ -125,8 +125,8 @@ PMC 同学请特别注意认真检查 `LICENSE` + `NOTICE` 文件,确保文件
 # 请优先使用/切换到 `java 11` 版本进行后序的编译和运行操作 (注:`Computer` 仅支持 `java >= 11`) 
 # java --version
 
-# 尝试在 Unix 环境下编译测试是否正常 (stage 表示从 stage 仓库拉取依赖)
-mvn clean package -P stage -Dmaven.test.skip=true -Dcheckstyle.skip=true
+# 尝试在 Unix 环境下编译测试是否正常
+mvn clean package -DskipTests -Dcheckstyle.skip=true -P stage
 ```
 
 ##### B. 二进制包
diff --git a/content/en/docs/contribution-guidelines/validate-release.md 
b/content/en/docs/contribution-guidelines/validate-release.md
index 398d79c1..83613d13 100644
--- a/content/en/docs/contribution-guidelines/validate-release.md
+++ b/content/en/docs/contribution-guidelines/validate-release.md
@@ -136,8 +136,8 @@ After decompressing `*hugegraph*src.tar.gz`, Do the 
following checks:
 # prefer to use/switch to `java 11` for the following operations 
(compiling/running) (Note: `Computer` only supports `java >= 11`)
 # java --version
 
-# try to compile in the Unix env to check if it works well
-mvn clean package -P stage -Dmaven.test.skip=true -Dcheckstyle.skip=true
+# try to compile in the Unix env to check if it works well (-P is optional)
+mvn clean package -P stage -DskipTests -Dcheckstyle.skip=true
 ```
 
 ##### B. binary package
diff --git a/dist/README.md b/dist/README.md
new file mode 100644
index 00000000..0ccdb8ce
--- /dev/null
+++ b/dist/README.md
@@ -0,0 +1,353 @@
+# Apache HugeGraph 发版验证脚本
+
+Apache HugeGraph (Incubating) 发布包的自动化验证脚本。
+
+## 概述
+
+`validate-release.sh` 脚本对 Apache HugeGraph 发布包进行全面验证,自动执行 [Apache 
发布政策](https://www.apache.org/legal/release-policy.html) 和 
[孵化器发布检查清单](https://cwiki.apache.org/confluence/display/INCUBATOR/Incubator+Release+Checklist)
 要求的大部分检查。
+
+## 功能特性
+
+- ✅ **自动依赖检查** - 验证所有必需工具(svn、gpg、java、maven 等)
+- ✅ **SHA512 和 GPG 签名验证** - 确保包的完整性和真实性
+- ✅ **许可证合规性验证** - 检查禁止的 ASF Category X 和需要文档化的 Category B 许可证
+- ✅ **详细的许可证错误报告** - 对 Category X 违规显示文件路径、许可证名称和上下文
+- ✅ **包内容验证** - 验证必需文件(LICENSE、NOTICE、DISCLAIMER)
+- ✅ **ASF 许可证头检查** - 验证所有源文件中的许可证头,支持第三方代码文档化
+- ✅ **版本一致性验证** - 验证 Maven `<revision>` 属性与预期发布版本匹配
+- ✅ **多语言项目支持** - 自动跳过 Python 项目(hugegraph-ai)的 Maven 版本检查
+- ✅ **源码包编译** - 编译源码包以验证构建正确性
+- ✅ **运行时测试** - 测试服务器和工具链(loader、tool、hubble)功能
+- ✅ **智能进度跟踪** - 显示实时进度、步骤指示器和执行时间
+- ✅ **上下文化错误报告** - 错误和警告包含步骤、包名和索引编号
+- ✅ **详细日志记录** - 将所有输出保存到带时间戳的日志文件
+- ✅ **全面的错误摘要** - 收集所有错误并在最后显示格式化摘要
+
+## 环境要求
+
+- Java 11(HugeGraph 1.5.0+ 必需)
+- Maven 3.x
+- svn(Subversion 客户端)
+- gpg(用于签名验证的 GnuPG)
+- wget 或 curl
+- 标准 Unix 工具(bash、find、grep、awk、perl 等)
+
+脚本会自动检查所有依赖项,如果缺少任何内容会提供安装说明。
+
+## 使用方法
+
+### 基本用法
+
+```bash
+# 查看帮助信息
+./validate-release.sh --help
+
+# 从 Apache SVN 验证(自动下载发布文件)
+./validate-release.sh <版本号> <apache-用户名>
+
+# 示例
+./validate-release.sh 1.7.0 pengjunzhi
+```
+
+### 高级用法
+
+```bash
+# 从本地目录验证(如果已经下载了文件)
+./validate-release.sh <版本号> <apache-用户名> <本地路径>
+
+# 示例
+./validate-release.sh 1.7.0 pengjunzhi /path/to/downloaded/dist
+
+# 指定 Java 版本(默认:11)
+./validate-release.sh <版本号> <apache-用户名> <本地路径> <java-版本>
+
+# 示例 - 使用 Java 11
+./validate-release.sh 1.7.0 pengjunzhi /path/to/dist 11
+
+# 示例 - SVN 模式使用 Java 11
+./validate-release.sh 1.7.0 pengjunzhi "" 11
+
+# 非交互模式(用于 CI/CD)
+./validate-release.sh --non-interactive 1.7.0 pengjunzhi
+```
+
+### 命令行选项
+
+- `--help`, `-h` - 显示帮助信息并退出
+- `--version`, `-v` - 显示脚本版本并退出
+- `--non-interactive` - 无提示运行(用于 CI/CD 管道)
+
+## 验证步骤
+
+脚本执行以下 9 个验证步骤:
+
+1. **检查依赖项** - 验证所有必需工具已安装并显示版本信息
+2. **准备发布文件** - 从 Apache SVN 下载或使用本地目录
+3. **导入并信任 GPG 密钥** - 导入 KEYS 文件并信任所有公钥
+4. **验证 SHA512 和 GPG 签名** - 验证所有包的校验和和签名
+5. **验证源码包** - 对源码包进行全面检查:
+   - 包命名(包含 "incubating")
+   - 必需文件(LICENSE、NOTICE、DISCLAIMER)
+   - 许可证合规性(禁止 Category X,记录 Category B)
+   - 详细的许可证违规报告(文件路径、许可证名称、上下文)
+   - 无空文件或目录
+   - 文件大小限制(无文件 > 800KB)
+   - 二进制文件文档化(在 LICENSE 中声明)
+   - 所有源文件的许可证头(支持第三方代码文档化)
+   - Maven `<revision>` 属性版本一致性(跳过 Python 项目)
+   - NOTICE 文件版权年份
+   - 源码编译测试
+6. **测试编译的服务器** - 初始化并启动编译的 HugeGraph 服务器
+7. **测试编译的工具链** - 从编译包测试 loader、tool 和 hubble
+8. **验证二进制包** - 检查二进制包的必需文件、licenses 目录和许可证合规性
+9. **测试二进制包** - 从二进制包测试服务器和工具链功能
+
+## 输出结果
+
+### 进度指示器
+
+脚本提供实时进度信息:
+
+```
+━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+    Apache HugeGraph Release Validation v2.0.0
+━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+
+  Version:   1.7.0
+  User:      pengjunzhi
+  Java:      11
+  Mode:      SVN Download
+  Log:       logs/validate-1.7.0-20251115-021742.log
+
+━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+
+━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+Step [1/9]: Check Dependencies
+━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+✓ svn: version 1.14.1
+✓ gpg: gpg (GnuPG) 2.2.41
+✓ java: 11.0.21
+✓ mvn: Apache Maven 3.9.5
+...
+```
+
+### 彩色结果
+
+- ✓ **绿色** - 成功的检查
+- ✗ **红色** - 需要修复的错误
+- ⚠ **黄色** - 需要审查的警告
+- **蓝色** - 步骤标题和进度信息
+
+### 日志文件
+
+所有输出都保存到 `logs/validate-<version>-<timestamp>.log` 以供后续查看。
+
+### 最终摘要
+
+验证结束时,会显示一个全面的摘要,包含执行时间和详细的错误/警告信息:
+
+```
+━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+                    VALIDATION SUMMARY
+━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+
+Execution Time: 6m 34s
+Total Checks:   139
+Passed:         134
+Failed:         3
+Warnings:       2
+
+━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+                        ERRORS
+━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+
+[E1] [Step 8: Validate Binary Packages] [xxxx] contains 1 prohibited ASF 
Category X license(s):
+xxxxx
+
+━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+                       WARNINGS
+━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+xxxx
+━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+
+VALIDATION FAILED
+Log file: logs/validate-1.7.0-20251115-021742.log
+```
+
+## 许可证检查说明
+
+### Category X 许可证(禁止使用)
+
+脚本会严格检查以下 ASF Category X 许可证,发现后会报错并提供详细信息:
+
+- GPL, LGPL 系列
+- Sleepycat License
+- BSD-4-Clause
+- BCL (Binary Code License)
+- JSR-275
+- Amazon Software License
+- RSAL (Reciprocal Public License)
+- QPL (Q Public License)
+- SSPL (Server Side Public License)
+- CPOL (Code Project Open License)
+- NPL1 (Netscape Public License)
+- Creative Commons Non-Commercial
+- **JSON.org** (JSON License)
+
+**错误报告格式:**
+```
+Package 'xxx.tar.gz' contains 1 prohibited ASF Category X license(s):
+    - File: licenses/LICENSE-json.txt
+      License: JSON.org
+      Context: Copyright (c) 2002 JSON.org
+```
+
+### Category B 许可证(需要文档化)
+
+以下许可证会触发警告,提醒检查是否在 LICENSE 文件中正确记录:
+
+- CDDL1, CPL, EPL, IPL, MPL, SPL
+- OSL-3.0
+- UnRAR License
+- Erlang Public License
+- OFL (SIL Open Font License)
+- Ubuntu Font License Version 1.0
+- IPA Font License Agreement v1.0
+- EPL2.0
+- CC-BY (Creative Commons Attribution)
+
+**警告报告格式(简洁):**
+```
+Package 'xxx.tar.gz' contains 2 ASF Category B license(s) - please verify 
documentation
+```
+
+### 许可证头检查
+
+脚本会检查所有源代码文件(Java、Shell、Python、Go、JavaScript、TypeScript、C/C++、Scala、Groovy、Rust、Kotlin、Proto
 等)是否包含 ASF 许可证头。
+
+**第三方代码处理:**
+- 如果源文件没有 ASF 许可证头,脚本会检查该文件是否在 LICENSE 文件中被文档化
+- 支持通过文件名或相对路径匹配
+- 已文档化的第三方代码会被标记为合法并单独统计
+- 只有未文档化且缺少 ASF 头的文件才会报错
+
+## 错误处理
+
+脚本使用**"继续并报告"**方式:
+
+- 不会在第一个错误时退出
+- 收集所有验证错误和警告
+- 在最后显示全面摘要,包含:
+  - 执行总时间
+  - 检查统计(总数、通过、失败、警告)
+  - 带编号和上下文的错误列表
+  - 带编号和上下文的警告列表
+- 退出码 0 = 所有检查通过
+- 退出码 1 = 一个或多个检查失败
+
+每个错误和警告都包含:
+- 编号索引([E1], [E2], [W1], [W2] 等)
+- 步骤上下文(哪个验证步骤)
+- 包名上下文(哪个包)
+- 详细的错误描述
+
+这允许你一次看到所有问题,并能快速定位到具体的失败点。
+
+## 特殊处理
+
+### Python 项目(hugegraph-ai)
+
+- 自动跳过编译步骤
+- 自动跳过 Maven `<revision>` 版本检查
+- 仍然执行其他所有验证(许可证、文件结构等)
+
+### Computer 模块
+
+- 在特殊目录结构下编译(`cd computer && mvn package`)
+- 支持 Java 8 和 Java 11
+
+## 故障排除
+
+### Java 版本不匹配
+
+如果看到 Java 版本错误:
+
+```bash
+# 检查你的 Java 版本
+java -version
+
+# 使用 JAVA_HOME 指定 Java 11
+export JAVA_HOME=/path/to/java11
+export PATH=$JAVA_HOME/bin:$PATH
+```
+
+### GPG 密钥问题
+
+如果 GPG 密钥导入失败:
+
+```bash
+# 手动下载并导入 KEYS
+curl https://downloads.apache.org/incubator/hugegraph/KEYS > KEYS
+gpg --import KEYS
+
+# 信任特定密钥
+gpg --edit-key <user-email>
+# 在 GPG 提示符中,输入: trust, 然后 5, 然后 y, 然后 quit
+
+# 或者信任所有导入的密钥
+for key in $(gpg --no-tty --list-keys --with-colons | awk -F: '/^pub/ {print 
$5}'); do
+    echo -e "5\ny\n" | gpg --batch --command-fd 0 --edit-key "$key" trust
+done
+```
+
+### 权限被拒绝
+
+确保脚本可执行:
+
+```bash
+chmod +x validate-release.sh
+```
+
+### 许可证检查误报
+
+如果合法的第三方代码被标记为缺少许可证头:
+
+1. 确保在根目录的 `LICENSE` 文件中记录了该文件
+2. 记录格式可以是文件名或相对路径
+3. 重新运行验证脚本
+
+示例 LICENSE 文件条目:
+```
+This product bundles ThirdParty.java from XYZ project,
+which is available under a "MIT License".
+For details, see licenses/LICENSE-mit.txt
+```
+
+### 查看详细日志
+
+如果需要更多调试信息:
+
+```bash
+# 查看完整日志
+cat logs/validate-<version>-<timestamp>.log
+
+# 搜索特定错误
+grep "ERROR" logs/validate-*.log
+
+# 查看特定步骤
+grep "Step \[5/9\]" logs/validate-*.log
+```
+
+## 参考文档
+
+- [Apache 发布政策](https://www.apache.org/legal/release-policy.html)
+- 
[孵化器发布检查清单](https://cwiki.apache.org/confluence/display/INCUBATOR/Incubator+Release+Checklist)
+- [Apache 许可证分类](https://www.apache.org/legal/resolved.html)
+- [HugeGraph 
验证发布指南](../content/cn/docs/contribution-guidelines/validate-release.md)
+
+## 贡献
+
+如果发现问题或有改进建议,请:
+
+1. 查看现有问题:https://github.com/apache/incubator-hugegraph-doc/issues
+2. 提交新问题或 pull request
diff --git a/dist/validate-release-in-local.sh 
b/dist/validate-release-in-local.sh
deleted file mode 100755
index e935be69..00000000
--- a/dist/validate-release-in-local.sh
+++ /dev/null
@@ -1,356 +0,0 @@
-#!/usr/bin/env bash
-#
-# 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.
-#
-# This script is used to validate the release package, including:
-# 1. Check the release package name & content
-# 2. Check the release package sha512 & gpg signature
-# 3. Compile the source package & run server & toolchain
-# 4. Run server & toolchain in binary package
-
-# exit when any error occurs
-set -e
-
-# release version (input by committer)
-RELEASE_VERSION=$1 # like 1.2.0
-JAVA_VERSION=$2 # like 11
-USER=$3
-LOCAL_DIST_PATH=$4 # local directory path containing release files
-
-# this URL is only valid during the release process
-SVN_URL_PREFIX="https://dist.apache.org/repos/dist/dev/incubator/hugegraph";
-
-# git release branch (check it carefully)
-#GIT_BRANCH="release-${RELEASE_VERSION}"
-
-RELEASE_VERSION=${RELEASE_VERSION:?"Please input the release version, like 
1.2.0"}
-USER=${USER:-"imbajin"}
-WORK_DIR=$(
-  cd "$(dirname "$0")"
-  pwd
-)
-
-# Use local directory if provided, otherwise use default dist path
-if [[ -n "${LOCAL_DIST_PATH}" ]]; then
-  DIST_DIR="${LOCAL_DIST_PATH}"
-  echo "Using local directory: ${DIST_DIR}"
-else
-  DIST_DIR="${WORK_DIR}/dist/${RELEASE_VERSION}"
-  echo "Using default directory: ${DIST_DIR}"
-fi
-
-# Validate local directory exists
-if [[ ! -d "${DIST_DIR}" ]]; then
-  echo "Error: Directory ${DIST_DIR} does not exist"
-  exit 1
-fi
-
-cd "${WORK_DIR}"
-echo "Current work dir: $(pwd)"
-echo "Release files directory: ${DIST_DIR}"
-
-################################
-# Step 1: Validate Local Directory #
-################################
-cd "${DIST_DIR}"
-echo "Contents of ${DIST_DIR}:"
-ls -lh
-
-##################################################
-# Step 2: Check Environment & Import Public Keys #
-##################################################
-shasum --version 1>/dev/null
-gpg --version 1>/dev/null
-
-wget https://downloads.apache.org/incubator/hugegraph/KEYS
-echo "Import KEYS:" && gpg --import KEYS
-# TODO: how to trust all public keys in gpg list, currently only trust the 
first one
-echo -e "5\ny\n" | gpg --batch --command-fd 0 --edit-key $USER trust
-
-echo "trust all pk"
-for key in $(gpg --no-tty --list-keys --with-colons | awk -F: '/^pub/ {print 
$5}'); do
-  echo -e "5\ny\n" | gpg --batch --command-fd 0 --edit-key "$key" trust
-done
-
-########################################
-# Step 3: Check SHA512 & GPG Signature #
-########################################
-cd "${DIST_DIR}"
-
-for i in *.tar.gz; do
-  echo "$i"
-  shasum -a 512 --check "$i".sha512
-  eval gpg "${GPG_OPT}" --verify "$i".asc "$i"
-done
-
-####################################
-# Step 4: Validate Source Packages #
-####################################
-cd "${DIST_DIR}"
-
-CATEGORY_X="\bGPL|\bLGPL|Sleepycat License|BSD-4-Clause|\bBCL\b|JSR-275|Amazon 
Software License|\bRSAL\b|\bQPL\b|\bSSPL|\bCPOL|\bNPL1|Creative Commons 
Non-Commercial|JSON\.org"
-CATEGORY_B="\bCDDL1|\bCPL|\bEPL|\bIPL|\bMPL|\bSPL|OSL-3.0|UnRAR License|Erlang 
Public License|\bOFL\b|Ubuntu Font License Version 1.0|IPA Font License 
Agreement v1.0|EPL2.0|CC-BY"
-ls -lh ./*.tar.gz
-for i in *src.tar.gz; do
-  echo "$i"
-
-  # 4.1: check the directory name include "incubating"
-  if [[ ! "$i" =~ "incubating" ]]; then
-    echo "The package name $i should include incubating" && exit 1
-  fi
-
-  MODULE_DIR=$(basename "$i" .tar.gz)
-  rm -rf ${MODULE_DIR}
-  tar -xzvf "$i"
-  pushd ${MODULE_DIR}
-  echo "Start to check the package content: ${MODULE_DIR}"
-
-  # 4.2: check the directory include "NOTICE" and "LICENSE" file and 
"DISCLAIMER" file
-  if [[ ! -f "LICENSE" ]]; then
-    echo "The package $i should include LICENSE file" && exit 1
-  fi
-  if [[ ! -f "NOTICE" ]]; then
-    echo "The package $i should include NOTICE file" && exit 1
-  fi
-  if [[ ! -f "DISCLAIMER" ]]; then
-    echo "The package $i should include DISCLAIMER file" && exit 1
-  fi
-
-  # 4.3: ensure doesn't contains ASF CATEGORY X License dependencies in 
LICENSE and NOTICE files
-  COUNT=$(grep -E "$CATEGORY_X" LICENSE NOTICE | wc -l)
-  if [[ $COUNT -ne 0 ]]; then
-     grep -E "$CATEGORY_X" LICENSE NOTICE
-     echo "The package $i shouldn't include invalid ASF category X 
dependencies, but get $COUNT" && exit 1
-  fi
-
-  # 4.4: ensure doesn't contains ASF CATEGORY B License dependencies in 
LICENSE and NOTICE files
-  COUNT=$(grep -E "$CATEGORY_B" LICENSE NOTICE | wc -l)
-  if [[ $COUNT -ne 0 ]]; then
-     grep -E "$CATEGORY_B" LICENSE NOTICE
-     echo "The package $i shouldn't include invalid ASF category B 
dependencies, but get $COUNT" && exit 1
-  fi
-
-  # 4.5: ensure doesn't contains empty directory or file
-  find . -type d -empty | while read -r EMPTY_DIR; do
-    find . -type d -empty
-    echo "The package $i shouldn't include empty directory: $EMPTY_DIR is 
empty" && exit 1
-  done
-  find . -type f -empty | while read -r EMPTY_FILE; do
-    find . -type f -empty
-    echo "The package $i shouldn't include empty file: $EMPTY_FILE is empty" 
&& exit 1
-  done
-
-  # 4.6: ensure any file should less than 800kb
-  find . -type f -size +800k | while read -r FILE; do
-    find . -type f -size +800k
-    echo "The package $i shouldn't include file larger than 800kb: $FILE is 
larger than 800kb" && exit 1
-  done
-
-  # 4.7: ensure all binary files are documented in LICENSE
-  find . -type f | perl -lne 'print if -B' | while read -r BINARY_FILE; do
-    FILE_NAME=$(basename "$BINARY_FILE")
-    if grep -q "$FILE_NAME" LICENSE; then
-      echo "Binary file $BINARY_FILE is documented in LICENSE, please check 
manually"
-    else
-      echo "Error: Binary file $BINARY_FILE is not documented in LICENSE" && 
exit 1
-    fi
-  done
-
-  # 4.8: test compile the packages
-  if [[ ($JAVA_VERSION == 8 && "$i" =~ "hugegraph-computer") ]]; then
-    echo "Skip compile $i module in java8"
-  elif [[ "$i" =~ 'hugegraph-ai' ]]; then
-    echo "Skip compile $i module in all versions"
-  elif [[ "$i" =~ "hugegraph-commons" ]]; then
-    mvn install -DskipTests -Papache-release -ntp -e
-  elif [[ "$i" =~ "hugegraph-computer" ]]; then
-    cd computer
-    mvn install -DskipTests -Papache-release -ntp -e
-  else
-    # TODO: consider using commands that are entirely consistent with building 
binary packages
-    mvn package -DskipTests -Papache-release -ntp -e
-    ls -lh
-  fi
-  popd
-done
-
-###########################################
-# Step 5: Run Compiled Packages of Server #
-###########################################
-cd "${DIST_DIR}"
-
-ls -lh
-pushd 
./*hugegraph-incubating*src/hugegraph-server/*hugegraph*"${RELEASE_VERSION}"
-bin/init-store.sh
-sleep 3
-bin/start-hugegraph.sh
-popd
-
-#######################################################################
-# Step 6: Run Compiled Packages of ToolChain (Loader & Tool & Hubble) #
-#######################################################################
-cd "${DIST_DIR}"
-
-pushd ./*toolchain*src
-ls -lh
-pushd ./*toolchain*"${RELEASE_VERSION}"
-ls -lh
-
-# 6.1: load some data first
-echo "test loader"
-pushd ./*loader*"${RELEASE_VERSION}"
-bin/hugegraph-loader.sh -f ./example/file/struct.json -s 
./example/file/schema.groovy \
-  -g hugegraph
-popd
-
-# 6.2: try some gremlin query & api in tool
-echo "test tool"
-pushd ./*tool*"${RELEASE_VERSION}"
-bin/hugegraph gremlin-execute --script 'g.V().count()'
-bin/hugegraph task-list
-bin/hugegraph backup -t all --directory ./backup-test
-popd
-
-# 6.3: start hubble and connect to server
-echo "test hubble"
-pushd ./*hubble*"${RELEASE_VERSION}"
-# TODO: add hubble doc & test it
-cat conf/hugegraph-hubble.properties
-bin/start-hubble.sh
-bin/stop-hubble.sh
-popd
-
-popd
-popd
-# stop server
-pushd 
./*hugegraph-incubating*src/hugegraph-server/*hugegraph*"${RELEASE_VERSION}"
-bin/stop-hugegraph.sh
-popd
-
-# clear source packages
-#rm -rf ./*src*
-#ls -lh
-
-####################################
-# Step 7: Validate Binary Packages #
-####################################
-cd "${DIST_DIR}"
-
-for i in *.tar.gz; do
-  if [[ "$i" == *-src.tar.gz ]]; then
-    # skip source packages
-    continue
-  fi
-
-  echo "$i"
-
-  # 7.1: check the directory name include "incubating"
-  if [[ ! "$i" =~ "incubating" ]]; then
-    echo "The package name $i should include incubating" && exit 1
-  fi
-
-  MODULE_DIR=$(basename "$i" .tar.gz)
-  rm -rf ${MODULE_DIR}
-  tar -xzvf "$i"
-  pushd ${MODULE_DIR}
-  ls -lh
-  echo "Start to check the package content: ${MODULE_DIR}"
-
-  # 7.2: check root dir include "NOTICE"/"LICENSE"/"DISCLAIMER" files & 
"licenses" dir
-  if [[ ! -f "LICENSE" ]]; then
-    echo "The package $i should include LICENSE file" && exit 1
-  fi
-  if [[ ! -f "NOTICE" ]]; then
-    echo "The package $i should include NOTICE file" && exit 1
-  fi
-  if [[ ! -f "DISCLAIMER" ]]; then
-    echo "The package $i should include DISCLAIMER file" && exit 1
-  fi
-  if [[ ! -d "licenses" ]]; then
-    echo "The package $i should include licenses dir" && exit 1
-  fi
-
-  # 7.3: ensure doesn't contains ASF CATEGORY X License dependencies in 
LICENSE/NOTICE and licenses/* files
-  COUNT=$(grep -r -E "$CATEGORY_X" LICENSE NOTICE licenses | wc -l)
-  if [[ $COUNT -ne 0 ]]; then
-    grep -r -E "$CATEGORY_X" LICENSE NOTICE licenses
-    echo "The package $i shouldn't include invalid ASF category X 
dependencies, but get $COUNT" && exit 1
-  fi
-
-  # 7.4: ensure doesn't contains empty directory or file
-  find . -type d -empty | while read -r EMPTY_DIR; do
-    find . -type d -empty
-    echo "The package $i shouldn't include empty directory: $EMPTY_DIR is 
empty" && exit 1
-  done
-  find . -type f -empty | while read -r EMPTY_FILE; do
-    find . -type f -empty
-    echo "The package $i shouldn't include empty file: $EMPTY_FILE is empty" 
&& exit 1
-  done
-
-  popd
-done
-
-# TODO: skip the following steps by comparing the artifacts built from source 
packages with binary packages
-#########################################
-# Step 8: Run Binary Packages of Server #
-#########################################
-cd "${DIST_DIR}"
-
-# TODO: run pd & store
-pushd 
./*hugegraph-incubating*"${RELEASE_VERSION}"/*hugegraph-server-incubating*"${RELEASE_VERSION}"
-bin/init-store.sh
-sleep 3
-bin/start-hugegraph.sh
-popd
-
-#####################################################################
-# Step 9: Run Binary Packages of ToolChain (Loader & Tool & Hubble) #
-#####################################################################
-cd "${DIST_DIR}"
-
-pushd ./*toolchain*"${RELEASE_VERSION}"
-ls -lh
-
-# 9.1: load some data first
-echo "test loader"
-pushd ./*loader*"${RELEASE_VERSION}"
-bin/hugegraph-loader.sh -f ./example/file/struct.json -s 
./example/file/schema.groovy -g hugegraph
-popd
-
-# 9.2: try some gremlin query & api in tool
-echo "test tool"
-pushd ./*tool*"${RELEASE_VERSION}"
-bin/hugegraph gremlin-execute --script 'g.V().count()'
-bin/hugegraph task-list
-bin/hugegraph backup -t all --directory ./backup-test
-popd
-
-# 9.3: start hubble and connect to server
-echo "test hubble"
-pushd ./*hubble*"${RELEASE_VERSION}"
-# TODO: add hubble doc & test it
-cat conf/hugegraph-hubble.properties
-bin/start-hubble.sh
-bin/stop-hubble.sh
-popd
-
-popd
-# stop server
-pushd 
./*hugegraph-incubating*"${RELEASE_VERSION}"/*hugegraph-server-incubating*"${RELEASE_VERSION}"
-bin/stop-hugegraph.sh
-popd
-
-echo "Finish validate, please check all steps manually again!"
diff --git a/dist/validate-release.sh b/dist/validate-release.sh
index 8df1645f..cf39fdc6 100755
--- a/dist/validate-release.sh
+++ b/dist/validate-release.sh
@@ -1,340 +1,1384 @@
 #!/usr/bin/env bash
+################################################################################
+# Apache HugeGraph Release Validation Script
+################################################################################
 #
-# 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
+# This script validates Apache HugeGraph (Incubating) release packages:
+#   1. Check package integrity (SHA512, GPG signatures)
+#   2. Validate package names and required files
+#   3. Check license compliance (ASF categories)
+#   4. Validate package contents
+#   5. Compile source packages
+#   6. Run server and toolchain tests
 #
-#    http://www.apache.org/licenses/LICENSE-2.0
+# Usage:
+#   validate-release.sh <version> <user> [local-path] [java-version]
+#   validate-release.sh --help
 #
-# 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.
+# Arguments:
+#   version       Release version (e.g., 1.7.0)
+#   user          Apache username for GPG key trust
+#   local-path    (Optional) Local directory containing release files
+#                 If omitted, downloads from Apache SVN
+#   java-version  (Optional) Java version to validate (default: 11)
 #
-# This script is used to validate the release package, including:
-# 1. Check the release package name & content
-# 2. Check the release package sha512 & gpg signature
-# 3. Compile the source package & run server & toolchain
-# 4. Run server & toolchain in binary package
-
-# exit when any error occurs
-set -e
-
-# release version (input by committer)
-RELEASE_VERSION=$1 # like 1.2.0
-JAVA_VERSION=$2 # like 11
-USER=$3
-
-# this URL is only valid during the release process
-SVN_URL_PREFIX="https://dist.apache.org/repos/dist/dev/incubator/hugegraph";
-
-# git release branch (check it carefully)
-#GIT_BRANCH="release-${RELEASE_VERSION}"
-
-RELEASE_VERSION=${RELEASE_VERSION:?"Please input the release version, like 
1.2.0"}
-USER=${USER:-"imbajin"}
-WORK_DIR=$(
-  cd "$(dirname "$0")"
-  pwd
-)
-
-cd "${WORK_DIR}"
-echo "Current work dir: $(pwd)"
-
-################################
-# Step 1: Download SVN Sources #
-################################
-rm -rf "${WORK_DIR}/dist/${RELEASE_VERSION}"
-mkdir -p "${WORK_DIR}/dist/${RELEASE_VERSION}"
-cd "${WORK_DIR}/dist/${RELEASE_VERSION}"
-svn co "${SVN_URL_PREFIX}/${RELEASE_VERSION}" .
-
-##################################################
-# Step 2: Check Environment & Import Public Keys #
-##################################################
-shasum --version 1>/dev/null
-gpg --version 1>/dev/null
-
-wget https://downloads.apache.org/incubator/hugegraph/KEYS
-echo "Import KEYS:" && gpg --import KEYS
-# TODO: how to trust all public keys in gpg list, currently only trust the 
first one
-echo -e "5\ny\n" | gpg --batch --command-fd 0 --edit-key $USER trust
-
-echo "trust all pk"
-for key in $(gpg --no-tty --list-keys --with-colons | awk -F: '/^pub/ {print 
$5}'); do
-  echo -e "5\ny\n" | gpg --batch --command-fd 0 --edit-key "$key" trust
-done
-
-########################################
-# Step 3: Check SHA512 & GPG Signature #
-########################################
-cd "${WORK_DIR}/dist/${RELEASE_VERSION}"
-
-for i in *.tar.gz; do
-  echo "$i"
-  shasum -a 512 --check "$i".sha512
-  eval gpg "${GPG_OPT}" --verify "$i".asc "$i"
-done
-
-####################################
-# Step 4: Validate Source Packages #
-####################################
-cd "${WORK_DIR}/dist/${RELEASE_VERSION}"
-
-CATEGORY_X="\bGPL|\bLGPL|Sleepycat License|BSD-4-Clause|\bBCL\b|JSR-275|Amazon 
Software License|\bRSAL\b|\bQPL\b|\bSSPL|\bCPOL|\bNPL1|Creative Commons 
Non-Commercial|JSON\.org"
-CATEGORY_B="\bCDDL1|\bCPL|\bEPL|\bIPL|\bMPL|\bSPL|OSL-3.0|UnRAR License|Erlang 
Public License|\bOFL\b|Ubuntu Font License Version 1.0|IPA Font License 
Agreement v1.0|EPL2.0|CC-BY"
-ls -lh ./*.tar.gz
-for i in *src.tar.gz; do
-  echo "$i"
-
-  # 4.1: check the directory name include "incubating"
-  if [[ ! "$i" =~ "incubating" ]]; then
-    echo "The package name $i should include incubating" && exit 1
-  fi
-
-  MODULE_DIR=$(basename "$i" .tar.gz)
-  rm -rf ${MODULE_DIR}
-  tar -xzvf "$i"
-  pushd ${MODULE_DIR}
-  echo "Start to check the package content: ${MODULE_DIR}"
-
-  # 4.2: check the directory include "NOTICE" and "LICENSE" file and 
"DISCLAIMER" file
-  if [[ ! -f "LICENSE" ]]; then
-    echo "The package $i should include LICENSE file" && exit 1
-  fi
-  if [[ ! -f "NOTICE" ]]; then
-    echo "The package $i should include NOTICE file" && exit 1
-  fi
-  if [[ ! -f "DISCLAIMER" ]]; then
-    echo "The package $i should include DISCLAIMER file" && exit 1
-  fi
-
-  # 4.3: ensure doesn't contains ASF CATEGORY X License dependencies in 
LICENSE and NOTICE files
-  COUNT=$(grep -E "$CATEGORY_X" LICENSE NOTICE | wc -l)
-  if [[ $COUNT -ne 0 ]]; then
-     grep -E "$CATEGORY_X" LICENSE NOTICE
-     echo "The package $i shouldn't include invalid ASF category X 
dependencies, but get $COUNT" && exit 1
-  fi
-
-  # 4.4: ensure doesn't contains ASF CATEGORY B License dependencies in 
LICENSE and NOTICE files
-  COUNT=$(grep -E "$CATEGORY_B" LICENSE NOTICE | wc -l)
-  if [[ $COUNT -ne 0 ]]; then
-     grep -E "$CATEGORY_B" LICENSE NOTICE
-     echo "The package $i shouldn't include invalid ASF category B 
dependencies, but get $COUNT" && exit 1
-  fi
-
-  # 4.5: ensure doesn't contains empty directory or file
-  find . -type d -empty | while read -r EMPTY_DIR; do
-    find . -type d -empty
-    echo "The package $i shouldn't include empty directory: $EMPTY_DIR is 
empty" && exit 1
-  done
-  find . -type f -empty | while read -r EMPTY_FILE; do
-    find . -type f -empty
-    echo "The package $i shouldn't include empty file: $EMPTY_FILE is empty" 
&& exit 1
-  done
-
-  # 4.6: ensure any file should less than 800kb
-  find . -type f -size +800k | while read -r FILE; do
-    find . -type f -size +800k
-    echo "The package $i shouldn't include file larger than 800kb: $FILE is 
larger than 800kb" && exit 1
-  done
-
-  # 4.7: ensure all binary files are documented in LICENSE
-  find . -type f | perl -lne 'print if -B' | while read -r BINARY_FILE; do
-    FILE_NAME=$(basename "$BINARY_FILE")
-    if grep -q "$FILE_NAME" LICENSE; then
-      echo "Binary file $BINARY_FILE is documented in LICENSE, please check 
manually"
+# Examples:
+#   # Validate from Apache SVN
+#   ./validate-release.sh 1.7.0 pengjunzhi
+#
+#   # Validate from local directory
+#   ./validate-release.sh 1.7.0 pengjunzhi /path/to/dist
+#
+#   # Specify Java version
+#   ./validate-release.sh 1.7.0 pengjunzhi /path/to/dist 11
+#
+################################################################################
+
+# Strict mode - but don't exit on error yet (we collect all errors)
+set -o pipefail
+set -o nounset
+
+################################################################################
+# Configuration Constants
+################################################################################
+
+readonly SCRIPT_VERSION="2.0.0"
+readonly SCRIPT_NAME=$(basename "$0")
+
+# URLs
+readonly 
SVN_URL_PREFIX="https://dist.apache.org/repos/dist/dev/incubator/hugegraph";
+readonly KEYS_URL="https://downloads.apache.org/incubator/hugegraph/KEYS";
+
+# Validation Rules
+readonly MAX_FILE_SIZE="800k"
+readonly SERVER_START_DELAY=3
+readonly SERVICE_HEALTH_TIMEOUT=30
+
+# License Patterns (ASF Category X - Prohibited)
+readonly CATEGORY_X="\bGPL|\bLGPL|Sleepycat 
License|BSD-4-Clause|\bBCL\b|JSR-275|Amazon Software 
License|\bRSAL\b|\bQPL\b|\bSSPL|\bCPOL|\bNPL1|Creative Commons 
Non-Commercial|JSON\.org"
+
+# License Patterns (ASF Category B - Must be documented)
+readonly CATEGORY_B="\bCDDL1|\bCPL|\bEPL|\bIPL|\bMPL|\bSPL|OSL-3.0|UnRAR 
License|Erlang Public License|\bOFL\b|Ubuntu Font License Version 1.0|IPA Font 
License Agreement v1.0|EPL2.0|CC-BY"
+
+# Color Definitions
+readonly RED='\033[0;31m'
+readonly GREEN='\033[0;32m'
+readonly YELLOW='\033[0;33m'
+readonly BLUE='\033[0;34m'
+readonly NC='\033[0m' # No Color
+
+################################################################################
+# Global Variables
+################################################################################
+
+# Script state
+WORK_DIR=""
+LOG_FILE=""
+DIST_DIR=""
+RELEASE_VERSION=""
+USER=""
+LOCAL_DIST_PATH=""
+JAVA_VERSION=11
+NON_INTERACTIVE=0
+
+# Error tracking
+declare -a VALIDATION_ERRORS=()
+declare -a VALIDATION_WARNINGS=()
+TOTAL_CHECKS=0
+PASSED_CHECKS=0
+FAILED_CHECKS=0
+CURRENT_STEP=""
+CURRENT_PACKAGE=""
+
+# Service tracking for cleanup
+SERVER_STARTED=0
+HUBBLE_STARTED=0
+
+# Script execution time tracking
+SCRIPT_START_TIME=0
+
+################################################################################
+# Helper Functions - Output & Logging
+################################################################################
+
+show_usage() {
+    cat << EOF
+Apache HugeGraph Release Validation Script v${SCRIPT_VERSION}
+
+Usage: ${SCRIPT_NAME} <version> <user> [local-path] [java-version]
+       ${SCRIPT_NAME} --help | -h
+       ${SCRIPT_NAME} --version | -v
+
+Validates Apache HugeGraph release packages including:
+  - Package integrity (SHA512, GPG signatures)
+  - License compliance (ASF categories)
+  - Package contents and structure
+  - Compilation and runtime testing
+
+Arguments:
+  version       Release version (e.g., 1.7.0)
+  user          Apache username for GPG key trust
+  local-path    (Optional) Local directory path containing release files
+                If omitted, downloads from Apache SVN
+  java-version  (Optional) Java version to validate (default: 11)
+
+Options:
+  --help, -h            Show this help message
+  --version, -v         Show script version
+  --non-interactive     Run without prompts (for CI/CD)
+
+Examples:
+  # Validate from Apache SVN (downloads files)
+  ${SCRIPT_NAME} 1.7.0 pengjunzhi
+
+  # Validate from local directory
+  ${SCRIPT_NAME} 1.7.0 pengjunzhi /path/to/dist
+
+  # Specify Java version
+  ${SCRIPT_NAME} 1.7.0 pengjunzhi "" 11
+  ${SCRIPT_NAME} 1.7.0 pengjunzhi /path/to/dist 11
+
+  # Non-interactive mode for CI
+  ${SCRIPT_NAME} --non-interactive 1.7.0 pengjunzhi
+
+For more information, visit:
+  https://github.com/apache/incubator-hugegraph-doc/tree/master/dist
+
+EOF
+}
+
+log() {
+    local level=$1
+    shift
+    local message="$*"
+    local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
+    echo "[${timestamp}] [${level}] ${message}" | tee -a 
"${LOG_FILE:-/dev/null}"
+}
+
+info() {
+    echo -e "$*"
+    log "INFO" "$*"
+}
+
+success() {
+    echo -e "${GREEN}✓ $*${NC}"
+    log "SUCCESS" "$*"
+}
+
+warn() {
+    echo -e "${YELLOW}⚠ $*${NC}" >&2
+    log "WARN" "$*"
+}
+
+error() {
+    echo -e "${RED}✗ $*${NC}" >&2
+    log "ERROR" "$*"
+}
+
+print_step() {
+    local step=$1
+    local total=$2
+    local description=$3
+    CURRENT_STEP="Step $step: $description"
+    echo ""
+    echo -e 
"${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
+    echo -e "${BLUE}Step [$step/$total]: $description${NC}"
+    echo -e 
"${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
+    log "STEP" "[$step/$total] $description"
+}
+
+print_progress() {
+    local current=$1
+    local total=$2
+    local item=$3
+    echo -e "  [${current}/${total}] ${item}"
+}
+
+collect_error() {
+    local error_msg="$1"
+    local context=""
+
+    # Build context string
+    if [[ -n "$CURRENT_STEP" ]]; then
+        context="[$CURRENT_STEP]"
+    fi
+
+    if [[ -n "$CURRENT_PACKAGE" ]]; then
+        if [[ -n "$context" ]]; then
+            context="$context [$CURRENT_PACKAGE]"
+        else
+            context="[$CURRENT_PACKAGE]"
+        fi
+    fi
+
+    # Store error with context
+    if [[ -n "$context" ]]; then
+        VALIDATION_ERRORS+=("$context $error_msg")
+    else
+        VALIDATION_ERRORS+=("$error_msg")
+    fi
+
+    FAILED_CHECKS=$((FAILED_CHECKS + 1))
+    error "$error_msg"
+}
+
+collect_warning() {
+    local warning_msg="$1"
+    local context=""
+
+    # Build context string
+    if [[ -n "$CURRENT_STEP" ]]; then
+        context="[$CURRENT_STEP]"
+    fi
+
+    if [[ -n "$CURRENT_PACKAGE" ]]; then
+        if [[ -n "$context" ]]; then
+            context="$context [$CURRENT_PACKAGE]"
+        else
+            context="[$CURRENT_PACKAGE]"
+        fi
+    fi
+
+    # Store warning with context
+    if [[ -n "$context" ]]; then
+        VALIDATION_WARNINGS+=("$context $warning_msg")
     else
-      echo "Error: Binary file $BINARY_FILE is not documented in LICENSE" && 
exit 1
-    fi
-  done
-
-  # 4.8: test compile the packages
-  if [[ ($JAVA_VERSION == 8 && "$i" =~ "hugegraph-computer") ]]; then
-    echo "Skip compile $i module in java8"
-  elif [[ "$i" =~ 'hugegraph-ai' ]]; then
-    echo "Skip compile $i module in all versions"
-  elif [[ "$i" =~ "hugegraph-commons" ]]; then
-    mvn install -DskipTests -Papache-release -ntp -e
-  elif [[ "$i" =~ "hugegraph-computer" ]]; then
-    cd computer
-    mvn install -DskipTests -Papache-release -ntp -e
-  else
-    # TODO: consider using commands that are entirely consistent with building 
binary packages
-    mvn package -DskipTests -Papache-release -ntp -e
-    ls -lh
-  fi
-  popd
-done
-
-###########################################
-# Step 5: Run Compiled Packages of Server #
-###########################################
-cd "${WORK_DIR}/dist/${RELEASE_VERSION}"
-
-ls -lh
-pushd 
./*hugegraph-incubating*src/hugegraph-server/*hugegraph*"${RELEASE_VERSION}"
-bin/init-store.sh
-sleep 3
-bin/start-hugegraph.sh
-popd
-
-#######################################################################
-# Step 6: Run Compiled Packages of ToolChain (Loader & Tool & Hubble) #
-#######################################################################
-cd "${WORK_DIR}/dist/${RELEASE_VERSION}"
-
-pushd ./*toolchain*src
-ls -lh
-pushd ./*toolchain*"${RELEASE_VERSION}"
-ls -lh
-
-# 6.1: load some data first
-echo "test loader"
-pushd ./*loader*"${RELEASE_VERSION}"
-bin/hugegraph-loader.sh -f ./example/file/struct.json -s 
./example/file/schema.groovy \
-  -g hugegraph
-popd
-
-# 6.2: try some gremlin query & api in tool
-echo "test tool"
-pushd ./*tool*"${RELEASE_VERSION}"
-bin/hugegraph gremlin-execute --script 'g.V().count()'
-bin/hugegraph task-list
-bin/hugegraph backup -t all --directory ./backup-test
-popd
-
-# 6.3: start hubble and connect to server
-echo "test hubble"
-pushd ./*hubble*"${RELEASE_VERSION}"
-# TODO: add hubble doc & test it
-cat conf/hugegraph-hubble.properties
-bin/start-hubble.sh
-bin/stop-hubble.sh
-popd
-
-popd
-popd
-# stop server
-pushd 
./*hugegraph-incubating*src/hugegraph-server/*hugegraph*"${RELEASE_VERSION}"
-bin/stop-hugegraph.sh
-popd
-
-# clear source packages
-#rm -rf ./*src*
-#ls -lh
-
-####################################
-# Step 7: Validate Binary Packages #
-####################################
-cd "${WORK_DIR}/dist/${RELEASE_VERSION}"
-
-for i in *.tar.gz; do
-  if [[ "$i" == *-src.tar.gz ]]; then
-    # skip source packages
-    continue
-  fi
-
-  echo "$i"
-
-  # 7.1: check the directory name include "incubating"
-  if [[ ! "$i" =~ "incubating" ]]; then
-    echo "The package name $i should include incubating" && exit 1
-  fi
-
-  MODULE_DIR=$(basename "$i" .tar.gz)
-  rm -rf ${MODULE_DIR}
-  tar -xzvf "$i"
-  pushd ${MODULE_DIR}
-  ls -lh
-  echo "Start to check the package content: ${MODULE_DIR}"
-
-  # 7.2: check root dir include "NOTICE"/"LICENSE"/"DISCLAIMER" files & 
"licenses" dir
-  if [[ ! -f "LICENSE" ]]; then
-    echo "The package $i should include LICENSE file" && exit 1
-  fi
-  if [[ ! -f "NOTICE" ]]; then
-    echo "The package $i should include NOTICE file" && exit 1
-  fi
-  if [[ ! -f "DISCLAIMER" ]]; then
-    echo "The package $i should include DISCLAIMER file" && exit 1
-  fi
-  if [[ ! -d "licenses" ]]; then
-    echo "The package $i should include licenses dir" && exit 1
-  fi
-
-  # 7.3: ensure doesn't contains ASF CATEGORY X License dependencies in 
LICENSE/NOTICE and licenses/* files
-  COUNT=$(grep -r -E "$CATEGORY_X" LICENSE NOTICE licenses | wc -l)
-  if [[ $COUNT -ne 0 ]]; then
-    grep -r -E "$CATEGORY_X" LICENSE NOTICE licenses
-    echo "The package $i shouldn't include invalid ASF category X 
dependencies, but get $COUNT" && exit 1
-  fi
-
-  # 7.4: ensure doesn't contains empty directory or file
-  find . -type d -empty | while read -r EMPTY_DIR; do
-    find . -type d -empty
-    echo "The package $i shouldn't include empty directory: $EMPTY_DIR is 
empty" && exit 1
-  done
-  find . -type f -empty | while read -r EMPTY_FILE; do
-    find . -type f -empty
-    echo "The package $i shouldn't include empty file: $EMPTY_FILE is empty" 
&& exit 1
-  done
-
-  popd
-done
-
-# TODO: skip the following steps by comparing the artifacts built from source 
packages with binary packages
-#########################################
-# Step 8: Run Binary Packages of Server #
-#########################################
-cd "${WORK_DIR}/dist/${RELEASE_VERSION}"
-
-# TODO: run pd & store
-pushd 
./*hugegraph-incubating*"${RELEASE_VERSION}"/*hugegraph-server-incubating*"${RELEASE_VERSION}"
-bin/init-store.sh
-sleep 3
-bin/start-hugegraph.sh
-popd
-
-#####################################################################
-# Step 9: Run Binary Packages of ToolChain (Loader & Tool & Hubble) #
-#####################################################################
-cd "${WORK_DIR}/dist/${RELEASE_VERSION}"
-
-pushd ./*toolchain*"${RELEASE_VERSION}"
-ls -lh
-
-# 9.1: load some data first
-echo "test loader"
-pushd ./*loader*"${RELEASE_VERSION}"
-bin/hugegraph-loader.sh -f ./example/file/struct.json -s 
./example/file/schema.groovy -g hugegraph
-popd
-
-# 9.2: try some gremlin query & api in tool
-echo "test tool"
-pushd ./*tool*"${RELEASE_VERSION}"
-bin/hugegraph gremlin-execute --script 'g.V().count()'
-bin/hugegraph task-list
-bin/hugegraph backup -t all --directory ./backup-test
-popd
-
-# 9.3: start hubble and connect to server
-echo "test hubble"
-pushd ./*hubble*"${RELEASE_VERSION}"
-# TODO: add hubble doc & test it
-cat conf/hugegraph-hubble.properties
-bin/start-hubble.sh
-bin/stop-hubble.sh
-popd
-
-popd
-# stop server
-pushd 
./*hugegraph-incubating*"${RELEASE_VERSION}"/*hugegraph-server-incubating*"${RELEASE_VERSION}"
-bin/stop-hugegraph.sh
-popd
-
-echo "Finish validate, please check all steps manually again!"
+        VALIDATION_WARNINGS+=("$warning_msg")
+    fi
+
+    warn "$warning_msg"
+}
+
+mark_check_passed() {
+    PASSED_CHECKS=$((PASSED_CHECKS + 1))
+}
+
+mark_check_failed() {
+    FAILED_CHECKS=$((FAILED_CHECKS + 1))
+}
+
+################################################################################
+# Helper Functions - System & Environment
+################################################################################
+
+setup_logging() {
+    local log_dir="${WORK_DIR}/logs"
+    mkdir -p "$log_dir"
+    LOG_FILE="$log_dir/validate-${RELEASE_VERSION}-$(date +%Y%m%d-%H%M%S).log"
+
+    info "Logging to: ${LOG_FILE}"
+    log "INIT" "Starting validation for HugeGraph ${RELEASE_VERSION}"
+    log "INIT" "User: ${USER}, Java: ${JAVA_VERSION}"
+}
+
+check_dependencies() {
+    local missing_deps=()
+    local required_commands=("svn" "gpg" "shasum" "mvn" "java" "wget" "tar" 
"curl" "awk" "grep" "find" "perl")
+
+    info "Checking required dependencies..."
+
+    for cmd in "${required_commands[@]}"; do
+        if ! command -v "$cmd" &> /dev/null; then
+            missing_deps+=("$cmd")
+            error "Missing: $cmd"
+        else
+            local version_info
+            case "$cmd" in
+                java)
+                    version_info=$(java -version 2>&1 | head -n1 | cut -d'"' 
-f2)
+                    ;;
+                mvn)
+                    version_info=$(mvn --version 2>&1 | head -n1 | awk '{print 
$3}')
+                    ;;
+                *)
+                    version_info=$($cmd --version 2>&1 | head -n1 || echo 
"installed")
+                    ;;
+            esac
+            success "$cmd: $version_info"
+        fi
+    done
+
+    if [[ ${#missing_deps[@]} -gt 0 ]]; then
+        error "Missing required dependencies: ${missing_deps[*]}"
+        echo ""
+        echo "Please install missing dependencies:"
+        echo "  Ubuntu/Debian: sudo apt-get install ${missing_deps[*]}"
+        echo "  macOS: brew install ${missing_deps[*]}"
+        exit 1
+    fi
+
+    success "All dependencies are installed"
+}
+
+check_java_version() {
+    local required_version=$1
+
+    info "Checking Java version..."
+
+    if ! command -v java &> /dev/null; then
+        collect_error "Java is not installed or not in PATH"
+        return 1
+    fi
+
+    local current_version=$(java -version 2>&1 | head -n 1 | awk -F '"' 
'{print $2}' | awk -F '.' '{print $1}')
+    info "Current Java version: $current_version (Required: 
${required_version})"
+
+    if [[ "$current_version" != "$required_version" ]]; then
+        collect_error "Java version mismatch! Current: Java $current_version, 
Required: Java ${required_version}"
+        collect_error "Please switch to Java ${required_version} before 
running this script"
+        return 1
+    fi
+
+    success "Java version check passed: Java $current_version"
+    mark_check_passed
+    return 0
+}
+
+find_package_dir() {
+    local pattern=$1
+    local base_dir=${2:-"${DIST_DIR}"}
+
+    local found=$(find "$base_dir" -maxdepth 3 -type d -path "$pattern" 
2>/dev/null | head -n1)
+
+    if [[ -z "$found" ]]; then
+        collect_error "Could not find directory matching pattern: $pattern"
+        return 1
+    fi
+
+    echo "$found"
+}
+
+################################################################################
+# Helper Functions - GPG & Signatures
+################################################################################
+
+import_and_trust_gpg_keys() {
+    local user=$1
+
+    info "Downloading KEYS file from ${KEYS_URL}..."
+    if ! wget -q "${KEYS_URL}" -O KEYS; then
+        collect_error "Failed to download KEYS file from ${KEYS_URL}"
+        return 1
+    fi
+    success "KEYS file downloaded"
+
+    info "Importing GPG keys..."
+    local import_output=$(gpg --import KEYS 2>&1)
+    local imported_count=$(echo "$import_output" | grep -c "imported" || echo 
"0")
+
+    if [[ "$imported_count" == "0" ]]; then
+        warn "No new keys imported (may already exist in keyring)"
+    else
+        success "Imported GPG keys"
+    fi
+
+    # Trust specific user key
+    if ! gpg --list-keys "$user" &>/dev/null; then
+        collect_error "User '$user' key not found in imported keys. Please 
verify the username."
+        return 1
+    fi
+
+    info "Trusting GPG key for user: $user"
+    echo -e "5\ny\n" | gpg --batch --command-fd 0 --edit-key "$user" trust 
2>/dev/null
+    success "Trusted key for $user"
+
+    # Trust all imported keys
+    info "Trusting all imported public keys..."
+    local trusted=0
+    for key in $(gpg --no-tty --list-keys --with-colons | awk -F: '/^pub/ 
{print $5}'); do
+        echo -e "5\ny\n" | gpg --batch --command-fd 0 --edit-key "$key" trust 
2>/dev/null
+        trusted=$((trusted + 1))
+    done
+    success "Trusted $trusted GPG keys"
+
+    mark_check_passed
+    return 0
+}
+
+################################################################################
+# Validation Functions - Package Checks
+################################################################################
+
+check_incubating_name() {
+    local package=$1
+    TOTAL_CHECKS=$((TOTAL_CHECKS + 1))
+
+    if [[ ! "$package" =~ "incubating" ]]; then
+        collect_error "Package name '$package' should include 'incubating'"
+        return 1
+    fi
+
+    mark_check_passed
+    return 0
+}
+
+check_required_files() {
+    local package=$1
+    local require_disclaimer=${2:-true}
+    local has_error=0
+
+    if [[ ! -f "LICENSE" ]]; then
+        collect_error "Package '$package' missing LICENSE file"
+        has_error=1
+    else
+        mark_check_passed
+    fi
+
+    if [[ ! -f "NOTICE" ]]; then
+        collect_error "Package '$package' missing NOTICE file"
+        has_error=1
+    else
+        mark_check_passed
+    fi
+
+    if [[ "$require_disclaimer" == "true" ]] && [[ ! -f "DISCLAIMER" ]]; then
+        collect_error "Package '$package' missing DISCLAIMER file"
+        has_error=1
+    else
+        mark_check_passed
+    fi
+
+    return $has_error
+}
+
+check_license_categories() {
+    local package=$1
+    local files=$2
+    local has_error=0
+
+    # Check Category X (Prohibited)
+    TOTAL_CHECKS=$((TOTAL_CHECKS + 1))
+    local cat_x_matches=$(grep -r -E "$CATEGORY_X" $files 2>/dev/null)
+    local cat_x_count=$(echo "$cat_x_matches" | grep -v '^$' | wc -l | tr -d ' 
')
+
+    if [[ $cat_x_count -ne 0 ]]; then
+        # Build detailed error message with license information
+        local error_details="Package '$package' contains $cat_x_count 
prohibited ASF Category X license(s):"
+
+        # Extract and format each violation
+        while IFS= read -r match_line; do
+            if [[ -n "$match_line" ]]; then
+                # Parse file:content format
+                local file_name=$(echo "$match_line" | cut -d':' -f1)
+                local license_info=$(echo "$match_line" | cut -d':' -f2-)
+
+                # Try to extract specific license name
+                local license_name=$(echo "$license_info" | grep -oE 
"$CATEGORY_X" | head -n1)
+
+                error_details="${error_details}\n    - File: ${file_name}\n    
  License: ${license_name}\n      Context: ${license_info}"
+            fi
+        done <<< "$cat_x_matches"
+
+        collect_error "$error_details"
+        has_error=1
+    else
+        mark_check_passed
+    fi
+
+    # Check Category B (Must be documented - warning only)
+    TOTAL_CHECKS=$((TOTAL_CHECKS + 1))
+    local cat_b_count=$(grep -r -E "$CATEGORY_B" $files 2>/dev/null | wc -l | 
tr -d ' ')
+    if [[ $cat_b_count -ne 0 ]]; then
+        collect_warning "Package '$package' contains $cat_b_count ASF Category 
B license(s) - please verify documentation"
+    else
+        mark_check_passed
+    fi
+
+    return $has_error
+}
+
+check_empty_files_and_dirs() {
+    local package=$1
+    local has_error=0
+
+    TOTAL_CHECKS=$((TOTAL_CHECKS + 1))
+
+    # Find empty directories
+    local empty_dirs=()
+    while IFS= read -r empty_dir; do
+        empty_dirs+=("$empty_dir")
+    done < <(find . -type d -empty 2>/dev/null)
+
+    # Find empty files
+    local empty_files=()
+    while IFS= read -r empty_file; do
+        empty_files+=("$empty_file")
+    done < <(find . -type f -empty 2>/dev/null)
+
+    if [[ ${#empty_dirs[@]} -gt 0 ]]; then
+        collect_error "Package '$package' contains ${#empty_dirs[@]} empty 
director(y/ies):"
+        printf '    %s\n' "${empty_dirs[@]}"
+        has_error=1
+    fi
+
+    if [[ ${#empty_files[@]} -gt 0 ]]; then
+        collect_error "Package '$package' contains ${#empty_files[@]} empty 
file(s):"
+        printf '    %s\n' "${empty_files[@]}"
+        has_error=1
+    fi
+
+    if [[ $has_error -eq 0 ]]; then
+        mark_check_passed
+    fi
+
+    return $has_error
+}
+
+check_file_sizes() {
+    local package=$1
+    local max_size=$2
+    local has_error=0
+
+    TOTAL_CHECKS=$((TOTAL_CHECKS + 1))
+
+    local large_files=()
+    while IFS= read -r large_file; do
+        large_files+=("$large_file")
+    done < <(find . -type f -size "+${max_size}" 2>/dev/null)
+
+    if [[ ${#large_files[@]} -gt 0 ]]; then
+        collect_error "Package '$package' contains ${#large_files[@]} file(s) 
larger than ${max_size}:"
+        for file in "${large_files[@]}"; do
+            local size=$(du -h "$file" | awk '{print $1}')
+            echo "    $file ($size)"
+        done
+        has_error=1
+    else
+        mark_check_passed
+    fi
+
+    return $has_error
+}
+
+check_binary_files() {
+    local package=$1
+    local has_error=0
+
+    TOTAL_CHECKS=$((TOTAL_CHECKS + 1))
+
+    info "Checking for undocumented binary files..."
+
+    local binary_count=0
+    local undocumented_count=0
+
+    # Find binary files using perl
+    while IFS= read -r binary_file; do
+        binary_count=$((binary_count + 1))
+        local file_name=$(basename "$binary_file")
+
+        # Check if documented in LICENSE
+        if grep -q "$file_name" LICENSE 2>/dev/null; then
+            success "Binary file '$binary_file' is documented in LICENSE"
+        else
+            collect_error "Undocumented binary file: $binary_file"
+            undocumented_count=$((undocumented_count + 1))
+            has_error=1
+        fi
+    done < <(find . -type f 2>/dev/null | perl -lne 'print if -B $_')
+
+    if [[ $binary_count -eq 0 ]]; then
+        success "No binary files found"
+        mark_check_passed
+    elif [[ $undocumented_count -eq 0 ]]; then
+        success "All $binary_count binary file(s) are documented"
+        mark_check_passed
+    fi
+
+    return $has_error
+}
+
+check_license_headers() {
+    local package=$1
+
+    TOTAL_CHECKS=$((TOTAL_CHECKS + 1))
+
+    info "Checking for ASF license headers in source files..."
+
+    # Define file patterns to check for license headers
+    # Including: Java, Shell scripts, Python, Go, JavaScript, TypeScript, 
C/C++, Scala, Groovy, etc.
+    local -a file_patterns=(
+        "*.java"      # Java files
+        "*.sh"        # Shell scripts
+        "*.py"        # Python files
+        "*.go"        # Go files
+        "*.js"        # JavaScript files
+        "*.ts"        # TypeScript files
+        "*.jsx"       # React JSX files
+        "*.tsx"       # React TypeScript files
+        "*.c"         # C files
+        "*.h"         # C header files
+        "*.cpp"       # C++ files
+        "*.cc"        # C++ files
+        "*.cxx"       # C++ files
+        "*.hpp"       # C++ header files
+        "*.scala"     # Scala files
+        "*.groovy"    # Groovy files
+        "*.gradle"    # Gradle build files
+        "*.rs"        # Rust files
+        "*.kt"        # Kotlin files
+        "*.proto"     # Protocol buffer files
+    )
+
+    # Files to exclude from license header check
+    local -a exclude_patterns=(
+        "*.min.js"              # Minified JavaScript
+        "*.min.css"             # Minified CSS
+        "*node_modules*"        # Node.js dependencies
+        "*target*"              # Maven build output
+        "*build*"               # Build directories
+        "*.pb.go"               # Generated protobuf files
+        "*generated*"           # Generated code
+        "*third_party*"         # Third party code
+        "*vendor*"              # Vendor dependencies
+    )
+
+    local files_without_license=()
+    local total_checked=0
+    local excluded_count=0
+
+    # Build find command with all patterns
+    local find_cmd="find . -type f \\("
+    local first=1
+    for pattern in "${file_patterns[@]}"; do
+        if [[ $first -eq 1 ]]; then
+            find_cmd="$find_cmd -name \"$pattern\""
+            first=0
+        else
+            find_cmd="$find_cmd -o -name \"$pattern\""
+        fi
+    done
+    find_cmd="$find_cmd \\) 2>/dev/null"
+
+    # Check each source file for ASF license header
+    local documented_count=0
+    while IFS= read -r source_file; do
+        # Skip if file matches exclude patterns
+        local should_exclude=0
+        for exclude_pattern in "${exclude_patterns[@]}"; do
+            if [[ "$source_file" == $exclude_pattern ]]; then
+                should_exclude=1
+                excluded_count=$((excluded_count + 1))
+                break
+            fi
+        done
+
+        if [[ $should_exclude -eq 1 ]]; then
+            continue
+        fi
+
+        total_checked=$((total_checked + 1))
+
+        # Check first 30 lines for Apache license header
+        # Looking for the standard ASF license header text
+        if ! head -n 30 "$source_file" | grep -q "Licensed to the Apache 
Software Foundation"; then
+            # No ASF header found - check if it's documented in LICENSE file 
as third-party code
+            local file_name=$(basename "$source_file")
+            local file_path_relative=$(echo "$source_file" | sed 's|^\./||')
+
+            # Check if file name or path is mentioned in LICENSE file
+            if [[ -f "LICENSE" ]] && (grep -q "$file_name" LICENSE 2>/dev/null 
|| grep -q "$file_path_relative" LICENSE 2>/dev/null); then
+                # File is documented in LICENSE as third-party code - this is 
allowed
+                documented_count=$((documented_count + 1))
+            else
+                # Not documented - this is an error
+                files_without_license+=("$source_file")
+            fi
+        fi
+    done < <(eval "$find_cmd")
+
+    # Report results
+    info "Checked $total_checked source file(s) for ASF license headers 
(excluded $excluded_count generated/vendored files)"
+
+    if [[ $documented_count -gt 0 ]]; then
+        info "Found $documented_count source file(s) documented in LICENSE as 
third-party code (allowed)"
+    fi
+
+    if [[ ${#files_without_license[@]} -gt 0 ]]; then
+        collect_error "Found ${#files_without_license[@]} source file(s) 
without ASF license headers:"
+
+        # Show first 20 files without headers (to avoid overwhelming output)
+        local show_count=${#files_without_license[@]}
+        if [[ $show_count -gt 20 ]]; then
+            show_count=20
+        fi
+
+        for ((i=0; i<show_count; i++)); do
+            echo "    ${files_without_license[$i]}"
+        done
+
+        if [[ ${#files_without_license[@]} -gt 20 ]]; then
+            echo "    ... and $((${#files_without_license[@]} - 20)) more 
files"
+        fi
+
+        echo ""
+        collect_error "All source files must include the Apache License header 
or be documented in LICENSE file"
+        collect_error "You can use 'mvn apache-rat:check' for detailed license 
header analysis"
+        return 1
+    else
+        success "All $total_checked source file(s) have ASF license headers or 
are documented in LICENSE"
+        mark_check_passed
+        return 0
+    fi
+}
+
+check_version_consistency() {
+    local package=$1
+    local expected_version=$2
+
+    TOTAL_CHECKS=$((TOTAL_CHECKS + 1))
+
+    # Skip version check for Python projects (hugegraph-ai)
+    if [[ "$package" =~ 'hugegraph-ai' ]]; then
+        info "Skipping version check for Python project: $package"
+        mark_check_passed
+        return 0
+    fi
+
+    info "Checking version consistency (revision property)..."
+
+    # Find the parent/root pom.xml that defines the revision property
+    local root_pom=""
+    local revision_value=""
+
+    # Look for pom.xml files that define the revision property
+    while IFS= read -r pom_file; do
+        if grep -q "<revision>" "$pom_file" 2>/dev/null; then
+            # Extract the revision value
+            revision_value=$(grep "<revision>" "$pom_file" | head -1 | sed 
's/.*<revision>\(.*\)<\/revision>.*/\1/')
+            root_pom="$pom_file"
+            break
+        fi
+    done < <(find . -name "pom.xml" -type f 2>/dev/null)
+
+    if [[ -z "$root_pom" ]]; then
+        collect_warning "No <revision> property found in pom.xml files - 
skipping version check"
+        mark_check_passed
+        return 0
+    fi
+
+    info "Found revision property in $root_pom: 
<revision>$revision_value</revision>"
+
+    # Check if revision matches expected version
+    if [[ "$revision_value" != "$expected_version" ]]; then
+        collect_error "Version mismatch: <revision>$revision_value</revision> 
in $root_pom (expected: $expected_version)"
+        return 1
+    fi
+
+    success "Version consistency check passed: revision=$revision_value"
+    mark_check_passed
+    return 0
+}
+
+check_notice_year() {
+    local package=$1
+
+    TOTAL_CHECKS=$((TOTAL_CHECKS + 1))
+
+    if [[ ! -f "NOTICE" ]]; then
+        return 0  # Already checked in check_required_files
+    fi
+
+    local current_year=$(date +%Y)
+    if ! grep -q "$current_year" NOTICE; then
+        collect_warning "Package '$package': NOTICE file may not contain 
current year ($current_year). Please verify copyright dates."
+    else
+        mark_check_passed
+    fi
+}
+
+################################################################################
+# Main Validation Functions
+################################################################################
+
+validate_source_package() {
+    local package_file=$1
+    local package_dir=$(basename "$package_file" .tar.gz)
+
+    # Set current package context for error reporting
+    CURRENT_PACKAGE="$package_file"
+
+    info "Validating source package: $package_file"
+
+    # Extract package
+    rm -rf "$package_dir"
+    tar -xzf "$package_file"
+
+    if [[ ! -d "$package_dir" ]]; then
+        collect_error "Failed to extract package: $package_file"
+        CURRENT_PACKAGE=""
+        return 1
+    fi
+
+    pushd "$package_dir" > /dev/null
+
+    # Run all checks
+    check_incubating_name "$package_file"
+    check_required_files "$package_file" true
+    check_license_categories "$package_file" "LICENSE NOTICE"
+    check_empty_files_and_dirs "$package_file"
+    check_file_sizes "$package_file" "$MAX_FILE_SIZE"
+    check_binary_files "$package_file"
+    check_license_headers "$package_file"
+    check_version_consistency "$package_file" "$RELEASE_VERSION"
+    check_notice_year "$package_file"
+
+    # Compile check
+    info "Compiling source package: $package_file"
+    TOTAL_CHECKS=$((TOTAL_CHECKS + 1))
+
+    if [[ "$package_file" =~ 'hugegraph-ai' ]]; then
+        warn "Skipping compilation for AI module (not required)"
+        mark_check_passed
+    elif [[ "$package_file" =~ "hugegraph-computer" ]]; then
+        if cd computer 2>/dev/null && mvn clean package -DskipTests 
-Dcheckstyle.skip=true -ntp -e; then
+            success "Compilation successful: $package_file"
+            mark_check_passed
+        else
+            collect_error "Compilation failed: $package_file"
+        fi
+        cd ..
+    else
+        if mvn clean package -DskipTests -Dcheckstyle.skip=true -ntp -e; then
+            success "Compilation successful: $package_file"
+            mark_check_passed
+        else
+            collect_error "Compilation failed: $package_file"
+        fi
+    fi
+
+    popd > /dev/null
+
+    # Clear package context
+    CURRENT_PACKAGE=""
+
+    info "Finished validating source package: $package_file"
+}
+
+validate_binary_package() {
+    local package_file=$1
+    local package_dir=$(basename "$package_file" .tar.gz)
+
+    # Set current package context for error reporting
+    CURRENT_PACKAGE="$package_file"
+
+    info "Validating binary package: $package_file"
+
+    # Extract package
+    rm -rf "$package_dir"
+    tar -xzf "$package_file"
+
+    if [[ ! -d "$package_dir" ]]; then
+        collect_error "Failed to extract package: $package_file"
+        CURRENT_PACKAGE=""
+        return 1
+    fi
+
+    pushd "$package_dir" > /dev/null
+
+    # Run checks
+    check_incubating_name "$package_file"
+    check_required_files "$package_file" true
+
+    # Binary packages should have licenses directory
+    TOTAL_CHECKS=$((TOTAL_CHECKS + 1))
+    if [[ ! -d "licenses" ]]; then
+        collect_error "Package '$package_file' missing licenses directory"
+    else
+        mark_check_passed
+    fi
+
+    check_license_categories "$package_file" "LICENSE NOTICE licenses"
+    check_empty_files_and_dirs "$package_file"
+
+    popd > /dev/null
+
+    # Clear package context
+    CURRENT_PACKAGE=""
+
+    info "Finished validating binary package: $package_file"
+}
+
+################################################################################
+# Cleanup Function
+################################################################################
+
+cleanup() {
+    local exit_code=$?
+
+    log "CLEANUP" "Starting cleanup (exit code: $exit_code)"
+
+    # Stop running services
+    if [[ $SERVER_STARTED -eq 1 ]]; then
+        info "Stopping HugeGraph server..."
+        local server_dir=$(find_package_dir 
"*hugegraph-incubating*src/hugegraph-server/*hugegraph*${RELEASE_VERSION}" 
2>/dev/null || echo "")
+        if [[ -n "$server_dir" ]] && [[ -d "$server_dir" ]]; then
+            pushd "$server_dir" > /dev/null 2>&1
+            bin/stop-hugegraph.sh || true
+            popd > /dev/null 2>&1
+        fi
+    fi
+
+    if [[ $HUBBLE_STARTED -eq 1 ]]; then
+        info "Stopping Hubble..."
+        # Hubble stop is handled in the test flow
+    fi
+
+    # Show final report
+    echo ""
+    echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
+    echo "                    VALIDATION SUMMARY                        "
+    echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
+    echo ""
+
+    # Calculate execution time
+    local script_end_time=$(date +%s)
+    local execution_seconds=$((script_end_time - SCRIPT_START_TIME))
+    local execution_minutes=$((execution_seconds / 60))
+    local execution_seconds_remainder=$((execution_seconds % 60))
+
+    echo "Execution Time: ${execution_minutes}m 
${execution_seconds_remainder}s"
+    echo "Total Checks:   $TOTAL_CHECKS"
+    echo -e "${GREEN}Passed:         $PASSED_CHECKS${NC}"
+    echo -e "${RED}Failed:         $FAILED_CHECKS${NC}"
+    echo -e "${YELLOW}Warnings:       ${#VALIDATION_WARNINGS[@]}${NC}"
+    echo ""
+
+    if [[ ${#VALIDATION_ERRORS[@]} -gt 0 ]]; then
+        echo -e 
"${RED}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
+        echo -e "${RED}                        ERRORS                          
      ${NC}"
+        echo -e 
"${RED}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
+        echo ""
+        local err_index=1
+        for err in "${VALIDATION_ERRORS[@]}"; do
+            echo -e "${RED}[E${err_index}] $err${NC}"
+            echo ""  # Blank line between errors for readability
+            err_index=$((err_index + 1))
+        done
+    fi
+
+    if [[ ${#VALIDATION_WARNINGS[@]} -gt 0 ]]; then
+        echo -e 
"${YELLOW}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
+        echo -e "${YELLOW}                       WARNINGS                      
        ${NC}"
+        echo -e 
"${YELLOW}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
+        echo ""
+        local warn_index=1
+        for warn in "${VALIDATION_WARNINGS[@]}"; do
+            echo -e "${YELLOW}[W${warn_index}] $warn${NC}"
+            echo ""  # Blank line between warnings for readability
+            warn_index=$((warn_index + 1))
+        done
+    fi
+
+    echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
+    echo ""
+
+    if [[ ${#VALIDATION_ERRORS[@]} -gt 0 ]]; then
+        echo -e "${RED}VALIDATION FAILED${NC}"
+        echo -e "Log file: ${LOG_FILE}"
+        echo ""
+        exit 1
+    else
+        echo -e "${GREEN}✓ VALIDATION PASSED${NC}"
+        echo -e "Log file: ${LOG_FILE}"
+        echo ""
+        echo "Please review the validation results and provide feedback in the"
+        echo "release voting thread on the mailing list."
+        echo ""
+        exit 0
+    fi
+}
+
+# Set trap for cleanup
+trap cleanup EXIT
+trap 'echo -e "${RED}Script interrupted${NC}"; exit 130' INT TERM
+
+################################################################################
+# Main Execution
+################################################################################
+
+main() {
+    # Record script start time
+    SCRIPT_START_TIME=$(date +%s)
+
+    # Parse command line arguments
+    while [[ $# -gt 0 ]]; do
+        case $1 in
+            --help|-h)
+                show_usage
+                exit 0
+                ;;
+            --version|-v)
+                echo "Apache HugeGraph Release Validation Script 
v${SCRIPT_VERSION}"
+                exit 0
+                ;;
+            --non-interactive)
+                NON_INTERACTIVE=1
+                shift
+                ;;
+            *)
+                break
+                ;;
+        esac
+    done
+
+    # Parse positional arguments
+    RELEASE_VERSION=${1:-}
+    USER=${2:-}
+    LOCAL_DIST_PATH=${3:-}
+    JAVA_VERSION=${4:-11}
+
+    # Validate required arguments
+    if [[ -z "$RELEASE_VERSION" ]]; then
+        error "Missing required argument: version"
+        echo ""
+        show_usage
+        exit 1
+    fi
+
+    if [[ -z "$USER" ]]; then
+        error "Missing required argument: user"
+        echo ""
+        show_usage
+        exit 1
+    fi
+
+    # Initialize
+    WORK_DIR=$(cd "$(dirname "$0")" && pwd)
+    cd "${WORK_DIR}"
+
+    setup_logging
+
+    echo ""
+    echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
+    echo "    Apache HugeGraph Release Validation v${SCRIPT_VERSION}"
+    echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
+    echo ""
+    echo "  Version:   ${RELEASE_VERSION}"
+    echo "  User:      ${USER}"
+    echo "  Java:      ${JAVA_VERSION}"
+    echo "  Mode:      $([ -n "${LOCAL_DIST_PATH}" ] && echo "Local 
(${LOCAL_DIST_PATH})" || echo "SVN Download")"
+    echo "  Log:       ${LOG_FILE}"
+    echo ""
+    echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
+    echo ""
+
+    ####################################################
+    # Step 1: Check Dependencies
+    ####################################################
+    print_step 1 9 "Check Dependencies"
+    check_dependencies
+    check_java_version "$JAVA_VERSION"
+
+    ####################################################
+    # Step 2: Prepare Release Files
+    ####################################################
+    print_step 2 9 "Prepare Release Files"
+
+    if [[ -n "${LOCAL_DIST_PATH}" ]]; then
+        # Use local directory
+        DIST_DIR="${LOCAL_DIST_PATH}"
+        info "Using local directory: ${DIST_DIR}"
+
+        if [[ ! -d "${DIST_DIR}" ]]; then
+            collect_error "Directory ${DIST_DIR} does not exist"
+            exit 1
+        fi
+
+        info "Contents of ${DIST_DIR}:"
+        ls -lh "${DIST_DIR}"
+    else
+        # Download from SVN
+        DIST_DIR="${WORK_DIR}/dist/${RELEASE_VERSION}"
+        info "Downloading from SVN to: ${DIST_DIR}"
+
+        rm -rf "${DIST_DIR}"
+        mkdir -p "${DIST_DIR}"
+
+        if ! svn co "${SVN_URL_PREFIX}/${RELEASE_VERSION}" "${DIST_DIR}"; then
+            collect_error "Failed to download from SVN: 
${SVN_URL_PREFIX}/${RELEASE_VERSION}"
+            exit 1
+        fi
+
+        success "Downloaded release files from SVN"
+    fi
+
+    cd "${DIST_DIR}"
+
+    ####################################################
+    # Step 3: Import GPG Keys
+    ####################################################
+    print_step 3 9 "Import & Trust GPG Keys"
+    import_and_trust_gpg_keys "$USER"
+
+    ####################################################
+    # Step 4: Check SHA512 & GPG Signatures
+    ####################################################
+    print_step 4 9 "Verify SHA512 & GPG Signatures"
+
+    local package_count=0
+    local packages=()
+    for pkg in *.tar.gz; do
+        if [[ -f "$pkg" ]]; then
+            packages+=("$pkg")
+            package_count=$((package_count + 1))
+        fi
+    done
+
+    local current=0
+    for pkg in "${packages[@]}"; do
+        current=$((current + 1))
+        print_progress $current $package_count "$pkg"
+
+        # Check SHA512
+        TOTAL_CHECKS=$((TOTAL_CHECKS + 1))
+        if shasum -a 512 --check "${pkg}.sha512"; then
+            success "SHA512 verified: $pkg"
+            mark_check_passed
+        else
+            collect_error "SHA512 verification failed: $pkg"
+        fi
+
+        # Check GPG signature
+        TOTAL_CHECKS=$((TOTAL_CHECKS + 1))
+        if gpg --verify "${pkg}.asc" "$pkg" 2>&1 | grep -q "Good signature"; 
then
+            success "GPG signature verified: $pkg"
+            mark_check_passed
+        else
+            collect_error "GPG signature verification failed: $pkg"
+        fi
+    done
+
+    ####################################################
+    # Step 5: Validate Source Packages
+    ####################################################
+    print_step 5 9 "Validate Source Packages"
+
+    local src_packages=()
+    for pkg in *-src.tar.gz; do
+        if [[ -f "$pkg" ]]; then
+            src_packages+=("$pkg")
+        fi
+    done
+
+    info "Found ${#src_packages[@]} source package(s)"
+
+    for src_pkg in "${src_packages[@]}"; do
+        validate_source_package "$src_pkg"
+    done
+
+    ####################################################
+    # Step 6: Run Compiled Packages (Server)
+    ####################################################
+    print_step 6 9 "Test Compiled Server Package"
+
+    local server_dir=$(find_package_dir 
"*hugegraph-incubating*src/hugegraph-server/*hugegraph*${RELEASE_VERSION}")
+    if [[ -n "$server_dir" ]]; then
+        info "Starting HugeGraph server from: $server_dir"
+        pushd "$server_dir" > /dev/null
+
+        if bin/init-store.sh; then
+            success "Store initialized"
+        else
+            collect_error "Failed to initialize store"
+        fi
+
+        sleep $SERVER_START_DELAY
+
+        if bin/start-hugegraph.sh; then
+            success "Server started"
+            SERVER_STARTED=1
+        else
+            collect_error "Failed to start server"
+        fi
+
+        popd > /dev/null
+    else
+        collect_error "Could not find compiled server directory"
+    fi
+
+    ####################################################
+    # Step 7: Test Toolchain (Loader, Tool, Hubble)
+    ####################################################
+    print_step 7 9 "Test Compiled Toolchain Packages"
+
+    local toolchain_src=$(find_package_dir "*toolchain*src")
+    if [[ -n "$toolchain_src" ]]; then
+        pushd "$toolchain_src" > /dev/null
+
+        local toolchain_dir=$(find . -maxdepth 1 -type d -name 
"*toolchain*${RELEASE_VERSION}" | head -n1)
+        if [[ -n "$toolchain_dir" ]]; then
+            pushd "$toolchain_dir" > /dev/null
+
+            # Test Loader
+            info "Testing HugeGraph Loader..."
+            local loader_dir=$(find . -maxdepth 1 -type d -name 
"*loader*${RELEASE_VERSION}" | head -n1)
+            if [[ -n "$loader_dir" ]]; then
+                pushd "$loader_dir" > /dev/null
+                if bin/hugegraph-loader.sh -f ./example/file/struct.json -s 
./example/file/schema.groovy -g hugegraph; then
+                    success "Loader test passed"
+                else
+                    collect_error "Loader test failed"
+                fi
+                popd > /dev/null
+            fi
+
+            # Test Tool
+            info "Testing HugeGraph Tool..."
+            local tool_dir=$(find . -maxdepth 1 -type d -name 
"*tool*${RELEASE_VERSION}" | head -n1)
+            if [[ -n "$tool_dir" ]]; then
+                pushd "$tool_dir" > /dev/null
+                if bin/hugegraph gremlin-execute --script 'g.V().count()' && \
+                   bin/hugegraph task-list && \
+                   bin/hugegraph backup -t all --directory ./backup-test; then
+                    success "Tool test passed"
+                else
+                    collect_error "Tool test failed"
+                fi
+                popd > /dev/null
+            fi
+
+            # Test Hubble
+            info "Testing HugeGraph Hubble..."
+            local hubble_dir=$(find . -maxdepth 1 -type d -name 
"*hubble*${RELEASE_VERSION}" | head -n1)
+            if [[ -n "$hubble_dir" ]]; then
+                pushd "$hubble_dir" > /dev/null
+                if bin/start-hubble.sh; then
+                    HUBBLE_STARTED=1
+                    success "Hubble started"
+                    sleep 2
+                    bin/stop-hubble.sh
+                    HUBBLE_STARTED=0
+                    success "Hubble stopped"
+                else
+                    collect_error "Hubble test failed"
+                fi
+                popd > /dev/null
+            fi
+
+            popd > /dev/null
+        fi
+
+        popd > /dev/null
+    fi
+
+    # Stop server after toolchain tests
+    if [[ $SERVER_STARTED -eq 1 ]] && [[ -n "$server_dir" ]]; then
+        info "Stopping server..."
+        pushd "$server_dir" > /dev/null
+        bin/stop-hugegraph.sh
+        SERVER_STARTED=0
+        success "Server stopped"
+        popd > /dev/null
+    fi
+
+    ####################################################
+    # Step 8: Validate Binary Packages
+    ####################################################
+    print_step 8 9 "Validate Binary Packages"
+
+    cd "${DIST_DIR}"
+
+    local bin_packages=()
+    for pkg in *.tar.gz; do
+        if [[ "$pkg" != *-src.tar.gz ]]; then
+            bin_packages+=("$pkg")
+        fi
+    done
+
+    info "Found ${#bin_packages[@]} binary package(s)"
+
+    for bin_pkg in "${bin_packages[@]}"; do
+        validate_binary_package "$bin_pkg"
+    done
+
+    ####################################################
+    # Step 9: Test Binary Packages
+    ####################################################
+    print_step 9 9 "Test Binary Server & Toolchain"
+
+    # Test binary server
+    local bin_server_dir=$(find_package_dir 
"*hugegraph-incubating*${RELEASE_VERSION}/*hugegraph-server-incubating*${RELEASE_VERSION}")
+    if [[ -n "$bin_server_dir" ]]; then
+        info "Testing binary server package..."
+        pushd "$bin_server_dir" > /dev/null
+
+        if bin/init-store.sh && sleep $SERVER_START_DELAY && 
bin/start-hugegraph.sh; then
+            success "Binary server started"
+            SERVER_STARTED=1
+        else
+            collect_error "Failed to start binary server"
+        fi
+
+        popd > /dev/null
+    fi
+
+    # Test binary toolchain
+    local bin_toolchain=$(find_package_dir "*toolchain*${RELEASE_VERSION}" 
"${DIST_DIR}")
+    if [[ -n "$bin_toolchain" ]]; then
+        pushd "$bin_toolchain" > /dev/null
+
+        # Test binary loader
+        local bin_loader=$(find . -maxdepth 1 -type d -name 
"*loader*${RELEASE_VERSION}" | head -n1)
+        if [[ -n "$bin_loader" ]]; then
+            pushd "$bin_loader" > /dev/null
+            if bin/hugegraph-loader.sh -f ./example/file/struct.json -s 
./example/file/schema.groovy -g hugegraph; then
+                success "Binary loader test passed"
+            else
+                collect_error "Binary loader test failed"
+            fi
+            popd > /dev/null
+        fi
+
+        # Test binary tool
+        local bin_tool=$(find . -maxdepth 1 -type d -name 
"*tool*${RELEASE_VERSION}" | head -n1)
+        if [[ -n "$bin_tool" ]]; then
+            pushd "$bin_tool" > /dev/null
+            if bin/hugegraph gremlin-execute --script 'g.V().count()' && \
+               bin/hugegraph task-list && \
+               bin/hugegraph backup -t all --directory ./backup-test; then
+                success "Binary tool test passed"
+            else
+                collect_error "Binary tool test failed"
+            fi
+            popd > /dev/null
+        fi
+
+        # Test binary hubble
+        local bin_hubble=$(find . -maxdepth 1 -type d -name 
"*hubble*${RELEASE_VERSION}" | head -n1)
+        if [[ -n "$bin_hubble" ]]; then
+            pushd "$bin_hubble" > /dev/null
+            if bin/start-hubble.sh; then
+                HUBBLE_STARTED=1
+                success "Binary hubble started"
+                sleep 2
+                bin/stop-hubble.sh
+                HUBBLE_STARTED=0
+                success "Binary hubble stopped"
+            else
+                collect_error "Binary hubble test failed"
+            fi
+            popd > /dev/null
+        fi
+
+        popd > /dev/null
+    fi
+
+    # Stop binary server
+    if [[ $SERVER_STARTED -eq 1 ]] && [[ -n "$bin_server_dir" ]]; then
+        pushd "$bin_server_dir" > /dev/null
+        bin/stop-hugegraph.sh
+        SERVER_STARTED=0
+        success "Binary server stopped"
+        popd > /dev/null
+    fi
+
+    ####################################################
+    # Validation Complete
+    ####################################################
+    success "All validation steps completed!"
+
+    # Cleanup function will show the final report
+}
+
+# Run main function
+main "$@"


Reply via email to