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.
Files changed (234) hide show
  1. {roar_cli-0.2.7 → roar_cli-0.2.9}/PKG-INFO +1 -1
  2. {roar_cli-0.2.7 → roar_cli-0.2.9}/pyproject.toml +4 -1
  3. roar_cli-0.2.9/roar/bin/libroar_tracer_preload.so +0 -0
  4. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/bin/roar-proxy +0 -0
  5. roar_cli-0.2.9/roar/bin/roar-tracer +0 -0
  6. roar_cli-0.2.9/roar/bin/roar-tracer-ebpf +0 -0
  7. roar_cli-0.2.9/roar/bin/roar-tracer-preload +0 -0
  8. roar_cli-0.2.9/roar/bin/roard +0 -0
  9. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/cli/commands/_execution.py +31 -0
  10. roar_cli-0.2.9/roar/cli/commands/_ray_job_submit.py +298 -0
  11. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/cli/commands/init.py +31 -22
  12. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/cli/commands/register.py +26 -20
  13. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/cli/commands/run.py +56 -0
  14. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/cli/context.py +10 -2
  15. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/cli/decorators.py +39 -1
  16. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/config.py +0 -5
  17. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/core/interfaces/repositories.py +4 -0
  18. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/core/models/config.py +0 -1
  19. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/db/repositories/session.py +43 -2
  20. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/filters/files.py +148 -17
  21. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/glaas/auth.py +2 -2
  22. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/glaas_client.py +14 -0
  23. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/presenters/dag_data_builder.py +48 -4
  24. roar_cli-0.2.9/roar/ray/actor.py +38 -0
  25. roar_cli-0.2.9/roar/ray/collector.py +849 -0
  26. roar_cli-0.2.9/roar/ray/driver_entrypoint.py +143 -0
  27. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/ray/fragment.py +1 -0
  28. roar_cli-0.2.9/roar/ray/fragment_key.py +38 -0
  29. roar_cli-0.2.9/roar/ray/fragment_reconstituter.py +675 -0
  30. roar_cli-0.2.9/roar/ray/glaas_fragment_streamer.py +135 -0
  31. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/ray/node_agent.py +47 -7
  32. roar_cli-0.2.9/roar/ray/proxy_fragments.py +99 -0
  33. roar_cli-0.2.9/roar/ray/roar_worker.py +1687 -0
  34. roar_cli-0.2.9/roar/ray/s3_key_paths.py +57 -0
  35. roar_cli-0.2.9/roar/services/execution/__init__.py +44 -0
  36. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/execution/coordinator.py +3 -0
  37. roar_cli-0.2.9/roar/services/execution/inject/sitecustomize.py +1473 -0
  38. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/execution/provenance/data_loader.py +4 -0
  39. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/execution/proxy.py +6 -4
  40. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/execution/tracer_backends.py +96 -1
  41. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/put/composite_builder.py +19 -1
  42. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/registration/register_service.py +335 -26
  43. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/upload/lineage_collector.py +141 -0
  44. roar_cli-0.2.7/roar/bin/libroar_tracer_preload.so +0 -0
  45. roar_cli-0.2.7/roar/bin/roar-tracer +0 -0
  46. roar_cli-0.2.7/roar/bin/roar-tracer-ebpf +0 -0
  47. roar_cli-0.2.7/roar/bin/roar-tracer-preload +0 -0
  48. roar_cli-0.2.7/roar/bin/roard +0 -0
  49. roar_cli-0.2.7/roar/ray/actor.py +0 -22
  50. roar_cli-0.2.7/roar/ray/collector.py +0 -999
  51. roar_cli-0.2.7/roar/ray/roar_worker.py +0 -607
  52. roar_cli-0.2.7/roar/ray/worker.py +0 -550
  53. roar_cli-0.2.7/roar/services/execution/__init__.py +0 -21
  54. roar_cli-0.2.7/roar/services/execution/inject/sitecustomize.py +0 -833
  55. {roar_cli-0.2.7 → roar_cli-0.2.9}/LICENSE +0 -0
  56. {roar_cli-0.2.7 → roar_cli-0.2.9}/README.md +0 -0
  57. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/__init__.py +0 -0
  58. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/__main__.py +0 -0
  59. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/analyzers/__init__.py +0 -0
  60. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/analyzers/base.py +0 -0
  61. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/analyzers/experiment_trackers.py +0 -0
  62. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/cli/__init__.py +0 -0
  63. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/cli/commands/__init__.py +0 -0
  64. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/cli/commands/auth.py +0 -0
  65. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/cli/commands/build.py +0 -0
  66. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/cli/commands/config.py +0 -0
  67. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/cli/commands/dag.py +0 -0
  68. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/cli/commands/env.py +0 -0
  69. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/cli/commands/get.py +0 -0
  70. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/cli/commands/lineage.py +0 -0
  71. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/cli/commands/log.py +0 -0
  72. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/cli/commands/pop.py +0 -0
  73. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/cli/commands/proxy.py +0 -0
  74. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/cli/commands/put.py +0 -0
  75. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/cli/commands/reproduce.py +0 -0
  76. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/cli/commands/reset.py +0 -0
  77. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/cli/commands/show.py +0 -0
  78. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/cli/commands/status.py +0 -0
  79. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/cli/commands/tracer.py +0 -0
  80. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/core/__init__.py +0 -0
  81. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/core/bootstrap.py +0 -0
  82. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/core/container.py +0 -0
  83. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/core/di.py +0 -0
  84. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/core/digests.py +0 -0
  85. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/core/dto/__init__.py +0 -0
  86. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/core/dto/registration.py +0 -0
  87. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/core/exceptions.py +0 -0
  88. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/core/interfaces/__init__.py +0 -0
  89. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/core/interfaces/cloud.py +0 -0
  90. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/core/interfaces/command.py +0 -0
  91. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/core/interfaces/config.py +0 -0
  92. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/core/interfaces/logger.py +0 -0
  93. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/core/interfaces/presenter.py +0 -0
  94. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/core/interfaces/provenance.py +0 -0
  95. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/core/interfaces/registration.py +0 -0
  96. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/core/interfaces/reproduction.py +0 -0
  97. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/core/interfaces/run.py +0 -0
  98. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/core/interfaces/services.py +0 -0
  99. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/core/interfaces/telemetry.py +0 -0
  100. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/core/interfaces/upload.py +0 -0
  101. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/core/interfaces/vcs.py +0 -0
  102. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/core/logging.py +0 -0
  103. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/core/models/__init__.py +0 -0
  104. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/core/models/artifact.py +0 -0
  105. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/core/models/base.py +0 -0
  106. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/core/models/command.py +0 -0
  107. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/core/models/dag.py +0 -0
  108. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/core/models/dataset_identifier.py +0 -0
  109. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/core/models/glaas.py +0 -0
  110. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/core/models/job.py +0 -0
  111. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/core/models/lineage.py +0 -0
  112. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/core/models/provenance.py +0 -0
  113. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/core/models/run.py +0 -0
  114. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/core/models/session.py +0 -0
  115. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/core/models/telemetry.py +0 -0
  116. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/core/models/vcs.py +0 -0
  117. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/core/registry.py +0 -0
  118. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/core/settings.py +0 -0
  119. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/core/tracer_modes.py +0 -0
  120. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/core/validation.py +0 -0
  121. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/db/__init__.py +0 -0
  122. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/db/context.py +0 -0
  123. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/db/engine.py +0 -0
  124. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/db/hashing/__init__.py +0 -0
  125. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/db/hashing/backend.py +0 -0
  126. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/db/models.py +0 -0
  127. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/db/repositories/__init__.py +0 -0
  128. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/db/repositories/artifact.py +0 -0
  129. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/db/repositories/collection.py +0 -0
  130. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/db/repositories/composite.py +0 -0
  131. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/db/repositories/hash_cache.py +0 -0
  132. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/db/repositories/job.py +0 -0
  133. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/db/schema.py +0 -0
  134. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/db/services/__init__.py +0 -0
  135. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/db/services/hashing.py +0 -0
  136. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/db/services/job_recording.py +0 -0
  137. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/db/services/lineage.py +0 -0
  138. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/db/services/session.py +0 -0
  139. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/filters/__init__.py +0 -0
  140. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/filters/omit.py +0 -0
  141. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/glaas/__init__.py +0 -0
  142. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/glaas/transport.py +0 -0
  143. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/plugins/__init__.py +0 -0
  144. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/plugins/cloud/__init__.py +0 -0
  145. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/plugins/cloud/base.py +0 -0
  146. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/plugins/telemetry/__init__.py +0 -0
  147. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/plugins/telemetry/base.py +0 -0
  148. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/plugins/telemetry/wandb.py +0 -0
  149. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/plugins/vcs/__init__.py +0 -0
  150. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/plugins/vcs/base.py +0 -0
  151. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/plugins/vcs/git.py +0 -0
  152. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/presenters/__init__.py +0 -0
  153. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/presenters/console.py +0 -0
  154. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/presenters/dag_renderer.py +0 -0
  155. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/presenters/formatting.py +0 -0
  156. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/presenters/null.py +0 -0
  157. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/presenters/run_report.py +0 -0
  158. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/presenters/show_renderer.py +0 -0
  159. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/presenters/spinner.py +0 -0
  160. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/ray/__init__.py +0 -0
  161. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/ray/_agent_names.py +0 -0
  162. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/__init__.py +0 -0
  163. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/execution/args.py +0 -0
  164. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/execution/backup.py +0 -0
  165. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/execution/dag_resolver.py +0 -0
  166. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/execution/dataset_identifier.py +0 -0
  167. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/execution/execution_service.py +0 -0
  168. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/execution/inject/__init__.py +0 -0
  169. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/execution/job_recording.py +0 -0
  170. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/execution/provenance/__init__.py +0 -0
  171. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/execution/provenance/assembler.py +0 -0
  172. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/execution/provenance/build_pip_collector.py +0 -0
  173. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/execution/provenance/build_tool_collector.py +0 -0
  174. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/execution/provenance/file_filter.py +0 -0
  175. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/execution/provenance/package_collector.py +0 -0
  176. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/execution/provenance/process_summarizer.py +0 -0
  177. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/execution/provenance/runtime_collector.py +0 -0
  178. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/execution/provenance/service.py +0 -0
  179. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/execution/signal_handler.py +0 -0
  180. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/execution/tracer.py +0 -0
  181. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/get/__init__.py +0 -0
  182. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/get/backends/__init__.py +0 -0
  183. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/get/backends/base.py +0 -0
  184. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/get/backends/gcs.py +0 -0
  185. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/get/backends/http.py +0 -0
  186. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/get/backends/noop.py +0 -0
  187. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/get/backends/s3.py +0 -0
  188. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/get/service.py +0 -0
  189. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/logging.py +0 -0
  190. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/lookup/__init__.py +0 -0
  191. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/lookup/entity_lookup.py +0 -0
  192. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/lookup/step_parser.py +0 -0
  193. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/put/__init__.py +0 -0
  194. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/put/backends/__init__.py +0 -0
  195. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/put/backends/base.py +0 -0
  196. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/put/backends/gcs.py +0 -0
  197. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/put/backends/memory.py +0 -0
  198. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/put/backends/noop.py +0 -0
  199. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/put/backends/s3.py +0 -0
  200. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/put/git.py +0 -0
  201. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/put/resolver.py +0 -0
  202. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/put/service.py +0 -0
  203. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/registration/__init__.py +0 -0
  204. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/registration/_artifact_ref.py +0 -0
  205. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/registration/_dataset_label.py +0 -0
  206. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/registration/_dataset_profile.py +0 -0
  207. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/registration/artifact.py +0 -0
  208. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/registration/coordinator.py +0 -0
  209. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/registration/job.py +0 -0
  210. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/registration/session.py +0 -0
  211. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/reproduction/__init__.py +0 -0
  212. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/reproduction/environment_setup.py +0 -0
  213. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/reproduction/installers.py +0 -0
  214. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/reproduction/pipeline_executor.py +0 -0
  215. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/reproduction/pipeline_metadata.py +0 -0
  216. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/reproduction/service.py +0 -0
  217. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/secrets/__init__.py +0 -0
  218. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/secrets/filter_service.py +0 -0
  219. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/transfer/__init__.py +0 -0
  220. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/transfer/backend_resolution.py +0 -0
  221. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/transfer/common.py +0 -0
  222. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/upload/__init__.py +0 -0
  223. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/upload/service.py +0 -0
  224. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/vcs/__init__.py +0 -0
  225. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/services/vcs/git_access.py +0 -0
  226. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/utils/__init__.py +0 -0
  227. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/utils/cloud.py +0 -0
  228. {roar_cli-0.2.7 → roar_cli-0.2.9}/roar/utils/git_url.py +0 -0
  229. {roar_cli-0.2.7 → roar_cli-0.2.9}/rust/Cargo.lock +0 -0
  230. {roar_cli-0.2.7 → roar_cli-0.2.9}/rust/Cargo.toml +0 -0
  231. {roar_cli-0.2.7 → roar_cli-0.2.9}/rust/crates/artifact-hash-core/Cargo.toml +0 -0
  232. {roar_cli-0.2.7 → roar_cli-0.2.9}/rust/crates/artifact-hash-core/src/lib.rs +0 -0
  233. {roar_cli-0.2.7 → roar_cli-0.2.9}/rust/crates/artifact-hash-py/Cargo.toml +0 -0
  234. {roar_cli-0.2.7 → roar_cli-0.2.9}/rust/crates/artifact-hash-py/src/lib.rs +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: roar-cli
