errand-run 0.1.1__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 (250) hide show
  1. errand_run-0.1.1/.gitignore +22 -0
  2. errand_run-0.1.1/LICENSE +21 -0
  3. errand_run-0.1.1/Makefile +136 -0
  4. errand_run-0.1.1/PKG-INFO +166 -0
  5. errand_run-0.1.1/README.md +143 -0
  6. errand_run-0.1.1/docs/README.md +43 -0
  7. errand_run-0.1.1/docs/node_modules/@algolia/abtesting/LICENSE +21 -0
  8. errand_run-0.1.1/docs/node_modules/@algolia/abtesting/README.md +76 -0
  9. errand_run-0.1.1/docs/node_modules/@algolia/autocomplete-core/README.md +17 -0
  10. errand_run-0.1.1/docs/node_modules/@algolia/autocomplete-plugin-algolia-insights/README.md +15 -0
  11. errand_run-0.1.1/docs/node_modules/@algolia/autocomplete-preset-algolia/README.md +15 -0
  12. errand_run-0.1.1/docs/node_modules/@algolia/client-abtesting/LICENSE +21 -0
  13. errand_run-0.1.1/docs/node_modules/@algolia/client-abtesting/README.md +76 -0
  14. errand_run-0.1.1/docs/node_modules/@algolia/client-analytics/LICENSE +21 -0
  15. errand_run-0.1.1/docs/node_modules/@algolia/client-analytics/README.md +76 -0
  16. errand_run-0.1.1/docs/node_modules/@algolia/client-common/LICENSE +21 -0
  17. errand_run-0.1.1/docs/node_modules/@algolia/client-insights/LICENSE +21 -0
  18. errand_run-0.1.1/docs/node_modules/@algolia/client-insights/README.md +76 -0
  19. errand_run-0.1.1/docs/node_modules/@algolia/client-personalization/LICENSE +21 -0
  20. errand_run-0.1.1/docs/node_modules/@algolia/client-personalization/README.md +76 -0
  21. errand_run-0.1.1/docs/node_modules/@algolia/client-query-suggestions/LICENSE +21 -0
  22. errand_run-0.1.1/docs/node_modules/@algolia/client-query-suggestions/README.md +76 -0
  23. errand_run-0.1.1/docs/node_modules/@algolia/client-search/LICENSE +21 -0
  24. errand_run-0.1.1/docs/node_modules/@algolia/client-search/README.md +76 -0
  25. errand_run-0.1.1/docs/node_modules/@algolia/ingestion/LICENSE +21 -0
  26. errand_run-0.1.1/docs/node_modules/@algolia/ingestion/README.md +76 -0
  27. errand_run-0.1.1/docs/node_modules/@algolia/monitoring/LICENSE +21 -0
  28. errand_run-0.1.1/docs/node_modules/@algolia/monitoring/README.md +76 -0
  29. errand_run-0.1.1/docs/node_modules/@algolia/recommend/LICENSE +21 -0
  30. errand_run-0.1.1/docs/node_modules/@algolia/recommend/README.md +76 -0
  31. errand_run-0.1.1/docs/node_modules/@algolia/requester-browser-xhr/LICENSE +21 -0
  32. errand_run-0.1.1/docs/node_modules/@algolia/requester-fetch/LICENSE +21 -0
  33. errand_run-0.1.1/docs/node_modules/@algolia/requester-node-http/LICENSE +21 -0
  34. errand_run-0.1.1/docs/node_modules/@babel/helper-string-parser/LICENSE +22 -0
  35. errand_run-0.1.1/docs/node_modules/@babel/helper-string-parser/README.md +19 -0
  36. errand_run-0.1.1/docs/node_modules/@babel/helper-validator-identifier/LICENSE +22 -0
  37. errand_run-0.1.1/docs/node_modules/@babel/helper-validator-identifier/README.md +19 -0
  38. errand_run-0.1.1/docs/node_modules/@babel/parser/LICENSE +19 -0
  39. errand_run-0.1.1/docs/node_modules/@babel/parser/README.md +19 -0
  40. errand_run-0.1.1/docs/node_modules/@babel/types/LICENSE +22 -0
  41. errand_run-0.1.1/docs/node_modules/@babel/types/README.md +19 -0
  42. errand_run-0.1.1/docs/node_modules/@docsearch/css/README.md +27 -0
  43. errand_run-0.1.1/docs/node_modules/@docsearch/js/README.md +46 -0
  44. errand_run-0.1.1/docs/node_modules/@docsearch/react/README.md +43 -0
  45. errand_run-0.1.1/docs/node_modules/@esbuild/darwin-arm64/README.md +3 -0
  46. errand_run-0.1.1/docs/node_modules/@iconify/types/README.md +459 -0
  47. errand_run-0.1.1/docs/node_modules/@iconify-json/simple-icons/README.md +20 -0
  48. errand_run-0.1.1/docs/node_modules/@jridgewell/sourcemap-codec/LICENSE +19 -0
  49. errand_run-0.1.1/docs/node_modules/@jridgewell/sourcemap-codec/README.md +264 -0
  50. errand_run-0.1.1/docs/node_modules/@rollup/rollup-darwin-arm64/README.md +3 -0
  51. errand_run-0.1.1/docs/node_modules/@shikijs/core/LICENSE +22 -0
  52. errand_run-0.1.1/docs/node_modules/@shikijs/core/README.md +5 -0
  53. errand_run-0.1.1/docs/node_modules/@shikijs/engine-javascript/LICENSE +22 -0
  54. errand_run-0.1.1/docs/node_modules/@shikijs/engine-javascript/README.md +9 -0
  55. errand_run-0.1.1/docs/node_modules/@shikijs/engine-oniguruma/LICENSE +22 -0
  56. errand_run-0.1.1/docs/node_modules/@shikijs/engine-oniguruma/README.md +9 -0
  57. errand_run-0.1.1/docs/node_modules/@shikijs/langs/LICENSE +22 -0
  58. errand_run-0.1.1/docs/node_modules/@shikijs/langs/README.md +7 -0
  59. errand_run-0.1.1/docs/node_modules/@shikijs/themes/LICENSE +22 -0
  60. errand_run-0.1.1/docs/node_modules/@shikijs/themes/README.md +7 -0
  61. errand_run-0.1.1/docs/node_modules/@shikijs/transformers/LICENSE +22 -0
  62. errand_run-0.1.1/docs/node_modules/@shikijs/transformers/README.md +9 -0
  63. errand_run-0.1.1/docs/node_modules/@shikijs/types/LICENSE +22 -0
  64. errand_run-0.1.1/docs/node_modules/@shikijs/types/README.md +7 -0
  65. errand_run-0.1.1/docs/node_modules/@shikijs/vscode-textmate/README.md +9 -0
  66. errand_run-0.1.1/docs/node_modules/@types/estree/LICENSE +21 -0
  67. errand_run-0.1.1/docs/node_modules/@types/estree/README.md +15 -0
  68. errand_run-0.1.1/docs/node_modules/@types/hast/LICENSE +21 -0
  69. errand_run-0.1.1/docs/node_modules/@types/hast/README.md +15 -0
  70. errand_run-0.1.1/docs/node_modules/@types/linkify-it/LICENSE +21 -0
  71. errand_run-0.1.1/docs/node_modules/@types/linkify-it/README.md +15 -0
  72. errand_run-0.1.1/docs/node_modules/@types/markdown-it/LICENSE +21 -0
  73. errand_run-0.1.1/docs/node_modules/@types/markdown-it/README.md +15 -0
  74. errand_run-0.1.1/docs/node_modules/@types/mdast/LICENSE +21 -0
  75. errand_run-0.1.1/docs/node_modules/@types/mdast/README.md +15 -0
  76. errand_run-0.1.1/docs/node_modules/@types/mdurl/LICENSE +21 -0
  77. errand_run-0.1.1/docs/node_modules/@types/mdurl/README.md +15 -0
  78. errand_run-0.1.1/docs/node_modules/@types/unist/LICENSE +21 -0
  79. errand_run-0.1.1/docs/node_modules/@types/unist/README.md +15 -0
  80. errand_run-0.1.1/docs/node_modules/@types/web-bluetooth/LICENSE +21 -0
  81. errand_run-0.1.1/docs/node_modules/@types/web-bluetooth/README.md +15 -0
  82. errand_run-0.1.1/docs/node_modules/@ungap/structured-clone/LICENSE +15 -0
  83. errand_run-0.1.1/docs/node_modules/@ungap/structured-clone/README.md +95 -0
  84. errand_run-0.1.1/docs/node_modules/@vitejs/plugin-vue/LICENSE +21 -0
  85. errand_run-0.1.1/docs/node_modules/@vitejs/plugin-vue/README.md +278 -0
  86. errand_run-0.1.1/docs/node_modules/@vue/compiler-core/LICENSE +21 -0
  87. errand_run-0.1.1/docs/node_modules/@vue/compiler-core/README.md +1 -0
  88. errand_run-0.1.1/docs/node_modules/@vue/compiler-dom/LICENSE +21 -0
  89. errand_run-0.1.1/docs/node_modules/@vue/compiler-dom/README.md +1 -0
  90. errand_run-0.1.1/docs/node_modules/@vue/compiler-sfc/LICENSE +21 -0
  91. errand_run-0.1.1/docs/node_modules/@vue/compiler-sfc/README.md +79 -0
  92. errand_run-0.1.1/docs/node_modules/@vue/compiler-ssr/LICENSE +21 -0
  93. errand_run-0.1.1/docs/node_modules/@vue/compiler-ssr/README.md +1 -0
  94. errand_run-0.1.1/docs/node_modules/@vue/devtools-api/LICENSE +21 -0
  95. errand_run-0.1.1/docs/node_modules/@vue/devtools-api/README.md +7 -0
  96. errand_run-0.1.1/docs/node_modules/@vue/devtools-kit/LICENSE +21 -0
  97. errand_run-0.1.1/docs/node_modules/@vue/devtools-kit/README.md +3 -0
  98. errand_run-0.1.1/docs/node_modules/@vue/devtools-shared/LICENSE +21 -0
  99. errand_run-0.1.1/docs/node_modules/@vue/devtools-shared/README.md +3 -0
  100. errand_run-0.1.1/docs/node_modules/@vue/reactivity/LICENSE +21 -0
  101. errand_run-0.1.1/docs/node_modules/@vue/reactivity/README.md +19 -0
  102. errand_run-0.1.1/docs/node_modules/@vue/runtime-core/LICENSE +21 -0
  103. errand_run-0.1.1/docs/node_modules/@vue/runtime-core/README.md +28 -0
  104. errand_run-0.1.1/docs/node_modules/@vue/runtime-dom/LICENSE +21 -0
  105. errand_run-0.1.1/docs/node_modules/@vue/runtime-dom/README.md +13 -0
  106. errand_run-0.1.1/docs/node_modules/@vue/server-renderer/LICENSE +21 -0
  107. errand_run-0.1.1/docs/node_modules/@vue/server-renderer/README.md +178 -0
  108. errand_run-0.1.1/docs/node_modules/@vue/shared/LICENSE +21 -0
  109. errand_run-0.1.1/docs/node_modules/@vue/shared/README.md +3 -0
  110. errand_run-0.1.1/docs/node_modules/@vueuse/core/LICENSE +21 -0
  111. errand_run-0.1.1/docs/node_modules/@vueuse/integrations/LICENSE +21 -0
  112. errand_run-0.1.1/docs/node_modules/@vueuse/integrations/README.md +46 -0
  113. errand_run-0.1.1/docs/node_modules/@vueuse/metadata/LICENSE +21 -0
  114. errand_run-0.1.1/docs/node_modules/@vueuse/shared/LICENSE +21 -0
  115. errand_run-0.1.1/docs/node_modules/algoliasearch/LICENSE +21 -0
  116. errand_run-0.1.1/docs/node_modules/algoliasearch/README.md +82 -0
  117. errand_run-0.1.1/docs/node_modules/birpc/LICENSE +21 -0
  118. errand_run-0.1.1/docs/node_modules/birpc/README.md +163 -0
  119. errand_run-0.1.1/docs/node_modules/copy-anything/LICENSE +21 -0
  120. errand_run-0.1.1/docs/node_modules/copy-anything/README.md +217 -0
  121. errand_run-0.1.1/docs/node_modules/csstype/LICENSE +19 -0
  122. errand_run-0.1.1/docs/node_modules/csstype/README.md +291 -0
  123. errand_run-0.1.1/docs/node_modules/emoji-regex-xs/LICENSE +21 -0
  124. errand_run-0.1.1/docs/node_modules/emoji-regex-xs/README.md +69 -0
  125. errand_run-0.1.1/docs/node_modules/entities/LICENSE +11 -0
  126. errand_run-0.1.1/docs/node_modules/esbuild/README.md +3 -0
  127. errand_run-0.1.1/docs/node_modules/estree-walker/LICENSE +7 -0
  128. errand_run-0.1.1/docs/node_modules/estree-walker/README.md +48 -0
  129. errand_run-0.1.1/docs/node_modules/focus-trap/LICENSE +21 -0
  130. errand_run-0.1.1/docs/node_modules/focus-trap/README.md +633 -0
  131. errand_run-0.1.1/docs/node_modules/hookable/README.md +271 -0
  132. errand_run-0.1.1/docs/node_modules/magic-string/LICENSE +7 -0
  133. errand_run-0.1.1/docs/node_modules/magic-string/README.md +325 -0
  134. errand_run-0.1.1/docs/node_modules/mark.js/LICENSE +21 -0
  135. errand_run-0.1.1/docs/node_modules/mark.js/README.md +54 -0
  136. errand_run-0.1.1/docs/node_modules/minisearch/README.md +347 -0
  137. errand_run-0.1.1/docs/node_modules/mitt/LICENSE +21 -0
  138. errand_run-0.1.1/docs/node_modules/mitt/README.md +205 -0
  139. errand_run-0.1.1/docs/node_modules/nanoid/LICENSE +20 -0
  140. errand_run-0.1.1/docs/node_modules/nanoid/README.md +39 -0
  141. errand_run-0.1.1/docs/node_modules/oniguruma-to-es/LICENSE +21 -0
  142. errand_run-0.1.1/docs/node_modules/oniguruma-to-es/README.md +1075 -0
  143. errand_run-0.1.1/docs/node_modules/perfect-debounce/LICENSE +21 -0
  144. errand_run-0.1.1/docs/node_modules/perfect-debounce/README.md +92 -0
  145. errand_run-0.1.1/docs/node_modules/picocolors/LICENSE +15 -0
  146. errand_run-0.1.1/docs/node_modules/picocolors/README.md +21 -0
  147. errand_run-0.1.1/docs/node_modules/postcss/LICENSE +20 -0
  148. errand_run-0.1.1/docs/node_modules/postcss/README.md +27 -0
  149. errand_run-0.1.1/docs/node_modules/preact/LICENSE +21 -0
  150. errand_run-0.1.1/docs/node_modules/preact/README.md +188 -0
  151. errand_run-0.1.1/docs/node_modules/regex/LICENSE +21 -0
  152. errand_run-0.1.1/docs/node_modules/regex/README.md +972 -0
  153. errand_run-0.1.1/docs/node_modules/regex-recursion/LICENSE +21 -0
  154. errand_run-0.1.1/docs/node_modules/regex-recursion/README.md +151 -0
  155. errand_run-0.1.1/docs/node_modules/regex-utilities/LICENSE +21 -0
  156. errand_run-0.1.1/docs/node_modules/regex-utilities/README.md +78 -0
  157. errand_run-0.1.1/docs/node_modules/rfdc/LICENSE +15 -0
  158. errand_run-0.1.1/docs/node_modules/rollup/README.md +134 -0
  159. errand_run-0.1.1/docs/node_modules/search-insights/README.md +88 -0
  160. errand_run-0.1.1/docs/node_modules/shiki/LICENSE +22 -0
  161. errand_run-0.1.1/docs/node_modules/shiki/README.md +15 -0
  162. errand_run-0.1.1/docs/node_modules/source-map-js/LICENSE +28 -0
  163. errand_run-0.1.1/docs/node_modules/source-map-js/README.md +765 -0
  164. errand_run-0.1.1/docs/node_modules/speakingurl/LICENSE +12 -0
  165. errand_run-0.1.1/docs/node_modules/speakingurl/Makefile +13 -0
  166. errand_run-0.1.1/docs/node_modules/speakingurl/README.md +422 -0
  167. errand_run-0.1.1/docs/node_modules/speakingurl/examples/browser-example.html +49 -0
  168. errand_run-0.1.1/docs/node_modules/speakingurl/examples/node-example.js +115 -0
  169. errand_run-0.1.1/docs/node_modules/superjson/LICENSE +21 -0
  170. errand_run-0.1.1/docs/node_modules/superjson/README.md +355 -0
  171. errand_run-0.1.1/docs/node_modules/tabbable/LICENSE +22 -0
  172. errand_run-0.1.1/docs/node_modules/tabbable/README.md +293 -0
  173. errand_run-0.1.1/docs/node_modules/vite/README.md +20 -0
  174. errand_run-0.1.1/docs/node_modules/vitepress/LICENSE +21 -0
  175. errand_run-0.1.1/docs/node_modules/vitepress/README.md +28 -0
  176. errand_run-0.1.1/docs/node_modules/vue/LICENSE +21 -0
  177. errand_run-0.1.1/docs/node_modules/vue/README.md +58 -0
  178. errand_run-0.1.1/errand/__init__.py +101 -0
  179. errand_run-0.1.1/errand/__main__.py +5 -0
  180. errand_run-0.1.1/errand/_pytest_plugin.py +25 -0
  181. errand_run-0.1.1/errand/batch.py +335 -0
  182. errand_run-0.1.1/errand/cli.py +1752 -0
  183. errand_run-0.1.1/errand/config.py +358 -0
  184. errand_run-0.1.1/errand/detect.py +216 -0
  185. errand_run-0.1.1/errand/discovery.py +448 -0
  186. errand_run-0.1.1/errand/entries.py +266 -0
  187. errand_run-0.1.1/errand/expr.py +65 -0
  188. errand_run-0.1.1/errand/fuzzy.py +154 -0
  189. errand_run-0.1.1/errand/history.py +42 -0
  190. errand_run-0.1.1/errand/layers.py +839 -0
  191. errand_run-0.1.1/errand/local.py +104 -0
  192. errand_run-0.1.1/errand/queue.py +400 -0
  193. errand_run-0.1.1/errand/results.py +397 -0
  194. errand_run-0.1.1/errand/runner.py +804 -0
  195. errand_run-0.1.1/errand/session.py +494 -0
  196. errand_run-0.1.1/errand/setup.py +151 -0
  197. errand_run-0.1.1/errand/targets.py +285 -0
  198. errand_run-0.1.1/errand/tui.py +1386 -0
  199. errand_run-0.1.1/errand/vm.py +1292 -0
  200. errand_run-0.1.1/errand/yamlish.py +203 -0
  201. errand_run-0.1.1/examples/01-environments/README.md +122 -0
  202. errand_run-0.1.1/examples/01-environments/bench_solver.py +79 -0
  203. errand_run-0.1.1/examples/01-environments/containers/Dockerfile +5 -0
  204. errand_run-0.1.1/examples/01-environments/containers/cuda.def +13 -0
  205. errand_run-0.1.1/examples/01-environments/errand-envs.py +87 -0
  206. errand_run-0.1.1/examples/01-environments/errand-project.py +9 -0
  207. errand_run-0.1.1/examples/01-environments/requirements.txt +2 -0
  208. errand_run-0.1.1/examples/01-environments/src/solver.py +22 -0
  209. errand_run-0.1.1/examples/02-existing-suite/README.md +97 -0
  210. errand_run-0.1.1/examples/02-existing-suite/cpp/Makefile +18 -0
  211. errand_run-0.1.1/examples/02-existing-suite/cpp/geometry.h +4 -0
  212. errand_run-0.1.1/examples/02-existing-suite/cpp/test_geometry.cpp +19 -0
  213. errand_run-0.1.1/examples/02-existing-suite/errand-project.py +40 -0
  214. errand_run-0.1.1/examples/02-existing-suite/requirements.txt +2 -0
  215. errand_run-0.1.1/examples/02-existing-suite/rust/Cargo.lock +7 -0
  216. errand_run-0.1.1/examples/02-existing-suite/rust/Cargo.toml +8 -0
  217. errand_run-0.1.1/examples/02-existing-suite/rust/src/lib.rs +14 -0
  218. errand_run-0.1.1/examples/02-existing-suite/src/geometry.py +6 -0
  219. errand_run-0.1.1/examples/02-existing-suite/tests/test_api.py +23 -0
  220. errand_run-0.1.1/examples/03-entries/README.md +75 -0
  221. errand_run-0.1.1/examples/03-entries/primes.py +20 -0
  222. errand_run-0.1.1/examples/03-entries/test_primes.py +80 -0
  223. errand_run-0.1.1/examples/README.md +14 -0
  224. errand_run-0.1.1/pyproject.toml +56 -0
  225. errand_run-0.1.1/tests/_demo.py +64 -0
  226. errand_run-0.1.1/tests/_infra.py +165 -0
  227. errand_run-0.1.1/tests/_tty.py +210 -0
  228. errand_run-0.1.1/tests/test_args.py +122 -0
  229. errand_run-0.1.1/tests/test_bare.py +61 -0
  230. errand_run-0.1.1/tests/test_batch.py +185 -0
  231. errand_run-0.1.1/tests/test_config.py +250 -0
  232. errand_run-0.1.1/tests/test_containers.py +211 -0
  233. errand_run-0.1.1/tests/test_detect.py +70 -0
  234. errand_run-0.1.1/tests/test_entries.py +131 -0
  235. errand_run-0.1.1/tests/test_expr.py +63 -0
  236. errand_run-0.1.1/tests/test_fuzzy.py +74 -0
  237. errand_run-0.1.1/tests/test_layers.py +337 -0
  238. errand_run-0.1.1/tests/test_parallel.py +150 -0
  239. errand_run-0.1.1/tests/test_phases.py +168 -0
  240. errand_run-0.1.1/tests/test_queue.py +341 -0
  241. errand_run-0.1.1/tests/test_results.py +171 -0
  242. errand_run-0.1.1/tests/test_run.py +80 -0
  243. errand_run-0.1.1/tests/test_runners.py +531 -0
  244. errand_run-0.1.1/tests/test_session.py +174 -0
  245. errand_run-0.1.1/tests/test_slurm.py +227 -0
  246. errand_run-0.1.1/tests/test_ssh.py +137 -0
  247. errand_run-0.1.1/tests/test_targets.py +112 -0
  248. errand_run-0.1.1/tests/test_tui.py +331 -0
  249. errand_run-0.1.1/tests/test_windows.py +339 -0
  250. errand_run-0.1.1/tests/test_yamlish.py +91 -0
