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.
- errand_run-0.1.1/.gitignore +22 -0
- errand_run-0.1.1/LICENSE +21 -0
- errand_run-0.1.1/Makefile +136 -0
- errand_run-0.1.1/PKG-INFO +166 -0
- errand_run-0.1.1/README.md +143 -0
- errand_run-0.1.1/docs/README.md +43 -0
- errand_run-0.1.1/docs/node_modules/@algolia/abtesting/LICENSE +21 -0
- errand_run-0.1.1/docs/node_modules/@algolia/abtesting/README.md +76 -0
- errand_run-0.1.1/docs/node_modules/@algolia/autocomplete-core/README.md +17 -0
- errand_run-0.1.1/docs/node_modules/@algolia/autocomplete-plugin-algolia-insights/README.md +15 -0
- errand_run-0.1.1/docs/node_modules/@algolia/autocomplete-preset-algolia/README.md +15 -0
- errand_run-0.1.1/docs/node_modules/@algolia/client-abtesting/LICENSE +21 -0
- errand_run-0.1.1/docs/node_modules/@algolia/client-abtesting/README.md +76 -0
- errand_run-0.1.1/docs/node_modules/@algolia/client-analytics/LICENSE +21 -0
- errand_run-0.1.1/docs/node_modules/@algolia/client-analytics/README.md +76 -0
- errand_run-0.1.1/docs/node_modules/@algolia/client-common/LICENSE +21 -0
- errand_run-0.1.1/docs/node_modules/@algolia/client-insights/LICENSE +21 -0
- errand_run-0.1.1/docs/node_modules/@algolia/client-insights/README.md +76 -0
- errand_run-0.1.1/docs/node_modules/@algolia/client-personalization/LICENSE +21 -0
- errand_run-0.1.1/docs/node_modules/@algolia/client-personalization/README.md +76 -0
- errand_run-0.1.1/docs/node_modules/@algolia/client-query-suggestions/LICENSE +21 -0
- errand_run-0.1.1/docs/node_modules/@algolia/client-query-suggestions/README.md +76 -0
- errand_run-0.1.1/docs/node_modules/@algolia/client-search/LICENSE +21 -0
- errand_run-0.1.1/docs/node_modules/@algolia/client-search/README.md +76 -0
- errand_run-0.1.1/docs/node_modules/@algolia/ingestion/LICENSE +21 -0
- errand_run-0.1.1/docs/node_modules/@algolia/ingestion/README.md +76 -0
- errand_run-0.1.1/docs/node_modules/@algolia/monitoring/LICENSE +21 -0
- errand_run-0.1.1/docs/node_modules/@algolia/monitoring/README.md +76 -0
- errand_run-0.1.1/docs/node_modules/@algolia/recommend/LICENSE +21 -0
- errand_run-0.1.1/docs/node_modules/@algolia/recommend/README.md +76 -0
- errand_run-0.1.1/docs/node_modules/@algolia/requester-browser-xhr/LICENSE +21 -0
- errand_run-0.1.1/docs/node_modules/@algolia/requester-fetch/LICENSE +21 -0
- errand_run-0.1.1/docs/node_modules/@algolia/requester-node-http/LICENSE +21 -0
- errand_run-0.1.1/docs/node_modules/@babel/helper-string-parser/LICENSE +22 -0
- errand_run-0.1.1/docs/node_modules/@babel/helper-string-parser/README.md +19 -0
- errand_run-0.1.1/docs/node_modules/@babel/helper-validator-identifier/LICENSE +22 -0
- errand_run-0.1.1/docs/node_modules/@babel/helper-validator-identifier/README.md +19 -0
- errand_run-0.1.1/docs/node_modules/@babel/parser/LICENSE +19 -0
- errand_run-0.1.1/docs/node_modules/@babel/parser/README.md +19 -0
- errand_run-0.1.1/docs/node_modules/@babel/types/LICENSE +22 -0
- errand_run-0.1.1/docs/node_modules/@babel/types/README.md +19 -0
- errand_run-0.1.1/docs/node_modules/@docsearch/css/README.md +27 -0
- errand_run-0.1.1/docs/node_modules/@docsearch/js/README.md +46 -0
- errand_run-0.1.1/docs/node_modules/@docsearch/react/README.md +43 -0
- errand_run-0.1.1/docs/node_modules/@esbuild/darwin-arm64/README.md +3 -0
- errand_run-0.1.1/docs/node_modules/@iconify/types/README.md +459 -0
- errand_run-0.1.1/docs/node_modules/@iconify-json/simple-icons/README.md +20 -0
- errand_run-0.1.1/docs/node_modules/@jridgewell/sourcemap-codec/LICENSE +19 -0
- errand_run-0.1.1/docs/node_modules/@jridgewell/sourcemap-codec/README.md +264 -0
- errand_run-0.1.1/docs/node_modules/@rollup/rollup-darwin-arm64/README.md +3 -0
- errand_run-0.1.1/docs/node_modules/@shikijs/core/LICENSE +22 -0
- errand_run-0.1.1/docs/node_modules/@shikijs/core/README.md +5 -0
- errand_run-0.1.1/docs/node_modules/@shikijs/engine-javascript/LICENSE +22 -0
- errand_run-0.1.1/docs/node_modules/@shikijs/engine-javascript/README.md +9 -0
- errand_run-0.1.1/docs/node_modules/@shikijs/engine-oniguruma/LICENSE +22 -0
- errand_run-0.1.1/docs/node_modules/@shikijs/engine-oniguruma/README.md +9 -0
- errand_run-0.1.1/docs/node_modules/@shikijs/langs/LICENSE +22 -0
- errand_run-0.1.1/docs/node_modules/@shikijs/langs/README.md +7 -0
- errand_run-0.1.1/docs/node_modules/@shikijs/themes/LICENSE +22 -0
- errand_run-0.1.1/docs/node_modules/@shikijs/themes/README.md +7 -0
- errand_run-0.1.1/docs/node_modules/@shikijs/transformers/LICENSE +22 -0
- errand_run-0.1.1/docs/node_modules/@shikijs/transformers/README.md +9 -0
- errand_run-0.1.1/docs/node_modules/@shikijs/types/LICENSE +22 -0
- errand_run-0.1.1/docs/node_modules/@shikijs/types/README.md +7 -0
- errand_run-0.1.1/docs/node_modules/@shikijs/vscode-textmate/README.md +9 -0
- errand_run-0.1.1/docs/node_modules/@types/estree/LICENSE +21 -0
- errand_run-0.1.1/docs/node_modules/@types/estree/README.md +15 -0
- errand_run-0.1.1/docs/node_modules/@types/hast/LICENSE +21 -0
- errand_run-0.1.1/docs/node_modules/@types/hast/README.md +15 -0
- errand_run-0.1.1/docs/node_modules/@types/linkify-it/LICENSE +21 -0
- errand_run-0.1.1/docs/node_modules/@types/linkify-it/README.md +15 -0
- errand_run-0.1.1/docs/node_modules/@types/markdown-it/LICENSE +21 -0
- errand_run-0.1.1/docs/node_modules/@types/markdown-it/README.md +15 -0
- errand_run-0.1.1/docs/node_modules/@types/mdast/LICENSE +21 -0
- errand_run-0.1.1/docs/node_modules/@types/mdast/README.md +15 -0
- errand_run-0.1.1/docs/node_modules/@types/mdurl/LICENSE +21 -0
- errand_run-0.1.1/docs/node_modules/@types/mdurl/README.md +15 -0
- errand_run-0.1.1/docs/node_modules/@types/unist/LICENSE +21 -0
- errand_run-0.1.1/docs/node_modules/@types/unist/README.md +15 -0
- errand_run-0.1.1/docs/node_modules/@types/web-bluetooth/LICENSE +21 -0
- errand_run-0.1.1/docs/node_modules/@types/web-bluetooth/README.md +15 -0
- errand_run-0.1.1/docs/node_modules/@ungap/structured-clone/LICENSE +15 -0
- errand_run-0.1.1/docs/node_modules/@ungap/structured-clone/README.md +95 -0
- errand_run-0.1.1/docs/node_modules/@vitejs/plugin-vue/LICENSE +21 -0
- errand_run-0.1.1/docs/node_modules/@vitejs/plugin-vue/README.md +278 -0
- errand_run-0.1.1/docs/node_modules/@vue/compiler-core/LICENSE +21 -0
- errand_run-0.1.1/docs/node_modules/@vue/compiler-core/README.md +1 -0
- errand_run-0.1.1/docs/node_modules/@vue/compiler-dom/LICENSE +21 -0
- errand_run-0.1.1/docs/node_modules/@vue/compiler-dom/README.md +1 -0
- errand_run-0.1.1/docs/node_modules/@vue/compiler-sfc/LICENSE +21 -0
- errand_run-0.1.1/docs/node_modules/@vue/compiler-sfc/README.md +79 -0
- errand_run-0.1.1/docs/node_modules/@vue/compiler-ssr/LICENSE +21 -0
- errand_run-0.1.1/docs/node_modules/@vue/compiler-ssr/README.md +1 -0
- errand_run-0.1.1/docs/node_modules/@vue/devtools-api/LICENSE +21 -0
- errand_run-0.1.1/docs/node_modules/@vue/devtools-api/README.md +7 -0
- errand_run-0.1.1/docs/node_modules/@vue/devtools-kit/LICENSE +21 -0
- errand_run-0.1.1/docs/node_modules/@vue/devtools-kit/README.md +3 -0
- errand_run-0.1.1/docs/node_modules/@vue/devtools-shared/LICENSE +21 -0
- errand_run-0.1.1/docs/node_modules/@vue/devtools-shared/README.md +3 -0
- errand_run-0.1.1/docs/node_modules/@vue/reactivity/LICENSE +21 -0
- errand_run-0.1.1/docs/node_modules/@vue/reactivity/README.md +19 -0
- errand_run-0.1.1/docs/node_modules/@vue/runtime-core/LICENSE +21 -0
- errand_run-0.1.1/docs/node_modules/@vue/runtime-core/README.md +28 -0
- errand_run-0.1.1/docs/node_modules/@vue/runtime-dom/LICENSE +21 -0
- errand_run-0.1.1/docs/node_modules/@vue/runtime-dom/README.md +13 -0
- errand_run-0.1.1/docs/node_modules/@vue/server-renderer/LICENSE +21 -0
- errand_run-0.1.1/docs/node_modules/@vue/server-renderer/README.md +178 -0
- errand_run-0.1.1/docs/node_modules/@vue/shared/LICENSE +21 -0
- errand_run-0.1.1/docs/node_modules/@vue/shared/README.md +3 -0
- errand_run-0.1.1/docs/node_modules/@vueuse/core/LICENSE +21 -0
- errand_run-0.1.1/docs/node_modules/@vueuse/integrations/LICENSE +21 -0
- errand_run-0.1.1/docs/node_modules/@vueuse/integrations/README.md +46 -0
- errand_run-0.1.1/docs/node_modules/@vueuse/metadata/LICENSE +21 -0
- errand_run-0.1.1/docs/node_modules/@vueuse/shared/LICENSE +21 -0
- errand_run-0.1.1/docs/node_modules/algoliasearch/LICENSE +21 -0
- errand_run-0.1.1/docs/node_modules/algoliasearch/README.md +82 -0
- errand_run-0.1.1/docs/node_modules/birpc/LICENSE +21 -0
- errand_run-0.1.1/docs/node_modules/birpc/README.md +163 -0
- errand_run-0.1.1/docs/node_modules/copy-anything/LICENSE +21 -0
- errand_run-0.1.1/docs/node_modules/copy-anything/README.md +217 -0
- errand_run-0.1.1/docs/node_modules/csstype/LICENSE +19 -0
- errand_run-0.1.1/docs/node_modules/csstype/README.md +291 -0
- errand_run-0.1.1/docs/node_modules/emoji-regex-xs/LICENSE +21 -0
- errand_run-0.1.1/docs/node_modules/emoji-regex-xs/README.md +69 -0
- errand_run-0.1.1/docs/node_modules/entities/LICENSE +11 -0
- errand_run-0.1.1/docs/node_modules/esbuild/README.md +3 -0
- errand_run-0.1.1/docs/node_modules/estree-walker/LICENSE +7 -0
- errand_run-0.1.1/docs/node_modules/estree-walker/README.md +48 -0
- errand_run-0.1.1/docs/node_modules/focus-trap/LICENSE +21 -0
- errand_run-0.1.1/docs/node_modules/focus-trap/README.md +633 -0
- errand_run-0.1.1/docs/node_modules/hookable/README.md +271 -0
- errand_run-0.1.1/docs/node_modules/magic-string/LICENSE +7 -0
- errand_run-0.1.1/docs/node_modules/magic-string/README.md +325 -0
- errand_run-0.1.1/docs/node_modules/mark.js/LICENSE +21 -0
- errand_run-0.1.1/docs/node_modules/mark.js/README.md +54 -0
- errand_run-0.1.1/docs/node_modules/minisearch/README.md +347 -0
- errand_run-0.1.1/docs/node_modules/mitt/LICENSE +21 -0
- errand_run-0.1.1/docs/node_modules/mitt/README.md +205 -0
- errand_run-0.1.1/docs/node_modules/nanoid/LICENSE +20 -0
- errand_run-0.1.1/docs/node_modules/nanoid/README.md +39 -0
- errand_run-0.1.1/docs/node_modules/oniguruma-to-es/LICENSE +21 -0
- errand_run-0.1.1/docs/node_modules/oniguruma-to-es/README.md +1075 -0
- errand_run-0.1.1/docs/node_modules/perfect-debounce/LICENSE +21 -0
- errand_run-0.1.1/docs/node_modules/perfect-debounce/README.md +92 -0
- errand_run-0.1.1/docs/node_modules/picocolors/LICENSE +15 -0
- errand_run-0.1.1/docs/node_modules/picocolors/README.md +21 -0
- errand_run-0.1.1/docs/node_modules/postcss/LICENSE +20 -0
- errand_run-0.1.1/docs/node_modules/postcss/README.md +27 -0
- errand_run-0.1.1/docs/node_modules/preact/LICENSE +21 -0
- errand_run-0.1.1/docs/node_modules/preact/README.md +188 -0
- errand_run-0.1.1/docs/node_modules/regex/LICENSE +21 -0
- errand_run-0.1.1/docs/node_modules/regex/README.md +972 -0
- errand_run-0.1.1/docs/node_modules/regex-recursion/LICENSE +21 -0
- errand_run-0.1.1/docs/node_modules/regex-recursion/README.md +151 -0
- errand_run-0.1.1/docs/node_modules/regex-utilities/LICENSE +21 -0
- errand_run-0.1.1/docs/node_modules/regex-utilities/README.md +78 -0
- errand_run-0.1.1/docs/node_modules/rfdc/LICENSE +15 -0
- errand_run-0.1.1/docs/node_modules/rollup/README.md +134 -0
- errand_run-0.1.1/docs/node_modules/search-insights/README.md +88 -0
- errand_run-0.1.1/docs/node_modules/shiki/LICENSE +22 -0
- errand_run-0.1.1/docs/node_modules/shiki/README.md +15 -0
- errand_run-0.1.1/docs/node_modules/source-map-js/LICENSE +28 -0
- errand_run-0.1.1/docs/node_modules/source-map-js/README.md +765 -0
- errand_run-0.1.1/docs/node_modules/speakingurl/LICENSE +12 -0
- errand_run-0.1.1/docs/node_modules/speakingurl/Makefile +13 -0
- errand_run-0.1.1/docs/node_modules/speakingurl/README.md +422 -0
- errand_run-0.1.1/docs/node_modules/speakingurl/examples/browser-example.html +49 -0
- errand_run-0.1.1/docs/node_modules/speakingurl/examples/node-example.js +115 -0
- errand_run-0.1.1/docs/node_modules/superjson/LICENSE +21 -0
- errand_run-0.1.1/docs/node_modules/superjson/README.md +355 -0
- errand_run-0.1.1/docs/node_modules/tabbable/LICENSE +22 -0
- errand_run-0.1.1/docs/node_modules/tabbable/README.md +293 -0
- errand_run-0.1.1/docs/node_modules/vite/README.md +20 -0
- errand_run-0.1.1/docs/node_modules/vitepress/LICENSE +21 -0
- errand_run-0.1.1/docs/node_modules/vitepress/README.md +28 -0
- errand_run-0.1.1/docs/node_modules/vue/LICENSE +21 -0
- errand_run-0.1.1/docs/node_modules/vue/README.md +58 -0
- errand_run-0.1.1/errand/__init__.py +101 -0
- errand_run-0.1.1/errand/__main__.py +5 -0
- errand_run-0.1.1/errand/_pytest_plugin.py +25 -0
- errand_run-0.1.1/errand/batch.py +335 -0
- errand_run-0.1.1/errand/cli.py +1752 -0
- errand_run-0.1.1/errand/config.py +358 -0
- errand_run-0.1.1/errand/detect.py +216 -0
- errand_run-0.1.1/errand/discovery.py +448 -0
- errand_run-0.1.1/errand/entries.py +266 -0
- errand_run-0.1.1/errand/expr.py +65 -0
- errand_run-0.1.1/errand/fuzzy.py +154 -0
- errand_run-0.1.1/errand/history.py +42 -0
- errand_run-0.1.1/errand/layers.py +839 -0
- errand_run-0.1.1/errand/local.py +104 -0
- errand_run-0.1.1/errand/queue.py +400 -0
- errand_run-0.1.1/errand/results.py +397 -0
- errand_run-0.1.1/errand/runner.py +804 -0
- errand_run-0.1.1/errand/session.py +494 -0
- errand_run-0.1.1/errand/setup.py +151 -0
- errand_run-0.1.1/errand/targets.py +285 -0
- errand_run-0.1.1/errand/tui.py +1386 -0
- errand_run-0.1.1/errand/vm.py +1292 -0
- errand_run-0.1.1/errand/yamlish.py +203 -0
- errand_run-0.1.1/examples/01-environments/README.md +122 -0
- errand_run-0.1.1/examples/01-environments/bench_solver.py +79 -0
- errand_run-0.1.1/examples/01-environments/containers/Dockerfile +5 -0
- errand_run-0.1.1/examples/01-environments/containers/cuda.def +13 -0
- errand_run-0.1.1/examples/01-environments/errand-envs.py +87 -0
- errand_run-0.1.1/examples/01-environments/errand-project.py +9 -0
- errand_run-0.1.1/examples/01-environments/requirements.txt +2 -0
- errand_run-0.1.1/examples/01-environments/src/solver.py +22 -0
- errand_run-0.1.1/examples/02-existing-suite/README.md +97 -0
- errand_run-0.1.1/examples/02-existing-suite/cpp/Makefile +18 -0
- errand_run-0.1.1/examples/02-existing-suite/cpp/geometry.h +4 -0
- errand_run-0.1.1/examples/02-existing-suite/cpp/test_geometry.cpp +19 -0
- errand_run-0.1.1/examples/02-existing-suite/errand-project.py +40 -0
- errand_run-0.1.1/examples/02-existing-suite/requirements.txt +2 -0
- errand_run-0.1.1/examples/02-existing-suite/rust/Cargo.lock +7 -0
- errand_run-0.1.1/examples/02-existing-suite/rust/Cargo.toml +8 -0
- errand_run-0.1.1/examples/02-existing-suite/rust/src/lib.rs +14 -0
- errand_run-0.1.1/examples/02-existing-suite/src/geometry.py +6 -0
- errand_run-0.1.1/examples/02-existing-suite/tests/test_api.py +23 -0
- errand_run-0.1.1/examples/03-entries/README.md +75 -0
- errand_run-0.1.1/examples/03-entries/primes.py +20 -0
- errand_run-0.1.1/examples/03-entries/test_primes.py +80 -0
- errand_run-0.1.1/examples/README.md +14 -0
- errand_run-0.1.1/pyproject.toml +56 -0
- errand_run-0.1.1/tests/_demo.py +64 -0
- errand_run-0.1.1/tests/_infra.py +165 -0
- errand_run-0.1.1/tests/_tty.py +210 -0
- errand_run-0.1.1/tests/test_args.py +122 -0
- errand_run-0.1.1/tests/test_bare.py +61 -0
- errand_run-0.1.1/tests/test_batch.py +185 -0
- errand_run-0.1.1/tests/test_config.py +250 -0
- errand_run-0.1.1/tests/test_containers.py +211 -0
- errand_run-0.1.1/tests/test_detect.py +70 -0
- errand_run-0.1.1/tests/test_entries.py +131 -0
- errand_run-0.1.1/tests/test_expr.py +63 -0
- errand_run-0.1.1/tests/test_fuzzy.py +74 -0
- errand_run-0.1.1/tests/test_layers.py +337 -0
- errand_run-0.1.1/tests/test_parallel.py +150 -0
- errand_run-0.1.1/tests/test_phases.py +168 -0
- errand_run-0.1.1/tests/test_queue.py +341 -0
- errand_run-0.1.1/tests/test_results.py +171 -0
- errand_run-0.1.1/tests/test_run.py +80 -0
- errand_run-0.1.1/tests/test_runners.py +531 -0
- errand_run-0.1.1/tests/test_session.py +174 -0
- errand_run-0.1.1/tests/test_slurm.py +227 -0
- errand_run-0.1.1/tests/test_ssh.py +137 -0
- errand_run-0.1.1/tests/test_targets.py +112 -0
- errand_run-0.1.1/tests/test_tui.py +331 -0
- errand_run-0.1.1/tests/test_windows.py +339 -0
- 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
|
errand_run-0.1.1/LICENSE
ADDED
|
@@ -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).
|