diff --git a/.github/scripts/setup-build-cache.sh b/.github/scripts/setup-build-cache.sh index 7da3912c38..6742ea7b44 100755 --- a/.github/scripts/setup-build-cache.sh +++ b/.github/scripts/setup-build-cache.sh @@ -36,4 +36,13 @@ fi ln -s "$_cache_dir" "build" echo " Symlink: build -> $_cache_dir" + +# Report cache hit/miss based on whether compiled artifacts exist +_cache_sim=$(find "$_cache_dir" -name 'simulation' -type f 2>/dev/null | head -1) +if [ -n "$_cache_sim" ]; then + _cache_age=$(stat -c '%y' "$_cache_sim" 2>/dev/null || stat -f '%Sm' "$_cache_sim" 2>/dev/null || echo "unknown") + echo " Cache status: HIT (found existing build artifacts, last built: $_cache_age)" +else + echo " Cache status: MISS (no existing build artifacts)" +fi echo "=========================" diff --git a/.github/workflows/bench.yml b/.github/workflows/bench.yml index 382b2fee77..4ec557a3a7 100644 --- a/.github/workflows/bench.yml +++ b/.github/workflows/bench.yml @@ -13,6 +13,9 @@ concurrency: jobs: file-changes: name: Detect File Changes + if: > + github.event_name != 'pull_request_review' || + github.event.review.user.type != 'Bot' runs-on: 'ubuntu-latest' outputs: checkall: ${{ steps.changes.outputs.checkall }} diff --git a/.github/workflows/frontier/submit.sh b/.github/workflows/frontier/submit.sh index d5b416c65a..4c3e0e3e27 100644 --- a/.github/workflows/frontier/submit.sh +++ b/.github/workflows/frontier/submit.sh @@ -34,12 +34,13 @@ output_file="$job_slug.out" submit_output=$(sbatch < typing.List[TestCase]: cases = cases_[:] selected_cases = [] @@ -66,12 +66,19 @@ def __filter(cases_) -> typing.List[TestCase]: raise MFCException("Testing: Your specified range [--from,--to] is incorrect. Please ensure both IDs exist and are in the correct order.") if len(ARG("only")) > 0: + # UUIDs are 8-char hex (CRC32): use OR so --only UUID1 UUID2 selects + # any matching test. Labels use AND so --only 2D Bubbles selects both. + _uuid_mode = all(len(t) == 8 and t.isalnum() and not t.isalpha() for t in ARG("only")) for case in cases[:]: case: TestCase checkCase = case.trace.split(" -> ") checkCase.append(case.get_uuid()) - if not set(ARG("only")).issubset(set(checkCase)): + if _uuid_mode: + if not set(ARG("only")).intersection(set(checkCase)): + cases.remove(case) + skipped_cases.append(case) + elif not set(ARG("only")).issubset(set(checkCase)): cases.remove(case) skipped_cases.append(case) @@ -99,6 +106,14 @@ def __filter(cases_) -> typing.List[TestCase]: skipped_cases += example_cases cases = [case for case in cases if case not in example_cases] + if ARG("shard") is not None: + parts = ARG("shard").split("/") + if len(parts) != 2 or not all(p.isdigit() for p in parts) or int(parts[1]) < 1 or not 1 <= int(parts[0]) <= int(parts[1]): + raise MFCException(f"Invalid --shard '{ARG('shard')}': expected 'i/n' with 1 <= i <= n (e.g., '1/2').") + shard_idx, shard_count = int(parts[0]), int(parts[1]) + skipped_cases += [c for i, c in enumerate(cases) if i % shard_count != shard_idx - 1] + cases = [c for i, c in enumerate(cases) if i % shard_count == shard_idx - 1] + if ARG("percent") == 100: return cases, skipped_cases @@ -171,6 +186,8 @@ def test(): cons.print(" Progress Test Name Time(s) UUID") cons.print() + failed_uuids_path = os.path.join(common.MFC_TEST_DIR, "failed_uuids.txt") + # Select the correct number of threads to use to launch test cases # We can't use ARG("jobs") when the --case-optimization option is set # because running a test case may cause it to rebuild, and thus @@ -182,6 +199,13 @@ def test(): # Check if we aborted due to high failure rate if abort_tests.is_set(): + # Clean up stale failed_uuids.txt so CI doesn't retry wrong tests + try: + if os.path.exists(failed_uuids_path): + os.remove(failed_uuids_path) + except OSError: + pass + total_completed = nFAIL + nPASS cons.print() cons.unindent() @@ -206,6 +230,14 @@ def test(): # Build the summary report _print_test_summary(nPASS, nFAIL, nSKIP, minutes, seconds, failed_tests, skipped_cases) + # Write failed UUIDs to file for CI retry logic + if failed_tests: + with open(failed_uuids_path, "w") as f: + for test_info in failed_tests: + f.write(test_info['uuid'] + "\n") + elif os.path.exists(failed_uuids_path): + os.remove(failed_uuids_path) + exit(nFAIL)