diff --git a/.github/workflows/validate-release-new.yml b/.github/workflows/validate-release-new.yml new file mode 100644 index 000000000..08d678d11 --- /dev/null +++ b/.github/workflows/validate-release-new.yml @@ -0,0 +1,805 @@ +name: "Validate Apache Release (New)" + +on: + workflow_dispatch: + inputs: + release_version: + required: true + description: svn release version + default: '1.7.0' + gpg_user: + required: true + description: current release manager (gpg username) + default: 'pengjunzhi' + java_version: + required: false + description: Java version to validate + default: '11' + type: choice + options: + - '11' + - '17' + + push: + branches: + - 'release-*' + pull_request: + branches: + - 'release-*' + +jobs: + validate: + name: "Validate Release On ${{ matrix.os }} (java-${{ matrix.java_version }})" + runs-on: ${{ matrix.os }} + env: + RELEASE_VERSION: ${{ inputs.release_version || '1.7.0' }} + GPG_USER: ${{ inputs.gpg_user || 'pengjunzhi' }} + JAVA_VERSION: ${{ inputs.java_version || matrix.java_version || '11' }} + SVN_URL_PREFIX: https://dist.apache.org/repos/dist/dev/incubator/hugegraph + KEYS_URL: https://downloads.apache.org/incubator/hugegraph/KEYS + MAX_FILE_SIZE: 800k + SERVER_START_DELAY: 3 + # License Patterns (ASF Category X - Prohibited) + 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) + 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' + steps: + - name: Checkout source + uses: actions/checkout@v4 + + - name: Install JDK ${{ env.JAVA_VERSION }} + uses: actions/setup-java@v3 + with: + java-version: ${{ env.JAVA_VERSION }} + distribution: 'adopt' + + - name: Install dependencies + run: | + if [[ "${{ runner.os }}" == "macOS" ]]; then + brew install svn wget perl + elif [[ "${{ runner.os }}" == "Linux" ]]; then + sudo apt-get update + sudo apt-get install -y subversion wget perl + fi + # Verify all required commands + for cmd in svn gpg shasum mvn java wget tar curl awk grep find perl; do + if ! command -v "$cmd" &> /dev/null; then + echo "Error: Missing required dependency: $cmd" + exit 1 + fi + echo "✓ $cmd: $(command -v $cmd)" + done + + - name: Cache Maven packages + uses: actions/cache@v3 + with: + path: ~/.m2 + key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} + restore-keys: ${{ runner.os }}-m2 + + - name: Step 1 - Check Dependencies + run: | + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo "Step [1/9]: Check Dependencies" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + + # Check Java version + CURRENT_JAVA=$(java -version 2>&1 | head -n 1 | awk -F '"' '{print $2}' | awk -F '.' '{print $1}') + echo "Current Java version: $CURRENT_JAVA (Required: ${{ env.JAVA_VERSION }})" + if [[ "$CURRENT_JAVA" != "${{ env.JAVA_VERSION }}" ]]; then + echo "Error: Java version mismatch! Current: Java $CURRENT_JAVA, Required: Java ${{ env.JAVA_VERSION }}" + exit 1 + fi + echo "✓ Java version check passed: Java $CURRENT_JAVA" + + - name: Step 2 - Prepare Release Files + run: | + echo "" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo "Step [2/9]: Prepare Release Files" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + + DIST_DIR="dist/${{ env.RELEASE_VERSION }}" + echo "Downloading from SVN to: ${DIST_DIR}" + + rm -rf "${DIST_DIR}" + mkdir -p "${DIST_DIR}" + + if ! svn co "${SVN_URL_PREFIX}/${{ env.RELEASE_VERSION }}" "${DIST_DIR}"; then + echo "Error: Failed to download from SVN: ${SVN_URL_PREFIX}/${{ env.RELEASE_VERSION }}" + exit 1 + fi + + echo "✓ Downloaded release files from SVN" + cd "${DIST_DIR}" + ls -lh + + - name: Step 3 - Import & Trust GPG Keys + run: | + echo "" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo "Step [3/9]: Import & Trust GPG Keys" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + + cd dist/${{ env.RELEASE_VERSION }} + + echo "Downloading KEYS file from ${KEYS_URL}..." + if ! wget -q "${KEYS_URL}" -O KEYS; then + echo "Error: Failed to download KEYS file from ${KEYS_URL}" + exit 1 + fi + echo "✓ KEYS file downloaded" + + echo "Importing GPG keys..." + IMPORT_OUTPUT=$(gpg --import KEYS 2>&1) + IMPORTED_COUNT=$(echo "$IMPORT_OUTPUT" | grep -c "imported" || echo "0") + + if [[ "$IMPORTED_COUNT" == "0" ]]; then + echo "⚠ No new keys imported (may already exist in keyring)" + else + echo "✓ Imported GPG keys" + fi + + # Trust specific user key + if ! gpg --list-keys "${{ env.GPG_USER }}" &>/dev/null; then + echo "Error: User '${{ env.GPG_USER }}' key not found in imported keys. Please verify the username." + exit 1 + fi + + echo "Trusting GPG key for user: ${{ env.GPG_USER }}" + echo -e "5\ny\n" | gpg --batch --command-fd 0 --edit-key "${{ env.GPG_USER }}" trust 2>/dev/null + echo "✓ Trusted key for ${{ env.GPG_USER }}" + + # Trust all imported keys + echo "Trusting all imported public keys..." + 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 + echo "✓ Trusted $TRUSTED GPG keys" + + - name: Step 4 - Verify SHA512 & GPG Signatures + run: | + echo "" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo "Step [4/9]: Verify SHA512 & GPG Signatures" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + + cd dist/${{ env.RELEASE_VERSION }} + + PACKAGE_COUNT=0 + for pkg in *.tar.gz; do + if [[ -f "$pkg" ]]; then + PACKAGE_COUNT=$((PACKAGE_COUNT + 1)) + fi + done + + CURRENT=0 + for pkg in *.tar.gz; do + if [[ ! -f "$pkg" ]]; then + continue + fi + CURRENT=$((CURRENT + 1)) + echo " [${CURRENT}/${PACKAGE_COUNT}] $pkg" + + # Check SHA512 + if shasum -a 512 --check "${pkg}.sha512"; then + echo " ✓ SHA512 verified: $pkg" + else + echo " ✗ SHA512 verification failed: $pkg" + exit 1 + fi + + # Check GPG signature + if gpg --verify "${pkg}.asc" "$pkg" 2>&1 | grep -q "Good signature"; then + echo " ✓ GPG signature verified: $pkg" + else + echo " ✗ GPG signature verification failed: $pkg" + exit 1 + fi + done + + - name: Step 5 - Validate Source Packages + run: | + echo "" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo "Step [5/9]: Validate Source Packages" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + + cd dist/${{ env.RELEASE_VERSION }} + + SRC_PACKAGES=() + for pkg in *-src.tar.gz; do + if [[ -f "$pkg" ]]; then + SRC_PACKAGES+=("$pkg") + fi + done + + echo "Found ${#SRC_PACKAGES[@]} source package(s)" + + for src_pkg in "${SRC_PACKAGES[@]}"; do + echo "" + echo "Validating source package: $src_pkg" + + # Extract package + PACKAGE_DIR=$(basename "$src_pkg" .tar.gz) + rm -rf "$PACKAGE_DIR" + tar -xzf "$src_pkg" + + if [[ ! -d "$PACKAGE_DIR" ]]; then + echo "Error: Failed to extract package: $src_pkg" + exit 1 + fi + + pushd "$PACKAGE_DIR" + + # 5.1: Check incubating name + if [[ ! "$src_pkg" =~ "incubating" ]]; then + echo "Error: Package name '$src_pkg' should include 'incubating'" + exit 1 + fi + echo " ✓ Package name includes 'incubating'" + + # 5.2: Check required files + if [[ ! -f "LICENSE" ]]; then + echo "Error: Package '$src_pkg' missing LICENSE file" + exit 1 + fi + echo " ✓ LICENSE file exists" + + if [[ ! -f "NOTICE" ]]; then + echo "Error: Package '$src_pkg' missing NOTICE file" + exit 1 + fi + echo " ✓ NOTICE file exists" + + if [[ ! -f "DISCLAIMER" ]]; then + echo "Error: Package '$src_pkg' missing DISCLAIMER file" + exit 1 + fi + echo " ✓ DISCLAIMER file exists" + + # 5.3: Check license categories (Category X - Prohibited) + CAT_X_MATCHES=$(grep -r -E "${{ env.CATEGORY_X }}" LICENSE NOTICE 2>/dev/null || true) + CAT_X_COUNT=$(echo "$CAT_X_MATCHES" | grep -v '^$' | wc -l | tr -d ' ') + + if [[ $CAT_X_COUNT -ne 0 ]]; then + echo "Error: Package '$src_pkg' contains $CAT_X_COUNT prohibited ASF Category X license(s):" + echo "$CAT_X_MATCHES" + exit 1 + fi + echo " ✓ No Category X licenses found" + + # 5.4: Check license categories (Category B - Warning) + CAT_B_COUNT=$(grep -r -E "${{ env.CATEGORY_B }}" LICENSE NOTICE 2>/dev/null | wc -l | tr -d ' ' || echo "0") + if [[ $CAT_B_COUNT -ne 0 ]]; then + echo " ⚠ Warning: Package '$src_pkg' contains $CAT_B_COUNT ASF Category B license(s) - please verify documentation" + else + echo " ✓ No Category B licenses found" + fi + + # 5.5: Check empty files and directories + EMPTY_DIRS=$(find . -type d -empty 2>/dev/null || true) + EMPTY_FILES=$(find . -type f -empty 2>/dev/null || true) + + if [[ -n "$EMPTY_DIRS" ]]; then + echo "Error: Package '$src_pkg' contains empty director(y/ies):" + echo "$EMPTY_DIRS" + exit 1 + fi + + if [[ -n "$EMPTY_FILES" ]]; then + echo "Error: Package '$src_pkg' contains empty file(s):" + echo "$EMPTY_FILES" + exit 1 + fi + echo " ✓ No empty files or directories" + + # 5.6: Check file sizes + LARGE_FILES=$(find . -type f -size "+${{ env.MAX_FILE_SIZE }}" 2>/dev/null || true) + if [[ -n "$LARGE_FILES" ]]; then + echo "Error: Package '$src_pkg' contains file(s) larger than ${{ env.MAX_FILE_SIZE }}:" + echo "$LARGE_FILES" + exit 1 + fi + echo " ✓ All files are within size limit" + + # 5.7: Check binary files + BINARY_COUNT=0 + UNDOCUMENTED_COUNT=0 + while IFS= read -r binary_file; do + BINARY_COUNT=$((BINARY_COUNT + 1)) + FILE_NAME=$(basename "$binary_file") + if ! grep -q "$FILE_NAME" LICENSE 2>/dev/null; then + echo "Error: Undocumented binary file: $binary_file" + UNDOCUMENTED_COUNT=$((UNDOCUMENTED_COUNT + 1)) + fi + done < <(find . -type f 2>/dev/null | perl -lne 'print if -B $_' || true) + + if [[ $BINARY_COUNT -eq 0 ]]; then + echo " ✓ No binary files found" + elif [[ $UNDOCUMENTED_COUNT -eq 0 ]]; then + echo " ✓ All $BINARY_COUNT binary file(s) are documented" + else + echo "Error: Found $UNDOCUMENTED_COUNT undocumented binary file(s)" + exit 1 + fi + + # 5.8: Check license headers in source files + echo " Checking for ASF license headers in source files..." + + # Define file patterns to check + FILE_PATTERNS=("*.java" "*.sh" "*.py" "*.go" "*.js" "*.ts" "*.jsx" "*.tsx" "*.c" "*.h" "*.cpp" "*.cc" "*.cxx" "*.hpp" "*.scala" "*.groovy" "*.gradle" "*.rs" "*.kt" "*.proto") + + # Files to exclude + EXCLUDE_PATTERNS=("*.min.js" "*.min.css" "*node_modules*" "*target*" "*build*" "*.pb.go" "*generated*" "*third_party*" "*vendor*") + + FILES_WITHOUT_LICENSE=() + TOTAL_CHECKED=0 + EXCLUDED_COUNT=0 + DOCUMENTED_COUNT=0 + + # Build find command + FIND_CMD="find . -type f \\(" + 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 + while IFS= read -r source_file; do + # Skip if file matches exclude patterns + 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 + if ! head -n 30 "$source_file" | grep -q "Licensed to the Apache Software Foundation"; then + # Check if documented in LICENSE file + FILE_NAME=$(basename "$source_file") + FILE_PATH_RELATIVE=$(echo "$source_file" | sed 's|^\./||') + + if [[ -f "LICENSE" ]] && (grep -q "$FILE_NAME" LICENSE 2>/dev/null || grep -q "$FILE_PATH_RELATIVE" LICENSE 2>/dev/null); then + DOCUMENTED_COUNT=$((DOCUMENTED_COUNT + 1)) + else + FILES_WITHOUT_LICENSE+=("$source_file") + fi + fi + done < <(eval "$FIND_CMD") + + echo " Checked $TOTAL_CHECKED source file(s) for ASF license headers (excluded $EXCLUDED_COUNT generated/vendored files)" + + if [[ $DOCUMENTED_COUNT -gt 0 ]]; then + echo " Found $DOCUMENTED_COUNT source file(s) documented in LICENSE as third-party code (allowed)" + fi + + if [[ ${#FILES_WITHOUT_LICENSE[@]} -gt 0 ]]; then + echo "Error: Found ${#FILES_WITHOUT_LICENSE[@]} source file(s) without ASF license headers:" + SHOW_COUNT=${#FILES_WITHOUT_LICENSE[@]} + if [[ $SHOW_COUNT -gt 20 ]]; then + SHOW_COUNT=20 + fi + for ((i=0; i" "$pom_file" 2>/dev/null; then + REVISION_VALUE=$(grep "" "$pom_file" | head -1 | sed 's/.*\(.*\)<\/revision>.*/\1/') + ROOT_POM="$pom_file" + break + fi + done < <(find . -name "pom.xml" -type f 2>/dev/null) + + if [[ -n "$ROOT_POM" ]]; then + echo " Found revision property in $ROOT_POM: $REVISION_VALUE" + if [[ "$REVISION_VALUE" != "${{ env.RELEASE_VERSION }}" ]]; then + echo "Error: Version mismatch: $REVISION_VALUE in $ROOT_POM (expected: ${{ env.RELEASE_VERSION }})" + exit 1 + fi + echo " ✓ Version consistency check passed: revision=$REVISION_VALUE" + else + echo " ⚠ Warning: No property found in pom.xml files - skipping version check" + fi + else + echo " Skipping version check for Python project: $src_pkg" + fi + + # 5.10: Check NOTICE year + if [[ -f "NOTICE" ]]; then + CURRENT_YEAR=$(date +%Y) + if ! grep -q "$CURRENT_YEAR" NOTICE; then + echo " ⚠ Warning: NOTICE file may not contain current year ($CURRENT_YEAR). Please verify copyright dates." + else + echo " ✓ NOTICE file contains current year" + fi + fi + + # 5.11: Compile source package + echo " Compiling source package: $src_pkg" + + if [[ "$src_pkg" =~ 'hugegraph-ai' ]]; then + echo " ⚠ Skipping compilation for AI module (not required)" + elif [[ "$src_pkg" =~ "hugegraph-computer" ]]; then + if cd computer 2>/dev/null && mvn clean package -DskipTests -Dcheckstyle.skip=true -ntp -e; then + echo " ✓ Compilation successful: $src_pkg" + else + echo "Error: Compilation failed: $src_pkg" + exit 1 + fi + cd .. + else + if mvn clean package -DskipTests -Dcheckstyle.skip=true -ntp -e; then + echo " ✓ Compilation successful: $src_pkg" + else + echo "Error: Compilation failed: $src_pkg" + exit 1 + fi + fi + + popd + echo "✓ Finished validating source package: $src_pkg" + done + + - name: Step 6 - Test Compiled Server Package + run: | + echo "" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo "Step [6/9]: Test Compiled Server Package" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + + cd dist/${{ env.RELEASE_VERSION }} + + # Find server directory + SERVER_DIR=$(find . -maxdepth 3 -type d -path "*hugegraph-incubating*src/hugegraph-server/*hugegraph*${{ env.RELEASE_VERSION }}" 2>/dev/null | head -n1) + + if [[ -z "$SERVER_DIR" ]]; then + echo "Error: Could not find compiled server directory" + exit 1 + fi + + echo "Starting HugeGraph server from: $SERVER_DIR" + pushd "$SERVER_DIR" + + if bin/init-store.sh; then + echo " ✓ Store initialized" + else + echo "Error: Failed to initialize store" + exit 1 + fi + + sleep ${{ env.SERVER_START_DELAY }} + + if bin/start-hugegraph.sh; then + echo " ✓ Server started" + else + echo "Error: Failed to start server" + exit 1 + fi + + popd + + - name: Step 7 - Test Compiled Toolchain Packages + run: | + echo "" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo "Step [7/9]: Test Compiled Toolchain Packages" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + + cd dist/${{ env.RELEASE_VERSION }} + + TOOLCHAIN_SRC=$(find . -maxdepth 3 -type d -path "*toolchain*src" 2>/dev/null | head -n1) + + if [[ -n "$TOOLCHAIN_SRC" ]]; then + pushd "$TOOLCHAIN_SRC" + + TOOLCHAIN_DIR=$(find . -maxdepth 1 -type d -name "*toolchain*${{ env.RELEASE_VERSION }}" | head -n1) + if [[ -n "$TOOLCHAIN_DIR" ]]; then + pushd "$TOOLCHAIN_DIR" + + # Test Loader + echo "Testing HugeGraph Loader..." + LOADER_DIR=$(find . -maxdepth 1 -type d -name "*loader*${{ env.RELEASE_VERSION }}" | head -n1) + if [[ -n "$LOADER_DIR" ]]; then + pushd "$LOADER_DIR" + if bin/hugegraph-loader.sh -f ./example/file/struct.json -s ./example/file/schema.groovy -g hugegraph; then + echo " ✓ Loader test passed" + else + echo "Error: Loader test failed" + exit 1 + fi + popd + fi + + # Test Tool + echo "Testing HugeGraph Tool..." + TOOL_DIR=$(find . -maxdepth 1 -type d -name "*tool*${{ env.RELEASE_VERSION }}" | head -n1) + if [[ -n "$TOOL_DIR" ]]; then + pushd "$TOOL_DIR" + if bin/hugegraph gremlin-execute --script 'g.V().count()' && \ + bin/hugegraph task-list && \ + bin/hugegraph backup -t all --directory ./backup-test; then + echo " ✓ Tool test passed" + else + echo "Error: Tool test failed" + exit 1 + fi + popd + fi + + # Test Hubble + echo "Testing HugeGraph Hubble..." + HUBBLE_DIR=$(find . -maxdepth 1 -type d -name "*hubble*${{ env.RELEASE_VERSION }}" | head -n1) + if [[ -n "$HUBBLE_DIR" ]]; then + pushd "$HUBBLE_DIR" + if bin/start-hubble.sh; then + echo " ✓ Hubble started" + sleep 2 + bin/stop-hubble.sh + echo " ✓ Hubble stopped" + else + echo "Error: Hubble test failed" + exit 1 + fi + popd + fi + + popd + fi + + popd + fi + + # Stop server after toolchain tests + SERVER_DIR=$(find . -maxdepth 3 -type d -path "*hugegraph-incubating*src/hugegraph-server/*hugegraph*${{ env.RELEASE_VERSION }}" 2>/dev/null | head -n1) + if [[ -n "$SERVER_DIR" ]]; then + echo "Stopping server..." + pushd "$SERVER_DIR" + bin/stop-hugegraph.sh + echo " ✓ Server stopped" + popd + fi + + - name: Step 8 - Validate Binary Packages + run: | + echo "" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo "Step [8/9]: Validate Binary Packages" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + + cd dist/${{ env.RELEASE_VERSION }} + + BIN_PACKAGES=() + for pkg in *.tar.gz; do + if [[ "$pkg" != *-src.tar.gz ]] && [[ -f "$pkg" ]]; then + BIN_PACKAGES+=("$pkg") + fi + done + + echo "Found ${#BIN_PACKAGES[@]} binary package(s)" + + for bin_pkg in "${BIN_PACKAGES[@]}"; do + echo "" + echo "Validating binary package: $bin_pkg" + + # Extract package + PACKAGE_DIR=$(basename "$bin_pkg" .tar.gz) + rm -rf "$PACKAGE_DIR" + tar -xzf "$bin_pkg" + + if [[ ! -d "$PACKAGE_DIR" ]]; then + echo "Error: Failed to extract package: $bin_pkg" + exit 1 + fi + + pushd "$PACKAGE_DIR" + + # 8.1: Check incubating name + if [[ ! "$bin_pkg" =~ "incubating" ]]; then + echo "Error: Package name '$bin_pkg' should include 'incubating'" + exit 1 + fi + echo " ✓ Package name includes 'incubating'" + + # 8.2: Check required files + if [[ ! -f "LICENSE" ]]; then + echo "Error: Package '$bin_pkg' missing LICENSE file" + exit 1 + fi + echo " ✓ LICENSE file exists" + + if [[ ! -f "NOTICE" ]]; then + echo "Error: Package '$bin_pkg' missing NOTICE file" + exit 1 + fi + echo " ✓ NOTICE file exists" + + if [[ ! -f "DISCLAIMER" ]]; then + echo "Error: Package '$bin_pkg' missing DISCLAIMER file" + exit 1 + fi + echo " ✓ DISCLAIMER file exists" + + # 8.3: Check licenses directory + if [[ ! -d "licenses" ]]; then + echo "Error: Package '$bin_pkg' missing licenses directory" + exit 1 + fi + echo " ✓ licenses directory exists" + + # 8.4: Check license categories (Category X - Prohibited) + CAT_X_MATCHES=$(grep -r -E "${{ env.CATEGORY_X }}" LICENSE NOTICE licenses 2>/dev/null || true) + CAT_X_COUNT=$(echo "$CAT_X_MATCHES" | grep -v '^$' | wc -l | tr -d ' ') + + if [[ $CAT_X_COUNT -ne 0 ]]; then + echo "Error: Package '$bin_pkg' contains $CAT_X_COUNT prohibited ASF Category X license(s):" + echo "$CAT_X_MATCHES" + exit 1 + fi + echo " ✓ No Category X licenses found" + + # 8.5: Check license categories (Category B - Warning) + CAT_B_COUNT=$(grep -r -E "${{ env.CATEGORY_B }}" LICENSE NOTICE licenses 2>/dev/null | wc -l | tr -d ' ' || echo "0") + if [[ $CAT_B_COUNT -ne 0 ]]; then + echo " ⚠ Warning: Package '$bin_pkg' contains $CAT_B_COUNT ASF Category B license(s) - please verify documentation" + else + echo " ✓ No Category B licenses found" + fi + + # 8.6: Check empty files and directories + EMPTY_DIRS=$(find . -type d -empty 2>/dev/null || true) + EMPTY_FILES=$(find . -type f -empty 2>/dev/null || true) + + if [[ -n "$EMPTY_DIRS" ]]; then + echo "Error: Package '$bin_pkg' contains empty director(y/ies):" + echo "$EMPTY_DIRS" + exit 1 + fi + + if [[ -n "$EMPTY_FILES" ]]; then + echo "Error: Package '$bin_pkg' contains empty file(s):" + echo "$EMPTY_FILES" + exit 1 + fi + echo " ✓ No empty files or directories" + + popd + echo "✓ Finished validating binary package: $bin_pkg" + done + + - name: Step 9 - Test Binary Server & Toolchain + run: | + echo "" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo "Step [9/9]: Test Binary Server & Toolchain" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + + cd dist/${{ env.RELEASE_VERSION }} + + # Test binary server + BIN_SERVER_DIR=$(find . -maxdepth 3 -type d -path "*hugegraph-incubating*${{ env.RELEASE_VERSION }}/*hugegraph-server-incubating*${{ env.RELEASE_VERSION }}" 2>/dev/null | head -n1) + + if [[ -n "$BIN_SERVER_DIR" ]]; then + echo "Testing binary server package..." + pushd "$BIN_SERVER_DIR" + + if bin/init-store.sh && sleep ${{ env.SERVER_START_DELAY }} && bin/start-hugegraph.sh; then + echo " ✓ Binary server started" + else + echo "Error: Failed to start binary server" + exit 1 + fi + + popd + fi + + # Test binary toolchain + BIN_TOOLCHAIN=$(find . -maxdepth 3 -type d -path "*toolchain*${{ env.RELEASE_VERSION }}" 2>/dev/null | head -n1) + + if [[ -n "$BIN_TOOLCHAIN" ]]; then + pushd "$BIN_TOOLCHAIN" + + # Test binary loader + BIN_LOADER=$(find . -maxdepth 1 -type d -name "*loader*${{ env.RELEASE_VERSION }}" | head -n1) + if [[ -n "$BIN_LOADER" ]]; then + pushd "$BIN_LOADER" + if bin/hugegraph-loader.sh -f ./example/file/struct.json -s ./example/file/schema.groovy -g hugegraph; then + echo " ✓ Binary loader test passed" + else + echo "Error: Binary loader test failed" + exit 1 + fi + popd + fi + + # Test binary tool + BIN_TOOL=$(find . -maxdepth 1 -type d -name "*tool*${{ env.RELEASE_VERSION }}" | head -n1) + if [[ -n "$BIN_TOOL" ]]; then + pushd "$BIN_TOOL" + if bin/hugegraph gremlin-execute --script 'g.V().count()' && \ + bin/hugegraph task-list && \ + bin/hugegraph backup -t all --directory ./backup-test; then + echo " ✓ Binary tool test passed" + else + echo "Error: Binary tool test failed" + exit 1 + fi + popd + fi + + # Test binary hubble + BIN_HUBBLE=$(find . -maxdepth 1 -type d -name "*hubble*${{ env.RELEASE_VERSION }}" | head -n1) + if [[ -n "$BIN_HUBBLE" ]]; then + pushd "$BIN_HUBBLE" + if bin/start-hubble.sh; then + echo " ✓ Binary hubble started" + sleep 2 + bin/stop-hubble.sh + echo " ✓ Binary hubble stopped" + else + echo "Error: Binary hubble test failed" + exit 1 + fi + popd + fi + + popd + fi + + # Stop binary server + if [[ -n "$BIN_SERVER_DIR" ]]; then + pushd "$BIN_SERVER_DIR" + bin/stop-hugegraph.sh + echo " ✓ Binary server stopped" + popd + fi + + echo "" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo " VALIDATION SUMMARY " + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo "" + echo "✓ VALIDATION PASSED" + echo "" + echo "Please review the validation results and provide feedback in the" + echo "release voting thread on the mailing list." + + strategy: + fail-fast: false + matrix: + java_version: ['11'] + os: [ubuntu-latest, macos-latest] +