@@ -0,0 +1,22 @@
1
+ runs/
2
+ __pycache__/
3
+ *.pyc
4
+ *.egg-info/
5
+ dist/
6
+ errand-envs.py
7
+ .errand/
8
+ *.exe
9
+ build/
10
+
11
+
12
+ # what the examples build when you run them
13
+ examples/*/cpp/test_*
14
+ !examples/*/cpp/test_*.cpp
15
+ !examples/*/cpp/test_*.h
16
+ examples/*/rust/target/
17
+
18
+ .venv*/
19
+ dist/
20
+
21
+ # ...except where it is the example
22
+ !examples/*/errand-envs.py
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Hugo Leclerc
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,136 @@
1
+ # Building errand, and sending it out.
2
+ #
3
+ # None of this is needed to USE errand: it runs from a checkout, with no install
4
+ # at all and nothing to install. This is for the two other things -- putting it
5
+ # on the PATH of an environment, and publishing it.
6
+ #
7
+ # The release order. Every step is repeatable except the last, which is not:
8
+ #
9
+ # make test the suite ( written with errand ) must pass
10
+ # make build sdist + wheel, from an emptied dist/
11
+ # make check twine's reading of the metadata, strictly
12
+ # make testpypi upload to TestPyPI, then install it from there and run it
13
+ # make tag git tag v$(VERSION)
14
+ # make pypi THE one that cannot be undone: a version number on PyPI
15
+ # is spent forever, deleting the file does not free it
16
+ #
17
+ # So the version is bumped in ONE place -- errand/__init__.py -- and everything
18
+ # else, the metadata included, reads it from there.
19
+
20
+ PY ?= python3
21
+ VERSION := $(shell sed -n 's/^__version__ = "\(.*\)"/\1/p' errand/__init__.py)
22
+ DIST := errand-run
23
+ VENV ?= .venv
24
+
25
+ # The tools that build and upload live in a virtual environment of their own,
26
+ # built on demand. Publishing must not depend on what happens to be installed
27
+ # on the machine doing it -- and on a system python, pip will rightly refuse.
28
+ TOOLS := .venv-release
29
+ TOOLSPY := $(TOOLS)/bin/python
30
+
31
+ .PHONY: help dev venv test build check testpypi pypi tag clean site site-build site-deploy
32
+
33
+ help:
34
+ @echo "errand $(VERSION) ( distribution: $(DIST) )"
35
+ @echo
36
+ @echo " make venv a virtual environment with errand installed in it, editable"
37
+ @echo " make dev pip install -e . into the interpreter you are using now"
38
+ @echo " make test run the suite ( make test ARGS='test_queue.py::*' )"
39
+ @echo
40
+ @echo " make site the website, live reloading ( docs/, VitePress )"
41
+ @echo " make site-build the website into docs/.vitepress/dist"
42
+ @echo " make site-deploy site-build, then push it to the gh-pages branch ( https://hleclerc.github.io/errand/ )"
43
+ @echo
44
+ @echo " make build sdist + wheel into dist/"
45
+ @echo " make check twine check --strict"
46
+ @echo " make testpypi upload to TestPyPI"
47
+ @echo " make tag git tag v$(VERSION)"
48
+ @echo " make pypi upload to PyPI -- irreversible"
49
+ @echo " make clean dist/, build artefacts, __pycache__"
50
+
51
+ # ── using it ────────────────────────────────────────────────────────────────
52
+
53
+ # Editable, because the interesting install is the one where the checkout IS
54
+ # the package: `errand` on the PATH, and an edit to errand/cli.py takes effect
55
+ # on the next command without reinstalling anything.
56
+ dev:
57
+ $(PY) -m pip install -e .
58
+
59
+ venv:
60
+ $(PY) -m venv $(VENV)
61
+ $(VENV)/bin/python -m pip install --quiet --upgrade pip
62
+ $(VENV)/bin/python -m pip install -e .
63
+ @echo
64
+ @echo " . $(VENV)/bin/activate then: errand --tui"
65
+
66
+ # PYTHONPATH so this works from a bare checkout too -- the suite is the first
67
+ # thing to run, and having to install something first would be the wrong order.
68
+ test:
69
+ cd tests && PYTHONPATH=$(CURDIR) $(PY) -m errand run $(ARGS)
70
+
71
+ # ── the website ─────────────────────────────────────────────────────────────
72
+ #
73
+ # Everything web lives under docs/, including its package.json, so the project
74
+ # root stays a Python package root. `npm install` is idempotent and quick once
75
+ # the modules are there, so there is no separate step to remember.
76
+
77
+ site: docs/node_modules
78
+ cd docs && npm run dev
79
+
80
+ site-build: docs/node_modules
81
+ cd docs && npm run build
82
+
83
+ # The built site becomes the only commit of the gh-pages branch ( force-pushed: no history to keep ).
84
+ # .nojekyll, or GitHub Pages drops the _-prefixed files of VitePress. Once, on GitHub:
85
+ # Settings > Pages > Source = "Deploy from a branch", branch gh-pages, folder / ( root ).
86
+ SITE_REMOTE ?= $(shell git remote get-url origin)
87
+ SITE_BRANCH ?= gh-pages
88
+
89
+ site-deploy: site-build
90
+ cd docs/.vitepress/dist && touch .nojekyll && rm -rf .git \
91
+ && git init -q -b $(SITE_BRANCH) \
92
+ && git add -A \
93
+ && git commit -q -m "site: $$(cd ../../.. && git rev-parse --short HEAD)" \
94
+ && git push -f $(SITE_REMOTE) $(SITE_BRANCH) \
95
+ && rm -rf .git
96
+
97
+ docs/node_modules: docs/package.json
98
+ cd docs && npm install
99
+ @touch $@
100
+
101
+ # ── publishing ──────────────────────────────────────────────────────────────
102
+
103
+ $(TOOLSPY):
104
+ $(PY) -m venv $(TOOLS)
105
+ $(TOOLSPY) -m pip install --quiet --upgrade pip build twine
106
+
107
+ build: $(TOOLSPY)
108
+ rm -rf dist
109
+ $(TOOLSPY) -m build
110
+
111
+ check: build
112
+ $(TOOLSPY) -m twine check --strict dist/*
113
+
114
+ # Credentials: a PyPI API token, in ~/.pypirc or in TWINE_USERNAME=__token__ /
115
+ # TWINE_PASSWORD=pypi-… . Never in this file.
116
+ testpypi: check
117
+ $(TOOLSPY) -m twine upload --repository testpypi dist/*
118
+ @echo
119
+ @echo " check it came out whole:"
120
+ @echo " pip install -i https://test.pypi.org/simple/ $(DIST)==$(VERSION)"
121
+
122
+ pypi: check
123
+ @test -z "$$(git status --porcelain)" || { echo " the tree is not clean -- commit first"; exit 1; }
124
+ @case "$(VERSION)" in *dev*|*a*|*b*|*rc*) \
125
+ echo " $(VERSION) is a pre-release; say FORCE=1 if that is what you mean"; \
126
+ [ -n "$(FORCE)" ] || exit 1 ;; esac
127
+ $(TOOLSPY) -m twine upload dist/*
128
+ @echo " don't forget: make tag"
129
+
130
+ tag:
131
+ git tag -a v$(VERSION) -m "errand $(VERSION)"
132
+ @echo " git push origin v$(VERSION)"
133
+
134
+ clean:
135
+ rm -rf dist build *.egg-info
136
+ find . -name __pycache__ -type d -prune -exec rm -rf {} +
@@ -0,0 +1,166 @@
1
+ Metadata-Version: 2.5
2
+ Name: errand-run
3
+ Version: 0.1.1
4
+ Summary: Run work here, in a container or on another machine, and bring back what it produced
5
+ Project-URL: Homepage, https://github.com/hleclerc/errand
6
+ Project-URL: Source, https://github.com/hleclerc/errand
7
+ Project-URL: Issues, https://github.com/hleclerc/errand/issues
8
+ Author: Hugo Leclerc
9
+ License-Expression: MIT
10
+ License-File: LICENSE
11
+ Keywords: benchmark,experiment,hpc,remote,reproducibility,runner
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Environment :: Console
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: Intended Audience :: Science/Research
16
+ Classifier: Operating System :: MacOS
17
+ Classifier: Operating System :: POSIX
18
+ Classifier: Programming Language :: Python :: 3
19
+ Classifier: Topic :: Software Development :: Testing
20
+ Classifier: Topic :: System :: Distributed Computing
21
+ Requires-Python: >=3.10
22
+ Description-Content-Type: text/markdown
23
+
24
+ # errand
25
+
26
+ > **errand runs your work in any environment — a conda or uv env, a container, another machine
27
+ > over ssh, a Slurm allocation, any stack of them — building and updating it from what it
28
+ > declares, and syncing your files there and back.**
29
+
30
+ An *errand* is a trip made on someone's behalf: go there, do the thing, come back with what it
31
+ produced.
32
+
33
+ ```python
34
+ # errand-envs.py
35
+ import errand
36
+
37
+ errand.envs[ "local" ] = errand.Env( [ errand.Micromamba( "proj", python = "3.13", requirements = "requirements.txt" ) ] )
38
+ errand.envs[ "cluster" ] = errand.Env( [ errand.Ssh( host = "gpu-box", root = "~/proj" ),
39
+ errand.Slurm( gpus = 1, time = "2:00:00" ),
40
+ errand.Apptainer( image = "cuda.sif", recipe = "cuda.def" ) ] )
41
+ ```
42
+
43
+ ```bash
44
+ errand run --env local pytest # created and installed on first use
45
+ errand run --env cluster python train.py --lr 1e-3 # rsync, srun, apptainer, and back
46
+ errand run --env cluster --pull out make results # ...and out/ comes back here
47
+ errand run --env local,cluster ./bench # in both places, one after the other
48
+ errand shell --env cluster # be let in, to look around
49
+ ```
50
+
51
+ The site has everything, split by theme, with tutorials: **<https://hleclerc.github.io/errand/>**
52
+ (built from [`docs/`](docs/)).
53
+
54
+ ## Installing
55
+
56
+ ```bash
57
+ pip install errand-run
58
+ ```
59
+
60
+ The *distribution* is `errand-run` — `errand` on PyPI is somebody else's project — but what you type
61
+ and what you import is `errand`. It has **no dependencies, and is not going to get any**: its job is
62
+ to build the environment the work runs in, so it has to run before any environment exists.
63
+
64
+ ## Environments are stacks of layers
65
+
66
+ Read outside in: *that machine, an allocation on it, a container, your command.*
67
+
68
+ | layer | |
69
+ |---|---|
70
+ | `Micromamba`, `Conda`, `Uv`, `Venv` | an interpreter and its packages: `python =`, `requirements =`, `packages =`, `pip =` |
71
+ | `Nix`, `Guix`, `Module` | a flake or a manifest; `module load` the way a cluster picks a toolchain |
72
+ | `Apptainer`, `Docker`, `Podman` | a container, built from its `recipe` when it is missing or changed |
73
+ | `Ssh` | another machine: the project is rsynced there before, what was asked for comes back after |
74
+ | `Slurm`, `Oar`, `Pbs`, `Sge`, `Lsf` | an allocation, sized from what the work asks for |
75
+ | `Windows` | a Windows VM under QEMU that errand installs and boots — MSVC included |
76
+ | `Vars` | environment variables for the child |
77
+
78
+ **Kept current by themselves.** Before every run, errand compares what each layer declares — a
79
+ Python version, a `requirements.txt`, a container recipe, a pip list — with what it last built, and
80
+ brings it up to date when they have drifted. An environment that already exists the first time
81
+ errand sees it is adopted as it is, never rebuilt on somebody's first command.
82
+
83
+ ```bash
84
+ errand envs # what is declared, and in what state
85
+ errand setup --dry-run # what bringing it up to date would run
86
+ ```
87
+
88
+ **Files go there and come back.** Over `Ssh` the project is pushed with rsync before the command
89
+ runs; `--pull DIR` brings a directory back afterwards, and `also = [ "../lib" ]` pushes a sibling
90
+ checkout the work needs. Ask for an output directory with `-o` and it is made over there, handed to
91
+ the command as `$ERRAND_OUT_DIR`, and fetched back here.
92
+
93
+ **Several places at once.** A comma is several environments, and environments carry tags:
94
+
95
+ ```bash
96
+ errand run --env local,cluster ./bench
97
+ errand run -t cuda ./bench # every environment tagged cuda
98
+ errand run --fp 32,64 ./bench # an environment that does not say fp covers both
99
+ ```
100
+
101
+ ## What you get on top
102
+
103
+ - **The machine to yourself when it matters.** errand keeps a queue per host, outside the process:
104
+ `errand run -x ./bench` waits for what is running and holds everything else back; `--cpus 8`,
105
+ `--gpus 1` take a share, and a card is assigned, not merely counted. Inside a Slurm allocation
106
+ errand does not queue — the scheduler already decided.
107
+ - **Let it run.** `errand run --batch …` gives the shell back — a detached session here, a released
108
+ ssh command there, an `sbatch` on a cluster — and `errand status` / `errand watch` say how it goes.
109
+ - **A record of every run**, in `.errand/`: what ran, where, when, how it ended, what it printed.
110
+ With `-o`, a directory per run under `runs/`, whose path is computed from the work, the place and
111
+ the date, so that two machines or two days sit side by side.
112
+ - **One screen for all of it**: `errand tui`, drawn with the standard library's curses.
113
+
114
+ ## Files, suites and entries
115
+
116
+ What follows `errand run` is read from the command line alone, never guessed:
117
+
118
+ ```bash
119
+ errand run train.py --lr 1e-3 # a .py: run by the python of the place
120
+ errand run ./bench # anything else: executed as it is
121
+ errand run make check # ...or a command of the place
122
+ errand run 'tests/**::*' # x::y: the entries of a suite -- pytest, Catch2, cargo
123
+ errand run tests/test_api.py::test_ok # one of them
124
+ errand run -r bench 'tests/**::*' # through a runner of the project's own
125
+ ```
126
+
127
+ `x::y` goes to the runner that recognises the file — pytest, Catch2, cargo, or errand's own entries
128
+ — which collects one entry per test, records each, and translates its vocabulary: a
129
+ `@pytest.mark.slow` is a tag that `-e '!slow'` filters on. A project names its own runner
130
+ instances in `errand-project.py`, and says what a bare `errand run` means:
131
+
132
+ ```python
133
+ # errand-project.py
134
+ import errand
135
+
136
+ errand.runners[ "bench" ] = errand.Pytest( dirs = [ "tests/bench" ] )
137
+ errand.runners.replace( errand.Catch2( dir = "cpp", build = "cmake --build build" ) )
138
+ errand.default_run = [ "tests/**::*", "cpp/**::*" ]
139
+ ```
140
+
141
+ errand also has entries of its own, for work that has no framework — `if test( "name" ):` next to
142
+ the code, with typed parameters and numbers to follow. See
143
+ [Writing entries](https://hleclerc.github.io/errand/guide/declaring-work).
144
+
145
+ ## Configuration
146
+
147
+ None is needed: `errand run <command>` works in an empty directory. A project's `errand-*.py` files
148
+ sit at its root and are ordinary Python; `errand init` writes a first pair.
149
+
150
+ | | |
151
+ |---|---|
152
+ | `errand-project.py` | what the project does: runners, `default_run`, `errand.configure( … )`. Versioned |
153
+ | `errand-envs.py` | where it runs: the environments. Versioned when it names no machine |
154
+ | `errand-vars.py` | what only your machine knows — hosts, paths — read first, never versioned |
155
+
156
+ ## Examples
157
+
158
+ In [`examples/`](examples/): [environments over three machines](examples/01-environments/),
159
+ [an existing pytest / Catch2 / cargo project](examples/02-existing-suite/), and [errand's own
160
+ entries](examples/03-entries/).
161
+
162
+ ## Status
163
+
164
+ Everything above works and is covered by errand's own suite. Not released: the API may still move.
165
+ How errand's own entries are expressed — needs, numbers to follow, skips — is being reworked into a
166
+ protocol any program can follow ([ADR 0003](docs/adr/0003-run-launches-everything-output-on-request.md)).
@@ -0,0 +1,143 @@
1
+ # errand
2
+
3
+ > **errand runs your work in any environment — a conda or uv env, a container, another machine
4
+ > over ssh, a Slurm allocation, any stack of them — building and updating it from what it
5
+ > declares, and syncing your files there and back.**
6
+
7
+ An *errand* is a trip made on someone's behalf: go there, do the thing, come back with what it
8
+ produced.
9
+
10
+ ```python
11
+ # errand-envs.py
12
+ import errand
13
+
14
+ errand.envs[ "local" ] = errand.Env( [ errand.Micromamba( "proj", python = "3.13", requirements = "requirements.txt" ) ] )
15
+ errand.envs[ "cluster" ] = errand.Env( [ errand.Ssh( host = "gpu-box", root = "~/proj" ),
16
+ errand.Slurm( gpus = 1, time = "2:00:00" ),
17
+ errand.Apptainer( image = "cuda.sif", recipe = "cuda.def" ) ] )
18
+ ```
19
+
20
+ ```bash
21
+ errand run --env local pytest # created and installed on first use
22
+ errand run --env cluster python train.py --lr 1e-3 # rsync, srun, apptainer, and back
23
+ errand run --env cluster --pull out make results # ...and out/ comes back here
24
+ errand run --env local,cluster ./bench # in both places, one after the other
25
+ errand shell --env cluster # be let in, to look around
26
+ ```
27
+
28
+ The site has everything, split by theme, with tutorials: **<https://hleclerc.github.io/errand/>**
29
+ (built from [`docs/`](docs/)).
30
+
31
+ ## Installing
32
+
33
+ ```bash
34
+ pip install errand-run
35
+ ```
36
+
37
+ The *distribution* is `errand-run` — `errand` on PyPI is somebody else's project — but what you type
38
+ and what you import is `errand`. It has **no dependencies, and is not going to get any**: its job is
39
+ to build the environment the work runs in, so it has to run before any environment exists.
40
+
41
+ ## Environments are stacks of layers
42
+
43
+ Read outside in: *that machine, an allocation on it, a container, your command.*
44
+
45
+ | layer | |
46
+ |---|---|
47
+ | `Micromamba`, `Conda`, `Uv`, `Venv` | an interpreter and its packages: `python =`, `requirements =`, `packages =`, `pip =` |
48
+ | `Nix`, `Guix`, `Module` | a flake or a manifest; `module load` the way a cluster picks a toolchain |
49
+ | `Apptainer`, `Docker`, `Podman` | a container, built from its `recipe` when it is missing or changed |
50
+ | `Ssh` | another machine: the project is rsynced there before, what was asked for comes back after |
51
+ | `Slurm`, `Oar`, `Pbs`, `Sge`, `Lsf` | an allocation, sized from what the work asks for |
52
+ | `Windows` | a Windows VM under QEMU that errand installs and boots — MSVC included |
53
+ | `Vars` | environment variables for the child |
54
+
55
+ **Kept current by themselves.** Before every run, errand compares what each layer declares — a
56
+ Python version, a `requirements.txt`, a container recipe, a pip list — with what it last built, and
57
+ brings it up to date when they have drifted. An environment that already exists the first time
58
+ errand sees it is adopted as it is, never rebuilt on somebody's first command.
59
+
60
+ ```bash
61
+ errand envs # what is declared, and in what state
62
+ errand setup --dry-run # what bringing it up to date would run
63
+ ```
64
+
65
+ **Files go there and come back.** Over `Ssh` the project is pushed with rsync before the command
66
+ runs; `--pull DIR` brings a directory back afterwards, and `also = [ "../lib" ]` pushes a sibling
67
+ checkout the work needs. Ask for an output directory with `-o` and it is made over there, handed to
68
+ the command as `$ERRAND_OUT_DIR`, and fetched back here.
69
+
70
+ **Several places at once.** A comma is several environments, and environments carry tags:
71
+
72
+ ```bash
73
+ errand run --env local,cluster ./bench
74
+ errand run -t cuda ./bench # every environment tagged cuda
75
+ errand run --fp 32,64 ./bench # an environment that does not say fp covers both
76
+ ```
77
+
78
+ ## What you get on top
79
+
80
+ - **The machine to yourself when it matters.** errand keeps a queue per host, outside the process:
81
+ `errand run -x ./bench` waits for what is running and holds everything else back; `--cpus 8`,
82
+ `--gpus 1` take a share, and a card is assigned, not merely counted. Inside a Slurm allocation
83
+ errand does not queue — the scheduler already decided.
84
+ - **Let it run.** `errand run --batch …` gives the shell back — a detached session here, a released
85
+ ssh command there, an `sbatch` on a cluster — and `errand status` / `errand watch` say how it goes.
86
+ - **A record of every run**, in `.errand/`: what ran, where, when, how it ended, what it printed.
87
+ With `-o`, a directory per run under `runs/`, whose path is computed from the work, the place and
88
+ the date, so that two machines or two days sit side by side.
89
+ - **One screen for all of it**: `errand tui`, drawn with the standard library's curses.
90
+
91
+ ## Files, suites and entries
92
+
93
+ What follows `errand run` is read from the command line alone, never guessed:
94
+
95
+ ```bash
96
+ errand run train.py --lr 1e-3 # a .py: run by the python of the place
97
+ errand run ./bench # anything else: executed as it is
98
+ errand run make check # ...or a command of the place
99
+ errand run 'tests/**::*' # x::y: the entries of a suite -- pytest, Catch2, cargo
100
+ errand run tests/test_api.py::test_ok # one of them
101
+ errand run -r bench 'tests/**::*' # through a runner of the project's own
102
+ ```
103
+
104
+ `x::y` goes to the runner that recognises the file — pytest, Catch2, cargo, or errand's own entries
105
+ — which collects one entry per test, records each, and translates its vocabulary: a
106
+ `@pytest.mark.slow` is a tag that `-e '!slow'` filters on. A project names its own runner
107
+ instances in `errand-project.py`, and says what a bare `errand run` means:
108
+
109
+ ```python
110
+ # errand-project.py
111
+ import errand
112
+
113
+ errand.runners[ "bench" ] = errand.Pytest( dirs = [ "tests/bench" ] )
114
+ errand.runners.replace( errand.Catch2( dir = "cpp", build = "cmake --build build" ) )
115
+ errand.default_run = [ "tests/**::*", "cpp/**::*" ]
116
+ ```
117
+
118
+ errand also has entries of its own, for work that has no framework — `if test( "name" ):` next to
119
+ the code, with typed parameters and numbers to follow. See
120
+ [Writing entries](https://hleclerc.github.io/errand/guide/declaring-work).
121
+
122
+ ## Configuration
123
+
124
+ None is needed: `errand run <command>` works in an empty directory. A project's `errand-*.py` files
125
+ sit at its root and are ordinary Python; `errand init` writes a first pair.
126
+
127
+ | | |
128
+ |---|---|
129
+ | `errand-project.py` | what the project does: runners, `default_run`, `errand.configure( … )`. Versioned |
130
+ | `errand-envs.py` | where it runs: the environments. Versioned when it names no machine |
131
+ | `errand-vars.py` | what only your machine knows — hosts, paths — read first, never versioned |
132
+
133
+ ## Examples
134
+
135
+ In [`examples/`](examples/): [environments over three machines](examples/01-environments/),
136
+ [an existing pytest / Catch2 / cargo project](examples/02-existing-suite/), and [errand's own
137
+ entries](examples/03-entries/).
138
+
139
+ ## Status
140
+
141
+ Everything above works and is covered by errand's own suite. Not released: the API may still move.
142
+ How errand's own entries are expressed — needs, numbers to follow, skips — is being reworked into a
143
+ protocol any program can follow ([ADR 0003](docs/adr/0003-run-launches-everything-output-on-request.md)).
@@ -0,0 +1,43 @@
1
+ # The errand website
2
+
3
+ [VitePress](https://vitepress.dev). Everything web lives under this directory, so the project root
4
+ stays a Python package root.
5
+
6
+ ```bash
7
+ cd docs
8
+ npm install
9
+ npm run dev # http://localhost:5173/errand/
10
+ npm run build # -> .vitepress/dist
11
+ npm run preview # serve what was built
12
+ ```
13
+
14
+ From the project root, the Makefile says the same thing: `make site`, `make site-build`.
15
+
16
+ ## The layout
17
+
18
+ ```
19
+ index.md the home page: hero, features, the layers, "whatever you test with"
20
+ guide/ the documentation, by sub-theme
21
+ tutorials/ four walkthroughs, in order; the first three are examples/
22
+ reference/ the flags, the API, the layers, the providers, the files
23
+ .vitepress/config.mts nav, sidebar, base
24
+ .vitepress/theme/ the CSS that makes it errand's, and `Term.vue` + `scenes.js`: the terminal
25
+ recordings (`<Term scene="tui" />`), drawn by hand, cell by cell
26
+ ```
27
+
28
+ `base` is `/errand/`, for GitHub Pages under `github.com/hleclerc/errand`. Change it in
29
+ `config.mts` if the site moves.
30
+
31
+ ## Keeping it true
32
+
33
+ The README is the source of record for what errand does, and these pages are written from it. When
34
+ a behaviour changes, the page that describes it is part of the change — the build fails on a dead
35
+ internal link, which catches a page that was removed but not the one that went quietly out of date.
36
+
37
+ Pages that make a claim about a flag or an argument should be checked against `errand/cli.py`,
38
+ `errand/layers.py` and `errand/providers.py`, which are where the answers actually live.
39
+
40
+ ## Publishing
41
+
42
+ `.github/workflows/docs.yml` builds and deploys on every push to `main` that touches `docs/`. Pages
43
+ has to be switched to "GitHub Actions" as its source once, in the repository's settings.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2013-Present Algolia
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,76 @@
1
+ <p align="center">
2
+ <a href="https://www.algolia.com">
3
+ <img alt="Algolia for JavaScript" src="https://raw.githubusercontent.com/algolia/algoliasearch-client-common/master/banners/javascript.png" >
4
+ </a>
5
+
6
+ <h4 align="center">The perfect starting point to integrate <a href="https://algolia.com" target="_blank">Algolia</a> within your JavaScript project</h4>
7
+
8
+ <p align="center">
9
+ <a href="https://npmjs.com/package/@algolia/abtesting"><img src="https://img.shields.io/npm/v/@algolia/abtesting.svg?style=flat-square" alt="NPM version"></img></a>
10
+ <a href="http://npm-stat.com/charts.html?package=@algolia/abtesting"><img src="https://img.shields.io/npm/dm/@algolia/abtesting.svg?style=flat-square" alt="NPM downloads"></a>
11
+ <a href="https://www.jsdelivr.com/package/npm/@algolia/abtesting"><img src="https://data.jsdelivr.com/v1/package/npm/@algolia/abtesting/badge" alt="jsDelivr Downloads"></img></a>
12
+ <a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-green.svg?style=flat-square" alt="License"></a>
13
+ </p>
14
+ </p>
15
+
16
+ <p align="center">
17
+ <a href="https://www.algolia.com/doc/libraries/sdk/install#javascript" target="_blank">Documentation</a> •
18
+ <a href="https://www.algolia.com/doc/guides/building-search-ui/what-is-instantsearch/js/" target="_blank">InstantSearch</a> •
19
+ <a href="https://discourse.algolia.com" target="_blank">Community Forum</a> •
20
+ <a href="http://stackoverflow.com/questions/tagged/algolia" target="_blank">Stack Overflow</a> •
21
+ <a href="https://github.com/algolia/algoliasearch-client-javascript/issues" target="_blank">Report a bug</a> •
22
+ <a href="https://alg.li/support" target="_blank">Support</a>
23
+ </p>
24
+
25
+ ## ✨ Features
26
+
27
+ - Thin & **minimal low-level HTTP client** to interact with Algolia's API
28
+ - Works both on the **browser** and **node.js**
29
+ - **UMD and ESM compatible**, you can use it with any module loader
30
+ - Built with TypeScript
31
+
32
+ ## 💡 Getting Started
33
+
34
+ > [!TIP]
35
+ > This API client is already a dependency of [the algoliasearch client](https://www.npmjs.com/package/algoliasearch), you don't need to manually install `@algolia/abtesting` if you already have `algoliasearch` installed.
36
+
37
+ To get started, you first need to install @algolia/abtesting (or any other available API client package).
38
+ All of our clients comes with type definition, and are available for both browser and node environments.
39
+
40
+ ### With a package manager
41
+
42
+ ```bash
43
+ yarn add @algolia/abtesting@1.25.0
44
+ # or
45
+ npm install @algolia/abtesting@1.25.0
46
+ # or
47
+ pnpm add @algolia/abtesting@1.25.0
48
+ ```
49
+
50
+ ### Without a package manager
51
+
52
+ Add the following JavaScript snippet to the <head> of your website:
53
+
54
+ ```html
55
+ <script src="https://cdn.jsdelivr.net/npm/@algolia/abtesting@1.25.0/dist/builds/browser.umd.js"></script>
56
+ ```
57
+
58
+ ### Usage
59
+
60
+ You can now import the Algolia API client in your project and play with it.
61
+
62
+ ```js
63
+ import { abtestingV3Client } from '@algolia/abtesting';
64
+
65
+ const client = abtestingV3Client('YOUR_APP_ID', 'YOUR_API_KEY');
66
+ ```
67
+
68
+ For full documentation, visit the **[Algolia JavaScript API Client](https://www.algolia.com/doc/libraries/sdk/methods/abtesting-v3/)**.
69
+
70
+ ## ❓ Troubleshooting
71
+
72
+ Encountering an issue? Before reaching out to support, we recommend heading to our [FAQ](https://support.algolia.com/hc/en-us/sections/15061037630609-API-Client-FAQs) where you will find answers for the most common issues and gotchas with the client. You can also open [a GitHub issue](https://github.com/algolia/api-clients-automation/issues/new?assignees=&labels=&projects=&template=Bug_report.md)
73
+
74
+ ## 📄 License
75
+
76
+ The Algolia JavaScript API Client is an open-sourced software licensed under the [MIT license](LICENSE).
@@ -0,0 +1,17 @@
1
+ # @algolia/autocomplete-core
2
+
3
+ The [`autocomplete-core`](https://www.algolia.com/doc/ui-libraries/autocomplete/api-reference/autocomplete-core/createAutocomplete) package is the foundation of Autocomplete. It exposes primitives to build an autocomplete experience.
4
+
5
+ You likely don’t need to use this package directly unless you’re building a [renderer](https://www.algolia.com/doc/ui-libraries/autocomplete/guides/creating-a-renderer).
6
+
7
+ ## Installation
8
+
9
+ ```sh
10
+ yarn add @algolia/autocomplete-core
11
+ # or
12
+ npm install @algolia/autocomplete-core
13
+ ```
14
+
15
+ ## Documentation
16
+
17
+ See [**Documentation**](https://www.algolia.com/doc/ui-libraries/autocomplete/api-reference/autocomplete-core).
@@ -0,0 +1,15 @@
1
+ # @algolia/autocomplete-plugin-algolia-insights
2
+
3
+ The Algolia Insights plugin automatically sends click and conversion events to the [Algolia Insights API](https://www.algolia.com/doc/rest-api/insights]) whenever a user interacts with the autocomplete.
4
+
5
+ ## Installation
6
+
7
+ ```sh
8
+ yarn add @algolia/autocomplete-plugin-algolia-insights
9
+ # or
10
+ npm install @algolia/autocomplete-plugin-algolia-insights
11
+ ```
12
+
13
+ ## Documentation
14
+
15
+ See [**Documentation**](https://www.algolia.com/doc/ui-libraries/autocomplete/api-reference/autocomplete-plugin-algolia-insights).