build(deps): bump nvidia/tensorrt from 24.05-py3 to 26.03-py3 in /deploy/runpod #224
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: ci | |
| on: | |
| push: | |
| branches-ignore: | |
| - beads-sync | |
| - "ci-logs/**" | |
| pull_request: | |
| permissions: | |
| contents: read | |
| concurrency: | |
| group: ci-${{ github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| # Dependency tier policy: | |
| # - core: hash-locked runtime deps + local wheel install (packaging + CLI smoke) | |
| # - docs_mcp: hash-locked docs/MCP/Actions deps from exact top-level lock | |
| # - recommended: hash-locked requirements-ci.lock (regression + schema/manifest gates) | |
| # - full: GPU/backends in separate manual/nightly workflows | |
| # docs: docs/ci_dependency_tiers.md | |
| changes: | |
| name: change scope | |
| runs-on: ubuntu-latest | |
| outputs: | |
| full_ci: ${{ steps.filter.outputs.full_ci }} | |
| docs_mcp_ci: ${{ steps.filter.outputs.docs_mcp_ci }} | |
| manual_ci: ${{ steps.filter.outputs.manual_ci }} | |
| workflows_ci: ${{ steps.filter.outputs.workflows_ci }} | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 | |
| - name: Detect change scope | |
| id: filter | |
| uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4 | |
| with: | |
| filters: | | |
| full_ci: | |
| - 'yolozu/**' | |
| - '!yolozu/integrations/mcp_server.py' | |
| - '!yolozu/integrations/actions_api.py' | |
| - 'tools/**' | |
| - '!tools/run_mcp_server.py' | |
| - '!tools/run_actions_api.py' | |
| - '!tools/check_mcp_settings.py' | |
| - 'scripts/**' | |
| - 'rtdetr_pose/**' | |
| - 'tests/**' | |
| - '!tests/test_ai_first_mcp_surface.py' | |
| - '!tests/test_integrations_mcp_actions_parity.py' | |
| - '!tests/test_check_mcp_settings_tool.py' | |
| - '!tests/test_generated_integration_tool_reference.py' | |
| - 'configs/**' | |
| - 'data/**' | |
| - 'baselines/**' | |
| - 'requirements*.txt' | |
| - 'requirements*.lock' | |
| - 'pyproject.toml' | |
| - 'pytest.ini' | |
| - 'ruff.toml' | |
| - 'MANIFEST.in' | |
| - 'setup.py' | |
| - 'setup.cfg' | |
| docs_mcp_ci: | |
| - 'README.md' | |
| - 'Readme_jp.md' | |
| - 'docs/**' | |
| - 'tools/run_mcp_server.py' | |
| - 'tools/run_actions_api.py' | |
| - 'tools/check_mcp_settings.py' | |
| - 'yolozu/integrations/mcp_server.py' | |
| - 'yolozu/integrations/actions_api.py' | |
| - 'tests/test_ai_first_mcp_surface.py' | |
| - 'tests/test_integrations_mcp_actions_parity.py' | |
| - 'tests/test_check_mcp_settings_tool.py' | |
| - 'tests/test_generated_integration_tool_reference.py' | |
| manual_ci: | |
| - 'manual/**' | |
| workflows_ci: | |
| - '.github/workflows/**' | |
| docs_meta: | |
| name: docs/metadata fast path | |
| needs: | |
| - changes | |
| if: ${{ needs.changes.outputs.full_ci != 'true' && needs.changes.outputs.docs_mcp_ci != 'true' && needs.changes.outputs.manual_ci != 'true' && needs.changes.outputs.workflows_ci != 'true' }} | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Skip heavy CI for docs-only/metadata-only changes | |
| run: echo "No code/runtime changes detected. Heavy CI jobs are skipped." | |
| workflows_meta: | |
| name: workflows fast path | |
| needs: | |
| - changes | |
| if: ${{ needs.changes.outputs.full_ci != 'true' && needs.changes.outputs.docs_mcp_ci != 'true' && needs.changes.outputs.manual_ci != 'true' && needs.changes.outputs.workflows_ci == 'true' }} | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Skip heavy CI for workflow-only changes | |
| run: echo "Only .github/workflows/** changed. Heavy CI jobs are skipped." | |
| manual_build: | |
| name: manual build (main.tex only) | |
| needs: | |
| - changes | |
| if: ${{ needs.changes.outputs.full_ci != 'true' && needs.changes.outputs.docs_mcp_ci != 'true' && needs.changes.outputs.manual_ci == 'true' }} | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 | |
| - name: Build manual PDF (main.tex) | |
| uses: xu-cheng/latex-action@25080975331dc4fc8e6f1af55788e00f84f51fff # v4 | |
| with: | |
| working_directory: manual | |
| root_file: main.tex | |
| args: -pdf -interaction=nonstopmode -halt-on-error -outdir=build -jobname=yolozu_manual | |
| pre_compile: python3 ../tools/generate_manual_import_graphs.py | |
| - name: Verify manual artifact | |
| run: | | |
| test -f manual/build/yolozu_manual.pdf | |
| ls -lh manual/build/yolozu_manual.pdf | |
| docs_mcp_gate: | |
| name: "ci tier: docs+mcp (lightweight)" | |
| needs: | |
| - changes | |
| if: ${{ needs.changes.outputs.full_ci != 'true' && needs.changes.outputs.docs_mcp_ci == 'true' }} | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 | |
| - name: Set up Python | |
| uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6 | |
| with: | |
| python-version: "3.11" | |
| - name: Install lightweight deps (hash-locked docs/MCP/actions set) | |
| run: | | |
| python3 tools/ci/install_with_hashes.py --requirements requirements-docs-actions.lock | |
| - name: Manifest + docs references gate | |
| run: | | |
| python tools/validate_tool_manifest.py --manifest tools/manifest.json --require-declarative | |
| python -m unittest tests.test_packaged_tools_manifest tests.test_manifest_docs_references -v | |
| - name: MCP/actions smoke | |
| run: | | |
| python3 tools/run_mcp_server.py --help | |
| python3 tools/run_mcp_server.py --sample-generate-config > reports/ai_generate_config_ci.json | |
| python3 tools/run_mcp_server.py --sample-review-config reports/ai_generate_config_ci.json | |
| python3 tools/run_actions_api.py --help | |
| python3 tools/check_mcp_settings.py --output reports/mcp_settings_check_ci.json | |
| - name: MCP-focused tests | |
| run: | | |
| python -m unittest \ | |
| tests.test_ai_first_mcp_surface \ | |
| tests.test_integrations_mcp_actions_parity \ | |
| tests.test_check_mcp_settings_tool \ | |
| tests.test_generated_integration_tool_reference -v | |
| smoke_gate: | |
| name: "ci tier: core (runtime smoke)" | |
| needs: | |
| - changes | |
| if: ${{ needs.changes.outputs.full_ci == 'true' }} | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 | |
| - name: Set up Python | |
| uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6 | |
| with: | |
| python-version: "3.11" | |
| - name: Install (hash-locked runtime + local wheel) | |
| run: | | |
| python3 tools/ci/install_with_hashes.py --requirements requirements-runtime.lock --install-local-wheel | |
| - name: README smoke script | |
| run: | | |
| bash scripts/smoke.sh | |
| quality_gate: | |
| name: "ci tier: recommended (ruff + focused pytest)" | |
| needs: | |
| - changes | |
| - smoke_gate | |
| if: ${{ needs.changes.outputs.full_ci == 'true' }} | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 | |
| - name: Set up Python | |
| uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6 | |
| with: | |
| python-version: "3.11" | |
| - name: Install deps (hash-locked recommended tier) | |
| run: | | |
| python3 tools/ci/install_with_hashes.py --requirements requirements-ci.lock | |
| - name: Ruff (focused paths) | |
| run: | | |
| ruff check tools yolozu tests | |
| - name: Focused pytest (YOLO26 quality) | |
| run: | | |
| pytest -q \ | |
| tests/test_eval_suite_export_settings.py \ | |
| tests/test_eval_suite_determinism.py \ | |
| tests/test_check_map_targets_contract.py \ | |
| tests/test_yolo26_protocol.py \ | |
| tests/test_predictions.py \ | |
| tests/test_manifest_docs_references.py \ | |
| tests/test_backend_shape_format_contracts.py \ | |
| tests/test_external_inference_templates_smoke.py \ | |
| tests/test_ai_first_mcp_surface.py | |
| - name: Smoke walkthrough (deep, offline) | |
| run: | | |
| bash scripts/smoke.sh --profile deep --torch-device cpu | |
| - name: MCP/AI surface smoke | |
| run: | | |
| python3 tools/run_mcp_server.py --help | |
| python3 tools/run_mcp_server.py --sample-generate-config > reports/ai_generate_config_ci.json | |
| python3 tools/run_mcp_server.py --sample-review-config reports/ai_generate_config_ci.json | |
| python3 tools/run_actions_api.py --help | |
| pip_smoke: | |
| name: "ci tier: core (pip smoke, py${{ matrix.python-version }})" | |
| needs: | |
| - changes | |
| - smoke_gate | |
| if: ${{ needs.changes.outputs.full_ci == 'true' }} | |
| runs-on: ubuntu-latest | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| python-version: | |
| - "3.10" | |
| - "3.11" | |
| - "3.12" | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 | |
| - name: Set up Python | |
| uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6 | |
| with: | |
| python-version: ${{ matrix.python-version }} | |
| - name: Install (hash-locked runtime + local wheel) | |
| run: | | |
| python3 tools/ci/install_with_hashes.py --requirements requirements-runtime.lock --install-local-wheel | |
| - name: Repo smoke script (offline dry-run path) | |
| run: | | |
| bash scripts/smoke.sh | |
| - name: CLI smoke (doctor + demo) | |
| run: | | |
| yolozu doctor --output - | |
| yolozu demo instance-seg --num-images 2 --image-size 48 --max-instances 2 --run-dir "${RUNNER_TEMP}/yolozu_demo" | |
| test: | |
| name: "ci tier: recommended (full gate suite)" | |
| needs: [changes, smoke_gate, quality_gate] | |
| if: ${{ needs.changes.outputs.full_ci == 'true' }} | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 | |
| - name: Set up Python | |
| uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6 | |
| with: | |
| python-version: "3.11" | |
| - name: Install deps (hash-locked recommended tier) | |
| run: | | |
| python3 tools/ci/install_with_hashes.py --requirements requirements-ci.lock | |
| - name: Packaging smoke (hash-locked local wheel + yolozu doctor) | |
| run: | | |
| python3 tools/ci/install_with_hashes.py --requirements requirements-runtime.lock --install-local-wheel | |
| yolozu doctor --output - | |
| yolozu export --help | |
| python -c "from yolozu import resources; paths = resources.list_resource_paths(); assert 'schemas/predictions.schema.json' in paths; assert 'protocols/yolo26_eval.json' in paths; print('resources OK:', len(paths))" | |
| - name: Install build tooling (hash-locked) | |
| run: | | |
| python3 tools/ci/install_with_hashes.py --requirements requirements-build.lock | |
| - name: Wheel contents gate (schemas/protocols/minimal docs) | |
| run: | | |
| python -m build --wheel | |
| python - <<'PY' | |
| import glob | |
| import sys | |
| import zipfile | |
| wheels = sorted(glob.glob("dist/*.whl")) | |
| if not wheels: | |
| raise SystemExit("no wheels found in dist/") | |
| whl = wheels[-1] | |
| with zipfile.ZipFile(whl, "r") as z: | |
| names = set(z.namelist()) | |
| required = [ | |
| "yolozu/data/schemas/predictions.schema.json", | |
| "yolozu/data/protocols/yolo26_eval.json", | |
| "yolozu/data/manifest/tools_manifest.json", | |
| "yolozu/data/docs/README.md", | |
| "yolozu/data/docs/proof_onepager.md", | |
| "yolozu/data/docs/backend_parity_matrix.md", | |
| "yolozu/data/docs/onnx_export_parity.md", | |
| "yolozu/data/docs/tensorrt_pipeline.md", | |
| "yolozu/data/docs/yolo26_baseline_repro.md", | |
| "yolozu/data/docs/tta_support_matrix.md", | |
| "yolozu/data/docs/adapter_strategy.md", | |
| ] | |
| missing = [p for p in required if p not in names] | |
| if missing: | |
| print("wheel:", whl) | |
| print("missing:") | |
| for p in missing: | |
| print(" -", p) | |
| sys.exit(1) | |
| print("wheel contents OK:", whl) | |
| PY | |
| - name: Sdist contents gate (schemas/protocols/docs/manifest) | |
| run: | | |
| python -m build --sdist | |
| python - <<'PY' | |
| import glob | |
| import tarfile | |
| import sys | |
| sdist_files = sorted(glob.glob("dist/*.tar.gz")) | |
| if not sdist_files: | |
| raise SystemExit("no sdist found in dist/") | |
| sdist = sdist_files[-1] | |
| required = [ | |
| "LICENSE", | |
| "NOTICE", | |
| "COPYRIGHT", | |
| "SECURITY.md", | |
| "RELEASE.md", | |
| "tools/manifest.json", | |
| "yolozu/data/schemas/predictions.schema.json", | |
| "yolozu/data/protocols/yolo26_eval.json", | |
| "yolozu/data/manifest/tools_manifest.json", | |
| "yolozu/data/docs/README.md", | |
| "yolozu/data/docs/proof_onepager.md", | |
| "yolozu/data/docs/backend_parity_matrix.md", | |
| "yolozu/data/docs/onnx_export_parity.md", | |
| "yolozu/data/docs/tensorrt_pipeline.md", | |
| "yolozu/data/docs/yolo26_baseline_repro.md", | |
| "yolozu/data/docs/tta_support_matrix.md", | |
| "yolozu/data/docs/adapter_strategy.md", | |
| ] | |
| with tarfile.open(sdist, "r:gz") as tf: | |
| names = tf.getnames() | |
| def has_suffix(path_suffix: str) -> bool: | |
| suffix = "/" + path_suffix | |
| return any(name.endswith(suffix) for name in names) | |
| missing = [path for path in required if not has_suffix(path)] | |
| if missing: | |
| print("sdist:", sdist) | |
| print("missing:") | |
| for path in missing: | |
| print(" -", path) | |
| sys.exit(1) | |
| print("sdist contents OK:", sdist) | |
| PY | |
| - name: License policy | |
| run: | | |
| python tools/check_license_policy.py | |
| - name: Lint | |
| run: | | |
| ruff check . | |
| - name: Manifest declarative completeness gate | |
| run: | | |
| python tools/validate_tool_manifest.py --manifest tools/manifest.json --require-declarative | |
| - name: Manifest sync gate (tools -> packaged copy) | |
| run: | | |
| python tools/sync_tools_manifest.py | |
| git diff --exit-code -- tools/manifest.json yolozu/data/manifest/tools_manifest.json | |
| - name: Advisory audit (yolozu flags vs manifest) | |
| continue-on-error: true | |
| run: | | |
| python tools/audit_yolozu_manifest_flags.py | |
| - name: Reference adapter micro gate - interface contract (hard) | |
| run: | | |
| python tools/run_reference_adapter_regression.py \ | |
| --dataset data/smoke \ | |
| --split val \ | |
| --max-images 2 \ | |
| --profile micro \ | |
| --device cpu \ | |
| --image-size 160 \ | |
| --score-threshold 0.05 \ | |
| --max-detections 20 \ | |
| --init-seed 2026 \ | |
| --repro-policy relaxed \ | |
| --capture-provenance minimal \ | |
| --score-gate-mode off \ | |
| --perf-gate-mode off \ | |
| --runtime-lock requirements-ci.lock \ | |
| --enforce-runtime-lock \ | |
| --enforce-weights-hash \ | |
| --baseline baselines/reference_adapter/rtdetr_pose_smoke_val.json \ | |
| --output reports/reference_adapter_regression_contract_ci.json | |
| - name: Reference adapter micro gate - behavior (warn) | |
| run: | | |
| python tools/run_reference_adapter_regression.py \ | |
| --dataset data/smoke \ | |
| --split val \ | |
| --max-images 2 \ | |
| --profile micro \ | |
| --device cpu \ | |
| --image-size 160 \ | |
| --score-threshold 0.05 \ | |
| --max-detections 20 \ | |
| --init-seed 2026 \ | |
| --repro-policy relaxed \ | |
| --capture-provenance minimal \ | |
| --schema-gate-mode off \ | |
| --consistency-gate-mode off \ | |
| --score-gate-mode warn \ | |
| --perf-gate-mode warn \ | |
| --runtime-lock requirements-ci.lock \ | |
| --enforce-runtime-lock \ | |
| --baseline baselines/reference_adapter/rtdetr_pose_smoke_val.json \ | |
| --output reports/reference_adapter_regression_behavior_ci.json | |
| - name: Reference adapter fixed real scenario smoke (write/check) | |
| run: | | |
| # CI does not ship real images in git; prepare a tiny real-image few-shot dataset deterministically. | |
| python tools/prepare_real_multitask_fewshot.py \ | |
| --out data/real_multitask_fewshot \ | |
| --train-images 1 \ | |
| --val-images 1 \ | |
| --download-if-missing \ | |
| --allow-auto-download \ | |
| --accept-dataset-license \ | |
| --download-num-images 2 \ | |
| --download-seed 0 \ | |
| --strict-provenance \ | |
| --force | |
| baseline_path="reports/reference_adapter_real_multitask_micro_baseline_ci.json" | |
| python tools/run_reference_adapter_regression.py \ | |
| --dataset data/real_multitask_fewshot \ | |
| --split val \ | |
| --max-images 1 \ | |
| --profile micro \ | |
| --device cpu \ | |
| --image-size 160 \ | |
| --score-threshold 0.05 \ | |
| --max-detections 20 \ | |
| --init-seed 2026 \ | |
| --repro-policy relaxed \ | |
| --capture-provenance minimal \ | |
| --score-gate-mode off \ | |
| --perf-gate-mode off \ | |
| --runtime-lock requirements-ci.lock \ | |
| --enforce-runtime-lock \ | |
| --baseline "${baseline_path}" \ | |
| --write-baseline \ | |
| --output reports/reference_adapter_regression_real_scenario_baseline_write_ci.json | |
| python tools/run_reference_adapter_regression.py \ | |
| --dataset data/real_multitask_fewshot \ | |
| --split val \ | |
| --max-images 1 \ | |
| --profile micro \ | |
| --device cpu \ | |
| --image-size 160 \ | |
| --score-threshold 0.05 \ | |
| --max-detections 20 \ | |
| --init-seed 2026 \ | |
| --repro-policy relaxed \ | |
| --capture-provenance minimal \ | |
| --score-gate-mode off \ | |
| --perf-gate-mode off \ | |
| --runtime-lock requirements-ci.lock \ | |
| --enforce-runtime-lock \ | |
| --baseline "${baseline_path}" \ | |
| --output reports/reference_adapter_regression_real_scenario_ci.json | |
| - name: Unit tests | |
| run: | | |
| python -m unittest discover -s tests -t tests | |
| python -m unittest discover -s rtdetr_pose/tests -t rtdetr_pose/tests | |
| - name: Schema compatibility gate | |
| run: | | |
| python3 tools/check_schema_compatibility.py | |
| - name: Golden compatibility gate | |
| run: | | |
| python3 tools/check_golden_compatibility.py | |
| - name: Migration smoke (migrate + validate) | |
| run: | | |
| python - <<'PY' | |
| import os | |
| import json | |
| from pathlib import Path | |
| from PIL import Image | |
| out_dir = Path("reports") / "ci_migrate" | |
| out_dir.mkdir(parents=True, exist_ok=True) | |
| dataset_root = Path(os.environ["RUNNER_TEMP"]) / "ultra_seg_dataset" | |
| images = dataset_root / "images" / "val" | |
| labels = dataset_root / "labels" / "val" | |
| images.mkdir(parents=True, exist_ok=True) | |
| labels.mkdir(parents=True, exist_ok=True) | |
| Image.new("RGB", (32, 32), color=(0, 0, 0)).save(images / "000001.jpg") | |
| (labels / "000001.txt").write_text("0 0.1 0.1 0.9 0.1 0.9 0.9 0.1 0.9\n", encoding="utf-8") | |
| data_yaml = Path(os.environ["RUNNER_TEMP"]) / "ultra_data.yaml" | |
| data_yaml.write_text( | |
| "\n".join( | |
| [ | |
| f"path: {dataset_root}", | |
| "train: images/train", | |
| "val: images/val", | |
| "task: segment", | |
| "", | |
| ] | |
| ), | |
| encoding="utf-8", | |
| ) | |
| args_yaml = Path(os.environ["RUNNER_TEMP"]) / "ultra_args.yaml" | |
| args_yaml.write_text(f"data: {data_yaml}\ntask: segment\n", encoding="utf-8") | |
| voc_root = Path(os.environ["RUNNER_TEMP"]) / "voc_smoke" / "VOC2012" | |
| (voc_root / "JPEGImages").mkdir(parents=True, exist_ok=True) | |
| (voc_root / "SegmentationClass").mkdir(parents=True, exist_ok=True) | |
| (voc_root / "ImageSets" / "Segmentation").mkdir(parents=True, exist_ok=True) | |
| (voc_root / "JPEGImages" / "0001.jpg").write_bytes(b"") | |
| (voc_root / "SegmentationClass" / "0001.png").write_bytes(b"") | |
| (voc_root / "ImageSets" / "Segmentation" / "val.txt").write_text("0001\n", encoding="utf-8") | |
| coco_root = Path(os.environ["RUNNER_TEMP"]) / "coco_smoke" | |
| (coco_root / "images" / "val2017").mkdir(parents=True, exist_ok=True) | |
| (coco_root / "annotations").mkdir(parents=True, exist_ok=True) | |
| Image.new("RGB", (64, 32), color=(0, 0, 0)).save(coco_root / "images" / "val2017" / "0001.jpg") | |
| instances = { | |
| "images": [{"id": 1, "file_name": "0001.jpg", "width": 64, "height": 32}], | |
| "annotations": [{"id": 1, "image_id": 1, "category_id": 7, "bbox": [0, 0, 10, 20], "iscrowd": 0}], | |
| "categories": [{"id": 7, "name": "thing"}], | |
| } | |
| (coco_root / "annotations" / "instances_val2017.json").write_text(json.dumps(instances), encoding="utf-8") | |
| results = [{"image_id": 1, "category_id": 7, "bbox": [0, 0, 10, 20], "score": 0.9}] | |
| (coco_root / "results.json").write_text(json.dumps(results), encoding="utf-8") | |
| print("prepared:", dataset_root, data_yaml, args_yaml, voc_root) | |
| PY | |
| yolozu migrate dataset \ | |
| --from ultralytics \ | |
| --data "${RUNNER_TEMP}/ultra_data.yaml" \ | |
| --args "${RUNNER_TEMP}/ultra_args.yaml" \ | |
| --output reports/ci_migrate/ultra_wrapper \ | |
| --force | |
| yolozu validate dataset reports/ci_migrate/ultra_wrapper --split val | |
| yolozu validate dataset "${RUNNER_TEMP}/ultra_seg_dataset" --split val --label-format segment | |
| yolozu migrate seg-dataset \ | |
| --from voc \ | |
| --root "${RUNNER_TEMP}/voc_smoke/VOC2012" \ | |
| --split val \ | |
| --output reports/ci_migrate/voc_seg_dataset.json \ | |
| --force | |
| python - <<'PY' | |
| import json | |
| from pathlib import Path | |
| from yolozu.segmentation_dataset import load_seg_dataset_descriptor | |
| d = load_seg_dataset_descriptor("reports/ci_migrate/voc_seg_dataset.json") | |
| assert d.task == "semantic_segmentation" | |
| assert len(d.samples) == 1 | |
| preds = Path("reports/ci_migrate/seg_preds.json") | |
| preds.write_text(json.dumps([{"id": "0001", "mask": "pred.png"}]), encoding="utf-8") | |
| print("ok", preds) | |
| PY | |
| yolozu validate seg reports/ci_migrate/seg_preds.json | |
| yolozu migrate dataset \ | |
| --from coco \ | |
| --coco-root "${RUNNER_TEMP}/coco_smoke" \ | |
| --split val2017 \ | |
| --output reports/ci_migrate/coco_wrapper \ | |
| --force | |
| yolozu validate dataset reports/ci_migrate/coco_wrapper --split val2017 | |
| yolozu migrate predictions \ | |
| --from coco-results \ | |
| --results "${RUNNER_TEMP}/coco_smoke/results.json" \ | |
| --instances "${RUNNER_TEMP}/coco_smoke/annotations/instances_val2017.json" \ | |
| --output reports/ci_migrate/coco_predictions.json \ | |
| --force | |
| yolozu validate predictions reports/ci_migrate/coco_predictions.json --strict | |
| yolozu import dataset \ | |
| --from coco-instances \ | |
| --instances "${RUNNER_TEMP}/coco_smoke/annotations/instances_val2017.json" \ | |
| --images-dir "${RUNNER_TEMP}/coco_smoke/images/val2017" \ | |
| --split val2017 \ | |
| --output reports/ci_migrate/coco_import_wrapper \ | |
| --force | |
| yolozu validate dataset reports/ci_migrate/coco_import_wrapper --split val2017 | |
| - name: Upload migration artifacts | |
| if: always() | |
| uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 | |
| with: | |
| name: ci_migrate_artifacts | |
| path: reports/ci_migrate | |
| - name: Scenario smoke run | |
| run: | | |
| if bash tools/fetch_coco128.sh; then | |
| python tools/run_scenarios.py --adapter dummy --max-images 10 | |
| else | |
| echo "warning: coco128 fetch failed; running dataset-free scenario suite smoke" | |
| python tools/run_scenario_suite.py --output reports/scenario_suite_ci.json | |
| fi | |
| - name: Benchmark publication smoke | |
| run: | | |
| python - <<'PY' | |
| import json | |
| from pathlib import Path | |
| reports = Path("reports") | |
| reports.mkdir(parents=True, exist_ok=True) | |
| a = { | |
| "schema_version": 1, | |
| "timestamp": "2026-01-01T00:00:00Z", | |
| "metrics": { | |
| "buckets": [ | |
| {"name": "yolo26n", "metrics": {"fps": 40.0, "latency_ms": {"mean": 25.0}}}, | |
| {"name": "yolo26s", "metrics": {"fps": 30.0, "latency_ms": {"mean": 33.0}}}, | |
| ] | |
| }, | |
| "meta": {"run_id": "ci_bench_a", "git_head": "deadbeef"}, | |
| } | |
| b = { | |
| "schema_version": 1, | |
| "timestamp": "2026-01-01T00:01:00Z", | |
| "metrics": {"summary": {"fps_mean": 20.0, "latency_ms_mean": 50.0}}, | |
| "meta": {"run_id": "ci_bench_b", "git_head": "deadbeef"}, | |
| } | |
| (reports / "benchmark_latency_ci_a.json").write_text(json.dumps(a), encoding="utf-8") | |
| (reports / "benchmark_latency_ci_b.json").write_text(json.dumps(b), encoding="utf-8") | |
| PY | |
| python tools/publish_benchmark_table.py \ | |
| --report reports/benchmark_latency_ci_a.json \ | |
| --report reports/benchmark_latency_ci_b.json \ | |
| --output-json reports/benchmark_table_ci.json \ | |
| --output-md reports/benchmark_table_ci.md \ | |
| --source-command "python3 tools/benchmark_latency.py --config configs/benchmark_latency_example.json" | |
| test -f reports/benchmark_table_ci.json | |
| test -f reports/benchmark_table_ci.md | |
| python - <<'PY' | |
| import json | |
| from pathlib import Path | |
| payload = json.loads(Path("reports/benchmark_table_ci.json").read_text(encoding="utf-8")) | |
| rows = payload.get("rows") or [] | |
| run_ids = {str(r.get("run_id")) for r in rows} | |
| assert "ci_bench_a" in run_ids and "ci_bench_b" in run_ids | |
| assert payload.get("protocol", {}).get("id") == "yolo26" | |
| print("benchmark publication smoke passed") | |
| PY | |
| - name: Training contract smoke (dry-run + ONNX export/parity) | |
| run: | | |
| python - <<'PY' | |
| import os | |
| from pathlib import Path | |
| from PIL import Image | |
| root = Path(os.environ["RUNNER_TEMP"]) / "yolozu_tiny_dataset" | |
| for split in ("train2017", "val2017"): | |
| images = root / "images" / split | |
| labels = root / "labels" / split | |
| images.mkdir(parents=True, exist_ok=True) | |
| labels.mkdir(parents=True, exist_ok=True) | |
| for idx in range(2): | |
| name = f"{idx:06d}" | |
| img_path = images / f"{name}.jpg" | |
| Image.new("RGB", (32, 32), color=(0, 0, 0)).save(img_path) | |
| (labels / f"{name}.txt").write_text("0 0.5 0.5 0.2 0.2\n", encoding="utf-8") | |
| print(root) | |
| PY | |
| yolozu train configs/examples/train_contract.yaml \ | |
| --run-id ci_smoke \ | |
| --dry-run \ | |
| --dataset-root "${RUNNER_TEMP}/yolozu_tiny_dataset" \ | |
| --split train2017 \ | |
| --val-split val2017 \ | |
| --device cpu \ | |
| --amp none \ | |
| --ddp-backend gloo \ | |
| --image-size 64 \ | |
| --batch-size 1 | |
| test -f runs/ci_smoke/reports/run_meta.json | |
| test -f runs/ci_smoke/reports/config_resolved.yaml | |
| test -f runs/ci_smoke/checkpoints/last.pt | |
| test -f runs/ci_smoke/checkpoints/best.pt | |
| test -f runs/ci_smoke/exports/model.onnx | |
| test -f runs/ci_smoke/reports/onnx_parity.json | |
| python - <<'PY' | |
| import json | |
| from pathlib import Path | |
| from yolozu.run_record import validate_run_record_contract | |
| meta = json.loads(Path("runs/ci_smoke/reports/run_meta.json").read_text(encoding="utf-8")) | |
| validate_run_record_contract(meta, require_git_sha=True) | |
| assert "dependency_lock" in meta and isinstance(meta["dependency_lock"], dict) | |
| assert "preprocess" in meta and isinstance(meta["preprocess"], dict) | |
| assert "command" in meta and isinstance(meta["command"], dict) | |
| print("run_meta keys OK") | |
| PY | |
| - name: DDP smoke (torchrun, CPU gloo, rank0-only artifacts) | |
| run: | | |
| python - <<'PY' | |
| import os | |
| from pathlib import Path | |
| dataset_root = Path(os.environ["RUNNER_TEMP"]) / "yolozu_tiny_dataset" | |
| cfg = Path(os.environ["RUNNER_TEMP"]) / "rtdetr_pose_ci_ddp.yaml" | |
| cfg.write_text( | |
| "\n".join( | |
| [ | |
| "config_version: 1", | |
| f"dataset_root: {dataset_root.as_posix()}", | |
| "split: train2017", | |
| "val_split: val2017", | |
| "seed: 0", | |
| "device: cpu", | |
| "amp: none", | |
| "ddp: true", | |
| "batch_size: 1", | |
| "val_batch_size: 1", | |
| "epochs: 1", | |
| "log_every: 1", | |
| "val_every: 0", | |
| "checkpoint_every: 100", | |
| "run_id: ci_smoke_ddp", | |
| "dry_run: true", | |
| "model_config: rtdetr_pose/configs/base.json", | |
| ] | |
| ) | |
| + "\n", | |
| encoding="utf-8", | |
| ) | |
| print(cfg) | |
| PY | |
| TORCH_DISTRIBUTED_DEFAULT_FIND_UNUSED_PARAMETERS=1 \ | |
| torchrun --standalone --nproc_per_node=2 rtdetr_pose/tools/train_minimal.py \ | |
| --config "${RUNNER_TEMP}/rtdetr_pose_ci_ddp.yaml" \ | |
| --run-contract | |
| test -f runs/ci_smoke_ddp/reports/run_meta.json | |
| test -f runs/ci_smoke_ddp/reports/config_resolved.yaml | |
| test -f runs/ci_smoke_ddp/checkpoints/last.pt | |
| test -f runs/ci_smoke_ddp/checkpoints/best.pt | |
| test -f runs/ci_smoke_ddp/exports/model.onnx | |
| test -f runs/ci_smoke_ddp/reports/onnx_parity.json | |
| - name: Backend parity matrix smoke (JSON + HTML) | |
| run: | | |
| python - <<'PY' | |
| import json | |
| import os | |
| import subprocess | |
| import sys | |
| from pathlib import Path | |
| from PIL import Image | |
| root = Path(os.environ["RUNNER_TEMP"]) / "backend_parity_matrix_smoke" | |
| root.mkdir(parents=True, exist_ok=True) | |
| image = root / "000001.jpg" | |
| Image.new("RGB", (8, 8), color=(0, 0, 0)).save(image) | |
| payload = [ | |
| { | |
| "image": str(image.resolve()), | |
| "detections": [ | |
| { | |
| "class_id": 0, | |
| "score": 0.9, | |
| "bbox": {"cx": 0.5, "cy": 0.5, "w": 0.2, "h": 0.2}, | |
| } | |
| ], | |
| } | |
| ] | |
| args = [] | |
| for backend in ("torch", "onnxrt", "trt", "opencv_dnn", "custom_cpp"): | |
| path = root / f"pred_{backend}.json" | |
| path.write_text(json.dumps(payload), encoding="utf-8") | |
| args.extend(["--backend-predictions", f"{backend}={path}"]) | |
| run_dir = Path("runs") / "backend_parity_matrix" / "ci_smoke" | |
| proc = subprocess.run( | |
| [ | |
| sys.executable, | |
| "tools/backend_parity_matrix.py", | |
| *args, | |
| "--reference-backend", | |
| "torch", | |
| "--image-size", | |
| "8,8", | |
| "--run-dir", | |
| str(run_dir), | |
| ], | |
| stdout=subprocess.PIPE, | |
| stderr=subprocess.PIPE, | |
| text=True, | |
| check=False, | |
| ) | |
| if proc.returncode != 0: | |
| raise SystemExit(f"backend parity matrix smoke failed\nSTDOUT:\n{proc.stdout}\nSTDERR:\n{proc.stderr}") | |
| report_json = run_dir / "reports" / "backend_parity_matrix.json" | |
| report_html = run_dir / "reports" / "backend_parity_matrix.html" | |
| if not report_json.is_file() or not report_html.is_file(): | |
| raise SystemExit(f"missing outputs: {report_json} {report_html}") | |
| report = json.loads(report_json.read_text(encoding="utf-8")) | |
| if not report.get("ok"): | |
| raise SystemExit("backend parity matrix smoke produced non-ok report") | |
| if len(report.get("matrix") or []) != 4: | |
| raise SystemExit("backend parity matrix smoke report has unexpected matrix size") | |
| print("backend parity matrix smoke passed") | |
| PY | |
| - name: PyInstaller smoke (build + resources list) | |
| run: | | |
| python3 tools/ci/install_with_hashes.py --requirements requirements-pyinstaller.lock | |
| pyinstaller -y -n yolozu_bin \ | |
| deploy/pyinstaller/yolozu_entrypoint.py \ | |
| --collect-data yolozu.data \ | |
| --collect-data rtdetr_pose | |
| ./dist/yolozu_bin/yolozu_bin --help | |
| ./dist/yolozu_bin/yolozu_bin resources list | head -n 5 | |
| - name: PyArmor + PyInstaller smoke (optional) | |
| if: ${{ vars.PYARMOR_SMOKE == '1' }} | |
| run: | | |
| python3 tools/ci/install_with_hashes.py --requirements requirements-pyarmor.lock | |
| rm -rf build/pyarmor_obf dist/yolozu_pyarmor yolozu_pyarmor.spec | |
| pyarmor gen -O build/pyarmor_obf deploy/pyinstaller/yolozu_entrypoint.py | |
| pyinstaller -y -n yolozu_pyarmor \ | |
| build/pyarmor_obf/yolozu_entrypoint.py \ | |
| --collect-data yolozu.data \ | |
| --collect-data rtdetr_pose | |
| ./dist/yolozu_pyarmor/yolozu_pyarmor --help |