Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
168 changes: 137 additions & 31 deletions .github/workflows/run-branch-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ on:
required: false
type: string
default: "3.10"
# Rust/cargo options
rust-version:
description: "Rust toolchain version for cargo test."
required: false
type: string
default: "stable"
# C++/CMake options
cmake-version:
description: "CMake version for C++ tests."
Expand Down Expand Up @@ -51,7 +57,7 @@ on:
type: string
default: '["self-hosted", "multithreaded"]'
parallel_workers:
description: "Number of parallel workers. Leave empty for auto-detect (6 for multithreaded, 1 for singlethreaded). Use 'auto' for CPU count."
description: "Number of parallel workers. Leave empty for auto-detect (6 for multithreaded, 1 for singlethreaded). Use 'auto' for cgroup-aware CPU count."
required: false
type: string
default: ""
Expand All @@ -72,6 +78,12 @@ on:
regression_count:
description: "Number of regressions (pytest)"
value: ${{ jobs.compare.outputs.regression_count }}
has_regressions_cargo:
description: "Whether regressions were detected (cargo)"
value: ${{ jobs.compare-cargo.outputs.has_regressions }}
regression_count_cargo:
description: "Number of regressions (cargo)"
value: ${{ jobs.compare-cargo.outputs.regression_count }}
cpp_has_regressions:
description: "Whether regressions were detected (C++)"
value: ${{ jobs.compare-cpp.outputs.has_regressions }}
Expand All @@ -85,6 +97,7 @@ jobs:
runs-on: ${{ fromJSON(inputs.runs_on) }}
outputs:
has_pytest: ${{ steps.detect.outputs.has_pytest }}
has_cargo: ${{ steps.detect.outputs.has_cargo }}
has_cpp: ${{ steps.detect.outputs.has_cpp }}
steps:
- uses: actions/checkout@v4.2.2
Expand All @@ -93,10 +106,18 @@ jobs:
run: |
# Detect pytest
if [ -f "pyproject.toml" ] || [ -f "setup.py" ] || [ -f "requirements.txt" ] || find . -name "test_*.py" -o -name "*_test.py" | head -1 | grep -q .; then
echo "has_pytest=true" >> $GITHUB_OUTPUT
echo "has_pytest=true" >> "$GITHUB_OUTPUT"
echo "✅ Detected: pytest"
else
echo "has_pytest=false" >> $GITHUB_OUTPUT
echo "has_pytest=false" >> "$GITHUB_OUTPUT"
fi

# Detect cargo (Rust)
if [ -f "Cargo.toml" ]; then
echo "has_cargo=true" >> "$GITHUB_OUTPUT"
echo "✅ Detected: cargo (Rust)"
else
echo "has_cargo=false" >> "$GITHUB_OUTPUT"
fi

# Detect C++ with CMake and tests
Expand All @@ -116,7 +137,7 @@ jobs:
echo "✅ Detected: C++ test files"
fi
fi
echo "has_cpp=$HAS_CPP" >> $GITHUB_OUTPUT
echo "has_cpp=$HAS_CPP" >> "$GITHUB_OUTPUT"

# Test source branch (always fresh, no caching)
test-source:
Expand Down Expand Up @@ -645,34 +666,56 @@ jobs:
current_no_tests_found: ${{ needs.test-source.outputs.no_tests_found }}
artifact_name: regression_pytest_${{ github.event.pull_request.number || github.run_id }}

# Notify on regressions
notify:
needs: [test-source, test-target, compare]
if: |
always() &&
(needs.compare.outputs.has_regressions == 'true' || needs.compare.result == 'failure')
runs-on: ${{ fromJSON(inputs.runs_on) }}
steps:
- name: Send notification
env:
WEBHOOK: ${{ secrets.DISCORD_WEBHOOK_URL }}
run: |
if [ -z "$WEBHOOK" ]; then
echo "No Discord webhook configured, skipping notification"
exit 0
fi
# ============== RUST/CARGO TESTS ==============

MSG="**Pytest Regression Alert**\n"
MSG+="PR #${{ github.event.pull_request.number }}: ${{ github.event.pull_request.title }}\n"
MSG+="\`${{ github.head_ref }}\` → \`${{ inputs.target_branch }}\`\n\n"
MSG+="Source: ${{ needs.test-source.outputs.passed }}/${{ needs.test-source.outputs.total }}\n"
MSG+="Target: ${{ needs.test-target.outputs.passed }}/${{ needs.test-target.outputs.total }}\n"
MSG+="Regressions: ${{ needs.compare.outputs.regression_count || '?' }}\n\n"
MSG+="[View Run](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})"
# Test source branch with cargo (always fresh, no caching)
test-source-cargo:
needs: detect-frameworks
if: needs.detect-frameworks.outputs.has_cargo == 'true'
uses: ./.github/workflows/test-rs-cargo.yml
with:
ref: "" # Default checkout = PR branch
rust-version: ${{ inputs.rust-version }}
runs_on: ${{ inputs.runs_on }}
artifact_name: cargo_source_${{ github.event.pull_request.number || github.run_id }}
parallel_workers: ${{ inputs.parallel_workers }}

curl -s -H "Content-Type: application/json" \
-d "{\"content\": \"$(echo -e "$MSG")\"}" \
"$WEBHOOK" || true
# Test target branch with cargo
test-target-cargo:
needs: detect-frameworks
if: needs.detect-frameworks.outputs.has_cargo == 'true'
uses: ./.github/workflows/test-rs-cargo.yml
with:
ref: ${{ inputs.target_branch }}
rust-version: ${{ inputs.rust-version }}
runs_on: ${{ inputs.runs_on }}
artifact_name: cargo_target_${{ github.event.pull_request.number || github.run_id }}
parallel_workers: ${{ inputs.parallel_workers }}