3
- Version: 0.2.7
3
+ Version: 0.2.9
4
4
  Classifier: Development Status :: 3 - Alpha
5
5
  Classifier: Intended Audience :: Developers
6
6
  Classifier: Intended Audience :: Science/Research
@@ -4,7 +4,7 @@ build-backend = "maturin"
4
4
 
5
5
  [project]
6
6
  name = "roar-cli"
7
- version = "0.2.7"
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
@@ -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
- # Create .roar directory
208
- roar_dir.mkdir()
209
- click.echo(f"Created {roar_dir}")
232
+ init_project(cwd)
210
233
 
211
- # Initialize the SQLite database and schema
212
- db_path = roar_dir / "roar.db"
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
- # Create default config.toml
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] <artifact_path>
4
+ Usage: roar register [options] <target>
5
5
 
6
- Registers an artifact and its complete lineage with GLaaS.
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("artifact_path", type=click.STRING)
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
- ctx: RoarContext, artifact_path: str, dry_run: bool, yes: bool, as_blake3: bool
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 the complete lineage of an artifact to the GLaaS server,
53
- including all jobs and artifacts in the dependency chain.
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
- The ARTIFACT_PATH must be a file that has been tracked by roar run.
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 artifact lineage
78
- result = service.register_artifact_lineage(
79
- artifact_path=artifact_path,
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
- click.echo(f" Artifact: {web_url}/artifact/{result.artifact_hash}")
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: {artifact_path}")
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
- # Print reproduce command
126
- click.echo("")
127
- click.echo("To reproduce this artifact:")
128
- click.echo(f" roar reproduce {result.artifact_hash}")
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
- click.echo(f" Artifact: {web_url}/artifact/{result.artifact_hash}")
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
+ )