roar-cli 0.2.7__tar.gz → 0.2.9__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {roar_cli-0.2.7 → roar_cli-0.2.9}/PKG-INFO +1 -1
- {roar_cli-0.2.7 → roar_cli-0.2.9}/pyproject.toml +4 -1
- roar_cli-0.2.9/roar/bin/libroar_tracer_preload.so +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/bin/roar-proxy +0 -0
- roar_cli-0.2.9/roar/bin/roar-tracer +0 -0
- roar_cli-0.2.9/roar/bin/roar-tracer-ebpf +0 -0
- roar_cli-0.2.9/roar/bin/roar-tracer-preload +0 -0
- roar_cli-0.2.9/roar/bin/roard +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/cli/commands/_execution.py +31 -0
- roar_cli-0.2.9/roar/cli/commands/_ray_job_submit.py +298 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/cli/commands/init.py +31 -22
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/cli/commands/register.py +26 -20
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/cli/commands/run.py +56 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/cli/context.py +10 -2
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/cli/decorators.py +39 -1
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/config.py +0 -5
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/core/interfaces/repositories.py +4 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/core/models/config.py +0 -1
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/db/repositories/session.py +43 -2
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/filters/files.py +148 -17
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/glaas/auth.py +2 -2
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/glaas_client.py +14 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/presenters/dag_data_builder.py +48 -4
- roar_cli-0.2.9/roar/ray/actor.py +38 -0
- roar_cli-0.2.9/roar/ray/collector.py +849 -0
- roar_cli-0.2.9/roar/ray/driver_entrypoint.py +143 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/ray/fragment.py +1 -0
- roar_cli-0.2.9/roar/ray/fragment_key.py +38 -0
- roar_cli-0.2.9/roar/ray/fragment_reconstituter.py +675 -0
- roar_cli-0.2.9/roar/ray/glaas_fragment_streamer.py +135 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/ray/node_agent.py +47 -7
- roar_cli-0.2.9/roar/ray/proxy_fragments.py +99 -0
- roar_cli-0.2.9/roar/ray/roar_worker.py +1687 -0
- roar_cli-0.2.9/roar/ray/s3_key_paths.py +57 -0
- roar_cli-0.2.9/roar/services/execution/__init__.py +44 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/execution/coordinator.py +3 -0
- roar_cli-0.2.9/roar/services/execution/inject/sitecustomize.py +1473 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/execution/provenance/data_loader.py +4 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/execution/proxy.py +6 -4
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/execution/tracer_backends.py +96 -1
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/put/composite_builder.py +19 -1
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/registration/register_service.py +335 -26
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/upload/lineage_collector.py +141 -0
- roar_cli-0.2.7/roar/bin/libroar_tracer_preload.so +0 -0
- roar_cli-0.2.7/roar/bin/roar-tracer +0 -0
- roar_cli-0.2.7/roar/bin/roar-tracer-ebpf +0 -0
- roar_cli-0.2.7/roar/bin/roar-tracer-preload +0 -0
- roar_cli-0.2.7/roar/bin/roard +0 -0
- roar_cli-0.2.7/roar/ray/actor.py +0 -22
- roar_cli-0.2.7/roar/ray/collector.py +0 -999
- roar_cli-0.2.7/roar/ray/roar_worker.py +0 -607
- roar_cli-0.2.7/roar/ray/worker.py +0 -550
- roar_cli-0.2.7/roar/services/execution/__init__.py +0 -21
- roar_cli-0.2.7/roar/services/execution/inject/sitecustomize.py +0 -833
- {roar_cli-0.2.7 → roar_cli-0.2.9}/LICENSE +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/README.md +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/__init__.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/__main__.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/analyzers/__init__.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/analyzers/base.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/analyzers/experiment_trackers.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/cli/__init__.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/cli/commands/__init__.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/cli/commands/auth.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/cli/commands/build.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/cli/commands/config.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/cli/commands/dag.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/cli/commands/env.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/cli/commands/get.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/cli/commands/lineage.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/cli/commands/log.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/cli/commands/pop.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/cli/commands/proxy.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/cli/commands/put.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/cli/commands/reproduce.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/cli/commands/reset.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/cli/commands/show.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/cli/commands/status.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/cli/commands/tracer.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/core/__init__.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/core/bootstrap.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/core/container.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/core/di.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/core/digests.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/core/dto/__init__.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/core/dto/registration.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/core/exceptions.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/core/interfaces/__init__.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/core/interfaces/cloud.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/core/interfaces/command.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/core/interfaces/config.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/core/interfaces/logger.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/core/interfaces/presenter.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/core/interfaces/provenance.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/core/interfaces/registration.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/core/interfaces/reproduction.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/core/interfaces/run.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/core/interfaces/services.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/core/interfaces/telemetry.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/core/interfaces/upload.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/core/interfaces/vcs.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/core/logging.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/core/models/__init__.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/core/models/artifact.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/core/models/base.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/core/models/command.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/core/models/dag.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/core/models/dataset_identifier.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/core/models/glaas.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/core/models/job.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/core/models/lineage.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/core/models/provenance.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/core/models/run.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/core/models/session.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/core/models/telemetry.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/core/models/vcs.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/core/registry.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/core/settings.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/core/tracer_modes.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/core/validation.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/db/__init__.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/db/context.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/db/engine.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/db/hashing/__init__.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/db/hashing/backend.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/db/models.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/db/repositories/__init__.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/db/repositories/artifact.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/db/repositories/collection.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/db/repositories/composite.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/db/repositories/hash_cache.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/db/repositories/job.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/db/schema.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/db/services/__init__.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/db/services/hashing.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/db/services/job_recording.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/db/services/lineage.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/db/services/session.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/filters/__init__.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/filters/omit.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/glaas/__init__.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/glaas/transport.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/plugins/__init__.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/plugins/cloud/__init__.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/plugins/cloud/base.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/plugins/telemetry/__init__.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/plugins/telemetry/base.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/plugins/telemetry/wandb.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/plugins/vcs/__init__.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/plugins/vcs/base.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/plugins/vcs/git.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/presenters/__init__.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/presenters/console.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/presenters/dag_renderer.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/presenters/formatting.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/presenters/null.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/presenters/run_report.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/presenters/show_renderer.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/presenters/spinner.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/ray/__init__.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/ray/_agent_names.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/__init__.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/execution/args.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/execution/backup.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/execution/dag_resolver.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/execution/dataset_identifier.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/execution/execution_service.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/execution/inject/__init__.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/execution/job_recording.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/execution/provenance/__init__.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/execution/provenance/assembler.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/execution/provenance/build_pip_collector.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/execution/provenance/build_tool_collector.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/execution/provenance/file_filter.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/execution/provenance/package_collector.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/execution/provenance/process_summarizer.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/execution/provenance/runtime_collector.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/execution/provenance/service.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/execution/signal_handler.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/execution/tracer.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/get/__init__.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/get/backends/__init__.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/get/backends/base.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/get/backends/gcs.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/get/backends/http.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/get/backends/noop.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/get/backends/s3.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/get/service.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/logging.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/lookup/__init__.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/lookup/entity_lookup.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/lookup/step_parser.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/put/__init__.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/put/backends/__init__.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/put/backends/base.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/put/backends/gcs.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/put/backends/memory.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/put/backends/noop.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/put/backends/s3.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/put/git.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/put/resolver.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/put/service.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/registration/__init__.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/registration/_artifact_ref.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/registration/_dataset_label.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/registration/_dataset_profile.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/registration/artifact.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/registration/coordinator.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/registration/job.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/registration/session.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/reproduction/__init__.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/reproduction/environment_setup.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/reproduction/installers.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/reproduction/pipeline_executor.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/reproduction/pipeline_metadata.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/reproduction/service.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/secrets/__init__.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/secrets/filter_service.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/transfer/__init__.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/transfer/backend_resolution.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/transfer/common.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/upload/__init__.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/upload/service.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/vcs/__init__.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/vcs/git_access.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/utils/__init__.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/utils/cloud.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/utils/git_url.py +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/rust/Cargo.lock +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/rust/Cargo.toml +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/rust/crates/artifact-hash-core/Cargo.toml +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/rust/crates/artifact-hash-core/src/lib.rs +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/rust/crates/artifact-hash-py/Cargo.toml +0 -0
- {roar_cli-0.2.7 → roar_cli-0.2.9}/rust/crates/artifact-hash-py/src/lib.rs +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "maturin"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "roar-cli"
|
|
7
|
-
version = "0.2.
|
|
7
|
+
version = "0.2.9"
|
|
8
8
|
description = "Reproducibility and provenance tracker for ML training pipelines"
|
|
9
9
|
authors = [
|
|
10
10
|
{ name="TReqs Team", email="info@treqs.ai" }
|
|
@@ -81,6 +81,7 @@ include = [
|
|
|
81
81
|
{ path = "LICENSE", format = "wheel" },
|
|
82
82
|
{ path = "roar/bin/*", format = "sdist" },
|
|
83
83
|
{ path = "roar/bin/*", format = "wheel" },
|
|
84
|
+
{ path = "roar_inject.pth", format = "wheel" },
|
|
84
85
|
]
|
|
85
86
|
|
|
86
87
|
[tool.pytest.ini_options]
|
|
@@ -97,6 +98,8 @@ markers = [
|
|
|
97
98
|
"cloud: Tests for cloud storage operations",
|
|
98
99
|
"happy_path: Happy path tests for core functionality",
|
|
99
100
|
"ray_e2e: Ray end-to-end tests requiring a running Docker cluster",
|
|
101
|
+
"ray_contract: User-facing Ray contract tests using `roar run ray job submit ...`",
|
|
102
|
+
"ray_diagnostic: Diagnostic Ray tests that intentionally inspect internal runtime details",
|
|
100
103
|
]
|
|
101
104
|
addopts = "-v --strict-markers -n auto --dist loadfile --ignore=tests/ebpf --ignore=tests/live_glaas --ignore=tests/benchmarks --ignore=tests/integration --ignore=tests/e2e"
|
|
102
105
|
timeout = 60
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -10,6 +10,7 @@ Usage:
|
|
|
10
10
|
from ._execution import validate_git_clean, get_quiet_setting, execute_and_report
|
|
11
11
|
"""
|
|
12
12
|
|
|
13
|
+
import os
|
|
13
14
|
from pathlib import Path
|
|
14
15
|
from typing import TYPE_CHECKING
|
|
15
16
|
|
|
@@ -35,6 +36,8 @@ def validate_git_clean() -> str:
|
|
|
35
36
|
"""
|
|
36
37
|
import subprocess
|
|
37
38
|
|
|
39
|
+
cwd = os.getcwd()
|
|
40
|
+
|
|
38
41
|
# Find repo root (no bootstrap needed)
|
|
39
42
|
try:
|
|
40
43
|
repo_root = subprocess.check_output(
|
|
@@ -43,6 +46,10 @@ def validate_git_clean() -> str:
|
|
|
43
46
|
text=True,
|
|
44
47
|
).strip()
|
|
45
48
|
except (subprocess.CalledProcessError, FileNotFoundError):
|
|
49
|
+
# Ray jobs run from extracted working dirs that are not git repos.
|
|
50
|
+
# Allow execution there while preserving git checks everywhere else.
|
|
51
|
+
if "RAY_JOB_ID" in os.environ:
|
|
52
|
+
return cwd
|
|
46
53
|
raise click.ClickException(
|
|
47
54
|
"roar requires the working directory to be inside a git repository."
|
|
48
55
|
) from None
|
|
@@ -79,6 +86,10 @@ def get_quiet_setting(quiet_flag: bool | None, repo_root: str | Path) -> bool:
|
|
|
79
86
|
The CLI flag takes precedence. If not provided, checks the config
|
|
80
87
|
for `output.quiet` setting.
|
|
81
88
|
|
|
89
|
+
Fast path: reads TOML directly — avoids loading Pydantic/settings for
|
|
90
|
+
this single key. Falls back to the full settings stack only if the
|
|
91
|
+
direct read fails.
|
|
92
|
+
|
|
82
93
|
Args:
|
|
83
94
|
quiet_flag: Explicit quiet flag from command line (None if not specified)
|
|
84
95
|
repo_root: Repository root for config lookup
|
|
@@ -89,6 +100,26 @@ def get_quiet_setting(quiet_flag: bool | None, repo_root: str | Path) -> bool:
|
|
|
89
100
|
if quiet_flag is not None:
|
|
90
101
|
return quiet_flag
|
|
91
102
|
|
|
103
|
+
# Fast path: read config TOML directly without loading Pydantic.
|
|
104
|
+
# This is the hot path — called before the child process starts.
|
|
105
|
+
from pathlib import Path as _Path
|
|
106
|
+
|
|
107
|
+
try:
|
|
108
|
+
try:
|
|
109
|
+
import tomllib as _tomllib
|
|
110
|
+
except ImportError:
|
|
111
|
+
import tomli as _tomllib # type: ignore[no-redef]
|
|
112
|
+
|
|
113
|
+
config_toml = _Path(repo_root) / ".roar" / "config.toml" if repo_root else None
|
|
114
|
+
if config_toml is not None and config_toml.exists():
|
|
115
|
+
data = _tomllib.loads(config_toml.read_text())
|
|
116
|
+
return bool(data.get("output", {}).get("quiet", False))
|
|
117
|
+
# No config file found — use default.
|
|
118
|
+
return False
|
|
119
|
+
except Exception:
|
|
120
|
+
pass
|
|
121
|
+
|
|
122
|
+
# Fallback: full settings stack (covers edge cases like custom config paths).
|
|
92
123
|
from ...config import load_config
|
|
93
124
|
|
|
94
125
|
config = load_config(start_dir=str(repo_root) if repo_root else None)
|
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
"""Rewrites for `ray job(s) submit` launched through `roar run`."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
import os
|
|
7
|
+
from dataclasses import dataclass
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
from ...glaas_client import GlaasClient
|
|
11
|
+
from ...ray.fragment_key import generate_fragment_key, save_key
|
|
12
|
+
|
|
13
|
+
_ROAR_WORKER_PY_EXECUTABLE = "roar-worker"
|
|
14
|
+
_ROAR_WORKER_SETUP_HOOK = "roar.ray.roar_worker._startup"
|
|
15
|
+
_ROAR_DRIVER_ENTRYPOINT_MODULE = "roar.ray.driver_entrypoint"
|
|
16
|
+
_ROAR_JOB_INSTRUMENTED_ENV_VAR = "ROAR_JOB_INSTRUMENTED"
|
|
17
|
+
_ROAR_CLUSTER_GLAAS_URL_ENV = "ROAR_CLUSTER_GLAAS_URL"
|
|
18
|
+
_ROAR_CLUSTER_AWS_ENDPOINT_URL_ENV = "ROAR_CLUSTER_AWS_ENDPOINT_URL"
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@dataclass(frozen=True)
|
|
22
|
+
class RayJobSubmitRewrite:
|
|
23
|
+
command: list[str]
|
|
24
|
+
session_id: str | None = None
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def maybe_rewrite_ray_job_submit(command: list[str]) -> RayJobSubmitRewrite:
|
|
28
|
+
"""Rewrite ray jobs submit commands for roar instrumentation."""
|
|
29
|
+
if not _is_ray_job_submit(command):
|
|
30
|
+
return RayJobSubmitRewrite(command=command)
|
|
31
|
+
|
|
32
|
+
if "--" not in command:
|
|
33
|
+
return RayJobSubmitRewrite(command=command)
|
|
34
|
+
|
|
35
|
+
separator_index = command.index("--")
|
|
36
|
+
before_separator = list(command[:separator_index])
|
|
37
|
+
entrypoint = list(command[separator_index + 1 :])
|
|
38
|
+
if not entrypoint:
|
|
39
|
+
return RayJobSubmitRewrite(command=command)
|
|
40
|
+
entrypoint = _wrap_entrypoint_for_driver_proxy(entrypoint)
|
|
41
|
+
|
|
42
|
+
runtime_env_json_arg = _find_runtime_env_json(before_separator)
|
|
43
|
+
runtime_env = _load_runtime_env(before_separator, runtime_env_json_arg)
|
|
44
|
+
if runtime_env is None:
|
|
45
|
+
# Invalid user-provided JSON - leave command untouched.
|
|
46
|
+
return RayJobSubmitRewrite(command=command)
|
|
47
|
+
|
|
48
|
+
merged_pip = _merge_roar_runtime_env_pip(runtime_env.get("pip"))
|
|
49
|
+
if merged_pip:
|
|
50
|
+
runtime_env["pip"] = merged_pip
|
|
51
|
+
else:
|
|
52
|
+
runtime_env.pop("pip", None)
|
|
53
|
+
|
|
54
|
+
# py_executable is intentionally NOT set at job level — it would apply to the
|
|
55
|
+
# JobSupervisor/driver process which doesn't have roar installed yet (pip runs after
|
|
56
|
+
# the supervisor starts). worker_process_setup_hook is sufficient: it runs inside
|
|
57
|
+
# each worker process after the runtime env (and pip) is ready.
|
|
58
|
+
runtime_env["worker_process_setup_hook"] = _ROAR_WORKER_SETUP_HOOK
|
|
59
|
+
|
|
60
|
+
env_vars = dict(runtime_env.get("env_vars", {}) or {})
|
|
61
|
+
env_vars[_ROAR_JOB_INSTRUMENTED_ENV_VAR] = "1"
|
|
62
|
+
env_vars["ROAR_WRAP"] = "1"
|
|
63
|
+
env_vars["ROAR_RAY_NODE_AGENTS"] = "1"
|
|
64
|
+
# Stable job_id shared by driver + workers for node agent name resolution.
|
|
65
|
+
import uuid as _uuid
|
|
66
|
+
|
|
67
|
+
env_vars["ROAR_JOB_ID"] = _uuid.uuid4().hex[:8]
|
|
68
|
+
|
|
69
|
+
# Tell the job driver where roar.db lives (CWD inside the Ray job is the
|
|
70
|
+
# extracted working_dir, not the original project directory).
|
|
71
|
+
roar_dir = os.environ.get("ROAR_PROJECT_DIR", "")
|
|
72
|
+
if not roar_dir:
|
|
73
|
+
# Use CWD — `roar run` is invoked from the project root.
|
|
74
|
+
roar_dir = os.getcwd()
|
|
75
|
+
if roar_dir and os.path.isfile(os.path.join(roar_dir, ".roar", "roar.db")):
|
|
76
|
+
env_vars["ROAR_PROJECT_DIR"] = roar_dir
|
|
77
|
+
|
|
78
|
+
# Route S3 traffic through the per-node proxy (port 19191) for I/O capture.
|
|
79
|
+
# Save the REAL upstream endpoint (not the roar-run local proxy) so the
|
|
80
|
+
# cluster-side proxy can forward to the actual S3 service.
|
|
81
|
+
original_endpoint = (
|
|
82
|
+
os.environ.get("ROAR_UPSTREAM_S3_ENDPOINT") or os.environ.get("AWS_ENDPOINT_URL") or ""
|
|
83
|
+
)
|
|
84
|
+
cluster_upstream_endpoint = _resolve_cluster_upstream_s3_endpoint(original_endpoint)
|
|
85
|
+
if cluster_upstream_endpoint:
|
|
86
|
+
env_vars["ROAR_UPSTREAM_S3_ENDPOINT"] = cluster_upstream_endpoint
|
|
87
|
+
env_vars["ROAR_PROXY_PORT"] = "19191"
|
|
88
|
+
env_vars["AWS_ENDPOINT_URL"] = "http://127.0.0.1:19191"
|
|
89
|
+
|
|
90
|
+
fragment_session_id: str | None = None
|
|
91
|
+
|
|
92
|
+
glaas_url = _resolve_glaas_url()
|
|
93
|
+
if glaas_url:
|
|
94
|
+
cluster_glaas_url = _resolve_cluster_glaas_url(glaas_url)
|
|
95
|
+
if cluster_glaas_url:
|
|
96
|
+
env_vars["GLAAS_URL"] = cluster_glaas_url
|
|
97
|
+
|
|
98
|
+
key = generate_fragment_key()
|
|
99
|
+
try:
|
|
100
|
+
_register_fragment_session(glaas_url, key["session_id"], key["token_hash"])
|
|
101
|
+
except Exception:
|
|
102
|
+
# Keep existing behavior when fragment preregistration is unavailable.
|
|
103
|
+
pass
|
|
104
|
+
else:
|
|
105
|
+
save_key(Path(os.getcwd()) / ".roar", key)
|
|
106
|
+
env_vars["ROAR_SESSION_ID"] = key["session_id"]
|
|
107
|
+
env_vars["ROAR_FRAGMENT_TOKEN"] = key["token"]
|
|
108
|
+
fragment_session_id = str(key["session_id"])
|
|
109
|
+
|
|
110
|
+
if env_vars:
|
|
111
|
+
runtime_env["env_vars"] = env_vars
|
|
112
|
+
else:
|
|
113
|
+
runtime_env.pop("env_vars", None)
|
|
114
|
+
|
|
115
|
+
before_separator = _store_runtime_env(before_separator, runtime_env, runtime_env_json_arg)
|
|
116
|
+
return RayJobSubmitRewrite(
|
|
117
|
+
command=[*before_separator, "--", *entrypoint],
|
|
118
|
+
session_id=fragment_session_id,
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def _is_ray_job_submit(command: list[str]) -> bool:
|
|
123
|
+
if len(command) < 3:
|
|
124
|
+
return False
|
|
125
|
+
|
|
126
|
+
binary = Path(command[0]).name.lower()
|
|
127
|
+
noun = command[1].lower()
|
|
128
|
+
verb = command[2].lower()
|
|
129
|
+
return binary == "ray" and noun in {"job", "jobs"} and verb == "submit"
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def _wrap_entrypoint_for_driver_proxy(entrypoint: list[str]) -> list[str]:
|
|
133
|
+
if (
|
|
134
|
+
len(entrypoint) >= 3
|
|
135
|
+
and entrypoint[1] == "-m"
|
|
136
|
+
and entrypoint[2] == _ROAR_DRIVER_ENTRYPOINT_MODULE
|
|
137
|
+
):
|
|
138
|
+
return entrypoint
|
|
139
|
+
|
|
140
|
+
return ["python", "-m", _ROAR_DRIVER_ENTRYPOINT_MODULE, "--", *entrypoint]
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def _find_runtime_env_json(command: list[str]) -> tuple[int, int | None] | None:
|
|
144
|
+
for index, arg in enumerate(command):
|
|
145
|
+
if arg == "--runtime-env-json":
|
|
146
|
+
if index + 1 < len(command):
|
|
147
|
+
return index, index + 1
|
|
148
|
+
return index, None
|
|
149
|
+
if arg.startswith("--runtime-env-json="):
|
|
150
|
+
return index, None
|
|
151
|
+
return None
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def _load_runtime_env(
|
|
155
|
+
command_before_separator: list[str], runtime_env_json_arg: tuple[int, int | None] | None
|
|
156
|
+
) -> dict | None:
|
|
157
|
+
if runtime_env_json_arg is None:
|
|
158
|
+
return {}
|
|
159
|
+
|
|
160
|
+
flag_index, value_index = runtime_env_json_arg
|
|
161
|
+
if value_index is not None:
|
|
162
|
+
payload = command_before_separator[value_index]
|
|
163
|
+
else:
|
|
164
|
+
payload = command_before_separator[flag_index].split("=", 1)[1]
|
|
165
|
+
|
|
166
|
+
try:
|
|
167
|
+
parsed = json.loads(payload)
|
|
168
|
+
except json.JSONDecodeError:
|
|
169
|
+
return None
|
|
170
|
+
|
|
171
|
+
if isinstance(parsed, dict):
|
|
172
|
+
return parsed
|
|
173
|
+
return {}
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def _store_runtime_env(
|
|
177
|
+
command_before_separator: list[str],
|
|
178
|
+
runtime_env: dict,
|
|
179
|
+
runtime_env_json_arg: tuple[int, int | None] | None,
|
|
180
|
+
) -> list[str]:
|
|
181
|
+
serialized = json.dumps(runtime_env, separators=(",", ":"))
|
|
182
|
+
command_out = list(command_before_separator)
|
|
183
|
+
|
|
184
|
+
if runtime_env_json_arg is None:
|
|
185
|
+
command_out.extend(["--runtime-env-json", serialized])
|
|
186
|
+
return command_out
|
|
187
|
+
|
|
188
|
+
flag_index, value_index = runtime_env_json_arg
|
|
189
|
+
if value_index is not None:
|
|
190
|
+
command_out[value_index] = serialized
|
|
191
|
+
else:
|
|
192
|
+
command_out[flag_index] = f"--runtime-env-json={serialized}"
|
|
193
|
+
return command_out
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def _merge_roar_runtime_env_pip(existing_pip: object) -> list[str] | None:
|
|
197
|
+
roar_req = _resolve_roar_requirement()
|
|
198
|
+
dependencies = _coerce_runtime_env_pip(existing_pip)
|
|
199
|
+
dependencies = [
|
|
200
|
+
dependency
|
|
201
|
+
for dependency in dependencies
|
|
202
|
+
if _requirement_name(dependency) not in {"roar-cli", "roar"}
|
|
203
|
+
# Also deduplicate URL-based requirements (e.g. presigned S3 URLs).
|
|
204
|
+
# _requirement_name() returns the full URL for these, so the name-based
|
|
205
|
+
# filter above never matches them — without this check the URL would
|
|
206
|
+
# survive the filter and get appended again, producing duplicates.
|
|
207
|
+
and dependency.strip() != roar_req.strip()
|
|
208
|
+
]
|
|
209
|
+
# "skip" means roar is already installed on workers (e.g. Docker image with editable install).
|
|
210
|
+
if roar_req != "skip":
|
|
211
|
+
dependencies.append(roar_req)
|
|
212
|
+
return dependencies if dependencies else None
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def _coerce_runtime_env_pip(value: object) -> list[str]:
|
|
216
|
+
if value is None:
|
|
217
|
+
return []
|
|
218
|
+
if isinstance(value, str):
|
|
219
|
+
return [value]
|
|
220
|
+
if isinstance(value, (list, tuple, set)):
|
|
221
|
+
return [str(item) for item in value if item]
|
|
222
|
+
return []
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
def _requirement_name(requirement: str) -> str:
|
|
226
|
+
text = requirement.strip()
|
|
227
|
+
if not text:
|
|
228
|
+
return ""
|
|
229
|
+
|
|
230
|
+
for delimiter in ("@", "==", ">=", "<=", "~=", "!=", ">", "<", ";", "["):
|
|
231
|
+
position = text.find(delimiter)
|
|
232
|
+
if position > 0:
|
|
233
|
+
text = text[:position]
|
|
234
|
+
break
|
|
235
|
+
return text.strip().lower()
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
def _resolve_roar_requirement() -> str:
|
|
239
|
+
import importlib.metadata as importlib_metadata
|
|
240
|
+
|
|
241
|
+
# Allow overriding the pip requirement — useful for testing unreleased wheels via S3 URL
|
|
242
|
+
# without a PyPI publish. Set ROAR_CLUSTER_PIP_REQ=https://... in the environment.
|
|
243
|
+
override = os.environ.get("ROAR_CLUSTER_PIP_REQ", "").strip()
|
|
244
|
+
if override:
|
|
245
|
+
return override
|
|
246
|
+
|
|
247
|
+
try:
|
|
248
|
+
version = importlib_metadata.version("roar-cli")
|
|
249
|
+
return f"roar-cli=={version}"
|
|
250
|
+
except importlib_metadata.PackageNotFoundError:
|
|
251
|
+
pass
|
|
252
|
+
except Exception:
|
|
253
|
+
pass
|
|
254
|
+
|
|
255
|
+
return "roar-cli"
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
def _resolve_glaas_url() -> str | None:
|
|
259
|
+
from ...glaas_client import get_glaas_url
|
|
260
|
+
|
|
261
|
+
url = get_glaas_url()
|
|
262
|
+
if not url:
|
|
263
|
+
return "https://api.glaas.ai"
|
|
264
|
+
return str(url)
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
def _resolve_cluster_glaas_url(host_glaas_url: str | None) -> str | None:
|
|
268
|
+
override = os.environ.get(_ROAR_CLUSTER_GLAAS_URL_ENV, "").strip()
|
|
269
|
+
if override:
|
|
270
|
+
return override
|
|
271
|
+
if not host_glaas_url:
|
|
272
|
+
return None
|
|
273
|
+
return str(host_glaas_url)
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
def _resolve_cluster_upstream_s3_endpoint(host_endpoint: str | None) -> str | None:
|
|
277
|
+
override = os.environ.get(_ROAR_CLUSTER_AWS_ENDPOINT_URL_ENV, "").strip()
|
|
278
|
+
if override:
|
|
279
|
+
return override
|
|
280
|
+
if not host_endpoint:
|
|
281
|
+
return None
|
|
282
|
+
return str(host_endpoint)
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
def _register_fragment_session(
|
|
286
|
+
glaas_url: str,
|
|
287
|
+
session_id: str,
|
|
288
|
+
token_hash: str,
|
|
289
|
+
ttl: int = 86400,
|
|
290
|
+
) -> None:
|
|
291
|
+
client = GlaasClient(base_url=glaas_url)
|
|
292
|
+
_result, error = client.register_fragment_session(
|
|
293
|
+
session_id=session_id,
|
|
294
|
+
token_hash=token_hash,
|
|
295
|
+
ttl_seconds=ttl,
|
|
296
|
+
)
|
|
297
|
+
if error:
|
|
298
|
+
raise RuntimeError(f"failed to pre-register fragment session {session_id}: {error}")
|
|
@@ -119,8 +119,6 @@ fallback_enabled = true
|
|
|
119
119
|
enabled = true
|
|
120
120
|
# Inject roar-cli into runtime_env.pip for remote workers
|
|
121
121
|
pip_install = true
|
|
122
|
-
# Shared log directory for Ray worker I/O capture
|
|
123
|
-
log_dir = "/shared/.roar-logs"
|
|
124
122
|
# Actor attribution mode for Ray actor methods (per_call | per_actor)
|
|
125
123
|
actor_attribution = "per_call"
|
|
126
124
|
|
|
@@ -156,6 +154,33 @@ def _add_to_gitignore(gitignore_path: Path, gitignore_content: str) -> None:
|
|
|
156
154
|
f.write(".roar/\n")
|
|
157
155
|
|
|
158
156
|
|
|
157
|
+
def init_project(cwd: Path) -> Path:
|
|
158
|
+
"""Create the minimal local roar project structure in ``cwd``."""
|
|
159
|
+
roar_dir = cwd / ".roar"
|
|
160
|
+
if roar_dir.exists():
|
|
161
|
+
return roar_dir
|
|
162
|
+
|
|
163
|
+
roar_dir.mkdir()
|
|
164
|
+
|
|
165
|
+
db_path = roar_dir / "roar.db"
|
|
166
|
+
engine = create_roar_engine(db_path)
|
|
167
|
+
init_database(engine)
|
|
168
|
+
engine.dispose()
|
|
169
|
+
|
|
170
|
+
raw_conn = _sqlite3.connect(str(db_path))
|
|
171
|
+
raw_conn.row_factory = _sqlite3.Row
|
|
172
|
+
try:
|
|
173
|
+
run_migrations(raw_conn)
|
|
174
|
+
raw_conn.commit()
|
|
175
|
+
finally:
|
|
176
|
+
raw_conn.close()
|
|
177
|
+
|
|
178
|
+
config_path = roar_dir / "config.toml"
|
|
179
|
+
config_path.write_text(DEFAULT_CONFIG_TEMPLATE)
|
|
180
|
+
|
|
181
|
+
return roar_dir
|
|
182
|
+
|
|
183
|
+
|
|
159
184
|
@click.command("init")
|
|
160
185
|
@click.option(
|
|
161
186
|
"--yes",
|
|
@@ -204,23 +229,10 @@ def init(ctx: RoarContext, yes: bool, no: bool, init_path: Path | None) -> None:
|
|
|
204
229
|
click.echo(f".roar directory already exists at {roar_dir}")
|
|
205
230
|
return
|
|
206
231
|
|
|
207
|
-
|
|
208
|
-
roar_dir.mkdir()
|
|
209
|
-
click.echo(f"Created {roar_dir}")
|
|
232
|
+
init_project(cwd)
|
|
210
233
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
engine = create_roar_engine(db_path)
|
|
214
|
-
init_database(engine)
|
|
215
|
-
engine.dispose()
|
|
216
|
-
raw_conn = _sqlite3.connect(str(db_path))
|
|
217
|
-
raw_conn.row_factory = _sqlite3.Row
|
|
218
|
-
try:
|
|
219
|
-
run_migrations(raw_conn)
|
|
220
|
-
raw_conn.commit()
|
|
221
|
-
finally:
|
|
222
|
-
raw_conn.close()
|
|
223
|
-
click.echo(f"Initialized database at {db_path}")
|
|
234
|
+
click.echo(f"Created {roar_dir}")
|
|
235
|
+
click.echo(f"Initialized database at {roar_dir / 'roar.db'}")
|
|
224
236
|
|
|
225
237
|
# Add privacy/data collection notice
|
|
226
238
|
click.echo("")
|
|
@@ -228,10 +240,7 @@ def init(ctx: RoarContext, yes: bool, no: bool, init_path: Path | None) -> None:
|
|
|
228
240
|
click.echo("It does not upload file contents to GLaaS.")
|
|
229
241
|
click.echo("")
|
|
230
242
|
|
|
231
|
-
|
|
232
|
-
config_path = roar_dir / "config.toml"
|
|
233
|
-
config_path.write_text(DEFAULT_CONFIG_TEMPLATE)
|
|
234
|
-
click.echo(f"Created {config_path}")
|
|
243
|
+
click.echo(f"Created {roar_dir / 'config.toml'}")
|
|
235
244
|
|
|
236
245
|
# Check if we're in a git repo
|
|
237
246
|
if ctx.repo_root is None:
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
"""
|
|
2
2
|
Native Click implementation of the register command.
|
|
3
3
|
|
|
4
|
-
Usage: roar register [options] <
|
|
4
|
+
Usage: roar register [options] <target>
|
|
5
5
|
|
|
6
|
-
Registers
|
|
6
|
+
Registers artifact, step, or session lineage with GLaaS.
|
|
7
7
|
"""
|
|
8
8
|
|
|
9
9
|
import click
|
|
@@ -25,7 +25,7 @@ def _confirm_secrets(detected_secrets: list[str]) -> bool:
|
|
|
25
25
|
|
|
26
26
|
|
|
27
27
|
@click.command("register")
|
|
28
|
-
@click.argument("
|
|
28
|
+
@click.argument("target", type=click.STRING)
|
|
29
29
|
@click.option(
|
|
30
30
|
"--dry-run",
|
|
31
31
|
is_flag=True,
|
|
@@ -44,15 +44,15 @@ def _confirm_secrets(detected_secrets: list[str]) -> bool:
|
|
|
44
44
|
)
|
|
45
45
|
@click.pass_obj
|
|
46
46
|
@require_init
|
|
47
|
-
def register(
|
|
48
|
-
|
|
49
|
-
) -> None:
|
|
50
|
-
"""Register artifact lineage with GLaaS.
|
|
47
|
+
def register(ctx: RoarContext, target: str, dry_run: bool, yes: bool, as_blake3: bool) -> None:
|
|
48
|
+
"""Register lineage with GLaaS.
|
|
51
49
|
|
|
52
|
-
Submits
|
|
53
|
-
|
|
50
|
+
Submits lineage to the GLaaS server, starting from one of:
|
|
51
|
+
- an artifact path
|
|
52
|
+
- a DAG step reference like ``@4``
|
|
53
|
+
- a local session hash/prefix previously shown by roar
|
|
54
54
|
|
|
55
|
-
|
|
55
|
+
Artifact paths must refer to files tracked by roar.
|
|
56
56
|
|
|
57
57
|
If secrets are detected in the data (API keys, tokens, passwords, etc.),
|
|
58
58
|
you will be prompted to confirm. Use --yes to skip the prompt and
|
|
@@ -67,6 +67,10 @@ def register(
|
|
|
67
67
|
|
|
68
68
|
roar register -y model.pt # Skip confirmation prompt
|
|
69
69
|
|
|
70
|
+
roar register @4 # Register the lineage for DAG step 4
|
|
71
|
+
|
|
72
|
+
roar register 8d7a1f2c... # Register a whole local session
|
|
73
|
+
|
|
70
74
|
roar register --as-blake3 model.pt # Upgrade S3 etag hashes
|
|
71
75
|
|
|
72
76
|
roar register outputs/metrics.json # Register from subdirectory
|
|
@@ -74,9 +78,9 @@ def register(
|
|
|
74
78
|
# Create service
|
|
75
79
|
service = RegisterService()
|
|
76
80
|
|
|
77
|
-
# Register the
|
|
78
|
-
result = service.
|
|
79
|
-
|
|
81
|
+
# Register the requested lineage target
|
|
82
|
+
result = service.register_lineage_target(
|
|
83
|
+
target=target,
|
|
80
84
|
roar_dir=ctx.roar_dir,
|
|
81
85
|
cwd=ctx.cwd,
|
|
82
86
|
dry_run=dry_run,
|
|
@@ -105,9 +109,10 @@ def register(
|
|
|
105
109
|
click.echo("")
|
|
106
110
|
click.echo("View on GLaaS:")
|
|
107
111
|
click.echo(f" Session: {web_url}/dag/{result.session_hash}")
|
|
108
|
-
|
|
112
|
+
if result.artifact_hash:
|
|
113
|
+
click.echo(f" Artifact: {web_url}/artifact/{result.artifact_hash}")
|
|
109
114
|
else:
|
|
110
|
-
click.echo(f"Registered lineage for: {
|
|
115
|
+
click.echo(f"Registered lineage for: {target}")
|
|
111
116
|
click.echo(f" Session: {result.session_hash[:12]}...")
|
|
112
117
|
click.echo(f" Jobs: {result.jobs_registered}")
|
|
113
118
|
click.echo(f" Artifacts: {result.artifacts_registered}")
|
|
@@ -122,12 +127,13 @@ def register(
|
|
|
122
127
|
for error in result.error.split("; "):
|
|
123
128
|
click.echo(f" - {error}", err=True)
|
|
124
129
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
130
|
+
if result.artifact_hash:
|
|
131
|
+
click.echo("")
|
|
132
|
+
click.echo("To reproduce this artifact:")
|
|
133
|
+
click.echo(f" roar reproduce {result.artifact_hash}")
|
|
129
134
|
|
|
130
135
|
click.echo("")
|
|
131
136
|
click.echo("View on GLaaS:")
|
|
132
137
|
click.echo(f" Session: {web_url}/dag/{result.session_hash}")
|
|
133
|
-
|
|
138
|
+
if result.artifact_hash:
|
|
139
|
+
click.echo(f" Artifact: {web_url}/artifact/{result.artifact_hash}")
|
|
@@ -6,10 +6,14 @@ Usage: roar run [options] <command>
|
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
8
|
import shlex
|
|
9
|
+
from pathlib import Path
|
|
9
10
|
|
|
10
11
|
import click
|
|
11
12
|
|
|
12
13
|
from ...core.tracer_modes import TRACER_MODE_VALUES
|
|
14
|
+
from ...glaas_client import get_glaas_url
|
|
15
|
+
from ...ray.fragment_key import load_key
|
|
16
|
+
from ...ray.fragment_reconstituter import FragmentReconstituter
|
|
13
17
|
from ..context import RoarContext
|
|
14
18
|
from ..decorators import require_init
|
|
15
19
|
from ._execution import (
|
|
@@ -18,6 +22,7 @@ from ._execution import (
|
|
|
18
22
|
get_quiet_setting,
|
|
19
23
|
validate_git_clean,
|
|
20
24
|
)
|
|
25
|
+
from ._ray_job_submit import maybe_rewrite_ray_job_submit
|
|
21
26
|
|
|
22
27
|
|
|
23
28
|
@click.command(
|
|
@@ -126,6 +131,18 @@ def run(
|
|
|
126
131
|
raise click.ClickException("No command specified")
|
|
127
132
|
job_type = None
|
|
128
133
|
|
|
134
|
+
if (
|
|
135
|
+
len(command) >= 3
|
|
136
|
+
and Path(command[0]).name.lower() == "ray"
|
|
137
|
+
and command[1].lower() in {"job", "jobs"}
|
|
138
|
+
and command[2].lower() == "submit"
|
|
139
|
+
):
|
|
140
|
+
rewritten = maybe_rewrite_ray_job_submit(command)
|
|
141
|
+
command = rewritten.command
|
|
142
|
+
session_id = rewritten.session_id
|
|
143
|
+
else:
|
|
144
|
+
session_id = None
|
|
145
|
+
|
|
129
146
|
# Execute and report
|
|
130
147
|
exit_code = execute_and_report(
|
|
131
148
|
ctx=ctx,
|
|
@@ -139,6 +156,9 @@ def run(
|
|
|
139
156
|
tracer_fallback=tracer_fallback,
|
|
140
157
|
)
|
|
141
158
|
|
|
159
|
+
if session_id:
|
|
160
|
+
_maybe_reconstitute_lineage(ctx, session_id)
|
|
161
|
+
|
|
142
162
|
if exit_code != 0:
|
|
143
163
|
raise SystemExit(exit_code)
|
|
144
164
|
|
|
@@ -214,3 +234,39 @@ Hash algorithms: blake3 (default), sha256, sha512, md5
|
|
|
214
234
|
Examples:
|
|
215
235
|
roar run python train.py
|
|
216
236
|
roar run @2 --epochs=10 # Re-run step 2 with parameter override"""
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
def _maybe_reconstitute_lineage(ctx: RoarContext, session_id: str) -> None:
|
|
240
|
+
glaas_url = get_glaas_url()
|
|
241
|
+
if not glaas_url:
|
|
242
|
+
return
|
|
243
|
+
|
|
244
|
+
try:
|
|
245
|
+
key_payload = load_key(ctx.roar_dir, session_id)
|
|
246
|
+
token = key_payload.get("token")
|
|
247
|
+
if not isinstance(token, str) or not token:
|
|
248
|
+
raise ValueError("missing token in key payload")
|
|
249
|
+
except Exception as exc:
|
|
250
|
+
click.echo(
|
|
251
|
+
f"[roar] warning: failed to load fragment key for session {session_id}: {exc}",
|
|
252
|
+
err=True,
|
|
253
|
+
)
|
|
254
|
+
return
|
|
255
|
+
|
|
256
|
+
try:
|
|
257
|
+
result = FragmentReconstituter(
|
|
258
|
+
session_id=session_id,
|
|
259
|
+
token=token,
|
|
260
|
+
glaas_url=str(glaas_url),
|
|
261
|
+
roar_db_path=ctx.roar_dir / "roar.db",
|
|
262
|
+
).reconstitute()
|
|
263
|
+
except Exception as exc:
|
|
264
|
+
click.echo(
|
|
265
|
+
f"[roar] warning: lineage reconstitution failed for session {session_id}: {exc}",
|
|
266
|
+
err=True,
|
|
267
|
+
)
|
|
268
|
+
return
|
|
269
|
+
|
|
270
|
+
click.echo(
|
|
271
|
+
f"[roar] lineage reconstituted: {result.jobs_merged} jobs, {result.artifacts_merged} artifacts"
|
|
272
|
+
)
|