# Compare cargo results
compare-cargo:
needs: [test-source-cargo, test-target-cargo]
if: always() && needs.test-source-cargo.result == 'success'
uses: ./.github/workflows/regression-test.yml
with:
runs_on: ${{ inputs.runs_on }}
baseline_label: ${{ inputs.target_branch }}
baseline_results_artifact: cargo_target_${{ github.event.pull_request.number || github.run_id }}
baseline_results_filename: test_data.json
current_label: ${{ github.head_ref || github.ref_name }}
current_results_artifact: cargo_source_${{ github.event.pull_request.number || github.run_id }}
current_results_filename: test_data.json
baseline_passed: ${{ needs.test-target-cargo.outputs.passed }}
baseline_total: ${{ needs.test-target-cargo.outputs.total }}
baseline_percentage: ${{ needs.test-target-cargo.outputs.percentage }}
current_passed: ${{ needs.test-source-cargo.outputs.passed }}
current_total: ${{ needs.test-source-cargo.outputs.total }}
current_percentage: ${{ needs.test-source-cargo.outputs.percentage }}
baseline_collection_errors: ${{ needs.test-target-cargo.outputs.collection_errors }}
baseline_no_tests_found: ${{ needs.test-target-cargo.outputs.no_tests_found }}
current_collection_errors: ${{ needs.test-source-cargo.outputs.collection_errors }}
current_no_tests_found: ${{ needs.test-source-cargo.outputs.no_tests_found }}
artifact_name: regression_cargo_${{ github.event.pull_request.number || github.run_id }}

# ============================================
# C++ Tests (GTest/CTest)
Expand Down Expand Up @@ -737,11 +780,74 @@ jobs:
current_no_tests_found: ${{ needs.test-source-cpp.outputs.no_tests_found }}
artifact_name: regression_cpp_${{ github.event.pull_request.number || github.run_id }}

# ============== NOTIFICATIONS ==============

# Notify on pytest regressions
notify:
needs: [detect-frameworks, test-source, test-target, compare]
if: |
always() &&
needs.detect-frameworks.outputs.has_pytest == 'true' &&
(needs.compare.outputs.has_regressions == 'true' || needs.compare.result == 'failure')
runs-on: ${{ fromJSON(inputs.runs_on) }}
steps:
- name: Send notification
env:
WEBHOOK: ${{ secrets.DISCORD_WEBHOOK_URL }}
run: |
if [ -z "$WEBHOOK" ]; then
echo "No Discord webhook configured, skipping notification"
exit 0
fi

MSG="**Pytest Regression Alert**\n"
MSG+="PR #${{ github.event.pull_request.number }}: ${{ github.event.pull_request.title }}\n"
MSG+="\`${{ github.head_ref }}\` → \`${{ inputs.target_branch }}\`\n\n"
MSG+="Source: ${{ needs.test-source.outputs.passed }}/${{ needs.test-source.outputs.total }}\n"
MSG+="Target: ${{ needs.test-target.outputs.passed }}/${{ needs.test-target.outputs.total }}\n"
MSG+="Regressions: ${{ needs.compare.outputs.regression_count || '?' }}\n\n"
MSG+="[View Run](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})"

curl -s -H "Content-Type: application/json" \
-d "{\"content\": \"$(echo -e "$MSG")\"}" \
"$WEBHOOK" || true

# Notify on cargo regressions
notify-cargo:
needs: [detect-frameworks, test-source-cargo, test-target-cargo, compare-cargo]
if: |
always() &&
needs.detect-frameworks.outputs.has_cargo == 'true' &&
(needs.compare-cargo.outputs.has_regressions == 'true' || needs.compare-cargo.result == 'failure')
runs-on: ${{ fromJSON(inputs.runs_on) }}
steps:
- name: Send notification
env:
WEBHOOK: ${{ secrets.DISCORD_WEBHOOK_URL }}
run: |
if [ -z "$WEBHOOK" ]; then
echo "No Discord webhook configured, skipping notification"
exit 0
fi

MSG="**Cargo Test Regression Alert**\n"
MSG+="PR #${{ github.event.pull_request.number }}: ${{ github.event.pull_request.title }}\n"
MSG+="\`${{ github.head_ref }}\` → \`${{ inputs.target_branch }}\`\n\n"
MSG+="Source: ${{ needs.test-source-cargo.outputs.passed }}/${{ needs.test-source-cargo.outputs.total }}\n"
MSG+="Target: ${{ needs.test-target-cargo.outputs.passed }}/${{ needs.test-target-cargo.outputs.total }}\n"
MSG+="Regressions: ${{ needs.compare-cargo.outputs.regression_count || '?' }}\n\n"
MSG+="[View Run](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})"

curl -s -H "Content-Type: application/json" \
-d "{\"content\": \"$(echo -e "$MSG")\"}" \
"$WEBHOOK" || true

# Notify on C++ regressions
notify-cpp:
needs: [test-source-cpp, test-target-cpp, compare-cpp]
needs: [detect-frameworks, test-source-cpp, test-target-cpp, compare-cpp]
if: |
always() &&
needs.detect-frameworks.outputs.has_cpp == 'true' &&
(needs.compare-cpp.outputs.has_regressions == 'true' || needs.compare-cpp.result == 'failure')
runs-on: ${{ fromJSON(inputs.runs_on) }}
steps:
Expand Down
Loading